diff --git a/app.py b/app.py index 73c6f58..dd464ec 100644 --- a/app.py +++ b/app.py @@ -1836,6 +1836,22 @@ def directus_db_object_pairs(obj_id): s_d, _ = _directus("GET", f"/items/db_statement/{sid}?fields=id,statement_de,level,status", token) if s_d.get("data"): statements.append(s_d["data"]) + # Wörter für jede Question laden + for q in questions: + qid = q["id"] + qw_junc, _ = _directus("GET", f"/items/db_question_db_words?filter[db_question_id][_eq]={qid}&fields=id,db_words_id.id,db_words_id.titel_de,db_words_id.level&limit=100", token) + q["words"] = [ + {"junction_id": e["id"], "word_id": e["db_words_id"]["id"], "titel_de": e["db_words_id"]["titel_de"], "level": e["db_words_id"]["level"]} + for e in (qw_junc.get("data") or []) if isinstance(e.get("db_words_id"), dict) + ] + # Wörter für jedes Statement laden + for s in statements: + sid = s["id"] + sw_junc, _ = _directus("GET", f"/items/db_statement_db_words?filter[db_statement_id][_eq]={sid}&fields=id,db_words_id.id,db_words_id.titel_de,db_words_id.level&limit=100", token) + s["words"] = [ + {"junction_id": e["id"], "word_id": e["db_words_id"]["id"], "titel_de": e["db_words_id"]["titel_de"], "level": e["db_words_id"]["level"]} + for e in (sw_junc.get("data") or []) if isinstance(e.get("db_words_id"), dict) + ] result.append({**pair, "questions": questions, "statements": statements}) return jsonify({"data": result}) else: @@ -1865,12 +1881,14 @@ def directus_db_object_pairs(obj_id): for we in words: titel_de = (we.get("titel_de") or "").strip() w_level = int(we.get("level") or level) + link_to = we.get("link_to", "both") # 'question', 'statement', 'both' if not titel_de: continue try: wid, _ = _find_or_create_db_word(titel_de, w_level, token) - _directus("POST", "/items/db_statement_db_words", token, {"db_statement_id": stmt_id, "db_words_id": wid}) - if q_id: + if link_to in ("statement", "both"): + _directus("POST", "/items/db_statement_db_words", token, {"db_statement_id": stmt_id, "db_words_id": wid}) + if link_to in ("question", "both") and q_id: _directus("POST", "/items/db_question_db_words", token, {"db_question_id": q_id, "db_words_id": wid}) except Exception as e: print(f"[db_object_pairs] word error '{titel_de}': {e}") @@ -1953,6 +1971,41 @@ def directus_db_pair(pair_id): _directus("POST", "/items/db_pairs_db_question", token, {"db_pairs_id": pair_id, "db_question_id": qid}) + # Neue Wörter hinzufügen + words_add = body.get("words_add", []) + # Statement-ID und Question-ID nochmal laden + s_junc2, _ = _directus("GET", f"/items/db_pairs_db_statement?filter[db_pairs_id][_eq]={pair_id}&fields=db_statement_id&limit=1", token) + stmt_id_edit = ((s_junc2.get("data") or [{}])[0] or {}).get("db_statement_id") + q_junc2, _ = _directus("GET", f"/items/db_pairs_db_question?filter[db_pairs_id][_eq]={pair_id}&fields=db_question_id&limit=1", token) + q_id_edit = ((q_junc2.get("data") or [{}])[0] or {}).get("db_question_id") + + for we in words_add: + titel_de = (we.get("titel_de") or "").strip() + w_level = int(we.get("level") or 50) + link_to = we.get("link_to", "both") + if not titel_de: + continue + try: + wid, _ = _find_or_create_db_word(titel_de, w_level, token) + if link_to in ("statement", "both") and stmt_id_edit: + _directus("POST", "/items/db_statement_db_words", token, {"db_statement_id": stmt_id_edit, "db_words_id": wid}) + if link_to in ("question", "both") and q_id_edit: + _directus("POST", "/items/db_question_db_words", token, {"db_question_id": q_id_edit, "db_words_id": wid}) + except Exception as e: + print(f"[db_pair_patch] word error '{titel_de}': {e}") + + # Wörter entfernen + words_remove = body.get("words_remove", []) + for wr in words_remove: + link_to = wr.get("link_to", "both") + junction_id = wr.get("junction_id") + if not junction_id: + continue + if link_to in ("statement", "both"): + _directus("DELETE", f"/items/db_statement_db_words/{junction_id}", token) + if link_to in ("question", "both"): + _directus("DELETE", f"/items/db_question_db_words/{junction_id}", token) + return jsonify({"ok": True}) else: # DELETE diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 846f9e2..7b88167 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -440,7 +440,7 @@ export async function createDbPair( question_de?: string statement_de: string level: number - words: { titel_de: string; level: number }[] + words: { titel_de: string; level: number; link_to: 'question' | 'statement' | 'both' }[] }, token: string ): Promise<{ ok: boolean; pair_id: string; statement_id: string; question_id: string | null }> { @@ -465,7 +465,13 @@ export async function getDbObjectWords(objectId: string, token: string): Promise export async function updateDbPair( pairId: string, - payload: { level?: number; question_de?: string; statement_de?: string }, + payload: { + level?: number + question_de?: string + statement_de?: string + words_add?: { titel_de: string; level: number; link_to: 'question' | 'statement' | 'both' }[] + words_remove?: { junction_id: number; link_to: 'question' | 'statement' | 'both' }[] + }, token: string ): Promise { const res = await fetch(`/api/directus/db-pairs/${pairId}`, { diff --git a/frontend/src/pages/GenerateIt.tsx b/frontend/src/pages/GenerateIt.tsx index 00196fc..cd1bbdf 100644 --- a/frontend/src/pages/GenerateIt.tsx +++ b/frontend/src/pages/GenerateIt.tsx @@ -6,14 +6,14 @@ import { getDbPictures, getDbObjects, getDbObjectPairs, - getDbObjectWords, + getDbPictureWords, createDbPair, updateDbPair, deleteDbPair, directusAssetUrl, } from '../api' import { useAuth } from '../context/AuthContext' -import type { DbPicture, DbObject, DbPair, CanvasObject } from '../types' +import type { DbPicture, DbObject, DbPair, DbPairWordEntry, CanvasObject } from '../types' const ChevronLeftIcon = () => ( @@ -31,14 +31,13 @@ const ChevronRightIcon = () => ( interface PendingWord { titel_de: string level: number + link_to: 'question' | 'statement' | 'both' } -import type { DbWord } from '../types' - interface PairFormProps { objectId: string token: string - suggestions: DbWord[] + suggestions: { titel_de: string; level: number }[] onSaved: () => void onCancel: () => void } @@ -54,11 +53,11 @@ function PairForm({ objectId, token, suggestions, onSaved, onCancel }: PairFormP const [error, setError] = useState('') const wordInputRef = useRef(null) - const addWord = (titel_de?: string, lvl?: number) => { + const addWord = (titel_de?: string, lvl?: number, defaultLinkTo: 'question' | 'statement' | 'both' = 'both') => { const t = (titel_de ?? wordInput).trim() const l = lvl ?? wordLevel if (!t || words.some(w => w.titel_de === t)) { if (!titel_de) setWordInput(''); return } - setWords(prev => [...prev, { titel_de: t, level: l }]) + setWords(prev => [...prev, { titel_de: t, level: l, link_to: defaultLinkTo }]) if (!titel_de) { setWordInput(''); setWordLevel(50); wordInputRef.current?.focus() } } @@ -136,17 +135,17 @@ function PairForm({ objectId, token, suggestions, onSaved, onCancel }: PairFormP {/* Words input */}
- {/* Vorschläge aus dem Objekt */} + {/* Vorschläge aus dem Bild */} {suggestions.length > 0 && (
- {suggestions.map(s => { + {suggestions.map((s, idx) => { const already = words.some(w => w.titel_de === s.titel_de) return ( + ))} + + ))} +
+ )} + {/* Neues Wort hinzufügen */} +
+ setEditWordInput(e.target.value)} + onKeyDown={e => { + if (e.key === 'Enter') { + const t = editWordInput.trim() + if (t) { setNewWords(prev => [...prev, { titel_de: t, level: editWordLevel, link_to: editWordLinkTo }]); setEditWordInput('') } + } + }} + placeholder="Wort…" + style={{ flex: 1, minWidth: 80, padding: '4px 7px', borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface-2)', color: 'var(--text-1)', fontSize: 12 }} + /> + setEditWordLevel(Math.min(100, Math.max(1, Number(e.target.value))))} + style={{ width: 44, padding: '4px', borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface-2)', color: 'var(--text-1)', fontSize: 12, textAlign: 'center' }} + /> + + +
+ {newWords.length > 0 && ( +
+ {newWords.map((w, i) => ( + + {w.titel_de} L{w.level} {w.link_to === 'question' ? 'F' : w.link_to === 'statement' ? 'A' : 'B'} + + + ))} +
+ )} +
+
- {/* Wörter des gewählten Objekts */} - {objectWords.length > 0 && ( + {/* Wörter des aktuellen Bildes */} + {pictureWords.length > 0 && (

- Wörter - {objectWords.length} + Bild-Wörter + {pictureWords.length}

- {objectWords.map(w => ( - ( +
diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 119f5d9..7e87ae9 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -80,11 +80,19 @@ export interface DbWord { status: string } +export interface DbPairWordEntry { + junction_id: number + word_id: string + titel_de: string + level: number +} + export interface DbPairQuestion { id: string question_de: string level: number status: string + words: DbPairWordEntry[] } export interface DbPairStatement { @@ -92,6 +100,7 @@ export interface DbPairStatement { statement_de: string level: number status: string + words: DbPairWordEntry[] } export interface DbPair {