Files
app-hejyou/knowledge/directus_struktur.md
2026-04-23 22:06:12 +02:00

14 KiB
Raw Blame History

Directus Datenbankstruktur — Language App

Domain: https://db.hejyou.com Stand: 2026-04-23 Eigene Collections: 10 (inkl. Junction questions_words) Frontend: React 19 + Vite, kein Router-Package (State-basiertes Routing)


App-Konzept (Kurzfassung)

Social-Media-Feed-Style-Sprachlern-App mit Fokus auf "Language Parenting" (Kontext & Immersion statt Grammatikdrill).

Datenfluss:

  1. Ein Bild wird in pictures hochgeladen
  2. KI-Analyse erkennt Objekte → werden in objects gespeichert (mit bbox, confidence, Label-Wort)
  3. Zum Bild werden questions generiert (z. B. "Kannst du die Möwe fliegen sehen?")
  4. Jedes Wort der Frage wird in words gespeichert und per M2M (questions_words) verknüpft

Lern-Flow in der App:

  • User scrollt durch Feed-Kacheln
  • Kacheln existieren nicht als DB-Einträge, sondern werden dynamisch aus words / questions generiert
  • Kachel-Typen: listen, speak, write, multiple_choice, image_match, sentence_fill
  • 1 EP pro gelöster Kachel
  • Erst alle Wörter einer Frage auf Ziellevel → dann Satzfrage freischalten
  • Danach gemischter Feed

Sprachrichtung

  • Zielsprachen: Alle drei gegenseitig (DE/EN/SE × DE/EN/SE)
  • Gespeichert in directus_users.language_native + language_target (M2O → language_options)
  • Zusätzlich learning_pairs pro aktive Sprachrichtung (mit eigenem Level + Punkten)

App-Architektur (Frontend)

Navigation

4 Seiten, über BottomNav (Tabs) gesteuerter useState-Router in App.jsx:

Tab Seite Status
Feed pages/Feed.jsx Implementiert, Directus-Daten
Game pages/Game.jsx Placeholder
Pro pages/Pro.jsx Placeholder
Profil pages/Profil.jsx Implementiert, Directus-Daten

App.jsx zeigt <AuthScreen /> wenn !user.username || !user.language_native || !user.language_target.

Auth-Flow (2 Schritte)

RegisterStep1 (E-Mail + Passwort)
  → registerUser() + login() → Token in localStorage (hejyou_token)
  → onSuccess(userId, token) → RegisterStep2

RegisterStep2 (Username + Muttersprache + Zielsprache)
  → checkUsername() → createProfile()
    → POST /items/users_language
    → PATCH /users/:id  (username, language_native, language_target)
    → PATCH /items/users_language/:id  (user-Verknüpfung)
    → POST /items/learning_pairs
  → setUser() → AuthScreen zeigt Erfolgsscreen

AuthContext überwacht Token, ruft getMe() beim Start

Token-Key im localStorage: hejyou_token

Frontend API-Funktionen (src/api/directus.js)

Funktion Endpoint Zweck
login(email, pw) POST /auth/login Token holen
getMe(token) GET /users/me Basisfelder für Auth-Check
getProfilData(token) GET /users/me (erweitert) Profil inkl. username-Objekt, points_total, streak_days
registerUser(email, pw) POST /users Neuen Directus-User anlegen
checkUsername(name) GET /items/users_language (filter) Verfügbarkeit prüfen
createProfile(...) 4 PATCH/POST-Calls Vollständiges Onboarding
getActiveLearningPair(profileId) GET /items/learning_pairs (filter active) Aktives Lernpaar des Nutzers
getWords(limit) GET /items/words Wörter für Feed-Karten
langById(id) lokal LANGUAGE_OPTIONS-Lookup per UUID

LANGUAGE_OPTIONS (Frontend-Konstante)

// Felder pro Sprache:
{ id, label, flag, suffix, speech }
Sprache suffix speech
Deutsch de de-DE
Englisch en en-US
Schwedisch se sv-SE

suffix wird genutzt um DB-Felder dynamisch zu bauen: title_${suffix} (z.B. title_de). speech wird an die Web Speech API (SpeechRecognition) übergeben.

Feed-Karten-Generierung

buildCards(words, fromLang, toLang) erzeugt pro Wort bis zu 3 Kacheln:

Kachel-Typ Komponente Bedingung
text NewWordTextCard immer
voice NewWordVoiceCard immer
letter LetterOrderCard word.length >= 4

Felder aus DB: word = words.title_${to.suffix}, translation = words.title_${from.suffix}

Zusätzlich werden Demo-Karten (DEMO_EXTRA) an den echten Feed angehängt, solange Bilder/Audio noch nicht in der DB vorhanden sind (ImagePickCard, AudioQuizCard, ImageQuizCard).

