/* eslint-disable no-undef */ const { useState, useEffect, useRef, useMemo, useCallback } = React; /* ========================================================= STORAGE ========================================================= */ const LS_FRIENDS = "porco.friends.v1"; const LS_LOGS = "porco.logs.v1"; const DEFAULT_FRIENDS = [ { id: "f_you", name: "You", color: "#FCD86A" }, { id: "f_pal1", name: "Pal", color: "#EE6388" }, { id: "f_pal2", name: "Bestie", color: "#7EE0C5" }, ]; const loadJSON = (k, fb) => { try { const v = JSON.parse(localStorage.getItem(k)); return v == null ? fb : v; } catch { return fb; } }; const saveJSON = (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch {} }; /* ========================================================= DATE HELPERS — pick the active week from today. The system date is set to 2026-05-15 for this build but the logic works for any date. ========================================================= */ function findCurrentWeekIdx(weeks, today = new Date()) { // First, any week where start ≤ today ≤ end for (let i = 0; i < weeks.length; i++) { const s = new Date(weeks[i].start + "T00:00:00"); const e = new Date(weeks[i].end + "T23:59:59"); if (today >= s && today <= e) return i; } // Else: next upcoming week for (let i = 0; i < weeks.length; i++) { if (today < new Date(weeks[i].start + "T00:00:00")) return i; } // Else: most recent past return weeks.length - 1; } /* ========================================================= APP ========================================================= */ function App() { const weeks = window.WEEKS; const [friends, setFriends] = useState(() => loadJSON(LS_FRIENDS, DEFAULT_FRIENDS)); const [logs, setLogs] = useState(() => loadJSON(LS_LOGS, {})); const [activeTab, setActiveTab] = useState("now"); // "now" | "season" const [manageOpen, setManageOpen] = useState(false); const [editing, setEditing] = useState(null); // { weekId, friendId } const [sort, setSort] = useState("chrono"); // "chrono" | "rated" | "visited" useEffect(() => saveJSON(LS_FRIENDS, friends), [friends]); useEffect(() => saveJSON(LS_LOGS, logs), [logs]); const currentIdx = useMemo(() => findCurrentWeekIdx(weeks), [weeks]); const currentWeek = weeks[currentIdx]; const todayISO = useMemo(() => new Date().toISOString().slice(0, 10), []); // ---- mutate helpers ---- const updateEntry = useCallback((weekId, friendId, patch) => { setLogs(prev => { const next = { ...prev }; next[weekId] = { ...(next[weekId] || {}) }; next[weekId][friendId] = { ...(next[weekId][friendId] || {}), ...patch, ts: Date.now() }; return next; }); }, []); const deleteEntry = useCallback((weekId, friendId) => { setLogs(prev => { const next = { ...prev }; if (!next[weekId]) return prev; const { [friendId]: _, ...rest } = next[weekId]; if (Object.keys(rest).length === 0) { delete next[weekId]; } else { next[weekId] = rest; } return next; }); }, []); const onPickFriend = (weekId, friendId) => { setEditing(prev => prev && prev.weekId === weekId && prev.friendId === friendId ? null : { weekId, friendId } ); }; // ---- tweaks ---- const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "theme": "indigo", "density": "comfy", "drips": "on", "sort": "chrono" }/*EDITMODE-END*/; const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); useEffect(() => { document.body.dataset.theme = t.theme; document.body.dataset.density = t.density; document.body.dataset.drips = t.drips; }, [t.theme, t.density, t.drips]); // Sort weeks for the season list const sortedWeeks = useMemo(() => { const arr = [...weeks]; if (t.sort === "rated") { const score = (w) => { const wl = logs[w.id]; if (!wl) return -1; const ratings = Object.values(wl).map(e => e.stars || 0).filter(s => s > 0); if (!ratings.length) return -1; return ratings.reduce((a,b) => a + b, 0) / ratings.length; }; arr.sort((a, b) => score(b) - score(a)); } else if (t.sort === "visited") { const has = (w) => logs[w.id] && Object.values(logs[w.id]).some(e => e.stars || e.flavors?.length || e.comment); arr.sort((a, b) => Number(has(b)) - Number(has(a))); } else { // chrono } return arr; }, [weeks, logs, t.sort]); return ( <>