Add RecordModal: row-click detail popup with inline editing

- 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>
This commit is contained in:
2026-05-21 21:53:45 +02:00
parent 74082cd333
commit e6c86a97fc
4 changed files with 408 additions and 4 deletions

View File

@@ -54,3 +54,14 @@ export async function apiFetch(path, options = {}) {
export async function fetchAll(endpoint) {
return apiFetch(`${endpoint}?limit=500&offset=0`);
}
export async function apiPatch(endpoint, id, body) {
return apiFetch(`${endpoint}/${id}`, {
method: 'PATCH',
body: JSON.stringify(body),
});
}
export async function apiFetchOne(path) {
return apiFetch(path);
}

View File

@@ -7,6 +7,13 @@ export const TABLES = {
primaryLabel: 'titel_de',
columns: ['titel_de', 'titel_en', 'status', 'difficulty_level', 'created_at'],
linkedFields: { picture_ids: 'pictures', category_ids: 'categories' },
editableFields: {
titel_de: { type: 'text' },
titel_en: { type: 'text' },
status: { type: 'select', options: ['published', 'blocked', 'draft', 'requested', 'translated'] },
difficulty_level:{ type: 'number', min: 1, max: 10 },
},
fetchRelated: [],
},
pictures: {
label: 'Bilder',
@@ -16,6 +23,15 @@ export const TABLES = {
primaryLabel: 'design',
columns: ['design', 'status', 'picture_link', 'blurhash', 'created_at'],
linkedFields: {},
editableFields: {
design: { type: 'text' },
status: { type: 'select', options: ['published', 'blocked', 'uploaded', 'requested', 'generated'] },
picture_link: { type: 'text' },
blurhash: { type: 'text' },
},
fetchRelated: [
{ key: 'words', label: 'Wörter', endpoint: id => `/pictures/${id}/words`, display: w => w.titel_de || w.id },
],
},
objects: {
label: 'Objekte',
@@ -25,6 +41,11 @@ export const TABLES = {
primaryLabel: 'notes',
columns: ['id', 'status', 'notes', 'created_at'],
linkedFields: { word_ids: 'words', picture_ids: 'pictures', pair_ids: 'pairs' },
editableFields: {
notes: { type: 'textarea' },
status: { type: 'select', options: ['published', 'blocked', 'draft'] },
},
fetchRelated: [],
},
pairs: {
label: 'Pairs',
@@ -33,7 +54,17 @@ export const TABLES = {
statusField: 'status',
primaryLabel: 'id',
columns: ['id', 'answer_type', 'difficulty_level', 'status', 'question_id', 'positive_statement_id', 'created_at'],
linkedFields: { question_id: 'questions', positive_statement_id: 'statements', negative_statement_id: 'statements' },
linkedFields: {
question_id: 'questions',
positive_statement_id: 'statements',
negative_statement_id: 'statements',
},
editableFields: {
answer_type: { type: 'select', options: ['word', 'sentence'] },
difficulty_level:{ type: 'number', min: 1, max: 10 },
status: { type: 'select', options: ['published', 'blocked', 'draft'] },
},
fetchRelated: [],
},
questions: {
label: 'Fragen',
@@ -43,6 +74,12 @@ export const TABLES = {
primaryLabel: 'sentence_de',
columns: ['sentence_de', 'sentence_en', 'status', 'created_at'],
linkedFields: {},
editableFields: {
sentence_de: { type: 'textarea' },
sentence_en: { type: 'textarea' },
status: { type: 'select', options: ['published', 'blocked', 'draft'] },
},
fetchRelated: [],
},
statements: {
label: 'Statements',
@@ -52,6 +89,14 @@ export const TABLES = {
primaryLabel: 'positive_sentence_de',
columns: ['positive_sentence_de', 'negative_sentence_de', 'status', 'created_at'],
linkedFields: {},
editableFields: {
positive_sentence_de: { type: 'textarea' },
negative_sentence_de: { type: 'textarea' },
positive_sentence_en: { type: 'textarea' },
negative_sentence_en: { type: 'textarea' },
status: { type: 'select', options: ['published', 'blocked', 'draft'] },
},
fetchRelated: [],
},
categories: {
label: 'Kategorien',
@@ -61,6 +106,12 @@ export const TABLES = {
primaryLabel: 'name_de',
columns: ['name_de', 'name_en', 'name_sv', 'created_at'],
linkedFields: {},
editableFields: {
name_de: { type: 'text' },
name_en: { type: 'text' },
name_sv: { type: 'text' },
},
fetchRelated: [],
},
};