diff --git a/src/components/PairReviewModal.jsx b/src/components/PairReviewModal.jsx index 481fcc8..778979b 100644 --- a/src/components/PairReviewModal.jsx +++ b/src/components/PairReviewModal.jsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { apiPost, apiPatch } from '../lib/api'; +import { buildRows } from '../lib/pairRows'; const LANGS = [ { code: 'de', flag: '🇩🇪' }, @@ -7,59 +8,6 @@ const LANGS = [ { code: 'sv', flag: '🇸🇪' }, ]; -// Platzhalter {{label.type:uuid}} → nur das Label anzeigen. -function strip(text) { - if (!text) return ''; - return text.replace(/\{\{([^}]+)\}\}/g, (_, inner) => { - const m = inner.match(/^(.+?)\.[a-z]+:[0-9a-f-]{36}$/i); - return m ? m[1] : inner; - }); -} - -// Baut die Anzeigezeilen je nach answer_type. Jede Zeile ist entweder -// - { kind: 'lang', label, color, cell(l) } → eine Zelle pro Sprache (übersetzbar) -// - { kind: 'single', label, color, value } → ein einzelner Wert (nicht sprachabhängig) -function buildRows(content) { - if (!content) return []; - const type = content?.answer_type; - const rows = []; - const wordsCell = (stmt) => (l) => - (stmt?.words || []).map(w => w[`titel_${l}`] || '—').join(', '); - const sentenceCell = (stmt, prefix) => (l) => - strip(stmt?.sentence?.[`${prefix}_${l}`] || ''); - - // Frage (yes_no / question / word) - if (content.question) { - rows.push({ kind: 'lang', label: 'Frage', color: 'text-slate-700', - cell: l => strip(content.question[`sentence_${l}`] || '') }); - } - - if (type === 'yes_no') { - // Ja/Nein-Antwort ist ein boolescher Wert, keine Übersetzung - const a = content.positive?.answer; - rows.push({ kind: 'single', label: 'Antwort', color: 'text-green-700', - value: a === true ? '✓ Ja' : a === false ? '✗ Nein' : null }); - } else if (type === 'word') { - rows.push({ kind: 'lang', label: 'Positiv-Wörter', color: 'text-green-700', - cell: wordsCell(content.positive) }); - // 'word' braucht laut Datenmodell Negativ-Wörter → Zeile immer zeigen, fehlende sichtbar machen - rows.push({ kind: 'lang', label: 'Negativ-Wörter', color: 'text-red-600', - cell: wordsCell(content.negative) }); - } else { - // text / question → Sätze - if (content.positive) - rows.push({ kind: 'lang', label: 'Positiv', color: 'text-green-700', - cell: sentenceCell(content.positive, 'positive_sentence') }); - // 'question' = Frage + Positiv + Negativ → Negativ-Zeile immer zeigen, auch wenn leer - // ('fehlt' statt stilles Ausblenden, damit eine fehlende Negativ-Antwort auffällt). - // 'text' hat per Definition kein Negativ. - if (type === 'question') - rows.push({ kind: 'lang', label: 'Negativ', color: 'text-red-600', - cell: sentenceCell(content.negative, 'negative_sentence') }); - } - return rows; -} - export default function PairReviewModal({ pair, content, onClose, onDone, onRetranslate }) { const [busy, setBusy] = useState(null); // 'review' | 'block' | 'retranslate' const [missing, setMissing] = useState(null); diff --git a/src/lib/pairRows.js b/src/lib/pairRows.js new file mode 100644 index 0000000..afba87d --- /dev/null +++ b/src/lib/pairRows.js @@ -0,0 +1,53 @@ +// Geteilte Anzeige-Helfer für Pair-Inhalte (3-Sprachen-Grid). +// Genutzt von PairReviewModal und der Veröffentlichen-Seite. + +// Platzhalter {{label.type:uuid}} → nur das Label anzeigen. +export function strip(text) { + if (!text) return ''; + return text.replace(/\{\{([^}]+)\}\}/g, (_, inner) => { + const m = inner.match(/^(.+?)\.[a-z]+:[0-9a-f-]{36}$/i); + return m ? m[1] : inner; + }); +} + +// Baut die Anzeigezeilen je nach answer_type. Jede Zeile ist entweder +// - { kind: 'lang', label, color, cell(l) } → eine Zelle pro Sprache (übersetzbar) +// - { kind: 'single', label, color, value } → ein einzelner Wert (nicht sprachabhängig) +export function buildRows(content) { + if (!content) return []; + const type = content?.answer_type; + const rows = []; + const wordsCell = (stmt) => (l) => + (stmt?.words || []).map(w => w[`titel_${l}`] || '—').join(', '); + const sentenceCell = (stmt, prefix) => (l) => + strip(stmt?.sentence?.[`${prefix}_${l}`] || ''); + + // Frage (yes_no / question / word) + if (content.question) { + rows.push({ kind: 'lang', label: 'Frage', color: 'text-slate-700', + cell: l => strip(content.question[`sentence_${l}`] || '') }); + } + + if (type === 'yes_no') { + // Ja/Nein-Antwort ist ein boolescher Wert, keine Übersetzung + const a = content.positive?.answer; + rows.push({ kind: 'single', label: 'Antwort', color: 'text-green-700', + value: a === true ? '✓ Ja' : a === false ? '✗ Nein' : null }); + } else if (type === 'word') { + rows.push({ kind: 'lang', label: 'Positiv-Wörter', color: 'text-green-700', + cell: wordsCell(content.positive) }); + // 'word' braucht laut Datenmodell Negativ-Wörter → Zeile immer zeigen, fehlende sichtbar machen + rows.push({ kind: 'lang', label: 'Negativ-Wörter', color: 'text-red-600', + cell: wordsCell(content.negative) }); + } else { + // text / question → Sätze + if (content.positive) + rows.push({ kind: 'lang', label: 'Positiv', color: 'text-green-700', + cell: sentenceCell(content.positive, 'positive_sentence') }); + // 'question' = Frage + Positiv + Negativ → Negativ-Zeile immer zeigen, auch wenn leer + if (type === 'question') + rows.push({ kind: 'lang', label: 'Negativ', color: 'text-red-600', + cell: sentenceCell(content.negative, 'negative_sentence') }); + } + return rows; +} diff --git a/src/pages/ContentCreation.jsx b/src/pages/ContentCreation.jsx index 5b67645..5015aa4 100644 --- a/src/pages/ContentCreation.jsx +++ b/src/pages/ContentCreation.jsx @@ -873,7 +873,7 @@ function EditPairForm({ pair, allObjects, onSaved, onCancel, onDeleted, onSavedA // ─── Left panel: Object list ────────────────────────────────────────────────── -function ObjectListPanel({ objects, loadingObjects, mode, selectedObjectId, onAddObject, onSelectObject, currentPicture, onObjectStatusChange }) { +function ObjectListPanel({ objects, loadingObjects, mode, selectedObjectId, onAddObject, onSelectObject, currentPicture, onObjectStatusChange, onPicturesReload }) { return (