This commit is contained in:
2026-04-14 16:23:16 +01:00
parent b7c59c6305
commit ed7b76394c
4 changed files with 176 additions and 57 deletions

View File

@@ -46,9 +46,16 @@ export default function App() {
const [notificationsEnabled, setNotificationsEnabled] = useState(true); const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [weatherAlerts, setWeatherAlerts] = useState(true); const [weatherAlerts, setWeatherAlerts] = useState(true);
const [language, setLanguage] = useState('PT'); const [language, setLanguage] = useState('PT');
const [theme, setTheme] = useState(() => localStorage.getItem('app-theme') || 'theme-indigo');
const t = (key) => translations[language]?.[key] || translations['PT'][key] || key; const t = (key) => translations[language]?.[key] || translations['PT'][key] || key;
useEffect(() => {
document.documentElement.classList.remove('theme-indigo', 'theme-rose', 'theme-emerald', 'theme-amber', 'theme-slate');
document.documentElement.classList.add(theme);
localStorage.setItem('app-theme', theme);
}, [theme]);
// 1. Inicializar Autenticação // 1. Inicializar Autenticação
useEffect(() => { useEffect(() => {
const initAuth = async () => { const initAuth = async () => {
@@ -224,14 +231,14 @@ export default function App() {
finally { setLoading(false); } finally { setLoading(false); }
}; };
if (loading && !user) return <div className="h-screen flex items-center justify-center bg-indigo-50 dark:bg-gray-950"><Loader2 className="animate-spin text-indigo-600" size={40} /></div>; if (loading && !user) return <div className="h-screen flex items-center justify-center bg-primary-50 dark:bg-gray-950"><Loader2 className="animate-spin text-primary-600" size={40} /></div>;
if (view === 'auth') { if (view === 'auth') {
return ( return (
<div className={`min-h-screen bg-gradient-to-br from-indigo-100 via-white to-purple-50 dark:from-gray-950 dark:to-gray-900 flex items-center justify-center p-6 text-gray-900 ${darkMode ? 'dark' : ''}`}> <div className={`min-h-screen bg-gradient-to-br from-primary-100 via-white to-purple-50 dark:from-gray-950 dark:to-gray-900 flex items-center justify-center p-6 text-gray-900 ${darkMode ? 'dark' : ''}`}>
<Card className="max-w-md w-full p-12 border-none shadow-2xl" darkMode={darkMode}> <Card className="max-w-md w-full p-12 border-none shadow-2xl" darkMode={darkMode}>
<div className="text-center mb-10"> <div className="text-center mb-10">
<div className="inline-flex p-5 bg-indigo-600 rounded-[2rem] shadow-2xl shadow-indigo-600/40 mb-6"> <div className="inline-flex p-5 bg-primary-600 rounded-[2rem] shadow-2xl shadow-primary-600/40 mb-6">
<Shirt className="text-white w-12 h-12" /> <Shirt className="text-white w-12 h-12" />
</div> </div>
<h1 className="text-5xl font-black tracking-tighter italic">MyCloset</h1> <h1 className="text-5xl font-black tracking-tighter italic">MyCloset</h1>
@@ -241,15 +248,15 @@ export default function App() {
{authError && <div className="mb-6 p-4 bg-red-50 text-red-600 text-[10px] rounded-2xl flex items-center gap-2 font-black uppercase tracking-widest border border-red-100"><AlertCircle size={16} /> {authError}</div>} {authError && <div className="mb-6 p-4 bg-red-50 text-red-600 text-[10px] rounded-2xl flex items-center gap-2 font-black uppercase tracking-widest border border-red-100"><AlertCircle size={16} /> {authError}</div>}
<form onSubmit={handleAuth} className="space-y-4"> <form onSubmit={handleAuth} className="space-y-4">
<input name="email" type="email" placeholder={t('emailPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" /> <input name="email" type="email" placeholder={t('emailPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-primary-500 outline-none font-bold" />
<input name="password" type="password" placeholder={t('passwordPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-indigo-500 outline-none font-bold" /> <input name="password" type="password" placeholder={t('passwordPlaceholder')} required className="w-full p-5 rounded-2xl bg-gray-50 dark:bg-gray-800 border-none focus:ring-2 focus:ring-primary-500 outline-none font-bold" />
<button className="w-full py-5 bg-indigo-600 text-white rounded-[2rem] font-black text-lg shadow-2xl hover:scale-[1.02] active:scale-95 transition-all"> <button className="w-full py-5 bg-primary-600 text-white rounded-[2rem] font-black text-lg shadow-2xl hover:scale-[1.02] active:scale-95 transition-all">
{authMode === 'login' ? t('loginBtn') : t('registerBtn')} {authMode === 'login' ? t('loginBtn') : t('registerBtn')}
</button> </button>
</form> </form>
<div className="mt-10 text-center"> <div className="mt-10 text-center">
<button onClick={() => setAuthMode(authMode === 'login' ? 'register' : 'login')} className="text-gray-400 font-black text-[10px] uppercase tracking-[0.3em] hover:text-indigo-600 transition-colors text-inherit"> <button onClick={() => setAuthMode(authMode === 'login' ? 'register' : 'login')} className="text-gray-400 font-black text-[10px] uppercase tracking-[0.3em] hover:text-primary-600 transition-colors text-inherit">
{authMode === 'login' ? t('createAccount') : t('haveAccount')} {authMode === 'login' ? t('createAccount') : t('haveAccount')}
</button> </button>
</div> </div>
@@ -269,7 +276,7 @@ export default function App() {
`}> `}>
<div className="p-10 h-full flex flex-col backdrop-blur-xl"> <div className="p-10 h-full flex flex-col backdrop-blur-xl">
<div className="flex items-center gap-4 mb-16"> <div className="flex items-center gap-4 mb-16">
<div className="p-3 bg-indigo-600 rounded-2xl shadow-xl shadow-indigo-600/30"> <div className="p-3 bg-primary-600 rounded-2xl shadow-xl shadow-primary-600/30">
<Shirt className="text-white" size={24} /> <Shirt className="text-white" size={24} />
</div> </div>
<span className="text-3xl font-black tracking-tighter italic">MyCloset</span> <span className="text-3xl font-black tracking-tighter italic">MyCloset</span>
@@ -286,7 +293,7 @@ export default function App() {
<button <button
key={item.id} key={item.id}
onClick={() => setView(item.id)} onClick={() => setView(item.id)}
className={`w-full flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-black text-[11px] uppercase tracking-widest ${view === item.id ? 'bg-indigo-600 text-white shadow-2xl shadow-indigo-600/30 scale-105' : 'opacity-40 hover:opacity-100 hover:bg-indigo-500/5'}`} className={`w-full flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-black text-[11px] uppercase tracking-widest ${view === item.id ? 'bg-primary-600 text-white shadow-2xl shadow-primary-600/30 scale-105' : 'opacity-40 hover:opacity-100 hover:bg-primary-500/5'}`}
> >
<item.icon size={20} /> <item.icon size={20} />
<span>{item.label}</span> <span>{item.label}</span>
@@ -296,7 +303,7 @@ export default function App() {
<div className="mt-auto pt-10 border-t border-inherit"> <div className="mt-auto pt-10 border-t border-inherit">
<div className="flex items-center gap-4 mb-8 px-2"> <div className="flex items-center gap-4 mb-8 px-2">
<div className={`w-12 h-12 rounded-2xl flex items-center justify-center font-black text-white shadow-xl ${darkMode ? 'bg-indigo-500' : 'bg-indigo-600'}`}> <div className={`w-12 h-12 rounded-2xl flex items-center justify-center font-black text-white shadow-xl ${darkMode ? 'bg-primary-500' : 'bg-primary-600'}`}>
{user?.email?.[0]?.toUpperCase() || 'U'} {user?.email?.[0]?.toUpperCase() || 'U'}
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
@@ -331,10 +338,10 @@ export default function App() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex bg-gray-100 dark:bg-gray-800 p-1.5 rounded-2xl"> <div className="flex bg-gray-100 dark:bg-gray-800 p-1.5 rounded-2xl">
<button onClick={() => setDarkMode(false)} className={`p-2 rounded-xl ${!darkMode ? 'bg-white shadow-md text-indigo-600' : 'text-gray-500'}`}><Sun size={18} /></button> <button onClick={() => setDarkMode(false)} className={`p-2 rounded-xl ${!darkMode ? 'bg-white shadow-md text-primary-600' : 'text-gray-500'}`}><Sun size={18} /></button>
<button onClick={() => setDarkMode(true)} className={`p-2 rounded-xl ${darkMode ? 'bg-gray-900 shadow-md text-indigo-400' : 'text-gray-500'}`}><Moon size={18} /></button> <button onClick={() => setDarkMode(true)} className={`p-2 rounded-xl ${darkMode ? 'bg-gray-900 shadow-md text-primary-400' : 'text-gray-500'}`}><Moon size={18} /></button>
</div> </div>
<button onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('add'); }} className="p-4 bg-indigo-600 text-white rounded-2xl shadow-xl shadow-indigo-600/30 hover:scale-105 active:scale-95 transition-all"> <button onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('add'); }} className="p-4 bg-primary-600 text-white rounded-2xl shadow-xl shadow-primary-600/30 hover:scale-105 active:scale-95 transition-all">
<Plus size={24} /> <Plus size={24} />
</button> </button>
</div> </div>
@@ -348,13 +355,13 @@ export default function App() {
<div className="space-y-12 animate-in fade-in duration-700"> <div className="space-y-12 animate-in fade-in duration-700">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{[ {[
{ label: t('readyClothes'), val: activeClothes.length, icon: Shirt, col: 'indigo' }, { label: t('readyClothes'), val: activeClothes.length, icon: Shirt, col: 'primary' },
{ label: t('inLaundry'), val: laundryClothes.length, icon: Droplets, col: 'blue' }, { label: t('inLaundry'), val: laundryClothes.length, icon: Droplets, col: 'blue' },
{ label: t('myLooks'), val: looks.length, icon: Sparkles, col: 'purple' }, { label: t('myLooks'), val: looks.length, icon: Sparkles, col: 'purple' },
{ label: t('favorites'), val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' }, { label: t('favorites'), val: activeClothes.filter(c => c.favorite).length, icon: Heart, col: 'rose' },
].map((s, i) => ( ].map((s, i) => (
<Card key={i} className="p-8 group hover:-translate-y-2" darkMode={darkMode}> <Card key={i} className="p-8 group hover:-translate-y-2" darkMode={darkMode}>
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center mb-6 shadow-inner ${darkMode ? 'bg-gray-700 text-indigo-400' : 'bg-indigo-50 text-indigo-600'}`}> <div className={`w-14 h-14 rounded-2xl flex items-center justify-center mb-6 shadow-inner ${darkMode ? 'bg-gray-700 text-primary-400' : 'bg-primary-50 text-primary-600'}`}>
<s.icon size={28} /> <s.icon size={28} />
</div> </div>
<p className="text-[10px] font-black uppercase tracking-widest opacity-40 mb-1">{s.label}</p> <p className="text-[10px] font-black uppercase tracking-widest opacity-40 mb-1">{s.label}</p>
@@ -364,21 +371,21 @@ export default function App() {
</div> </div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<Card className="lg:col-span-2 p-10 bg-indigo-600 text-white border-none shadow-2xl shadow-indigo-600/40 relative overflow-hidden" darkMode={darkMode}> <Card className="lg:col-span-2 p-10 bg-primary-600 text-white border-none shadow-2xl shadow-primary-600/40 relative overflow-hidden" darkMode={darkMode}>
<div className="relative z-10 flex flex-col justify-between h-full"> <div className="relative z-10 flex flex-col justify-between h-full">
<div> <div>
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<CloudSun size={28} className="text-indigo-200" /> <CloudSun size={28} className="text-primary-200" />
<Badge variant="warning">{t('todayIn')}</Badge> <Badge variant="warning">{t('todayIn')}</Badge>
</div> </div>
<h3 className="text-5xl font-black tracking-tighter mb-4">{t('weatherUpdate')}</h3> <h3 className="text-5xl font-black tracking-tighter mb-4">{t('weatherUpdate')}</h3>
<p className="text-indigo-100 text-lg font-medium max-w-lg leading-relaxed"> <p className="text-primary-100 text-lg font-medium max-w-lg leading-relaxed">
{t('weatherMsg')} {t('weatherMsg')}
</p> </p>
</div> </div>
<div className="mt-10 flex gap-4"> <div className="mt-10 flex gap-4">
{activeClothes.filter(c => c.category === 'Tops').slice(0, 2).map(c => ( {activeClothes.filter(c => c.category === 'Tops').slice(0, 2).map(c => (
<div key={c.id} className="w-16 h-16 rounded-xl overflow-hidden border-2 border-indigo-400"> <div key={c.id} className="w-16 h-16 rounded-xl overflow-hidden border-2 border-primary-400">
<img src={c.imageUrl} className="w-full h-full object-cover" alt="" /> <img src={c.imageUrl} className="w-full h-full object-cover" alt="" />
</div> </div>
))} ))}
@@ -400,7 +407,7 @@ export default function App() {
<span>{Math.floor(Math.random() * 40 + 20)}%</span> <span>{Math.floor(Math.random() * 40 + 20)}%</span>
</div> </div>
<div className="h-2 w-full bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden"> <div className="h-2 w-full bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden">
<div className="h-full bg-indigo-600" style={{ width: `${Math.floor(Math.random() * 40 + 20)}%` }}></div> <div className="h-full bg-primary-600" style={{ width: `${Math.floor(Math.random() * 40 + 20)}%` }}></div>
</div> </div>
</div> </div>
))} ))}
@@ -418,7 +425,7 @@ export default function App() {
<Search className="absolute left-6 top-1/2 -translate-y-1/2 text-gray-400" size={24} /> <Search className="absolute left-6 top-1/2 -translate-y-1/2 text-gray-400" size={24} />
<input <input
placeholder={t('searchPlaceholder')} placeholder={t('searchPlaceholder')}
className={`w-full pl-16 pr-8 py-6 rounded-[2rem] shadow-inner outline-none border-none focus:ring-4 focus:ring-indigo-500/10 font-bold text-lg ${darkMode ? 'bg-gray-800' : 'bg-gray-100'}`} className={`w-full pl-16 pr-8 py-6 rounded-[2rem] shadow-inner outline-none border-none focus:ring-4 focus:ring-primary-500/10 font-bold text-lg ${darkMode ? 'bg-gray-800' : 'bg-gray-100'}`}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
</div> </div>
@@ -428,7 +435,7 @@ export default function App() {
<button <button
key={cat} key={cat}
onClick={() => setCategoryFilter(cat)} onClick={() => setCategoryFilter(cat)}
className={`px-8 py-4 rounded-2xl font-black text-xs uppercase tracking-widest transition-all ${categoryFilter === cat ? 'bg-indigo-600 text-white shadow-xl shadow-indigo-600/30' : (darkMode ? 'bg-gray-800 text-gray-400' : 'bg-white text-gray-500 shadow-sm border border-gray-100')}`} className={`px-8 py-4 rounded-2xl font-black text-xs uppercase tracking-widest transition-all ${categoryFilter === cat ? 'bg-primary-600 text-white shadow-xl shadow-primary-600/30' : (darkMode ? 'bg-gray-800 text-gray-400' : 'bg-white text-gray-500 shadow-sm border border-gray-100')}`}
> >
{cat} {cat}
</button> </button>
@@ -444,7 +451,7 @@ export default function App() {
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-8 text-white"> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-8 text-white">
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<button onClick={() => { setEditingItem(item); setImageUrlDraft(''); setView('edit'); }} className="py-4 bg-white text-indigo-600 rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-indigo-50"><Edit2 size={16} /> {t('edit')}</button> <button onClick={() => { setEditingItem(item); setImageUrlDraft(''); setView('edit'); }} className="py-4 bg-white text-primary-600 rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-primary-50"><Edit2 size={16} /> {t('edit')}</button>
<button onClick={() => handleItemAction('laundry', item)} className="py-4 bg-blue-600 text-white rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-blue-700"><Droplets size={16} /> {t('makeDirty')}</button> <button onClick={() => handleItemAction('laundry', item)} className="py-4 bg-blue-600 text-white rounded-2xl font-black text-[10px] uppercase flex items-center justify-center gap-2 hover:bg-blue-700"><Droplets size={16} /> {t('makeDirty')}</button>
<button onClick={() => handleItemAction('trash', item)} className="py-4 bg-red-600/20 text-red-100 backdrop-blur-md rounded-2xl font-black text-[10px] uppercase hover:bg-red-600 transition-colors col-span-2">{t('moveToTrash')}</button> <button onClick={() => handleItemAction('trash', item)} className="py-4 bg-red-600/20 text-red-100 backdrop-blur-md rounded-2xl font-black text-[10px] uppercase hover:bg-red-600 transition-colors col-span-2">{t('moveToTrash')}</button>
</div> </div>
@@ -507,8 +514,8 @@ export default function App() {
<div className="space-y-12 animate-in fade-in duration-700 pb-20"> <div className="space-y-12 animate-in fade-in duration-700 pb-20">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12"> <div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div className="lg:col-span-1 space-y-8"> <div className="lg:col-span-1 space-y-8">
<Card className="p-8 border-indigo-200" darkMode={darkMode}> <Card className="p-8 border-primary-200" darkMode={darkMode}>
<h3 className="text-2xl font-black tracking-tighter mb-6 flex items-center gap-3 text-inherit"><Sparkles className="text-indigo-600" /> {t('createNewLook')}</h3> <h3 className="text-2xl font-black tracking-tighter mb-6 flex items-center gap-3 text-inherit"><Sparkles className="text-primary-600" /> {t('createNewLook')}</h3>
<form onSubmit={createLook} className="space-y-6"> <form onSubmit={createLook} className="space-y-6">
<input name="lookName" placeholder={t('lookName')} required className={`w-full p-4 rounded-xl border-none shadow-inner font-bold ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`} /> <input name="lookName" placeholder={t('lookName')} required className={`w-full p-4 rounded-xl border-none shadow-inner font-bold ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`} />
<div className="space-y-3"> <div className="space-y-3">
@@ -518,7 +525,7 @@ export default function App() {
const item = clothes.find(c => c.id === id); const item = clothes.find(c => c.id === id);
return ( return (
<div key={id} className="relative group"> <div key={id} className="relative group">
<img src={item?.imageUrl} className="w-12 h-12 rounded-lg object-cover border-2 border-indigo-500" alt="" /> <img src={item?.imageUrl} className="w-12 h-12 rounded-lg object-cover border-2 border-primary-500" alt="" />
<button onClick={() => setSelectedForLook(selectedForLook.filter(i => i !== id))} className="absolute -top-1 -right-1 bg-red-500 text-white rounded-full p-0.5 opacity-0 group-hover:opacity-100 transition-opacity"><X size={10} /></button> <button onClick={() => setSelectedForLook(selectedForLook.filter(i => i !== id))} className="absolute -top-1 -right-1 bg-red-500 text-white rounded-full p-0.5 opacity-0 group-hover:opacity-100 transition-opacity"><X size={10} /></button>
</div> </div>
); );
@@ -526,7 +533,7 @@ export default function App() {
{selectedForLook.length === 0 && <p className="text-xs text-gray-400 italic">{t('selectPieces')}</p>} {selectedForLook.length === 0 && <p className="text-xs text-gray-400 italic">{t('selectPieces')}</p>}
</div> </div>
</div> </div>
<button disabled={selectedForLook.length < 2} className="w-full py-4 bg-indigo-600 text-white rounded-2xl font-black uppercase tracking-widest text-xs shadow-xl shadow-indigo-600/30 disabled:opacity-30 transition-all"> <button disabled={selectedForLook.length < 2} className="w-full py-4 bg-primary-600 text-white rounded-2xl font-black uppercase tracking-widest text-xs shadow-xl shadow-primary-600/30 disabled:opacity-30 transition-all">
{t('saveLook')} {t('saveLook')}
</button> </button>
</form> </form>
@@ -536,9 +543,9 @@ export default function App() {
<p className="text-xs font-black uppercase opacity-50 tracking-widest px-2">{t('closetLabel')}</p> <p className="text-xs font-black uppercase opacity-50 tracking-widest px-2">{t('closetLabel')}</p>
<div className="grid grid-cols-4 gap-3 max-h-96 overflow-y-auto pr-2 custom-scrollbar"> <div className="grid grid-cols-4 gap-3 max-h-96 overflow-y-auto pr-2 custom-scrollbar">
{activeClothes.map(c => ( {activeClothes.map(c => (
<button key={c.id} onClick={() => !selectedForLook.includes(c.id) && setSelectedForLook([...selectedForLook, c.id])} className={`relative rounded-xl overflow-hidden aspect-square border-2 transition-all ${selectedForLook.includes(c.id) ? 'border-indigo-600 scale-90' : 'border-transparent hover:border-indigo-200'}`}> <button key={c.id} onClick={() => !selectedForLook.includes(c.id) && setSelectedForLook([...selectedForLook, c.id])} className={`relative rounded-xl overflow-hidden aspect-square border-2 transition-all ${selectedForLook.includes(c.id) ? 'border-primary-600 scale-90' : 'border-transparent hover:border-primary-200'}`}>
<img src={c.imageUrl} className="w-full h-full object-cover" alt="" /> <img src={c.imageUrl} className="w-full h-full object-cover" alt="" />
{selectedForLook.includes(c.id) && <div className="absolute inset-0 bg-indigo-600/40 flex items-center justify-center text-white"><Check size={20} /></div>} {selectedForLook.includes(c.id) && <div className="absolute inset-0 bg-primary-600/40 flex items-center justify-center text-white"><Check size={20} /></div>}
</button> </button>
))} ))}
</div> </div>
@@ -599,7 +606,7 @@ export default function App() {
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('category')}</label> <label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('category')}</label>
<select name="category" defaultValue={editingItem?.category || 'Tops'} className={`w-full p-5 rounded-2xl border-none outline-none focus:ring-4 focus:ring-indigo-500/10 font-bold ${darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100'}`}> <select name="category" defaultValue={editingItem?.category || 'Tops'} className={`w-full p-5 rounded-2xl border-none outline-none focus:ring-4 focus:ring-primary-500/10 font-bold ${darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100'}`}>
<option>{t('tops')}</option><option>{t('bottoms')}</option><option>{t('footwear')}</option><option>{t('coats')}</option><option>{t('accessories')}</option> <option>{t('tops')}</option><option>{t('bottoms')}</option><option>{t('footwear')}</option><option>{t('coats')}</option><option>{t('accessories')}</option>
</select> </select>
</div> </div>
@@ -609,7 +616,7 @@ export default function App() {
<div className="flex gap-4 pt-6"> <div className="flex gap-4 pt-6">
<button type="button" onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('closet'); }} className="flex-1 font-black uppercase text-[10px] opacity-40 hover:opacity-100 tracking-widest transition-all text-inherit">{t('cancel')}</button> <button type="button" onClick={() => { setEditingItem(null); setImageUrlDraft(''); setView('closet'); }} className="flex-1 font-black uppercase text-[10px] opacity-40 hover:opacity-100 tracking-widest transition-all text-inherit">{t('cancel')}</button>
<button type="submit" className="flex-1 py-5 bg-indigo-600 text-white rounded-[2rem] font-black uppercase tracking-widest text-[10px] shadow-2xl shadow-indigo-600/40 hover:scale-[1.02] active:scale-95 transition-all"> <button type="submit" className="flex-1 py-5 bg-primary-600 text-white rounded-[2rem] font-black uppercase tracking-widest text-[10px] shadow-2xl shadow-primary-600/40 hover:scale-[1.02] active:scale-95 transition-all">
{editingItem ? t('save') : t('register')} {editingItem ? t('save') : t('register')}
</button> </button>
</div> </div>
@@ -622,9 +629,9 @@ export default function App() {
{/* DEFINIÇÕES */} {/* DEFINIÇÕES */}
{view === 'settings' && ( {view === 'settings' && (
<div className="max-w-4xl mx-auto space-y-12 animate-in fade-in duration-700 pb-20"> <div className="max-w-4xl mx-auto space-y-12 animate-in fade-in duration-700 pb-20">
<Card className="p-10 border-indigo-100 relative overflow-hidden" darkMode={darkMode}> <Card className="p-10 border-primary-100 relative overflow-hidden" darkMode={darkMode}>
<div className="flex items-center gap-8 relative z-10 text-inherit"> <div className="flex items-center gap-8 relative z-10 text-inherit">
<div className="w-24 h-24 rounded-[2.5rem] bg-indigo-600 flex items-center justify-center text-white text-4xl font-black shadow-2xl"> <div className="w-24 h-24 rounded-[2.5rem] bg-primary-600 flex items-center justify-center text-white text-4xl font-black shadow-2xl">
{user?.email?.[0]?.toUpperCase() || 'U'} {user?.email?.[0]?.toUpperCase() || 'U'}
</div> </div>
<div> <div>
@@ -637,23 +644,47 @@ export default function App() {
{/* Preferências */} {/* Preferências */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<Card className="p-8" darkMode={darkMode}> <Card className="p-8" darkMode={darkMode}>
<h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Settings className="text-indigo-600" /> {t('preferences')}</h3> <h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Settings className="text-primary-600" /> {t('preferences')}</h3>
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="font-bold text-inherit">{t('darkMode')}</p> <p className="font-bold text-inherit">{t('darkMode')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('interfaceAppearance')}</p> <p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('interfaceAppearance')}</p>
</div> </div>
<button onClick={() => setDarkMode(!darkMode)} className={`w-14 h-8 rounded-full transition-colors relative ${darkMode ? 'bg-indigo-600' : 'bg-gray-200'}`}> <button onClick={() => setDarkMode(!darkMode)} className={`w-14 h-8 rounded-full transition-colors relative ${darkMode ? 'bg-primary-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${darkMode ? 'left-7' : 'left-1'}`}></div> <div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${darkMode ? 'left-7' : 'left-1'}`}></div>
</button> </button>
</div> </div>
<div className="flex items-center justify-between">
<div>
<p className="font-bold text-inherit">{t('themeColor') || 'Cor do Tema'}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('personalizeColor') || 'Personalize a cor'}</p>
</div>
<div className="flex gap-2">
{[
{ id: 'theme-indigo', color: '#4f46e5' },
{ id: 'theme-rose', color: '#e11d48' },
{ id: 'theme-emerald', color: '#10b981' },
{ id: 'theme-amber', color: '#f59e0b' },
{ id: 'theme-slate', color: '#64748b' }
].map(tObj => (
<button
key={tObj.id}
onClick={() => setTheme(tObj.id)}
className={`w-6 h-6 rounded-full transition-all flex items-center justify-center ${theme === tObj.id ? 'ring-2 ring-offset-2 ring-offset-white dark:ring-offset-gray-900 ring-primary-500 scale-110' : 'hover:scale-110'}`}
style={{ backgroundColor: tObj.color }}
>
{theme === tObj.id && <Check size={12} className="text-white" />}
</button>
))}
</div>
</div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="font-bold text-inherit flex items-center gap-2">{t('notifications')}</p> <p className="font-bold text-inherit flex items-center gap-2">{t('notifications')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('lookReminders')}</p> <p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('lookReminders')}</p>
</div> </div>
<button onClick={() => setNotificationsEnabled(!notificationsEnabled)} className={`w-14 h-8 rounded-full transition-colors relative ${notificationsEnabled ? 'bg-indigo-600' : 'bg-gray-200'}`}> <button onClick={() => setNotificationsEnabled(!notificationsEnabled)} className={`w-14 h-8 rounded-full transition-colors relative ${notificationsEnabled ? 'bg-primary-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${notificationsEnabled ? 'left-7' : 'left-1'}`}></div> <div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${notificationsEnabled ? 'left-7' : 'left-1'}`}></div>
</button> </button>
</div> </div>
@@ -662,7 +693,7 @@ export default function App() {
<p className="font-bold text-inherit flex items-center gap-2">{t('weatherAlerts')}</p> <p className="font-bold text-inherit flex items-center gap-2">{t('weatherAlerts')}</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('weatherSuggestions')}</p> <p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">{t('weatherSuggestions')}</p>
</div> </div>
<button onClick={() => setWeatherAlerts(!weatherAlerts)} className={`w-14 h-8 rounded-full transition-colors relative ${weatherAlerts ? 'bg-indigo-600' : 'bg-gray-200'}`}> <button onClick={() => setWeatherAlerts(!weatherAlerts)} className={`w-14 h-8 rounded-full transition-colors relative ${weatherAlerts ? 'bg-primary-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${weatherAlerts ? 'left-7' : 'left-1'}`}></div> <div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${weatherAlerts ? 'left-7' : 'left-1'}`}></div>
</button> </button>
</div> </div>
@@ -670,11 +701,11 @@ export default function App() {
</Card> </Card>
<Card className="p-8" darkMode={darkMode}> <Card className="p-8" darkMode={darkMode}>
<h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Globe className="text-indigo-600" /> {t('systemAndData')}</h3> <h3 className="text-xl font-black mb-6 flex items-center gap-3 text-inherit"><Globe className="text-primary-600" /> {t('systemAndData')}</h3>
<div className="space-y-6"> <div className="space-y-6">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('appLanguage')}</label> <label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit">{t('appLanguage')}</label>
<select value={language} onChange={(e) => setLanguage(e.target.value)} className={`w-full p-4 rounded-xl border-none outline-none focus:ring-2 focus:ring-indigo-500 font-bold ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-50'}`}> <select value={language} onChange={(e) => setLanguage(e.target.value)} className={`w-full 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="PT">{t('portuguese')}</option> <option value="PT">{t('portuguese')}</option>
<option value="EN">{t('english')}</option> <option value="EN">{t('english')}</option>
<option value="ES">{t('spanish')}</option> <option value="ES">{t('spanish')}</option>
@@ -682,16 +713,6 @@ export default function App() {
<option value="DE">{t('german')}</option> <option value="DE">{t('german')}</option>
</select> </select>
</div> </div>
<div>
<button onClick={() => alert(t('exportDataAlert'))} className="w-full py-4 rounded-xl font-black uppercase text-[10px] tracking-widest transition-all bg-indigo-50 text-indigo-600 hover:bg-indigo-100 dark:bg-indigo-900/30 dark:text-indigo-300 dark:hover:bg-indigo-900/50 flex items-center justify-center gap-2">
<Download size={16} /> {t('exportData')}
</button>
</div>
<div>
<button className="w-full py-4 rounded-xl font-black uppercase text-[10px] tracking-widest transition-all bg-gray-100 text-gray-500 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 flex items-center justify-center gap-2">
<ShieldAlert size={16} /> {t('privacyPolicy')}
</button>
</div>
</div> </div>
</Card> </Card>
</div> </div>
@@ -710,7 +731,7 @@ export default function App() {
<p className="text-[10px] font-black text-red-400 uppercase tracking-tighter">{t('deleted')}</p> <p className="text-[10px] font-black text-red-400 uppercase tracking-tighter">{t('deleted')}</p>
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1">
<button onClick={() => handleItemAction('restore', item)} className="p-3 text-indigo-600 hover:bg-indigo-50 rounded-2xl transition-all"><RotateCcw size={18} /></button> <button onClick={() => handleItemAction('restore', item)} className="p-3 text-primary-600 hover:bg-primary-50 rounded-2xl transition-all"><RotateCcw size={18} /></button>
<button onClick={() => handleItemAction('delete', item.id)} className="p-3 text-red-600 hover:bg-red-50 rounded-2xl transition-all"><Trash size={18} /></button> <button onClick={() => handleItemAction('delete', item.id)} className="p-3 text-red-600 hover:bg-red-50 rounded-2xl transition-all"><Trash size={18} /></button>
</div> </div>
</Card> </Card>

View File

@@ -2,6 +2,79 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@layer base {
:root {
/* Default Theme: Indigo */
--primary-50: 226 100% 97%;
--primary-100: 226 100% 94%;
--primary-200: 228 98% 87%;
--primary-300: 230 94% 78%;
--primary-400: 234 89% 68%;
--primary-500: 239 84% 61%;
--primary-600: 243 75% 59%;
--primary-700: 245 58% 51%;
--primary-800: 243 57% 42%;
--primary-900: 241 54% 35%;
--primary-950: 241 54% 20%;
}
.theme-rose {
--primary-50: 356 100% 97%;
--primary-100: 356 100% 94%;
--primary-200: 351 98% 87%;
--primary-300: 349 97% 76%;
--primary-400: 348 95% 64%;
--primary-500: 346 87% 53%;
--primary-600: 343 79% 46%;
--primary-700: 341 73% 38%;
--primary-800: 339 67% 33%;
--primary-900: 338 61% 29%;
--primary-950: 339 62% 16%;
}
.theme-emerald {
--primary-50: 152 81% 96%;
--primary-100: 149 80% 90%;
--primary-200: 152 76% 80%;
--primary-300: 156 73% 67%;
--primary-400: 158 64% 52%;
--primary-500: 160 84% 39%;
--primary-600: 161 94% 30%;
--primary-700: 163 94% 24%;
--primary-800: 164 86% 21%;
--primary-900: 164 80% 18%;
--primary-950: 164 85% 10%;
}
.theme-amber {
--primary-50: 45 100% 96%;
--primary-100: 48 100% 90%;
--primary-200: 46 100% 80%;
--primary-300: 42 100% 68%;
--primary-400: 38 92% 56%;
--primary-500: 35 92% 48%;
--primary-600: 30 87% 40%;
--primary-700: 25 81% 33%;
--primary-800: 22 76% 28%;
--primary-900: 20 73% 24%;
--primary-950: 19 80% 12%;
}
.theme-slate {
--primary-50: 210 40% 98%;
--primary-100: 210 40% 96%;
--primary-200: 214 32% 91%;
--primary-300: 213 27% 84%;
--primary-400: 215 20% 65%;
--primary-500: 215 16% 47%;
--primary-600: 215 19% 35%;
--primary-700: 215 21% 27%;
--primary-800: 215 25% 22%;
--primary-900: 215 28% 17%;
--primary-950: 215 31% 11%;
}
}
.custom-scrollbar::-webkit-scrollbar { .custom-scrollbar::-webkit-scrollbar {
height: 6px; height: 6px;
width: 6px; width: 6px;

View File

@@ -90,7 +90,9 @@ export const translations = {
colorBlack: "Preto", colorBlack: "Preto",
colorWhite: "Branco", colorWhite: "Branco",
colorBlue: "Azul", colorBlue: "Azul",
userTitle: "Utilizador" userTitle: "Utilizador",
themeColor: "Cor do Tema",
personalizeColor: "Personalizar a cor"
}, },
EN: { EN: {
loginModeIntro: "The Future of Your Style", loginModeIntro: "The Future of Your Style",
@@ -183,8 +185,9 @@ export const translations = {
colorBlack: "Black", colorBlack: "Black",
colorWhite: "White", colorWhite: "White",
colorBlue: "Blue", colorBlue: "Blue",
userTitle: "User" userTitle: "User",
themeColor: "Theme Color",
personalizeColor: "Personalize the color"
}, },
ES: { ES: {
loginModeIntro: "El Futuro de Tu Estilo", loginModeIntro: "El Futuro de Tu Estilo",
@@ -277,7 +280,9 @@ export const translations = {
colorBlack: "Negro", colorBlack: "Negro",
colorWhite: "Blanco", colorWhite: "Blanco",
colorBlue: "Azul", colorBlue: "Azul",
userTitle: "Usuario" userTitle: "Usuario",
themeColor: "Color del Tema",
personalizeColor: "Personaliza el color"
}, },
FR: { FR: {
loginModeIntro: "Le Futur de Ton Style", loginModeIntro: "Le Futur de Ton Style",
@@ -370,7 +375,9 @@ export const translations = {
colorBlack: "Noir", colorBlack: "Noir",
colorWhite: "Blanc", colorWhite: "Blanc",
colorBlue: "Bleu", colorBlue: "Bleu",
userTitle: "Utilisateur" userTitle: "Utilisateur",
themeColor: "Couleur du Thème",
personalizeColor: "Personnaliser la couleur"
}, },
DE: { DE: {
loginModeIntro: "Die Zukunft deines Stils", loginModeIntro: "Die Zukunft deines Stils",
@@ -463,6 +470,8 @@ export const translations = {
colorBlack: "Schwarz", colorBlack: "Schwarz",
colorWhite: "Weiß", colorWhite: "Weiß",
colorBlue: "Blau", colorBlue: "Blau",
userTitle: "Benutzer" userTitle: "Benutzer",
themeColor: "Themenfarbe",
personalizeColor: "Farbe anpassen"
} }
}; };

View File

@@ -6,7 +6,23 @@ export default {
], ],
darkMode: 'class', darkMode: 'class',
theme: { theme: {
extend: {}, extend: {
colors: {
primary: {
50: 'hsl(var(--primary-50) / <alpha-value>)',
100: 'hsl(var(--primary-100) / <alpha-value>)',
200: 'hsl(var(--primary-200) / <alpha-value>)',
300: 'hsl(var(--primary-300) / <alpha-value>)',
400: 'hsl(var(--primary-400) / <alpha-value>)',
500: 'hsl(var(--primary-500) / <alpha-value>)',
600: 'hsl(var(--primary-600) / <alpha-value>)',
700: 'hsl(var(--primary-700) / <alpha-value>)',
800: 'hsl(var(--primary-800) / <alpha-value>)',
900: 'hsl(var(--primary-900) / <alpha-value>)',
950: 'hsl(var(--primary-950) / <alpha-value>)',
}
}
},
}, },
plugins: [], plugins: [],
} }