diff --git a/backend/main.py b/backend/main.py index dd80b87..62e037b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -323,15 +323,18 @@ def ueben_antwort(a: UebenAntwort): @app.get("/api/topics/{topic}/baustein/{baustein_id}/karten") -def baustein_karten(topic: str, baustein_id: int): - # Flashcards genau dieses Bausteins — NICHT faellig-gefiltert (gerade gelesen → jetzt - # prüfen). Beantwortet wird über /api/ueben/antwort, Leitner-Spacing bleibt intakt. - rows = db.query( - "SELECT ar.id, ar.inhalt, a.titel, a.level, COALESCE(l.box,1) AS box" - " FROM artefakte ar JOIN atome a ON ar.atom_id=a.id" - " LEFT JOIN leitner l ON l.artefakt_id=ar.id" - " WHERE a.topic=? AND a.baustein_id=? AND ar.typ='flashcard'" - " AND ar.status='verifiziert' ORDER BY a.ord, ar.id", (topic, baustein_id)) +def baustein_karten(topic: str, baustein_id: int, alle: bool = False): + # Absolvierte Karten (richtig beantwortet → Leitner-faellig in der Zukunft) + # bleiben draußen — der Abruf startet nicht bei jedem Besuch von vorn. + # alle=True zeigt den vollen Stapel (Button „Alle erneut üben"). + sql = ("SELECT ar.id, ar.inhalt, a.titel, a.level, COALESCE(l.box,1) AS box" + " FROM artefakte ar JOIN atome a ON ar.atom_id=a.id" + " LEFT JOIN leitner l ON l.artefakt_id=ar.id" + " WHERE a.topic=? AND a.baustein_id=? AND ar.typ='flashcard'" + " AND ar.status='verifiziert'") + if not alle: + sql += " AND COALESCE(l.faellig, date('now')) <= date('now')" + rows = db.query(sql + " ORDER BY a.ord, ar.id", (topic, baustein_id)) return [{**r, "inhalt": db.uj(r["inhalt"], {})} for r in rows] diff --git a/frontend/src/api.js b/frontend/src/api.js index a2897da..b13a5bd 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -35,8 +35,8 @@ export const api = { ueben: (topic) => req(`/api/topics/${e(topic)}/ueben`), antwort: (artefakt_id, richtig) => req('/api/ueben/antwort', { method: 'POST', body: JSON.stringify({ artefakt_id, richtig }) }), - bausteinKarten: (topic, bausteinId) => - req(`/api/topics/${e(topic)}/baustein/${e(bausteinId)}/karten`), + bausteinKarten: (topic, bausteinId, alle = false) => + req(`/api/topics/${e(topic)}/baustein/${e(bausteinId)}/karten${alle ? '?alle=1' : ''}`), lernstand: (topic) => req(`/api/topics/${e(topic)}/lernstand`), bausteinFertig: (topic, bausteinId) => req(`/api/topics/${e(topic)}/baustein/${e(bausteinId)}/fertig`, { method: 'POST', body: '{}' }), diff --git a/frontend/src/components/Lesemodus.vue b/frontend/src/components/Lesemodus.vue index 6996109..7e0ba58 100644 --- a/frontend/src/components/Lesemodus.vue +++ b/frontend/src/components/Lesemodus.vue @@ -66,6 +66,22 @@ function taste(e) { e.preventDefault() buehne.value?.scrollBy({ top: e.key === 'ArrowDown' ? 140 : -140, behavior: 'smooth' }) } + // Abruf: Leertaste deckt auf, 1 = falsch, 2 = richtig + else if (screen.value.typ === 'abruf' && stapel.value.length) { + if (e.key === ' ') { e.preventDefault(); zeigeAntwort.value = true } + else if (zeigeAntwort.value && e.key === '1') antworten(false) + else if (zeigeAntwort.value && e.key === '2') antworten(true) + } + // Prüfung: 1–4 toggeln die Aussagen, Leertaste prüft / blättert zum nächsten Panel + else if (screen.value.typ === 'pruefung' && panel.value.length) { + if (e.key === ' ') { + e.preventDefault() + aufloesung.value ? naechstesPanel() : pruefen() + } else if (['1', '2', '3', '4'].includes(e.key)) { + const x = panel.value[Number(e.key) - 1] + if (x) toggle(x.id) + } + } } // Wisch-Navigation (mobil): klar horizontal + kurz genug = blättern. @@ -92,12 +108,23 @@ function wischEnde(e) { } // ── Abrufprüfung (Karteikarten) ──────────────────────────────────────────────── +// Persistenz über Leitner (DB): richtig beantwortete Karten sind bis zur +// Wiedervorlage nicht fällig und erscheinen beim nächsten Besuch nicht mehr. const stapel = ref([]) +const alleKarten = ref([]) // gefüllt, wenn alles absolviert → „Alle erneut üben" const zeigeAntwort = ref(false) const sende = ref(false) async function abrufLaden(bausteinId) { + zeigeAntwort.value = false + alleKarten.value = [] stapel.value = await api.bausteinKarten(props.topic, bausteinId) + if (!stapel.value.length) + alleKarten.value = await api.bausteinKarten(props.topic, bausteinId, true) +} +function nochmalAlle() { + stapel.value = alleKarten.value + alleKarten.value = [] zeigeAntwort.value = false } async function antworten(richtig) { @@ -108,6 +135,7 @@ async function antworten(richtig) { await api.antwort(karte.id, richtig) stapel.value = richtig ? stapel.value.slice(1) : [...stapel.value.slice(1), karte] zeigeAntwort.value = false + if (!stapel.value.length) await abrufLaden(screen.value.baustein) } finally { sende.value = false } } @@ -215,14 +243,21 @@ onUnmounted(() => {