feat(frontend): resume active session prompt

This commit is contained in:
2026-05-20 21:26:09 +02:00
parent f1abb64573
commit b1992d0dad
2 changed files with 44 additions and 2 deletions

View File

@@ -1,21 +1,38 @@
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import { statsApi, type Overview } from '../api/stats.js';
import { sessionsApi } from '../api/sessions.js';
import type { SessionRow } from '@flashcard/shared';
import { useLessons } from '../stores/lessonsStore.js';
import { formatDuration, formatDate } from '../lib/format.js';
export function DashboardPage() {
const { tree, refresh } = useLessons();
const [ov, setOv] = useState<Overview | null>(null);
const [active, setActive] = useState<SessionRow | null>(null);
const navigate = useNavigate();
useEffect(() => {
refresh();
statsApi.overview().then(setOv);
}, [refresh]);
useEffect(() => {
sessionsApi.active().then(setActive).catch(() => {});
}, []);
return (
<div className="mx-auto max-w-4xl p-6">
<h1 className="mb-6 text-3xl font-semibold">Dashboard</h1>
{active && (
<div className="mb-4 flex items-center justify-between rounded bg-amber-100 p-3 text-sm dark:bg-amber-900/30">
<span>Je hebt een open sessie ({active.cardsShown} kaarten behandeld).</span>
<div className="flex gap-2">
<button className="rounded bg-amber-600 px-3 py-1 text-white" onClick={() => navigate(`/practice/${active.lessonId}`)}>Hervatten</button>
<button className="rounded bg-slate-200 px-3 py-1 dark:bg-slate-800" onClick={async () => { await sessionsApi.abandon(active.id); setActive(null); }}>Afsluiten</button>
</div>
</div>
)}
<div className="mb-6 grid grid-cols-3 gap-4">
<Stat label="🔥 Streak" value={`${ov?.streakDays ?? 0} dagen`} />
<Stat label="Sessies" value={String(ov?.totalSessions ?? 0)} />

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import type { Card } from '@flashcard/shared';
import { cardsApi } from '../api/cards.js';
import { sessionsApi } from '../api/sessions.js';
import { useSession } from '../stores/sessionStore.js';
import { Flashcard } from '../components/Flashcard.js';
@@ -12,7 +13,31 @@ export function PracticePage() {
const [card, setCard] = useState<Card | null>(null);
useEffect(() => {
if (!session) { navigate(`/practice/${lessonId}/setup`); return; }
if (useSession.getState().session) return;
(async () => {
const active = await sessionsApi.active();
if (!active || String(active.lessonId) !== lessonId) return;
const nx = await sessionsApi.next(active.id);
useSession.setState({
session: active,
current: nx.done ? null : nx.item,
done: nx.done,
showAnswer: false,
shownAt: Date.now(),
});
})();
}, [lessonId]);
useEffect(() => {
if (session) return;
const t = setTimeout(async () => {
if (useSession.getState().session) return;
const active = await sessionsApi.active();
if (!active || String(active.lessonId) !== lessonId) {
navigate(`/practice/${lessonId}/setup`);
}
}, 50);
return () => clearTimeout(t);
}, [session, lessonId, navigate]);
useEffect(() => {