From c37fdd321ae17563a15b8de58d09ab00373c95fc Mon Sep 17 00:00:00 2001 From: 230419 <230419@epvc.pt> Date: Wed, 6 May 2026 11:02:38 +0100 Subject: [PATCH] novo botao --- src/App.jsx | 292 +++++++++++++++++++++++++++++++++++++----------- src/lib/i18n.js | 15 +++ 2 files changed, 243 insertions(+), 64 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 5a68d87..74be53f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -90,6 +90,7 @@ export default function App() { const [plannerMode, setPlannerMode] = useState('month'); const [plannerCurrentDate, setPlannerCurrentDate] = useState(new Date()); const [outfitPlans, setOutfitPlans] = useState([]); + const [showDailyOutfitModal, setShowDailyOutfitModal] = useState(false); const [showPlannerPicker, setShowPlannerPicker] = useState(false); const [plannerPickerDate, setPlannerPickerDate] = useState(null); @@ -407,13 +408,42 @@ export default function App() { const assignOutfitToDay = async (dateStr, lookId) => { if (!user) return; const planRef = doc(db, 'artifacts', appId, 'users', user.uid, 'outfitPlans', dateStr); - if (lookId) { - await setDoc(planRef, { date: dateStr, lookId, updatedAt: new Date().getTime() }); + + const existingPlan = outfitPlans.find(p => p.date === dateStr); + let currentLookIds = []; + if (existingPlan) { + currentLookIds = existingPlan.lookIds || (existingPlan.lookId ? [existingPlan.lookId] : []); + } + + if (lookId === null) { + await deleteDoc(planRef); + return; + } + + if (currentLookIds.includes(lookId)) { + currentLookIds = currentLookIds.filter(id => id !== lookId); + } else { + currentLookIds = [...currentLookIds, lookId]; + } + + if (currentLookIds.length > 0) { + await setDoc(planRef, { date: dateStr, lookIds: currentLookIds, updatedAt: new Date().getTime() }, { merge: true }); } else { await deleteDoc(planRef); } }; + const todayObj = new Date(); + todayObj.setHours(0, 0, 0, 0); + const todayStrGlobal = `${todayObj.getFullYear()}-${String(todayObj.getMonth()+1).padStart(2,'0')}-${String(todayObj.getDate()).padStart(2,'0')}`; + const getLooksForDayGlobal = (ds) => { + const p = outfitPlans.find(plan => plan.date === ds); + if (!p) return []; + const ids = p.lookIds || (p.lookId ? [p.lookId] : []); + return ids.map(id => looks.find(l => l.id === id)).filter(Boolean); + }; + const dailyLooks = getLooksForDayGlobal(todayStrGlobal); + const baseClothes = view === 'wishlist' ? wishlistClothes : activeClothes; const availableColors = useMemo(() => { @@ -1048,6 +1078,9 @@ export default function App() {
+
@@ -1196,50 +1229,122 @@ export default function App() { : cardSize === 'medium' ? 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-5 gap-8' : 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-10' }> - {filteredClothes.map(item => ( -
- - {item.name} + {filteredClothes.map(item => { + const styles = { + small: { + badgeClass: 'top-2 left-2 scale-[0.65] origin-top-left', + heartContClass: 'top-2 right-2', + heartBtnClass: 'p-1.5 rounded-lg', + heartIcon: 10, + overlayContClass: 'p-2 pb-[50px]', + btnPyClass: 'py-1 px-1', + btnTextClass: 'hidden', + btnIcon: 10, + btnGap: 'gap-1', + btnRadius: 'rounded-md', + infoContClass: 'bottom-2 left-2 right-2 p-2 rounded-xl', + titleClass: 'text-xs', + colorDotClass: 'w-2 h-2', + colorTextClass: 'text-[8px]', + secTextClass: 'text-[8px] px-1 py-0 rounded', + }, + medium: { + badgeClass: 'top-4 left-4 scale-90 origin-top-left', + heartContClass: 'top-4 right-4', + heartBtnClass: 'p-2 rounded-xl', + heartIcon: 14, + overlayContClass: 'p-4 pb-[90px]', + btnPyClass: 'py-2 px-2', + btnTextClass: 'text-[8px]', + btnIcon: 12, + btnGap: 'gap-1.5', + btnRadius: 'rounded-xl', + infoContClass: 'bottom-4 left-4 right-4 p-4 rounded-2xl', + titleClass: 'text-sm', + colorDotClass: 'w-3 h-3', + colorTextClass: 'text-[9px]', + secTextClass: 'text-[9px] px-1.5 py-0.5 rounded-md', + }, + large: { + badgeClass: 'top-6 left-6', + heartContClass: 'top-6 right-6', + heartBtnClass: 'p-3 rounded-2xl', + heartIcon: 18, + overlayContClass: 'p-6 pb-[136px]', + btnPyClass: 'py-3 px-2', + btnTextClass: 'text-[9px]', + btnIcon: 14, + btnGap: 'gap-2', + btnRadius: 'rounded-xl', + infoContClass: 'bottom-6 left-6 right-6 p-6 rounded-3xl', + titleClass: 'text-xl', + colorDotClass: 'w-4 h-4', + colorTextClass: 'text-[10px]', + secTextClass: 'text-[10px] px-2 py-0.5 rounded-md', + } + }[cardSize] || { + badgeClass: 'top-6 left-6', + heartContClass: 'top-6 right-6', + heartBtnClass: 'p-3 rounded-2xl', + heartIcon: 18, + overlayContClass: 'p-6 pb-[136px]', + btnPyClass: 'py-3 px-2', + btnTextClass: 'text-[9px]', + btnIcon: 14, + btnGap: 'gap-2', + btnRadius: 'rounded-xl', + infoContClass: 'bottom-6 left-6 right-6 p-6 rounded-3xl', + titleClass: 'text-xl', + colorDotClass: 'w-4 h-4', + colorTextClass: 'text-[10px]', + secTextClass: 'text-[10px] px-2 py-0.5 rounded-md', + }; -
-
- - - -
-
+ return ( +
+ + {item.name} -
{item.category}
-
- -
- -
-

{item.name}

-
-
-
- {item.color} +
+
+ + +
- {item.sections && item.sections.length > 0 && ( -
- {item.sections.map(secId => { - const sec = sections.find(s => s.id === secId); - return sec ? ( - - {sec.name} - - ) : null; - })} -
- )}
-
- -
- ))} + +
{item.category}
+
+ +
+ +
+

{item.name}

+
+
+
+ {item.color} +
+ {item.sections && item.sections.length > 0 && ( +
+ {item.sections.map(secId => { + const sec = sections.find(s => s.id === secId); + return sec ? ( + + {sec.name} + + ) : null; + })} +
+ )} +
+
+ +
+ ); + })}
)} @@ -1516,17 +1621,11 @@ export default function App() { {/* PLANEADOR */} {view === 'planner' && (() => { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const todayStr = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`; const year = plannerCurrentDate.getFullYear(); const month = plannerCurrentDate.getMonth(); const fmtDate = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`; - const getPlan = (ds) => outfitPlans.find(p => p.date === ds); - const getLookForDay = (ds) => { const p = getPlan(ds); return p ? looks.find(l => l.id === p.lookId) : null; }; - const getMonthDays = () => { const first = new Date(year, month, 1); const last = new Date(year, month + 1, 0); @@ -1566,29 +1665,33 @@ export default function App() { const DayCell = ({ date, cur = true }) => { const ds = fmtDate(date); - const look = getLookForDay(ds); - const isToday = ds === todayStr; + const dayLooks = getLooksForDayGlobal(ds); + const isToday = ds === todayStrGlobal; const isWeek = plannerMode === 'week'; return (
{ setPlannerPickerDate(ds); setShowPlannerPicker(true); }} - className={`relative rounded-2xl overflow-hidden cursor-pointer transition-all group border-2 ${isToday ? 'border-primary-600 shadow-lg shadow-primary-600/20' : !cur ? 'border-transparent opacity-30' : 'border-transparent hover:border-primary-300 dark:hover:border-primary-700'} ${darkMode ? 'bg-gray-800/80' : 'bg-gray-50'}`} + className={`relative rounded-2xl overflow-hidden cursor-pointer transition-all group border-2 flex flex-col ${isToday ? 'border-primary-600 shadow-lg shadow-primary-600/20' : !cur ? 'border-transparent opacity-30' : 'border-transparent hover:border-primary-300 dark:hover:border-primary-700'} ${darkMode ? 'bg-gray-800/80' : 'bg-gray-50'}`} style={{ minHeight: isWeek ? '180px' : '100px' }} > -
+
{date.getDate()} {isToday && {t('today')}}
- {look ? ( -
-
- {look.items.slice(0, isWeek ? 4 : 3).map(itemId => { - const it = clothes.find(c => c.id === itemId); - return it ?
: null; - })} -
-

{look.name}

- {isWeek &&

{look.items.length} {t('piecesShort')}

} + {dayLooks.length > 0 ? ( +
+ {dayLooks.map(look => ( +
+
+ {look.items.slice(0, isWeek ? 4 : 3).map(itemId => { + const it = clothes.find(c => c.id === itemId); + return it ?
: null; + })} +
+

{look.name}

+ {isWeek &&

{look.items.length} {t('piecesShort')}

} +
+ ))}
) : ( cur &&
@@ -2051,11 +2154,13 @@ export default function App() { {looks.length === 0 ? (
{t('noOutfitCreated')}
) : looks.map(look => { - const isSelected = outfitPlans.find(p => p.date === plannerPickerDate)?.lookId === look.id; + const plan = outfitPlans.find(p => p.date === plannerPickerDate); + const selectedIds = plan ? (plan.lookIds || (plan.lookId ? [plan.lookId] : [])) : []; + const isSelected = selectedIds.includes(look.id); return (
)} + {/* Modal de Outfit Diário */} + {showDailyOutfitModal && ( +
setShowDailyOutfitModal(false)}> +
e.stopPropagation()} + > +
+
+
+
+
+ +
+ {t('dailyOutfit')} +
+

{t('today')}

+
+
+ +
+ {dailyLooks.length > 0 ? ( +
+ {dailyLooks.map(look => ( +
+
+

{look.name}

+ {look.items.length} {t('piecesShort')} +
+
+ {look.items.map(itemId => { + const item = clothes.find(c => c.id === itemId); + return item ? ( +
+ +
+ ) : null; + })} +
+
+ ))} +
+ ) : ( +
+ +

{t('noOutfitPlanned')}

+

{t('goToPlanning')}

+
+ )} +
+ +
+ +
+
+
+ )}
); } diff --git a/src/lib/i18n.js b/src/lib/i18n.js index 1cffd87..a2ba7e3 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -18,6 +18,9 @@ export const translations = { outfits: "Outfits", settings: "Definições", online: "Online", + dailyOutfit: "Outfit Diário", + noOutfitPlanned: "Nenhum Outfit Planeado", + goToPlanning: "Vá ao planeamento para adicionar", logout: "Sair", overview: "Visão Geral", myCloset: "O Meu Armário", @@ -217,6 +220,9 @@ export const translations = { outfits: "Outfits", settings: "Settings", online: "Online", + dailyOutfit: "Daily Outfit", + noOutfitPlanned: "No Outfit Planned", + goToPlanning: "Go to planning to add one", logout: "Logout", overview: "Overview", myCloset: "My Closet", @@ -416,6 +422,9 @@ export const translations = { outfits: "Outfits", settings: "Ajustes", online: "En línea", + dailyOutfit: "Outfit Diario", + noOutfitPlanned: "Sin Outfit Planeado", + goToPlanning: "Ve a planificación para añadir", logout: "Cerrar Sesión", overview: "Visión General", myCloset: "Mi Armario", @@ -615,6 +624,9 @@ export const translations = { outfits: "Tenues", settings: "Paramètres", online: "En ligne", + dailyOutfit: "Tenue du Jour", + noOutfitPlanned: "Aucune Tenue Prévue", + goToPlanning: "Allez dans planification pour ajouter", logout: "Déconnexion", overview: "Vue d'ensemble", myCloset: "Mon Placard", @@ -814,6 +826,9 @@ export const translations = { outfits: "Outfits", settings: "Einstellungen", online: "Online", + dailyOutfit: "Tägliches Outfit", + noOutfitPlanned: "Kein Outfit Geplant", + goToPlanning: "Gehen Sie zur Planung, um eins hinzuzufügen", logout: "Abmelden", overview: "Übersicht", myCloset: "Mein Schrank",