14 KiB
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:
- Ein Bild wird in
pictureshochgeladen - KI-Analyse erkennt Objekte → werden in
objectsgespeichert (mit bbox, confidence, Label-Wort) - Zum Bild werden
questionsgeneriert (z. B. "Kannst du die Möwe fliegen sehen?") - Jedes Wort der Frage wird in
wordsgespeichert 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/questionsgeneriert - 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_pairspro 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 undlearning_pairs.pointswird 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_OPTIONSindirectus.jsergänzt die DB-Werte umsuffix(=short),speech(Web Speech API Code),flag(Emoji) undlabel(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 (1–10) | 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 (1–10) | Schwierigkeitslevel |
times_learned |
integer | Global, wie oft insgesamt gelernt |
related_questions |
alias M2M → questions |
Alle Fragen, in denen das Wort vorkommt |
getWords()fetcht aktuell nurid, title_de, title_en, title_se, level.imageundaudio_de/en/sewerden noch nicht geladen → ImagePickCard und AudioQuizCard laufen noch mit Demo-Daten. Hinweis (2026-04-23): Feldercategories,image,audio_de,audio_en,audio_sewurden mit korrekten FK-Relationen undspecial-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 (1–10) | 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 altepictures.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 0–1 |
bbox |
json | {x, y, w, h} |
polygon |
json | Polygon-Koordinaten |
parent |
uuid M2O → objects |
Hierarchie (Szene → Teilobjekte) |
Hinweis (2026-04-23):
objects.picturewurde ergänzt, um die O2M-Beziehung zupicturesherzustellen. 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 indirectus.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
wordODERquestionist 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/sevorhanden →listen,speakmöglichimagevorhanden →image_matchmöglich- Mindestens 3 Wörter in gleicher Kategorie →
multiple_choicemöglich - Zielsprache setzen via
learning_pairs.language_to
Pro Abschluss: +1 EP in user_progress.points_earned + Update in learning_pairs.points.