import React, { useEffect, useMemo, useState } from "react"; // Oscars Flashcards — single-file React app (fixed) // Changes in this revision: // - Fixed unterminated string and accidental paste in handleExport / mergeData // - Removed stray escaped quotes in JSX // - Added quick-load buttons for 1927–1939, 1961–1999, 2000–2024, and "Load All" // - Auto-loads the full dataset on first run (can be toggled off by removing localStorage flag) // - Added lightweight self-tests that surface in UI if something's off // - Kept all existing behavior; expanded features only // Starter dataset (validated in prior Q&A): 1940–1960 release years const STARTER_DATA = [ { film: "Rebecca", releaseYear: 1940, ceremonyYear: 1941 }, { film: "How Green Was My Valley", releaseYear: 1941, ceremonyYear: 1942 }, { film: "Mrs. Miniver", releaseYear: 1942, ceremonyYear: 1943 }, { film: "Casablanca", releaseYear: 1943, ceremonyYear: 1944 }, { film: "Going My Way", releaseYear: 1944, ceremonyYear: 1945 }, { film: "The Lost Weekend", releaseYear: 1945, ceremonyYear: 1946 }, { film: "The Best Years of Our Lives", releaseYear: 1946, ceremonyYear: 1947 }, { film: "Gentleman's Agreement", releaseYear: 1947, ceremonyYear: 1948 }, { film: "Hamlet", releaseYear: 1948, ceremonyYear: 1949 }, { film: "All the King's Men", releaseYear: 1949, ceremonyYear: 1950 }, { film: "All About Eve", releaseYear: 1950, ceremonyYear: 1951 }, { film: "An American in Paris", releaseYear: 1951, ceremonyYear: 1952 }, { film: "The Greatest Show on Earth", releaseYear: 1952, ceremonyYear: 1953 }, { film: "From Here to Eternity", releaseYear: 1953, ceremonyYear: 1954 }, { film: "On the Waterfront", releaseYear: 1954, ceremonyYear: 1955 }, { film: "Marty", releaseYear: 1955, ceremonyYear: 1956 }, { film: "Around the World in 80 Days", releaseYear: 1956, ceremonyYear: 1957 }, { film: "The Bridge on the River Kwai", releaseYear: 1957, ceremonyYear: 1958 }, { film: "Gigi", releaseYear: 1958, ceremonyYear: 1959 }, { film: "Ben-Hur", releaseYear: 1959, ceremonyYear: 1960 }, { film: "The Apartment", releaseYear: 1960, ceremonyYear: 1961 }, ]; // Extended dataset 1961–1999 (release years) const DATA_1961_1999 = [ { film: "West Side Story", releaseYear: 1961, ceremonyYear: 1962 }, { film: "Lawrence of Arabia", releaseYear: 1962, ceremonyYear: 1963 }, { film: "Tom Jones", releaseYear: 1963, ceremonyYear: 1964 }, { film: "My Fair Lady", releaseYear: 1964, ceremonyYear: 1965 }, { film: "The Sound of Music", releaseYear: 1965, ceremonyYear: 1966 }, { film: "A Man for All Seasons", releaseYear: 1966, ceremonyYear: 1967 }, { film: "In the Heat of the Night", releaseYear: 1967, ceremonyYear: 1968 }, { film: "Oliver!", releaseYear: 1968, ceremonyYear: 1969 }, { film: "Midnight Cowboy", releaseYear: 1969, ceremonyYear: 1970 }, { film: "Patton", releaseYear: 1970, ceremonyYear: 1971 }, { film: "The French Connection", releaseYear: 1971, ceremonyYear: 1972 }, { film: "The Godfather", releaseYear: 1972, ceremonyYear: 1973 }, { film: "The Sting", releaseYear: 1973, ceremonyYear: 1974 }, { film: "The Godfather Part II", releaseYear: 1974, ceremonyYear: 1975 }, { film: "One Flew Over the Cuckoo's Nest", releaseYear: 1975, ceremonyYear: 1976 }, { film: "Rocky", releaseYear: 1976, ceremonyYear: 1977 }, { film: "Annie Hall", releaseYear: 1977, ceremonyYear: 1978 }, { film: "The Deer Hunter", releaseYear: 1978, ceremonyYear: 1979 }, { film: "Kramer vs. Kramer", releaseYear: 1979, ceremonyYear: 1980 }, { film: "Ordinary People", releaseYear: 1980, ceremonyYear: 1981 }, { film: "Chariots of Fire", releaseYear: 1981, ceremonyYear: 1982 }, { film: "Gandhi", releaseYear: 1982, ceremonyYear: 1983 }, { film: "Terms of Endearment", releaseYear: 1983, ceremonyYear: 1984 }, { film: "Amadeus", releaseYear: 1984, ceremonyYear: 1985 }, { film: "Out of Africa", releaseYear: 1985, ceremonyYear: 1986 }, { film: "Platoon", releaseYear: 1986, ceremonyYear: 1987 }, { film: "The Last Emperor", releaseYear: 1987, ceremonyYear: 1988 }, { film: "Rain Man", releaseYear: 1988, ceremonyYear: 1989 }, { film: "Driving Miss Daisy", releaseYear: 1989, ceremonyYear: 1990 }, { film: "Dances with Wolves", releaseYear: 1990, ceremonyYear: 1991 }, { film: "The Silence of the Lambs", releaseYear: 1991, ceremonyYear: 1992 }, { film: "Unforgiven", releaseYear: 1992, ceremonyYear: 1993 }, { film: "Schindler's List", releaseYear: 1993, ceremonyYear: 1994 }, { film: "Forrest Gump", releaseYear: 1994, ceremonyYear: 1995 }, { film: "Braveheart", releaseYear: 1995, ceremonyYear: 1996 }, { film: "The English Patient", releaseYear: 1996, ceremonyYear: 1997 }, { film: "Titanic", releaseYear: 1997, ceremonyYear: 1998 }, { film: "Shakespeare in Love", releaseYear: 1998, ceremonyYear: 1999 }, { film: "American Beauty", releaseYear: 1999, ceremonyYear: 2000 }, ]; // Extended dataset 1927–1939 const DATA_1927_1939 = [ { film: "Wings", releaseYear: 1927, ceremonyYear: 1929 }, { film: "The Broadway Melody", releaseYear: 1929, ceremonyYear: 1930 }, { film: "All Quiet on the Western Front", releaseYear: 1930, ceremonyYear: 1930 }, { film: "Cimarron", releaseYear: 1931, ceremonyYear: 1931 }, { film: "Grand Hotel", releaseYear: 1932, ceremonyYear: 1932 }, { film: "Cavalcade", releaseYear: 1933, ceremonyYear: 1933 }, { film: "It Happened One Night", releaseYear: 1934, ceremonyYear: 1935 }, { film: "Mutiny on the Bounty", releaseYear: 1935, ceremonyYear: 1936 }, { film: "The Great Ziegfeld", releaseYear: 1936, ceremonyYear: 1937 }, { film: "The Life of Emile Zola", releaseYear: 1937, ceremonyYear: 1938 }, { film: "You Can't Take It With You", releaseYear: 1938, ceremonyYear: 1939 }, { film: "Gone with the Wind", releaseYear: 1939, ceremonyYear: 1940 }, ]; // Extended dataset 2000–2024 const DATA_2000_2024 = [ { film: "Gladiator", releaseYear: 2000, ceremonyYear: 2001 }, { film: "A Beautiful Mind", releaseYear: 2001, ceremonyYear: 2002 }, { film: "Chicago", releaseYear: 2002, ceremonyYear: 2003 }, { film: "The Lord of the Rings: The Return of the King", releaseYear: 2003, ceremonyYear: 2004 }, { film: "Million Dollar Baby", releaseYear: 2004, ceremonyYear: 2005 }, { film: "Crash", releaseYear: 2005, ceremonyYear: 2006 }, { film: "The Departed", releaseYear: 2006, ceremonyYear: 2007 }, { film: "No Country for Old Men", releaseYear: 2007, ceremonyYear: 2008 }, { film: "Slumdog Millionaire", releaseYear: 2008, ceremonyYear: 2009 }, { film: "The Hurt Locker", releaseYear: 2009, ceremonyYear: 2010 }, { film: "The King’s Speech", releaseYear: 2010, ceremonyYear: 2011 }, { film: "The Artist", releaseYear: 2011, ceremonyYear: 2012 }, { film: "Argo", releaseYear: 2012, ceremonyYear: 2013 }, { film: "12 Years a Slave", releaseYear: 2013, ceremonyYear: 2014 }, { film: "Birdman or (The Unexpected Virtue of Ignorance)", releaseYear: 2014, ceremonyYear: 2015 }, { film: "Spotlight", releaseYear: 2015, ceremonyYear: 2016 }, { film: "Moonlight", releaseYear: 2016, ceremonyYear: 2017 }, { film: "The Shape of Water", releaseYear: 2017, ceremonyYear: 2018 }, { film: "Green Book", releaseYear: 2018, ceremonyYear: 2019 }, { film: "Parasite", releaseYear: 2019, ceremonyYear: 2020 }, { film: "Nomadland", releaseYear: 2020, ceremonyYear: 2021 }, { film: "CODA", releaseYear: 2021, ceremonyYear: 2022 }, { film: "Everything Everywhere All at Once", releaseYear: 2022, ceremonyYear: 2023 }, { film: "Oppenheimer", releaseYear: 2023, ceremonyYear: 2024 }, { film: "Anora", releaseYear: 2024, ceremonyYear: 2025 }, ]; const STORAGE_KEY = "oscarsFlashcardsData_v1"; function useLocalStorage(key, initialValue) { const [value, setValue] = useState(() => { try { const raw = localStorage.getItem(key); return raw ? JSON.parse(raw) : initialValue; } catch { return initialValue; } }); useEffect(() => { try { localStorage.setItem(key, JSON.stringify(value)); } catch {} }, [key, value]); return [value, setValue]; } function yearRange(data) { if (!data.length) return [1900, 2100]; const years = data.flatMap((d) => [d.releaseYear, d.ceremonyYear ?? d.releaseYear + 1]); return [Math.min(...years), Math.max(...years)]; } function shuffle(array) { const a = array.slice(); for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } function uniqueFilms(arr) { const seen = new Set(); return arr.filter((x) => { const k = x.film.toLowerCase(); if (seen.has(k)) return false; seen.add(k); return true; }); } export default function App() { const [data, setData] = useLocalStorage(STORAGE_KEY, STARTER_DATA); const [useReleaseYear, setUseReleaseYear] = useState(true); // toggle for prompt year type const [mode, setMode] = useState("Y2F"); // Y2F: Year→Film, F2Y: Film→Year const [mcMode, setMcMode] = useState(true); // multiple choice vs open-ended const [minYear, maxYearDefault] = yearRange(data); const [range, setRange] = useState({ min: minYear, max: maxYearDefault }); const [index, setIndex] = useState(0); const [revealed, setRevealed] = useState(false); const [bookmarks, setBookmarks] = useLocalStorage("oscarsFlashcards_bookmarks", []); const [showImport, setShowImport] = useState(false); const [importText, setImportText] = useState(""); const [feedback, setFeedback] = useState(null); // correct/incorrect state for MC const [testResults, setTestResults] = useState([]); // Auto-load full dataset on first run useEffect(() => { const flag = localStorage.getItem("oscarsFlashcards_autoload_done"); if (!flag) { mergeData([...DATA_1927_1939, ...DATA_1961_1999, ...DATA_2000_2024]); localStorage.setItem("oscarsFlashcards_autoload_done", "1"); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Derived working set based on range const working = useMemo(() => { const filtered = data.filter((d) => { const y = useReleaseYear ? d.releaseYear : (d.ceremonyYear ?? d.releaseYear + 1); return y >= range.min && y <= range.max; }); const filteredOrder = shuffle(filtered.map((_, i) => i)); return { items: filtered, order: filteredOrder }; }, [data, range, useReleaseYear]); const current = useMemo(() => { if (!working.items.length) return undefined; const i = working.order[index % working.order.length] ?? 0; return working.items[i]; }, [working, index]); const promptYear = useMemo( () => (useReleaseYear ? current?.releaseYear : (current?.ceremonyYear ?? (current?.releaseYear ?? 0) + 1)), [current, useReleaseYear] ); // Multiple choice options const mcOptions = useMemo(() => { if (!current) return []; if (mode === "Y2F") { const others = shuffle(uniqueFilms(working.items).filter((d) => d.film !== current.film)).slice(0, 3).map((d) => d.film); return shuffle([current.film, ...others]); } else { // Film→Year const target = useReleaseYear ? current.releaseYear : (current.ceremonyYear ?? current.releaseYear + 1); const setYears = new Set([target]); const candidates = []; for (let y = target - 10; y <= target + 10 && candidates.length < 20; y++) candidates.push(y); const others = shuffle(candidates.filter((y) => !setYears.has(y) && y >= range.min && y <= range.max)).slice(0, 3); return shuffle([target, ...others]); } }, [current, mode, working.items, useReleaseYear, range.min, range.max]); const next = () => { if (!working.order.length) return; setIndex((i) => (i + 1) % Math.max(1, working.order.length)); setRevealed(false); setFeedback(null); }; const prev = () => { if (!working.order.length) return; setIndex((i) => (i - 1 + Math.max(1, working.order.length)) % Math.max(1, working.order.length)); setRevealed(false); setFeedback(null); }; const toggleBookmark = (entry) => { const key = `${entry.film}|${entry.releaseYear}`; setBookmarks((b) => (b.includes(key) ? b.filter((x) => x !== key) : [...b, key])); }; // Keyboard shortcuts useEffect(() => { const onKey = (e) => { if (e.key === " ") { e.preventDefault(); setRevealed((r) => !r); } else if (e.key === "ArrowRight") { next(); } else if (e.key === "ArrowLeft") { prev(); } else if (["1", "2", "3", "4"].includes(e.key) && mcMode && mcOptions.length === 4) { const idx = parseInt(e.key, 10) - 1; if (mode === "Y2F") { const choice = mcOptions[idx]; setFeedback(choice === current.film ? "correct" : "wrong"); if (choice === current.film) setTimeout(next, 500); } else { const target = useReleaseYear ? current.releaseYear : (current.ceremonyYear ?? current.releaseYear + 1); const choice = mcOptions[idx]; setFeedback(choice === target ? "correct" : "wrong"); if (choice === target) setTimeout(next, 500); } } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [mcMode, mcOptions, mode, current, useReleaseYear]); // Import helpers function tryParseImport(text) { // Accept JSON [{film,releaseYear,ceremonyYear?}, ...] try { const obj = JSON.parse(text); if (Array.isArray(obj)) { return obj .map((o) => ({ film: String(o.film).trim(), releaseYear: Number(o.releaseYear), ceremonyYear: o.ceremonyYear != null ? Number(o.ceremonyYear) : undefined, })) .filter((o) => o.film && !Number.isNaN(o.releaseYear)); } } catch {} // Accept CSV headers: film,releaseYear,ceremonyYear (order-insensitive) const lines = text .split(/\r?\n/) .map((l) => l.trim()) .filter(Boolean); if (lines.length) { const header = lines[0].split(/\s*,\s*/).map((h) => h.toLowerCase()); const idxFilm = header.indexOf("film"); const idxRelease = header.indexOf("releaseyear"); const idxCeremony = header.indexOf("ceremonyyear"); if (idxFilm !== -1 && idxRelease !== -1) { const out = []; for (let i = 1; i < lines.length; i++) { const cols = lines[i].split(/\s*,\s*/); const film = (cols[idxFilm] || "").trim(); const r = Number(cols[idxRelease]); const c = idxCeremony !== -1 ? Number(cols[idxCeremony]) : undefined; if (film && !Number.isNaN(r)) out.push({ film, releaseYear: r, ceremonyYear: Number.isNaN(c) ? undefined : c }); } if (out.length) return out; } } // Accept simple "YEAR - FILM" per line (assume release year) const pairs = []; for (const line of text.split(/\r?\n/)) { const m = line.match(/^(\d{4})\s*[-–—:]\s*(.+)$/); if (m) pairs.push({ releaseYear: Number(m[1]), film: m[2].trim() }); } if (pairs.length) return pairs; return null; } function handleImport() { const parsed = tryParseImport(importText); if (!parsed || !parsed.length) { alert("Could not parse data. Accepts JSON array, CSV with film/releaseYear/(ceremonyYear), or 'YEAR - FILM' lines."); return; } // Merge by film+releaseYear unique key mergeData(parsed); setShowImport(false); setImportText(""); } function handleExport() { const csv = ["film,releaseYear,ceremonyYear"].concat( data.map((d) => `${d.film.replaceAll(",", " ")},${d.releaseYear},${d.ceremonyYear ?? ""}`) ).join("\n"); const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "oscars_best_picture.csv"; a.click(); URL.revokeObjectURL(url); } function mergeData(list) { const byKey = new Map(); [...data, ...list].forEach((d) => { const key = `${d.film.toLowerCase()}|${d.releaseYear}`; byKey.set(key, d); }); const merged = Array.from(byKey.values()).sort((a, b) => a.releaseYear - b.releaseYear); setData(merged); } // Lightweight self-tests — updates when data changes useEffect(() => { const results = []; const assert = (cond, msg) => results.push({ pass: !!cond, msg }); const y1940 = data.find((d) => d.releaseYear === 1940); assert(!!y1940, "Has release year 1940"); assert(y1940 && y1940.film === "Rebecca", "1940 winner is Rebecca"); const y1954 = data.find((d) => d.releaseYear === 1954); assert(!!y1954, "Has release year 1954"); assert(y1954 && y1954.film === "On the Waterfront", "1954 winner is On the Waterfront"); const y1972 = data.find((d) => d.releaseYear === 1972); if (y1972) assert(y1972.film === "The Godfather", "1972 winner is The Godfather"); const y2003 = data.find((d) => d.releaseYear === 2003); if (y2003) assert(/Return of the King/.test(y2003.film), "2003 winner is LOTR: Return of the King"); const y2024 = data.find((d) => d.releaseYear === 2024); if (y2024) assert(y2024.film === "Anora", "2024 winner is Anora"); // No duplicate release years const yearCounts = data.reduce((m, d) => ((m[d.releaseYear] = (m[d.releaseYear] || 0) + 1), m), {}); const dupYears = Object.entries(yearCounts).filter(([, c]) => c > 1).map(([y]) => y); assert(dupYears.length === 0, "No duplicate release years"); setTestResults(results); }, [data]); const testFailed = testResults.some((r) => !r.pass); const isBookmarked = current && bookmarks.includes(`${current.film}|${current.releaseYear}`); return ( <div className="min-h-screen bg-neutral-50 text-neutral-900"> <div className="mx-auto max-w-5xl px-4 py-6"> <header className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between"> <div> <h1 className="text-2xl font-bold tracking-tight">Oscars Flashcards — Best Picture</h1> <p className="text-sm text-neutral-600">Drill Best Picture winners across Oscar history. Toggle year type, pick a range, and quiz away.</p> </div> <div className="flex gap-2"> <button onClick={() => setShowImport((s) => !s)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">{showImport ? "Close Import" : "Import / Manage Data"}</button> <button onClick={handleExport} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Export CSV</button> </div> </header> {testFailed && ( <div className="mb-4 rounded-xl border border-red-300 bg-red-50 p-3 text-sm text-red-800"> <div className="font-semibold">Self-test warnings:</div> <ul className="list-inside list-disc"> {testResults.filter((t) => !t.pass).map((t, i) => ( <li key={i}>{t.msg}</li> ))} </ul> </div> )} {showImport && ( <section className="mb-6 rounded-2xl border bg-white p-4 shadow-sm"> <h2 className="mb-2 text-lg font-semibold">Import data</h2> <p className="mb-3 text-sm text-neutral-700"> Paste JSON array, CSV (film, releaseYear, ceremonyYear), or simple lines like: <span className="font-mono">1954 - On the Waterfront</span>. You can copy from the Academy database or Wikipedia and clean quickly. </p> <textarea className="h-40 w-full rounded-xl border p-2 font-mono text-sm" placeholder={`Example CSV:\nfilm,releaseYear,ceremonyYear\nWings,1927,1929\nAnora,2024,2025`} value={importText} onChange={(e) => setImportText(e.target.value)} /> <div className="mt-3 flex flex-wrap gap-2"> <button onClick={handleImport} className="rounded-xl bg-black px-3 py-2 text-sm text-white shadow-sm hover:opacity-90">Import & Merge</button> <button onClick={() => setData(STARTER_DATA)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Reset to Starter (1940–1960)</button> <button onClick={() => mergeData(DATA_1927_1939)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Load 1927–1939</button> <button onClick={() => mergeData(DATA_1961_1999)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Load 1961–1999</button> <button onClick={() => mergeData(DATA_2000_2024)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Load 2000–2024</button> <button onClick={() => mergeData([...DATA_1927_1939, ...DATA_1961_1999, ...DATA_2000_2024])} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Load All</button> <button onClick={() => setData([])} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Clear All</button> <span className="ml-auto text-xs text-neutral-500">Current total: {data.length} films</span> </div> </section> )} <section className="mb-4 grid grid-cols-1 gap-3 sm:grid-cols-2"> <div className="rounded-2xl border bg-white p-3 shadow-sm"> <div className="mb-2 text-sm font-semibold">Mode</div> <div className="flex flex-wrap gap-2"> <button className={`rounded-xl px-3 py-2 text-sm shadow-sm border ${mode === "Y2F" ? "bg-black text-white" : "hover:bg-neutral-100"}`} onClick={() => setMode("Y2F")} >Year → Winner</button> <button className={`rounded-xl px-3 py-2 text-sm shadow-sm border ${mode === "F2Y" ? "bg-black text-white" : "hover:bg-neutral-100"}`} onClick={() => setMode("F2Y")} >Winner → Year</button> <label className="ml-2 flex items-center gap-2 text-sm"> <input type="checkbox" checked={mcMode} onChange={(e) => setMcMode(e.target.checked)} /> Multiple choice </label> </div> </div> <div className="rounded-2xl border bg-white p-3 shadow-sm"> <div className="mb-2 text-sm font-semibold">Year settings</div> <div className="flex flex-wrap items-center gap-2"> <label className="flex items-center gap-2 text-sm"> <input type="checkbox" checked={useReleaseYear} onChange={(e) => setUseReleaseYear(e.target.checked)} /> Use release year (off = ceremony year) </label> <div className="flex items-center gap-2 text-sm"> <span>Range:</span> <input type="number" className="w-24 rounded-lg border px-2 py-1" value={range.min} onChange={(e) => setRange((r) => ({ ...r, min: Number(e.target.value) }))} /> <span>to</span> <input type="number" className="w-24 rounded-lg border px-2 py-1" value={range.max} onChange={(e) => setRange((r) => ({ ...r, max: Number(e.target.value) }))} /> <button onClick={() => setIndex(0)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Apply</button> </div> </div> </div> </section> {/* Flashcard */} <section className="rounded-2xl border bg-white p-6 shadow-sm"> {working.items.length === 0 ? ( <div className="text-center text-neutral-600">No films in this range. Import more data or adjust the filters.</div> ) : ( <div className="flex flex-col items-center gap-4"> <div className="text-xs text-neutral-500">Card {((index % working.order.length) + 1)} / {working.order.length} • Dataset: {data.length} films</div> <div className="w-full rounded-2xl border bg-neutral-50 p-6 text-center shadow-inner"> {mode === "Y2F" ? ( <div> <div className="text-sm uppercase tracking-wide text-neutral-500">Which film won Best Picture for</div> <div className="mt-1 text-5xl font-extrabold">{promptYear}</div> <div className="mt-1 text-xs text-neutral-500">Year type: {useReleaseYear ? "Release" : "Ceremony"}</div> </div> ) : ( <div> <div className="text-sm uppercase tracking-wide text-neutral-500">What year did this Best Picture winner correspond to?</div> <div className="mt-1 text-3xl font-extrabold">{current.film}</div> <div className="mt-1 text-xs text-neutral-500">Answer expects {useReleaseYear ? "release year" : "ceremony year"}</div> </div> )} </div> {mcMode ? ( <div className="grid w-full grid-cols-1 gap-2 sm:grid-cols-2"> {mcOptions.map((opt, i) => ( <button key={i} className={`rounded-xl border px-4 py-3 text-left text-sm shadow-sm hover:bg-neutral-100 ${ feedback && ((mode === "Y2F" && opt === current.film) || (mode === "F2Y" && opt === (useReleaseYear ? current.releaseYear : (current.ceremonyYear ?? current.releaseYear + 1)))) ? "border-green-500" : feedback && (feedback === "wrong") ? "border-red-400" : "" }`} onClick={() => { if (mode === "Y2F") { setFeedback(opt === current.film ? "correct" : "wrong"); if (opt === current.film) setTimeout(next, 500); } else { const target = useReleaseYear ? current.releaseYear : (current.ceremonyYear ?? current.releaseYear + 1); setFeedback(opt === target ? "correct" : "wrong"); if (opt === target) setTimeout(next, 500); } }} > <div className="flex items-center gap-3"><span className="inline-flex h-6 w-6 items-center justify-center rounded-md border text-xs">{i + 1}</span><span>{opt}</span></div> </button> ))} </div> ) : ( <div className="w-full text-center"> {!revealed ? ( <button onClick={() => setRevealed(true)} className="rounded-xl bg-black px-4 py-3 text-sm text-white shadow-sm hover:opacity-90">Reveal</button> ) : ( <div className="rounded-xl border bg-neutral-50 p-4"> {mode === "Y2F" ? ( <div className="text-lg font-semibold">{current.film}</div> ) : ( <div className="text-lg font-semibold">{useReleaseYear ? current.releaseYear : (current.ceremonyYear ?? current.releaseYear + 1)}</div> )} </div> )} </div> )} <div className="mt-2 flex w-full flex-wrap items-center justify-between gap-2"> <div className="flex items-center gap-2"> <button onClick={prev} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Prev ←</button> <button onClick={next} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Next →</button> <button onClick={() => setIndex(0)} className="rounded-xl border px-3 py-2 text-sm shadow-sm hover:bg-neutral-100">Shuffle</button> </div> <div className="flex items-center gap-2 text-sm"> <button onClick={() => current && toggleBookmark(current)} className={`rounded-xl border px-3 py-2 shadow-sm ${isBookmarked ? "bg-yellow-100" : "hover:bg-neutral-100"}`}>{isBookmarked ? "★ Bookmarked" : "☆ Bookmark"}</button> <span className="text-neutral-500">Bookmarks: {bookmarks.length}</span> </div> </div> </div> )} </section> <footer className="mt-6 text-center text-xs text-neutral-500"> Tip: Build a complete set fast — paste lines like "1929 - The Broadway Melody" through to "2024 - Anora" in Import. Ceremony years can be added too. </footer> </div> </div> ); }