"use client"; import { useMemo, useState, useEffect, Suspense } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { todayISO } from "@/lib/rng"; const GAMES = [ { key: "queens", label: "Queens", symbol: "♛", href: (d: string) => `/queens/${d}`, storageKey: (d: string) => `queens-${d}`, isWon: (s: string) => { try { return (JSON.parse(s) as string[][]).flat().filter(c => c === "queen").length >= 6; } catch { return false; } }, }, { key: "tango", label: "Tango", symbol: "☀", href: (d: string) => `/tango/${d}`, storageKey: (d: string) => `tango-${d}`, isWon: (s: string) => { try { return (JSON.parse(s) as (string | null)[][]).flat().every(c => c !== null); } catch { return false; } }, }, { key: "zip", label: "Zip", symbol: "∞", href: (d: string) => `/zip/${d}`, storageKey: (d: string) => `zip-${d}`, isWon: (s: string) => { try { return (JSON.parse(s) as unknown[]).length === 25; } catch { return false; } }, }, { key: "sudoku", label: "Sudoku", symbol: "#", href: (d: string) => `/sudoku/${d}`, storageKey: (d: string) => `sudoku-${d}`, isWon: (s: string) => { try { return (JSON.parse(s) as number[][]).flat().every(n => n > 0); } catch { return false; } }, }, { key: "patches", label: "Patches", symbol: "▦", href: (d: string) => `/patches/${d}`, storageKey: (d: string) => `patches-${d}`, isWon: (s: string) => { try { const p = JSON.parse(s); // patches stores the placed pieces array return Array.isArray(p) && p.length > 0; } catch { return false; } }, }, ]; function getPastDates(n: number): string[] { const dates: string[] = []; const d = new Date(); for (let i = 0; i < n; i++) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); dates.push(`${y}-${m}-${day}`); d.setDate(d.getDate() - 1); } return dates; } function fmtDate(iso: string, today: string): string { if (iso === today) return "Aujourd'hui"; const [y, m, d] = iso.split("-").map(Number); const date = new Date(y, m - 1, d); const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); if (iso === getPastDates(2)[1]) return "Hier"; return date.toLocaleDateString("fr-FR", { weekday: "short", day: "numeric", month: "short" }); } function GameChip({ game, date, won }: { game: typeof GAMES[0]; date: string; won: boolean | undefined }) { return ( {game.symbol} {game.label} {won && ( )} ); } function ArchiveContent() { const params = useSearchParams(); const filter = params.get("game") ?? "all"; const [progress, setProgress] = useState>({}); const today = todayISO(); const dates = useMemo(() => getPastDates(90), []); useEffect(() => { const rec: Record = {}; for (const game of GAMES) { for (const date of dates) { const s = localStorage.getItem(game.storageKey(date)); if (s) { rec[`${game.key}-${date}`] = game.isWon(s); } } } setProgress(rec); }, [dates]); const visibleGames = filter === "all" ? GAMES : GAMES.filter(g => g.key === filter); // Compute completion summary per visible game const summary = useMemo(() => { return visibleGames.map(game => { const solved = dates.filter(d => progress[`${game.key}-${d}`] === true).length; return { key: game.key, label: game.label, solved }; }); }, [visibleGames, dates, progress]); return ( {/* Header */} Archives 90 derniers jours {/* Filter tabs */} {[{ k: "all", l: "Tous", sym: "" }, ...GAMES.map(g => ({ k: g.key, l: g.label, sym: g.symbol }))].map(({ k, l, sym }) => ( {sym && {sym}} {l} ))} {/* Completion summary chips */} {Object.keys(progress).length > 0 && ( {summary.map(({ key, label, solved }) => ( {solved} / 90 {label} ))} )} {/* Date rows */} {dates.map(date => { const isToday = date === today; const dateLabel = fmtDate(date, today); const solvedAll = visibleGames.every(g => progress[`${g.key}-${date}`] === true); const solvedCount = visibleGames.filter(g => progress[`${g.key}-${date}`] === true).length; return ( {/* Date label */} {dateLabel} {isToday && ( Aujourd'hui )} {/* Game chips */} {visibleGames.map(game => ( ))} {/* Right: completion indicator */} {solvedCount > 0 && ( {solvedCount}/{visibleGames.length} )} ); })} ); } export default function ArchivePage() { return ( {Array.from({ length: 10 }).map((_, i) => ( ))} }> ); }
90 derniers jours