Noch nicht implementiert: Nach Kartenabschluss wird aktuell kein user_progress-Eintrag angelegt und learning_pairs.points wird nicht aktualisiert. Das ist der nächste Schritt.


Collections im Überblick

Collection Zweck
categories Kategorien für Wörter/Objekte (Farben, Zuhause, Tiere, …)
language_options Verfügbare Sprachen (DE/EN/SE)
learning_pairs Aktive Sprachrichtungen pro Nutzer, mit Level & Punkten
objects Von KI erkannte Objekte in Bildern (bbox, confidence)
pictures Bilder, Ausgangspunkt für Fragen
questions Fragen/Sätze für den Lernfeed
questions_words Junction: M2M Fragen ↔ Wörter
user_progress Lernfortschritt pro Nutzer × Kachel
users_language Öffentliches Profil (Username, Avatar)
words Vokabeln in DE/EN/SE mit Level & Kategorie

Collection-Details

categories

Kategorien für Wörter und Objekte.

Feld Typ Notiz
id uuid (PK)
status string draft/published/archived
sort integer Sortierreihenfolge
Icon string Material-Icon-Name
type string Dropdown, Werte undefiniert
title_en/de/se string Titel in 3 Sprachen

language_options

Verfügbare Sprachen.

Feld Typ Notiz
id uuid (PK)
status string
short string Kurzcode: de, en, se
title_en/de/se string

Frontend-Mapping: LANGUAGE_OPTIONS in directus.js ergänzt die DB-Werte um suffix (= short), speech (Web Speech API Code), flag (Emoji) und label (Anzeigename auf Deutsch).

Aktuelle Einträge:

  • DE: 88053026-3d7e-4799-b10d-67187f7c1709
  • EN: 99fbaa9d-3cac-48cb-a5e2-dcb320e913e4
  • SE: 25350b32-e9ab-4fec-946e-c0f11eff70dd

learning_pairs

Welche Sprachrichtungen ein Nutzer aktiv lernt.

Feld Typ Notiz
id uuid (PK)
user uuid M2O → users_language
language_from uuid M2O → language_options Ausgangssprache
language_to uuid M2O → language_options Zielsprache
active boolean Aktuell aktiv?
current_level integer (110) Aktuelles Level in dieser Richtung
points integer Punkte in dieser Richtung

words

Vokabeln in 3 Sprachen.

Feld Typ Notiz
id uuid (PK)
status string
title_de/en/se string Wort in 3 Sprachen
categories uuid M2O → categories Ein Wort gehört zu einer Kategorie
image uuid file → directus_files Bild zum Wort (optional)
audio_de/en/se uuid file → directus_files Aussprache pro Sprache
level integer (110) Schwierigkeitslevel
times_learned integer Global, wie oft insgesamt gelernt
related_questions alias M2M → questions Alle Fragen, in denen das Wort vorkommt

getWords() fetcht aktuell nur id, title_de, title_en, title_se, level. image und audio_de/en/se werden noch nicht geladen → ImagePickCard und AudioQuizCard laufen noch mit Demo-Daten. Hinweis (2026-04-23): Felder categories, image, audio_de, audio_en, audio_se wurden mit korrekten FK-Relationen und special-Markierungen angelegt.


questions

Fragen/Sätze, die im Feed erscheinen.

Feld Typ Notiz
id uuid (PK)
status string
question_de/en/se string Frage in 3 Sprachen
answer_de/en/se string Korrekte Antwort
level integer (110) Schwierigkeitslevel
related_words alias M2M → words Alle Wörter der Frage

Hinweis: questions.type (Kachel-Typen) wurde entfernt Kacheln werden dynamisch in der App generiert, nicht pro Frage gespeichert.


questions_words (Junction-Collection für M2M)

Verknüpft Fragen und Wörter (M:N).

Feld Typ
id integer (PK, auto)
questions_id uuid → questions (CASCADE)
words_id uuid → words (CASCADE)

pictures

Bilder als Ausgangspunkt für Fragen.

Feld Typ Notiz
id uuid (PK)
media uuid → directus_files Das eigentliche Bild
type string simple_object / multi_object
season string spring/summer/fall/winter
objects uuid M2O → objects Legacy-Feld (zeigt auf ein einzelnes Objekt, nicht mehr nutzen)
objects (O2M alias) alias O2M ← objects.picture Alle Objekte dieses Bildes — über objects.picture Relation

Hinweis: Die korrekte Beziehung läuft über objects.picture (M2O von objects zu pictures). Das alte pictures.objects-Feld ist ein Legacy-FK und wird nicht mehr befüllt.


objects

Von der KI erkannte Objekte innerhalb eines Bildes.

