# 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