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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user