feedback do utilizador
This commit is contained in:
59
src/App.jsx
59
src/App.jsx
@@ -165,12 +165,12 @@ export default function App() {
|
|||||||
const locName = userProfile?.location || 'Lisboa, Portugal';
|
const locName = userProfile?.location || 'Lisboa, Portugal';
|
||||||
const geoRes = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(locName)}&count=1&language=pt&format=json`);
|
const geoRes = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(locName)}&count=1&language=pt&format=json`);
|
||||||
const geoData = await geoRes.json();
|
const geoData = await geoRes.json();
|
||||||
|
|
||||||
if (geoData.results && geoData.results.length > 0) {
|
if (geoData.results && geoData.results.length > 0) {
|
||||||
const { latitude, longitude, name, country } = geoData.results[0];
|
const { latitude, longitude, name, country } = geoData.results[0];
|
||||||
const weatherRes = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true&daily=temperature_2m_max,temperature_2m_min&timezone=auto`);
|
const weatherRes = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true&daily=temperature_2m_max,temperature_2m_min&timezone=auto`);
|
||||||
const weatherRaw = await weatherRes.json();
|
const weatherRaw = await weatherRes.json();
|
||||||
|
|
||||||
if (weatherRaw.current_weather && weatherRaw.daily) {
|
if (weatherRaw.current_weather && weatherRaw.daily) {
|
||||||
setWeatherData({
|
setWeatherData({
|
||||||
name: `${name}, ${country || ''}`.replace(/,\s*$/, ''),
|
name: `${name}, ${country || ''}`.replace(/,\s*$/, ''),
|
||||||
@@ -210,7 +210,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (totalWithColor === 0) return [];
|
if (totalWithColor === 0) return [];
|
||||||
|
|
||||||
return Object.entries(colorCounts)
|
return Object.entries(colorCounts)
|
||||||
.sort((a, b) => b[1] - a[1])
|
.sort((a, b) => b[1] - a[1])
|
||||||
.slice(0, 3)
|
.slice(0, 3)
|
||||||
@@ -275,7 +275,7 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
// Guardamos o id se for edição antes de apagar o estado
|
// Guardamos o id se for edição antes de apagar o estado
|
||||||
const currentEditId = editingItem ? editingItem.id : null;
|
const currentEditId = editingItem ? editingItem.id : null;
|
||||||
|
|
||||||
// Navegação instantânea (Optimistic UI Update)
|
// Navegação instantânea (Optimistic UI Update)
|
||||||
setEditingItem(null);
|
setEditingItem(null);
|
||||||
setImageUrlDraft('');
|
setImageUrlDraft('');
|
||||||
@@ -426,7 +426,7 @@ export default function App() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSavingProfile(true);
|
setSavingProfile(true);
|
||||||
const fd = new FormData(e.target);
|
const fd = new FormData(e.target);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const profileDoc = doc(db, 'artifacts', appId, 'users', user.uid, 'profile', 'data');
|
const profileDoc = doc(db, 'artifacts', appId, 'users', user.uid, 'profile', 'data');
|
||||||
const dobDay = fd.get('dobDay');
|
const dobDay = fd.get('dobDay');
|
||||||
@@ -907,15 +907,15 @@ export default function App() {
|
|||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<select name="dobDay" defaultValue={userProfile?.dob?.split('-')[2] || ''} className={`flex-1 p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
<select name="dobDay" defaultValue={userProfile?.dob?.split('-')[2] || ''} className={`flex-1 p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
||||||
<option value="">DD</option>
|
<option value="">DD</option>
|
||||||
{Array.from({length: 31}, (_, i) => String(i+1).padStart(2,'0')).map(d => <option key={d} value={d}>{d}</option>)}
|
{Array.from({ length: 31 }, (_, i) => String(i + 1).padStart(2, '0')).map(d => <option key={d} value={d}>{d}</option>)}
|
||||||
</select>
|
</select>
|
||||||
<select name="dobMonth" defaultValue={userProfile?.dob?.split('-')[1] || ''} className={`flex-1 p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
<select name="dobMonth" defaultValue={userProfile?.dob?.split('-')[1] || ''} className={`flex-1 p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
||||||
<option value="">MM</option>
|
<option value="">MM</option>
|
||||||
{Array.from({length: 12}, (_, i) => String(i+1).padStart(2,'0')).map(m => <option key={m} value={m}>{m}</option>)}
|
{Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, '0')).map(m => <option key={m} value={m}>{m}</option>)}
|
||||||
</select>
|
</select>
|
||||||
<select name="dobYear" defaultValue={userProfile?.dob?.split('-')[0] || ''} className={`flex-[1.5] p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
<select name="dobYear" defaultValue={userProfile?.dob?.split('-')[0] || ''} className={`flex-[1.5] p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}>
|
||||||
<option value="">YYYY</option>
|
<option value="">YYYY</option>
|
||||||
{Array.from({length: 100}, (_, i) => new Date().getFullYear() - i).map(y => <option key={y} value={y}>{y}</option>)}
|
{Array.from({ length: 100 }, (_, i) => new Date().getFullYear() - i).map(y => <option key={y} value={y}>{y}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -994,11 +994,11 @@ export default function App() {
|
|||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-inherit">{t('appLanguage')}</p>
|
<p className="font-bold text-inherit">{t('appLanguage')}</p>
|
||||||
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">
|
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">
|
||||||
{language === 'PT' ? '🇵🇹 ' + t('portuguese') :
|
{language === 'PT' ? '🇵🇹 ' + t('portuguese') :
|
||||||
language === 'EN' ? '🇬🇧 ' + t('english') :
|
language === 'EN' ? '🇬🇧 ' + t('english') :
|
||||||
language === 'ES' ? '🇪🇸 ' + t('spanish') :
|
language === 'ES' ? '🇪🇸 ' + t('spanish') :
|
||||||
language === 'FR' ? '🇫🇷 ' + t('french') :
|
language === 'FR' ? '🇫🇷 ' + t('french') :
|
||||||
language === 'DE' ? '🇩🇪 ' + t('german') : language}
|
language === 'DE' ? '🇩🇪 ' + t('german') : language}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => setShowLangModal(true)} className="px-5 py-2 font-black text-[10px] uppercase tracking-widest bg-primary-50 text-primary-600 rounded-xl hover:bg-primary-100 transition-colors dark:bg-primary-900/40 dark:text-primary-400">
|
<button onClick={() => setShowLangModal(true)} className="px-5 py-2 font-black text-[10px] uppercase tracking-widest bg-primary-50 text-primary-600 rounded-xl hover:bg-primary-100 transition-colors dark:bg-primary-900/40 dark:text-primary-400">
|
||||||
@@ -1008,6 +1008,35 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card className="p-8" darkMode={darkMode}>
|
||||||
|
<h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Bell className="text-primary-600" /> {t('feedbackTitle') || 'Suporte e Feedback'}</h3>
|
||||||
|
<p className="opacity-60 text-sm font-medium mb-6">{t('feedbackDesc') || 'Tem alguma ideia, sugestão ou encontrou algum problema? Envie uma mensagem diretamente para nós!'}</p>
|
||||||
|
<form onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const fd = new FormData(e.target);
|
||||||
|
const type = fd.get('type');
|
||||||
|
const msg = fd.get('message');
|
||||||
|
// Substitua pelo seu email real
|
||||||
|
const email = "faiker027@gmail.com";
|
||||||
|
window.location.href = `mailto:${email}?subject=${encodeURIComponent(`MyCloset Feedback: ${type}`)}&body=${encodeURIComponent(msg)}`;
|
||||||
|
e.target.reset();
|
||||||
|
}} className="space-y-4">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<label className="flex items-center gap-3 p-4 rounded-xl border border-gray-100 dark:border-gray-800 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
||||||
|
<input type="radio" name="type" value="Ideia/Sugestão" defaultChecked className="text-primary-600 focus:ring-primary-500" />
|
||||||
|
<span className="font-bold text-sm text-inherit">Ideia / Sugestão</span>
|
||||||
|
</label>
|
||||||
|
<label className="flex items-center gap-3 p-4 rounded-xl border border-gray-100 dark:border-gray-800 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
||||||
|
<input type="radio" name="type" value="Bug/Erro" className="text-primary-600 focus:ring-primary-500" />
|
||||||
|
<span className="font-bold text-sm text-inherit">Bug / Erro</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<textarea name="message" required placeholder="Escreva aqui a sua mensagem..." rows={4} className={`w-full p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold resize-none ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}></textarea>
|
||||||
|
<button type="submit" className="w-full py-4 bg-primary-600 text-white rounded-xl font-black uppercase text-[10px] tracking-widest shadow-xl shadow-primary-600/30 hover:scale-[1.01] transition-all">
|
||||||
|
Enviar Mensagem
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1056,7 +1085,7 @@ export default function App() {
|
|||||||
<h3 className="text-2xl font-black text-inherit flex items-center gap-3"><Filter size={24} className="text-primary-600" /> {t('advancedFilters')}</h3>
|
<h3 className="text-2xl font-black text-inherit flex items-center gap-3"><Filter size={24} className="text-primary-600" /> {t('advancedFilters')}</h3>
|
||||||
<button onClick={() => setShowClosetFilters(false)} className="p-2 bg-gray-100 dark:bg-gray-800 rounded-full hover:scale-110 transition-all text-inherit"><X size={20} /></button>
|
<button onClick={() => setShowClosetFilters(false)} className="p-2 bg-gray-100 dark:bg-gray-800 rounded-full hover:scale-110 transition-all text-inherit"><X size={20} /></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto space-y-8 pr-2 custom-scrollbar">
|
<div className="flex-1 overflow-y-auto space-y-8 pr-2 custom-scrollbar">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('closet')}</label>
|
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('closet')}</label>
|
||||||
@@ -1075,7 +1104,7 @@ export default function App() {
|
|||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('filterByColor')}</label>
|
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('filterByColor')}</label>
|
||||||
<select
|
<select
|
||||||
value={colorFilter}
|
value={colorFilter}
|
||||||
onChange={(e) => setColorFilter(e.target.value)}
|
onChange={(e) => setColorFilter(e.target.value)}
|
||||||
className={`w-full p-4 rounded-2xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}
|
className={`w-full p-4 rounded-2xl border-none outline-none focus:ring-2 focus:ring-primary-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}
|
||||||
|
|||||||
Reference in New Issue
Block a user