Feld Typ Notiz
id uuid (PK)
picture uuid M2O → pictures Bild, zu dem dieses Objekt gehört (O2M-Seite: pictures.objects)
media uuid → directus_files Optionaler Zuschnitt des Objekts im Bild
categories uuid M2O → categories
label uuid M2O → words Name des Objekts als Wort
color uuid M2O → words Farbe des Objekts (optional)
action uuid M2O → words Aktion des Objekts (z. B. "fliegen")
questions uuid M2O → questions Generierte Frage zum Objekt
resolution string z. B. 1920x1080
confidence float KI-Konfidenz 01
bbox json {x, y, w, h}
polygon json Polygon-Koordinaten
parent uuid M2O → objects Hierarchie (Szene → Teilobjekte)

Hinweis (2026-04-23): objects.picture wurde ergänzt, um die O2M-Beziehung zu pictures herzustellen. Ein Bild kann nun mehrere Objekte haben. Bestehende Testdaten (Möwen-Objekt) wurden verknüpft.


users_language

Öffentliches Nutzerprofil (gekoppelt mit directus_users).

Feld Typ Notiz
id uuid (PK)
username_public string Angezeigter Name
username_lowercases string Für Suche/Eindeutigkeit
user uuid M2O → directus_users Verknüpfung zum System-User

directus_users (System-Collection, custom fields)

Feld Typ Notiz
app_type string socialmedia / language
language_native uuid M2O → language_options Muttersprache
language_target uuid M2O → language_options Zielsprache
username uuid M2O → users_language Verknüpfung zum Profil
points_total integer Gesamtpunkte über alle Richtungen
streak_days integer Aktuelle Lernserie (Tage)
streak_last_activity date Für Streak-Berechnung

user_progress

Trackt jede gelöste/übersprungene Kachel pro Nutzer.

Status: Schema definiert, aber noch nicht in der App implementiert. Karten-Ergebnisse werden aktuell nur lokal angezeigt, nicht in die DB geschrieben. Nächster Schritt: saveProgress()-Funktion in directus.js + Aufruf nach Kartenabschluss.

Feld Typ Notiz
id uuid (PK)
user uuid M2O → users_language Nutzerprofil
question uuid M2O → questions Wenn Kachel zu einer Frage gehört
word uuid M2O → words Wenn Kachel zu einem Einzelwort gehört
card_type string listen / speak / write / multiple_choice / image_match / sentence_fill
result string correct / wrong / skipped
points_earned integer Verdiente EP (1 pro gelöster Kachel)
language_from uuid M2O → language_options Ausgangssprache
language_to uuid M2O → language_options Zielsprache

Entweder word ODER question ist gesetzt, je nachdem ob die Kachel ein Einzelwort oder eine Satzfrage trainiert.


Beziehungs-Diagramm

users_language ──┬── learning_pairs (pro Sprachrichtung)
                 ├── user_progress (pro Kachel)
                 └── directus_users (System-User)

pictures ──── objects (O2M via objects.picture) ──── questions
   │              │                                       │
   └── media      ├── picture → pictures (M2O)            ├── related_words (M2M via questions_words)
                  ├── label → words                       └── answer (de/en/se)
                  ├── color → words
                  └── action → words

words ──── categories (M2O)
      ├── image → directus_files
      └── audio_de/en/se → directus_files

Testdaten (Tim, Stand 2026-04-22)

  • User (users_language): 24f9a499-e36d-4df8-91f7-6a7abc6f42ce (Tim1505)
  • 3 Kategorien: Farben, Zuhause, Tiere
  • 12 Wörter (Rot, Blau, Grün, Tisch, Stuhl, Fenster, Hund, Katze, Vogel, Möwe, fliegen, sehen)
  • 8 Fragen (7 Single-Word + 1 komplexe Möwen-Frage mit 3 verknüpften Wörtern)
  • 1 Bild im Herbst (multi_object) mit erkanntem Möwen-Objekt (94% confidence)
  • 2 learning_pairs: DE→SE (aktiv, 30P), DE→EN (inaktiv, 10P)
  • 6 user_progress-Einträge mit verschiedenen card_types (listen/speak/write/multiple_choice/image_match)

Offene Punkte / Nächste Schritte

Priorität Aufgabe
🔴 user_progress schreiben nach Kartenabschluss
🔴 learning_pairs.points + directus_users.points_total updaten
🟠 words.image + words.audio_* in getWords() laden → echte ImagePickCard / AudioQuizCard
🟠 directus_users.streak_days + streak_last_activity serverseitig aktualisieren
🟡 questions Collection in Feed integrieren (Satzlevel-Kacheln)
🟡 Game und Pro Pages implementieren

Kachel-Logik (für die App)

Die App generiert Kacheln dynamisch aus den vorhandenen Daten eines Wortes:

  • audio_de/en/se vorhanden → listen, speak möglich
  • image vorhanden → image_match möglich
  • Mindestens 3 Wörter in gleicher Kategorie → multiple_choice möglich
  • Zielsprache setzen via learning_pairs.language_to

Pro Abschluss: +1 EP in user_progress.points_earned + Update in learning_pairs.points.