Fix ordering giveaways, the stuck false label, add Regenerate deck
Ordering cards - Generation prompt now forbids dates, years, times, step numbers and ordinals in ordering items (spec + hard rules), and the server-side validation drops any ordering card whose items still contain them instead of shipping a giveaway. True/False - The 'false ✕' swipe label was permanently visible on the card: its opacity curve was inverted, so the clamp at rest was 1. Flipped the range (and tightened both ranges for the elastic drag) — each label now only fades in while swiping in that direction. Regenerate deck - Results screen gains a Regenerate deck button: rebuilds the activities from the deck's stored source notes, replaces the deck in storage (attempts/best score reset) and starts the fresh run from slide 1. - StudyFeed tracks the current deck so the header, export and messages use the regenerated title/activities. - Fixed a spurious 0% attempt recorded when a regenerated deck swapped in: scroll anchoring held the old results position, so the feed briefly reported 5/5 with zero answers. The scroll now resets before the new slides render, overflow-anchor is disabled on the feed, and a run with no answers is never recorded. Tests: new deck-fixes suite 15/15 (TF overlay at rest/drag, regenerate with loading state + storage reset, ordering guard drop/keep); retry 14/14, deck chrome 15/15, pwa 16/16, final-ux 12/12, security 8/8, contrast 20/20.
This commit is contained in:
parent
1bacf43ed2
commit
33967f9454
6 changed files with 122 additions and 16 deletions
|
|
@ -6,7 +6,7 @@ import { motion } from "framer-motion";
|
|||
import type { Activity, Deck } from "@/lib/schemas";
|
||||
import { downloadDeck } from "@/lib/storage";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { BoltIcon, DownloadIcon, FlameIcon, HomeIcon, RefreshIcon, SnowflakeIcon } from "@/components/icons";
|
||||
import { BoltIcon, DownloadIcon, FlameIcon, HomeIcon, RefreshIcon, SnowflakeIcon, SparklesIcon } from "@/components/icons";
|
||||
import { Button, Chip } from "@/components/ui";
|
||||
|
||||
import type { AttemptResult } from "./StudyFeed";
|
||||
|
|
@ -60,6 +60,9 @@ export function ResultsScreen({
|
|||
retryMode,
|
||||
onRetryMissed,
|
||||
onRestart,
|
||||
onRegenerate,
|
||||
regenerating,
|
||||
regenerateError,
|
||||
}: {
|
||||
deck: Deck;
|
||||
xp: number;
|
||||
|
|
@ -71,6 +74,9 @@ export function ResultsScreen({
|
|||
retryMode: boolean;
|
||||
onRetryMissed: () => void;
|
||||
onRestart: () => void;
|
||||
onRegenerate: () => void;
|
||||
regenerating: boolean;
|
||||
regenerateError: string | null;
|
||||
}) {
|
||||
const percent = total > 0 ? Math.round((correct / total) * 100) : 0;
|
||||
const perfect = missed.length === 0 && total > 0;
|
||||
|
|
@ -151,6 +157,21 @@ export function ResultsScreen({
|
|||
<RefreshIcon className="size-5" /> Study the whole deck again
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
size="lg"
|
||||
variant="secondary"
|
||||
onClick={onRegenerate}
|
||||
loading={regenerating}
|
||||
disabled={regenerating}
|
||||
>
|
||||
<SparklesIcon className="size-5" />
|
||||
{regenerating ? "Regenerating…" : "Regenerate deck"}
|
||||
</Button>
|
||||
{regenerateError ? (
|
||||
<p className="text-center text-xs text-rose-600 dark:text-rose-300" role="alert">
|
||||
{regenerateError}
|
||||
</p>
|
||||
) : null}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="ghost" onClick={() => downloadDeck(deck)}>
|
||||
<DownloadIcon className="size-4" /> Export JSON
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue