language-learning-app/api/frontend-todo.md

186 lines
8.9 KiB
Markdown
Raw Permalink Normal View History

# 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), call `GET /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 `id` is 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:**
1. `POST /api/vocab` with `{ surface_text, language_pair_id, entry_pathway: "manual" }` → returns a `WordBankEntry` with a `bank_entry_id` and `disambiguation_status`.
2. If a sense was selected and `disambiguation_status != "auto_resolved"`: `PATCH /api/vocab/{entry_id}/sense` with `{ sense_id }`.
3. `POST /api/vocab/{entry_id}/flashcards` with `{ direction }` for each selected direction.
4. 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_text` and the linked sense highlighted (if present).
- Save updates the flashcard. *(Note: a `PATCH /api/flashcards/:id` endpoint does not yet exist — this needs to be added to the API.)*
### State notes
- `language_pair_id` must be known before this screen renders. Resolve it from the user's active language pair (stored in app state / from `GET /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, call `GET /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}/entries` with `{ surface_text, sense_id }`.
- `sense_id` is 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.gloss`
- `source_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