8.9 KiB
Frontend TODO
Screen 1 — User: Manual Flashcard Creator
Route: /flashcards/new (and /flashcards/:id/edit for editing)
Purpose: Allow a user to create a flashcard for a word they want to learn, with optional dictionary linking to anchor it to a specific sense.
Layout
A single-page form divided into two sections:
Section A — Word lookup
- Text input: "Word or phrase" (
surface_text). As the user types (debounced ~400ms), callGET /api/dictionary/wordforms?lang_code={target_lang}&text={input}and display results inline. - Dictionary search results display as a list of candidate cards. Each card shows:
- Lemma headword (bold), POS label (e.g. "verb", "noun"), gender if present (e.g. "m." / "f.")
- Indented list of senses, each showing: sense index, gloss (= English translation), topics/tags as small chips
- User clicks a sense to select it. Selected state: the sense is highlighted, the sense
idis stored, and Section B is pre-populated. - If no results are found, show a "No dictionary match — you can still create a card manually" message. The form remains usable without a sense link.
- "Clear" button to deselect and start over.
Section B — Card content
Four text inputs, pre-populated from the selected sense but always editable:
| Field | Pre-populated from | Label shown to user |
|---|---|---|
prompt_text |
lemma.headword (if sense selected, else blank) |
Prompt (target language) |
answer_text |
sense.gloss (if sense selected, else blank) |
Answer (English) |
prompt_context_text |
blank | Context for prompt (optional) |
answer_context_text |
blank | Context for answer (optional) |
Card direction selector: two toggle options — Recognition (target → English) and Production (English → target). Defaults to both selected (generates two cards). User can deselect one.
Save action:
POST /api/vocabwith{ surface_text, language_pair_id, entry_pathway: "manual" }→ returns aWordBankEntrywith abank_entry_idanddisambiguation_status.- If a sense was selected and
disambiguation_status != "auto_resolved":PATCH /api/vocab/{entry_id}/sensewith{ sense_id }. POST /api/vocab/{entry_id}/flashcardswith{ direction }for each selected direction.- On success: navigate to the flashcard list or show a confirmation with a "Study now" shortcut.
Edit mode (/flashcards/:id/edit):
- Pre-populate all fields from the existing flashcard record.
- Sense search is pre-filled with the existing
surface_textand the linked sense highlighted (if present). - Save updates the flashcard. (Note: a
PATCH /api/flashcards/:idendpoint does not yet exist — this needs to be added to the API.)
State notes
language_pair_idmust be known before this screen renders. Resolve it from the user's active language pair (stored in app state / fromGET /api/learnable-languages).- A user may have no dictionary match but still create a valid card manually. Do not block submission if the sense search returns nothing.
Screen 2 — Admin: WordBankPack List
Route: /admin/packs
Auth: Admin token required. All calls go to /api/admin/packs/*.
Purpose: Entry point to the pack CMS. Shows all packs (published and draft) and allows creation of new ones.
Layout
- Page header: "Word Packs" + "New Pack" button (opens Screen 3).
- Table with columns: Name (source lang), Name (target lang), Language pair, Proficiencies, Entries, Status (Published / Draft), Actions (Edit, Publish).
- "Publish" action:
POST /api/admin/packs/{id}/publish. Disabled if pack is already published. Show a confirmation modal before calling — publishing is not reversible via the API. - Row click → navigate to Screen 3 (edit mode).
Fetch: GET /api/admin/packs?source_lang={}&target_lang={} (filter controls optional).
Screen 3 — Admin: WordBankPack Detail / Editor
Route: /admin/packs/new and /admin/packs/:id
Purpose: Create or edit a pack, manage its entries, and add flashcard templates to each entry.
Layout
Three vertical sections on the same page:
Section A — Pack metadata
Fields mapping directly to CreatePackRequest / UpdatePackRequest:
| Field | Input type | Notes |
|---|---|---|
name |
text | Pack name in source language (English) |
name_target |
text | Pack name in target language (e.g. French) |
description |
textarea | Description in source language |
description_target |
textarea | Description in target language |
source_lang |
select (ISO 639-1) | Disabled after creation |
target_lang |
select (ISO 639-1) | Disabled after creation |
proficiencies |
multi-select | CEFR values: A1, A2, B1, B2, C1, C2 |
Save: POST /api/admin/packs (new) or PATCH /api/admin/packs/{id} (edit). After creation, the page transitions to edit mode with the new pack ID in the URL.
Section B — Pack entries
A table/list of the pack's word entries. Each row expands to show flashcard templates (Screen 3B).
Adding an entry:
Inline form above the list (always visible):
- Text input: "Word or phrase" (
surface_text). As the user types, callGET /api/dictionary/wordforms?lang_code={target_lang}&text={input}and display results in a dropdown. - Dropdown shows: headword + POS + gender + each sense's gloss. User selects a specific sense.
- Selected state shows a summary pill: e.g. "aller (verb) — to go". Clear button to deselect.
- "Add entry" button →
POST /api/admin/packs/{id}/entrieswith{ surface_text, sense_id }.sense_idis included if a sense was selected; omitted otherwise (entry is created without a sense link — this is valid but means no flashcards can be generated from it until a sense is linked).
Entry row (collapsed):
- Surface text (bold)
- Sense gloss if linked, or a "⚠ No sense linked" warning badge
- Template count (e.g. "2 templates")
- Delete button →
DELETE /api/admin/packs/{id}/entries/{entry_id}with confirmation. - Expand toggle.
Entry row (expanded) — Section 3B:
Shows the flashcard template sub-list for this entry. See Section C below.
Section C — Flashcard templates (per entry)
Rendered inside the expanded entry row.
A flashcard template defines the canonical prompt/answer for this word when a user adopts the pack. Fields map to AddFlashcardTemplateRequest:
| Field | Input type | Notes |
|---|---|---|
card_direction |
select | target_to_source (Recognition) / source_to_target (Production) |
prompt_text |
text | Pre-populated from sense: headword for target_to_source, gloss for source_to_target |
answer_text |
text | Opposite of prompt |
prompt_context_text |
text | Optional — example sentence or grammatical cue |
answer_context_text |
text | Optional — corresponding target-language context |
"Add template" button → POST /api/admin/packs/{id}/entries/{entry_id}/flashcards.
Existing templates are listed below the form, each showing all four fields read-only, with a delete button → DELETE /api/admin/packs/{id}/entries/{entry_id}/flashcards/{template_id}.
(Note: there is no PATCH endpoint for templates — delete and re-create to edit.)
Pre-population hint for admins: When a sense is linked to the entry, the "Add template" form should auto-fill prompt_text and answer_text based on the selected card_direction:
target_to_source: prompt =lemma.headword, answer =sense.glosssource_to_target: prompt =sense.gloss, answer =lemma.headword
These are editable before submitting.
API reference summary
| Endpoint | Used by |
|---|---|
GET /api/dictionary/wordforms?lang_code=&text= |
Screen 1 (live search), Screen 3 (entry add) |
POST /api/vocab |
Screen 1 (save) |
PATCH /api/vocab/{id}/sense |
Screen 1 (save, when sense selected) |
POST /api/vocab/{id}/flashcards |
Screen 1 (save) |
GET /api/admin/packs |
Screen 2 |
POST /api/admin/packs |
Screen 3 (new pack) |
GET /api/admin/packs/{id} |
Screen 3 (edit pack) |
PATCH /api/admin/packs/{id} |
Screen 3 (update metadata) |
POST /api/admin/packs/{id}/publish |
Screen 2 |
POST /api/admin/packs/{id}/entries |
Screen 3 (add entry) |
DELETE /api/admin/packs/{id}/entries/{entry_id} |
Screen 3 (remove entry) |
POST /api/admin/packs/{id}/entries/{entry_id}/flashcards |
Screen 3 (add template) |
DELETE /api/admin/packs/{id}/entries/{entry_id}/flashcards/{template_id} |
Screen 3 (remove template) |
API gaps (need to be added before frontend is complete)
PATCH /api/flashcards/{id}— update prompt/answer/context text on an existing user flashcard (needed for Screen 1 edit mode)GET /api/flashcards/{id}— fetch a single flashcard by ID (needed to pre-populate Screen 1 edit mode)GET /api/dictionary/lemmas?lang_code=&headword=or similar — a headword-level search returning all senses for a lemma directly, useful as a fallback when the wordform search returns no results but the user typed a known headword