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>
);
}