refactor: migrate to new db_* Directus collections

- DrawIt: load db_pictures (status=draft), create db_objects/db_words
  with blurhash placeholder, finish sets status=objects_created
- GenerateIt: load db_pictures (status=objects_created), right panel
  replaced with manual QA pairs (db_pairs + db_question + db_statement)
- Backend: new routes for db_pictures, db_objects, db_words, db_pairs
- Types/API: full db_* type definitions and API helpers
- Directus: user_notes field in db_objects, M2M db_words<->db_pictures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 08:03:23 +02:00
parent 5b99bef765
commit 7c983a7460
5 changed files with 811 additions and 564 deletions

View File

@@ -325,3 +325,131 @@ export async function purgeAllOrphans(token: string): Promise<{ orphans_removed:
if (!res.ok) throw new Error('Fehler beim globalen Bereinigen')
return data
}
// ── DB Pictures ───────────────────────────────────────────────────────────────
import type { DbPicture, DbObject, DbWord, DbPair } from './types'
export async function getDbPictures(token: string, status = 'draft'): Promise<DbPicture[]> {
const res = await fetch(`/api/directus/db-pictures?status=${status}`, {
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok) throw new Error('Fehler beim Laden der db_pictures')
const data = await res.json()
return data.data as DbPicture[]
}
export async function updateDbPictureStatus(pictureId: string, status: string, token: string): Promise<void> {
const res = await fetch(`/api/directus/db-pictures/${pictureId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ status }),
})
if (!res.ok) throw new Error('Fehler beim Aktualisieren des Bild-Status')
}
// ── DB Objects ────────────────────────────────────────────────────────────────
export async function getDbObjects(pictureId: string, token: string): Promise<DbObject[]> {
const res = await fetch(`/api/directus/db-objects?picture_id=${pictureId}`, {
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok) throw new Error('Fehler beim Laden der db_objects')
const data = await res.json()
return data.data as DbObject[]
}
export async function createDbObject(payload: {
picture: string
selections: import('./types').Selection[] | null
user_notes: string | null
}, token: string): Promise<DbObject> {
const res = await fetch('/api/directus/db-objects', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ ...payload, status: 'draft' }),
})
const data = await res.json()
if (!res.ok) throw new Error(data.errors?.[0]?.message || 'Fehler beim Erstellen des Objekts')
return data.data as DbObject
}
export async function updateDbObject(
objId: string,
payload: Partial<Pick<DbObject, 'user_notes'>>,
token: string
): Promise<DbObject> {
const res = await fetch(`/api/directus/db-objects/${objId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify(payload),
})
const data = await res.json()
if (!res.ok) throw new Error(data.errors?.[0]?.message || 'Fehler beim Aktualisieren')
return data.data as DbObject
}
export async function deleteDbObject(objId: string, token: string): Promise<void> {
const res = await fetch(`/api/directus/db-objects/${objId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok) throw new Error('Fehler beim Löschen des Objekts')
}
// ── DB Words for a picture ────────────────────────────────────────────────────
export async function getDbPictureWords(pictureId: string, token: string): Promise<DbWord[]> {
const res = await fetch(`/api/directus/db-pictures/${pictureId}/words`, {
headers: { Authorization: `Bearer ${token}` },
})
const data = await res.json()
if (!res.ok) throw new Error('Fehler beim Laden der Wörter')
return data.data as DbWord[]
}
export async function saveDbPictureWords(
pictureId: string,
words: { titel_de: string; level: number }[],
token: string
): Promise<{ saved: number }> {
const res = await fetch(`/api/directus/db-pictures/${pictureId}/words`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ words }),
})
const data = await res.json()
if (!res.ok) throw new Error(data.error || 'Fehler beim Speichern der Wörter')
return data
}
// ── DB Pairs for an object ────────────────────────────────────────────────────
export async function getDbObjectPairs(objectId: string, token: string): Promise<DbPair[]> {
const res = await fetch(`/api/directus/db-objects/${objectId}/pairs`, {
headers: { Authorization: `Bearer ${token}` },
})
const data = await res.json()
if (!res.ok) throw new Error('Fehler beim Laden der Pairs')
return data.data as DbPair[]
}
export async function createDbPair(
objectId: string,
payload: {
question_de?: string
statement_de: string
level: number
words: { titel_de: string; level: number }[]
},
token: string
): Promise<{ ok: boolean; pair_id: string; statement_id: string; question_id: string | null }> {
const res = await fetch(`/api/directus/db-objects/${objectId}/pairs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify(payload),
})
const data = await res.json()
if (!res.ok) throw new Error(data.error || 'Fehler beim Erstellen des Pairs')
return data
}