feat(frontend): due-overview card with start-review CTA
This commit is contained in:
55
packages/frontend/src/components/DueOverviewCard.tsx
Normal file
55
packages/frontend/src/components/DueOverviewCard.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { sessionsApi } from '../api/sessions.js';
|
||||||
|
import { useSession } from '../stores/sessionStore.js';
|
||||||
|
|
||||||
|
export interface DueOverview { overdue: number; today: number; tomorrow: number; thisWeek: number; }
|
||||||
|
|
||||||
|
export function DueOverviewCard({ data }: { data: DueOverview }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const total = data.overdue + data.today;
|
||||||
|
async function startReview() {
|
||||||
|
const r = await sessionsApi.startDue();
|
||||||
|
useSession.setState({
|
||||||
|
session: r.session,
|
||||||
|
current: r.queue[0] ?? null,
|
||||||
|
done: r.queue.length === 0,
|
||||||
|
showAnswer: false,
|
||||||
|
shownAt: Date.now(),
|
||||||
|
});
|
||||||
|
navigate(`/practice/${r.session.lessonId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="surface space-y-4 p-5">
|
||||||
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||||
|
<Badge tone="danger" label="Overdue" value={data.overdue} />
|
||||||
|
<Badge tone="brand" label="Vandaag" value={data.today} />
|
||||||
|
<Badge tone="success" label="Morgen" value={data.tomorrow} />
|
||||||
|
<Badge tone="muted" label="Deze week" value={data.thisWeek} />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="btn-primary w-full py-3"
|
||||||
|
onClick={startReview}
|
||||||
|
disabled={total === 0}
|
||||||
|
>
|
||||||
|
{total === 0
|
||||||
|
? 'Niets te reviewen — alles up-to-date 🎉'
|
||||||
|
: `Start review (${total} ${total === 1 ? 'kaart' : 'kaarten'})`}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Badge({ tone, label, value }: { tone: 'danger'|'brand'|'success'|'muted'; label: string; value: number }) {
|
||||||
|
const cls =
|
||||||
|
tone === 'danger' ? 'bg-danger-50 text-danger-700 dark:bg-danger-400/15 dark:text-danger-400'
|
||||||
|
: tone === 'brand' ? 'bg-brand-100 text-brand-700 dark:bg-brand-900/30 dark:text-brand-200'
|
||||||
|
: tone === 'success' ? 'bg-success-50 text-success-700 dark:bg-success-700/15 dark:text-success-400'
|
||||||
|
: 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300';
|
||||||
|
return (
|
||||||
|
<div className={`rounded-2xl p-3 ${cls}`}>
|
||||||
|
<div className="text-[10px] font-semibold uppercase tracking-wider opacity-80">{label}</div>
|
||||||
|
<div className="mt-1 font-display text-2xl font-bold">{value}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user