diff --git a/src/App.jsx b/src/App.jsx index 3df0d93..5e6abc5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,7 +7,7 @@ import { PanelLeftClose, PanelLeftOpen, Sparkles, CloudSun, ArrowRight, Droplets, CheckCircle2, PieChart, History, X, Download, Bell, Globe, Filter, ShoppingBag, Share2, - FolderOpen, Tag, Link + FolderOpen, Tag, Link, Calendar, ChevronLeft, ChevronRight } from 'lucide-react'; import { @@ -83,6 +83,13 @@ export default function App() { const [showNotificationsModal, setShowNotificationsModal] = useState(false); const [toastMessage, setToastMessage] = useState(null); + // Estado do Planeador + const [plannerMode, setPlannerMode] = useState('month'); + const [plannerCurrentDate, setPlannerCurrentDate] = useState(new Date()); + const [outfitPlans, setOutfitPlans] = useState([]); + const [showPlannerPicker, setShowPlannerPicker] = useState(false); + const [plannerPickerDate, setPlannerPickerDate] = useState(null); + const t = (key) => translations[language]?.[key] || translations['PT'][key] || key; // Mapeamento de nomes de cor (PT) para valores CSS @@ -265,6 +272,12 @@ export default function App() { setSections(snap.docs.map(d => ({ id: d.id, ...d.data() })).sort((a, b) => a.createdAt - b.createdAt)); }, (err) => console.error(err)); + // Planeador de Outfits + const plansCol = collection(db, 'artifacts', appId, 'users', user.uid, 'outfitPlans'); + const unsubPlans = onSnapshot(plansCol, (snap) => { + setOutfitPlans(snap.docs.map(d => ({ id: d.id, ...d.data() }))); + }, (err) => console.error(err)); + // Profile const profileDoc = doc(db, 'artifacts', appId, 'users', user.uid, 'profile', 'data'); const unsubProfile = onSnapshot(profileDoc, (snap) => { @@ -289,7 +302,7 @@ export default function App() { setNotifications(snap.docs.map(d => ({ id: d.id, ...d.data() })).sort((a, b) => b.createdAt - a.createdAt)); }, (err) => console.error('Notif listener error:', err)); - return () => { unsubClothes(); unsubLooks(); unsubSections(); unsubProfile(); unsubNotif(); }; + return () => { unsubClothes(); unsubLooks(); unsubSections(); unsubProfile(); unsubNotif(); unsubPlans(); }; }, [user]); // Fetch Weather Data @@ -382,6 +395,16 @@ export default function App() { if (activeSectionFilter === id) setActiveSectionFilter('all'); }; + 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() }); + } else { + await deleteDoc(planRef); + } + }; + const baseClothes = view === 'wishlist' ? wishlistClothes : activeClothes; const availableColors = useMemo(() => { @@ -905,6 +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: 'settings', label: t('settings'), icon: Settings }, ].map(item => ( +

+ {plannerMode === 'month' ? `${monthNames[month]} ${year}` : weekLabel} +

+ + + +
+ {['month','week'].map(m => ( + + ))} +
+ + + {/* Cabeçalhos dos dias */} +
+ {dayHeaders.map(h => ( +
{h}
+ ))} +
+ + {/* Grelha */} + {plannerMode === 'month' ? ( +
+ {getMonthDays().map(({ date, cur }) => )} +
+ ) : ( +
+ {getWeekDays().map(date => )} +
+ )} + + ); + })()} + {/* ADICIONAR / EDITAR */} {(view === 'add' || view === 'edit') && (
@@ -1782,6 +1933,65 @@ export default function App() {
+ {/* Modal do Planeador - Escolher Outfit */} + {showPlannerPicker && plannerPickerDate && ( +
setShowPlannerPicker(false)}> + e.stopPropagation()}> +
+
+

+ Escolher Outfit +

+

+ {new Date(plannerPickerDate + 'T12:00:00').toLocaleDateString('pt-PT', { weekday: 'long', day: 'numeric', month: 'long' })} +

+
+ +
+ + {outfitPlans.find(p => p.date === plannerPickerDate) && ( + + )} + +
+ {looks.length === 0 ? ( +
Nenhum outfit criado
+ ) : looks.map(look => { + const isSelected = outfitPlans.find(p => p.date === plannerPickerDate)?.lookId === look.id; + return ( + + ); + })} +
+
+
+ )} + {/* Toast Message */} {toastMessage && (
diff --git a/src/lib/i18n.js b/src/lib/i18n.js index a76f834..f9bcd5e 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -8,16 +8,16 @@ export const translations = { createAccount: "Criar Nova Conta", haveAccount: "Já Tenho Conta", authErrorDisabled: "O login por e-mail está desativado.", - dashboard: "Dashboard", + dashboard: "Painel", closet: "Armário", laundry: "Lavandaria", - outfits: "Looks", + outfits: "Outfits", settings: "Definições", online: "Online", logout: "Sair", overview: "Visão Geral", myCloset: "O Meu Armário", - outfitsAndStyle: "Looks & Estilo", + outfitsAndStyle: "Outfits", readyClothes: "Roupas Prontas", inLaundry: "Na Lavandaria", myLooks: "Meus Looks", @@ -42,13 +42,13 @@ export const translations = { laundryMsg: "Aqui encontras as peças que marcaste como sujas. Lava-as para que voltem ao armário principal.", washing: "A lavar", emptyBasket: "Cesto Vazio", - createNewLook: "Criar Novo Look", - lookName: "Nome do Look", + createNewLook: "Criar Novo Outfit", + lookName: "Nome do Outfit", selectedPieces: "Peças Selecionadas", selectPieces: "Seleciona peças...", - saveLook: "Guardar Look", + saveLook: "Guardar Outfit", closetLabel: "Armário", - lookHistory: "Histórico de Looks", + lookHistory: "Histórico de Outfits", pieces: "Peças", newItem: "Novo Item", preview: "Preview",