This commit is contained in:
2026-05-05 15:53:01 +01:00
parent 5dc41414a4
commit 771f4dd1da
2 changed files with 128 additions and 75 deletions

View File

@@ -928,7 +928,7 @@ export default function App() {
{ id: 'wishlist', label: t('wishlist') || 'Carrinho', icon: ShoppingBag },
{ id: 'laundry', label: t('laundry'), icon: Droplets },
{ id: 'outfits', label: t('outfits'), icon: Sparkles },
{ id: 'planner', label: 'Planeamento', icon: Calendar },
{ id: 'planner', label: t('planning'), icon: Calendar },
{ id: 'settings', label: t('settings'), icon: Settings },
].map(item => (
<button
@@ -982,7 +982,7 @@ export default function App() {
{view === 'wishlist' && (t('wishlist') || 'Carrinho')}
{view === 'laundry' && t('laundry')}
{view === 'outfits' && t('outfitsAndStyle')}
{view === 'planner' && 'Planeamento'}
{view === 'planner' && t('planning')}
{view === 'settings' && t('settings')}
{view === 'profile' && t('profileInfo')}
</h2>
@@ -1221,7 +1221,7 @@ export default function App() {
<div className="lg:col-span-1 space-y-8">
<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-primary-600" /> {editingLook ? t('editLook') || 'Editar Look' : t('createNewLook')}
<Sparkles className="text-primary-600" /> {editingLook ? t('editLook') || 'Editar Outfit' : t('createNewLook')}
</h3>
<form key={editingLook ? editingLook.id : 'new'} onSubmit={saveLook} className="space-y-6">
<input name="lookName" placeholder={t('lookName')} defaultValue={editingLook?.name || ''} required className={`w-full p-4 rounded-xl border-none shadow-inner font-bold ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`} />
@@ -1342,7 +1342,7 @@ export default function App() {
<button
onClick={() => shareLook(look)}
className={`p-2 transition-colors relative group/share ${copiedLookId === look.id ? 'text-green-500' : 'text-gray-300 hover:text-green-500'}`}
title="Partilhar look"
title="Partilhar outfit"
>
{copiedLookId === look.id ? <Check size={18} /> : <Share2 size={18} />}
<span className="absolute -top-8 left-1/2 -translate-x-1/2 bg-gray-900 text-white text-[9px] font-black uppercase tracking-widest px-2 py-1 rounded-lg whitespace-nowrap opacity-0 group-hover/share:opacity-100 transition-opacity pointer-events-none">
@@ -1350,7 +1350,7 @@ export default function App() {
</span>
</button>
<button onClick={() => { setEditingLook(look); setSelectedForLook(look.items); }} className="p-2 text-gray-300 hover:text-primary-500 transition-colors"><Edit2 size={18} /></button>
<button onClick={() => sendLookToLaundry(look)} className="p-2 text-gray-300 hover:text-blue-500 transition-colors" title="Lavar look inteiro"><Droplets size={18} /></button>
<button onClick={() => sendLookToLaundry(look)} className="p-2 text-gray-300 hover:text-blue-500 transition-colors" title="Lavar outfit inteiro"><Droplets size={18} /></button>
<button onClick={() => deleteLook(look.id)} className="p-2 text-gray-300 hover:text-red-500 transition-colors"><Trash size={18} /></button>
</div>
</div>
@@ -1436,7 +1436,7 @@ export default function App() {
<div className="space-y-6">
<div className="flex items-center gap-3 px-2">
<div className="w-2.5 h-2.5 rounded-full bg-blue-400"></div>
<h3 className="text-2xl font-black tracking-tighter text-inherit">A ser lavados <span className="text-sm font-bold opacity-40"> {t('unavailable')} ({laundryLooks.length})</span></h3>
<h3 className="text-2xl font-black tracking-tighter text-inherit">{t('toBeWashed')} <span className="text-sm font-bold opacity-40"> {t('unavailable')} ({laundryLooks.length})</span></h3>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{laundryLooks.map(renderLookCard)}
@@ -1483,8 +1483,17 @@ export default function App() {
return Array.from({ length: 7 }, (_, i) => { const x = new Date(mon); x.setDate(mon.getDate() + i); return x; });
};
const monthNames = ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'];
const dayHeaders = ['Seg','Ter','Qua','Qui','Sex','Sáb','Dom'];
const langLocaleMap = { PT: 'pt-PT', EN: 'en-GB', ES: 'es-ES', FR: 'fr-FR', DE: 'de-DE' };
const locale = langLocaleMap[language] || 'pt-PT';
const monthNames = Array.from({ length: 12 }, (_, i) => {
const d = new Date(2000, i, 1);
const name = d.toLocaleDateString(locale, { month: 'long' });
return name.charAt(0).toUpperCase() + name.slice(1);
});
const dayHeaders = Array.from({ length: 7 }, (_, i) => {
const d = new Date(2024, 0, i + 1); // 2024-01-01 is Monday
return d.toLocaleDateString(locale, { weekday: 'short' }).replace('.', '');
});
const prev = () => { const d = new Date(plannerCurrentDate); plannerMode === 'month' ? d.setMonth(month-1) : d.setDate(d.getDate()-7); setPlannerCurrentDate(d); };
const next = () => { const d = new Date(plannerCurrentDate); plannerMode === 'month' ? d.setMonth(month+1) : d.setDate(d.getDate()+7); setPlannerCurrentDate(d); };
@@ -1505,7 +1514,7 @@ export default function App() {
>
<div className={`px-3 py-2 flex items-center justify-between ${isToday ? 'bg-primary-600' : ''}`}>
<span className={`text-xs font-black ${isToday ? 'text-white' : ''}`}>{date.getDate()}</span>
{isToday && <span className="text-[8px] font-black text-white/80 uppercase tracking-widest">Hoje</span>}
{isToday && <span className="text-[8px] font-black text-white/80 uppercase tracking-widest">{t('today')}</span>}
</div>
{look ? (
<div className="px-2 pb-2 space-y-1">
@@ -1516,7 +1525,7 @@ export default function App() {
})}
</div>
<p className="text-[9px] font-black uppercase tracking-widest opacity-50 truncate">{look.name}</p>
{isWeek && <p className="text-[9px] opacity-40 font-bold">{look.items.length} peças</p>}
{isWeek && <p className="text-[9px] opacity-40 font-bold">{look.items.length} {t('piecesShort')}</p>}
</div>
) : (
cur && <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
@@ -1544,13 +1553,13 @@ export default function App() {
<ChevronRight size={20} />
</button>
<button onClick={() => setPlannerCurrentDate(new Date())} className="px-4 py-2 text-[10px] font-black uppercase tracking-widest text-primary-600 bg-primary-50 dark:bg-primary-900/20 rounded-xl hover:bg-primary-100 dark:hover:bg-primary-900/40 transition-colors">
Hoje
{t('today')}
</button>
</div>
<div className={`flex p-1.5 rounded-2xl gap-1 ${darkMode ? 'bg-gray-800' : 'bg-gray-100'}`}>
{['month','week'].map(m => (
<button key={m} onClick={() => setPlannerMode(m)} className={`px-5 py-2 rounded-xl font-black text-[10px] uppercase tracking-widest transition-all ${plannerMode === m ? `${darkMode ? 'bg-gray-700' : 'bg-white'} shadow-md text-primary-600` : 'text-gray-500 hover:text-gray-700'}`}>
{m === 'month' ? 'Mês' : 'Semana'}
{m === 'month' ? t('monthLabel') : t('weekLabel')}
</button>
))}
</div>
@@ -1940,10 +1949,10 @@ export default function App() {
<div className="flex items-center justify-between mb-6">
<div>
<h3 className="text-xl font-black text-inherit flex items-center gap-3">
<Calendar size={22} className="text-primary-600" /> Escolher Outfit
<Calendar size={22} className="text-primary-600" /> {t('chooseOutfit')}
</h3>
<p className="text-[10px] font-black uppercase tracking-widest opacity-40 mt-1">
{new Date(plannerPickerDate + 'T12:00:00').toLocaleDateString('pt-PT', { weekday: 'long', day: 'numeric', month: 'long' })}
{(() => { const locMap = { PT: 'pt-PT', EN: 'en-GB', ES: 'es-ES', FR: 'fr-FR', DE: 'de-DE' }; return new Date(plannerPickerDate + 'T12:00:00').toLocaleDateString(locMap[language] || 'pt-PT', { weekday: 'long', day: 'numeric', month: 'long' }); })()}
</p>
</div>
<button onClick={() => setShowPlannerPicker(false)} className="p-2 bg-gray-100 dark:bg-gray-800 rounded-full hover:scale-110 transition-all text-inherit"><X size={20} /></button>
@@ -1954,13 +1963,13 @@ export default function App() {
onClick={async () => { await assignOutfitToDay(plannerPickerDate, null); setShowPlannerPicker(false); }}
className="mb-4 w-full py-3 border-2 border-dashed border-red-200 dark:border-red-900/50 text-red-400 rounded-2xl font-black text-[10px] uppercase tracking-widest hover:border-red-400 hover:text-red-500 transition-all flex items-center justify-center gap-2"
>
<Trash size={14} /> Remover Outfit deste Dia
<Trash size={14} /> {t('removeOutfitDay')}
</button>
)}
<div className="flex-1 overflow-y-auto space-y-3 custom-scrollbar">
{looks.length === 0 ? (
<div className="py-12 text-center opacity-30 font-black uppercase tracking-[0.3em] text-sm">Nenhum outfit criado</div>
<div className="py-12 text-center opacity-30 font-black uppercase tracking-[0.3em] text-sm">{t('noOutfitCreated')}</div>
) : looks.map(look => {
const isSelected = outfitPlans.find(p => p.date === plannerPickerDate)?.lookId === look.id;
return (
@@ -1981,7 +1990,7 @@ export default function App() {
</div>
<div className="flex-1 min-w-0">
<p className="font-black text-sm truncate text-inherit">{look.name}</p>
<p className="text-[10px] uppercase tracking-widest opacity-40 font-bold">{look.items.length} peças</p>
<p className="text-[10px] uppercase tracking-widest opacity-40 font-bold">{look.items.length} {t('piecesShort')}</p>
</div>
{isSelected && <Check size={18} className="text-primary-600 shrink-0" />}
</button>