question/word-Pairs zeigen die Negativ-Zeile jetzt auch wenn leer ('fehlt'
statt stilles Ausblenden), damit eine fehlende Negativ-Antwort auffaellt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer Button im EditPairForm: speichert das Pair und öffnet direkt das
Übersetzen-&-Prüfen-Modal (handleSave({translateAfter}) → onSavedAndTranslate
→ handleTranslate). Bestehender Speichern-Button unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ruft /pairs/:id/translate mit overwrite:true, um falsche bestehende
Übersetzungen (z.B. SV) neu zu generieren, und lädt den Modal-Inhalt neu.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- PairReviewModal: zeigt Frage/Positiv/Negativ in de/en/sv (Wörter bei
word-Typ) zum Gegenprüfen, mit Reviewed/Blocked-Buttons; bei 409 wird
die missing-Liste inline angezeigt.
- ContentCreation: pro Pair-Karte '🪄 Übersetzen & prüfen' (ruft
/pairs/:id/translate, öffnet Modal); nach Review werden Pairs, Objekte
und Bilder neu geladen.
- api.js: Fehler-Payload (z.B. { missing }) wird am Error durchgereicht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Navigation: Dashboard/Inhalte/Audio/Veröffentlichen/Datenbank/Einstellungen mit Active-State
- Veröffentlichen (/publish): Pairs sortiert nach 'am wenigsten fehlt', 1-Klick-Publish je Sprache
- Einstellungen (/settings): TTS-Stimme + Parameter pro Sprache bearbeiten
- tts-settings in DB-Admin; Dashboard-Kacheln ergänzt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dashboard: Pipeline-Übersicht (Counts pro Status) + Werkzeug-Kacheln
- AudioHub (/audio): Coverage-Matrix je Tabelle×Sprache, Generieren-Buttons, Player
- WordGenerator (/content/words): Thema→KI-Vorschau→Übernehmen als translated
- reviewed in STATUS_COLORS + Status-Optionen (objects/questions/statements/pairs)
- audios-Tabelle um source_*/language erweitert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace async word search with synchronous lookup via allObjects._words,
which is already populated. Eliminates race conditions and fetch failures.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of waiting for the wordMap effect to reconcile, look up word IDs
for each label→objectId pair immediately in load() so objectAssignments
is set before the user sees the form.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract {{label.o:objectId}} refs from raw sentence text before resolving
placeholders, then reconcile with wordMap once it's built so the object
assignment dropdown reflects the saved state on re-open.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- STATUS_COLORS: published → violet, draft → gray
- StatusSelect: shared dropdown component with status colors
- Top bar: picture status dropdown (uploaded/published/blocked)
- ObjectListPanel: per-object status dropdown, changeable inline
- PairsPanel collapsed: difficulty badge (Leicht/Mittel) + status badge
- EditPairForm: status dropdown at top of edit form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Claude now generates 40 pairs: +10 × word type (positive_words / negative_words)
- difficulty easy→1, medium→2 stored as difficulty_level on each pair
- findOrCreateWord() looks up or creates words by title before linking
- savePairsForObject handles all 4 pair types (text/yes_no/question/word)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Button moves to ObjectListPanel (visible when ≥1 object exists).
One Claude call per object — image + that object's words & coordinates.
Progress shows "Objekt 2/3 — Pair 12/30". Pair saving logic extracted
into shared savePairsForObject() helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes direct browser→Anthropic call (CORS issue).
Now calls /api/claude/generate-pairs on the snakkimo-API,
which holds the API key server-side.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds "✨ Auto Pairs erstellen" button to the PairsPanel. On click,
sends the image and all object coordinates to Claude Haiku, which
returns 30 structured pairs (10× text, 10× yes_no, 10× question,
each split easy/medium difficulty) as JSON. Pairs are then created
and linked via the API with a live progress indicator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
{{wordId}} in JSX is parsed as a JS expression trying to resolve the variable
`wordId`. Wrap in a string expression so it renders as literal text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tokenize(): add try/catch + filter empty/null parts so a bad regex never
propagates to the React render tree
- Word detection useEffect: wrap entire async block and each per-token fetch
in try/catch; filter null/malformed word objects out of the map before
setWordMap() so the render never receives w.id === undefined
- "Erkannte Wörter" sections in PairForm and EditPairForm: filter wordMap
entries where w?.id is falsy; use (allObjects || []) defensively
- handleCreateWord: only update wordMap when the API response contains w.id
- After successful save in PairForm: reset all text/word state so the form
starts clean for the next pair
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-step: "Pair löschen" → confirm "Wirklich löschen" before DELETE /pairs/:id.
On success closes the form and reloads the pairs list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resolvePlaceholders: replace fragile [wo] char class with general
/^(.+?)\.[a-z]+:[0-9a-f-]{36}$/i — handles any type prefix safely
- Add sync stripPlaceholders() helper for card display (no async needed)
- PairsPanel cards now show actual sentence text instead of UUID stubs,
using enriched pair.question / pair.positive_statement data from API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Placeholder format: {{Hund.w:uuid}} / {{Hund.o:uuid}} — label embedded,
no API lookup needed on render/edit load
- resolvePlaceholders handles both new format (fast) and old {{uuid}} (fallback)
- Word auto-detection: fuzzy match via search API + client-side starts-with
+ 60% length ratio (e.g. "Hunde" matches "Hund")
- "Als Wort erstellen" now shows editable input pre-filled with selection,
user can correct before saving
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New ContentCreation.jsx: 3-column layout
Left: object list + "+ Objekt hinzufügen" button
Center: dual-mode canvas (draw OR highlight)
Right: ObjectAddPanel (mode=add) or PairsPanel (mode=objectId)
- After saving object → auto-switches to PairsPanel for that object
- All ObjectCreation + StatementCreation logic merged into one page
- All pictures loaded (no objects_created filter)
- "Objekte abgeschlossen" button marks picture (visual badge)
- ContentHub: 2 tiles (Content Erstellen + Veröffentlichen placeholder)
- App.jsx: /content/creation route, old /content/objects + /content/statements removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- EditPairForm: loads question + statement text (resolves {{uuid}} back
to word titles via resolvePlaceholders), pre-fills all fields
- Per-type editing: text/yes_no/question/word with same UX as create
- Word link diff on save (add new, remove removed via apiUnlink)
- Creates missing question/statement records if type or content added
- ✏️ button per pair card opens EditPairForm, replaces card inline
- onPairsReload: re-fetches pairs list after edit saved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- withPlaceholders(text, wordMap, objectAssignments) — uses {{objectId}}
when an object is assigned to a word, otherwise {{wordId}}
- PairForm: objectAssignments state (carried over between saves)
- Detected words section shows object dropdown per word
— only objects from current picture that have the word linked
— shows "Objekt #N — word1, word2" labels
— unselected = {{wordId}}, selected = {{objectId}}
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- answer_type is now a single value (text/yes_no/question/word)
- Dropdown replaces checkboxes, per-type field sections
- Text: only positive statement
- Yes/No: optional question + answer picker
- Question: question* + positive* + optional negative
- Word: optional question + word pickers
- After save: texts/words carry over, type resets for next pair
- Fix: closed missing div in PairsPanel scroll container
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the answer_type dropdown with 3 independent checkboxes:
- Text: positive statement required; negative requires question+positive;
question is optional standalone; auto-detects words + {{uuid}} placeholders
- Ja/Nein: addon, adds answer field (null/true/false) to positive statement
- Wort: opens positive/negative word pickers → linked via statement_positive/negative_words
Validation rules:
- At least one type must be checked
- Text: positive statement non-empty
- Negative statement only allowed when question + positive both filled
Word creation: highlight text in any field → 'Als Wort erstellen' button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ObjectCreation:
- Filter: only shows pictures where objects_created=false
- New '✓ Alle Objekte erstellt' button (enabled when ≥1 object linked)
→ PATCHes picture with objects_created:true + auto timestamp
→ removes picture from list immediately
ContentHub:
- Statement Creation tile enabled (was: grayed out)
api.js:
- getUserLang() → reads native_lang from stored user (default 'de')
- langField(suffix) → returns e.g. 'sentence_de' based on user lang
- login() stores native_lang in localStorage user object
StatementCreation (/content/statements):
- Shows pictures where objects_created=true
- Left 1/5: clickable objects list → highlights selections on canvas
- Center 2/5: image with canvas, draws selected object's polygons in color
- Right 2/5: PairsPanel
- answer_type dropdown + 'Add new pair' toggle
- PairForm: Question / Positive / Negative textareas
- HighlightedTextarea: overlay technique, auto-detects words from DB
(debounced GET /words?titel_de=, colored mark via rgba background)
- 'Als Wort erstellen' button when text is selected
- 'Save pair' → creates question + 2 separate statements + pair
→ sentences stored with {{uuid}} placeholders for matched words
→ pair linked to selected object
- List of existing pairs with question/positive/negative preview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a full Content Management section:
- Nav link "Content" in header (all pages)
- ContentHub at /content with 3 tiles (Object/Statement/Content Creation)
- ObjectCreation at /content/objects:
- Top bar: ← → pagination through all "uploaded" pictures
- Left panel (1/5): existing objects per picture with their words, + button to start draw mode
- Center: image on dark bg with canvas overlay for polygon drawing
- Right words panel (1/5): picture words + new object words (each with search/create)
- Right toolbar (1/5): draw instructions, "Auswahl hinzufügen", numbered selections list, "Objekt speichern" (requires ≥1 selection + ≥1 object word)
- Canvas drawing: click=add point, dblclick=close polygon, live preview line to mouse cursor
- Selections stored as [{points:[{x,y},...]}] (relative 0-1 coords) in objects.selections JSONB
- Object saved with status "draft", linked picture + words
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each row gets a trash can button that appears on hover. Clicking it
shows an inline confirm (✓ / ✕) to prevent accidental deletion. On
confirm, calls DELETE /endpoint/:id and removes the row from state.
If the deleted record is open in the modal, the modal closes too.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a split-layout hero section at the top of the RecordModal
for pictures: left side shows the image preview (~40%), right side
shows status, design (both editable) and the linked words relation
manager inline. Remaining fields (blurhash, picture_link, metadata)
continue to appear in the sections below.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- RelationManager component: shows linked items as removable tags,
live search to find and add new links (×-button to unlink)
- tables.js: full fetchRelated config with linkEndpoint + searchEndpoint
for words↔pictures, words↔categories, objects↔words, objects↔pictures
- api.js: add apiLink, apiUnlink, apiDelete helpers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- + Button in TableView for tables with a create form (words, pictures)
- Words: form with titel_de/en/sv + difficulty_level → POST /words
- Pictures: design field + image uploader → POST /pictures then POST /pictures/:id/upload
- Image drag-drop area with preview before upload, sends multipart to Hetzner via API
- New record prepended to table on success
- apiPost + apiUpload helpers added to api.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Click any table row to open a full-detail popup
- Editable fields (text, textarea, select, number) with PATCH save
- Read-only display for IDs, timestamps, arrays
- Pictures table fetches words via /pictures/:id/words in modal
- Stop propagation on linked-field chips so they don't trigger modal
- apiPatch + apiFetchOne helpers in api.js
- editableFields + fetchRelated config in tables.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React + Vite + Tailwind dashboard with:
- Login (JWT via snakkimo auth)
- Dashboard with Datenbankverwaltung + Contentverwaltung tiles
- Table overview with record counts (total, published, blocked)
- Table record viewer with text/status filters and linked field navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>