6.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm run dev # Vite dev server (default http://localhost:5173)
npm run build # Production build → dist/
npm run preview # Serve the built dist/ locally
There is no test runner and no linter configured — package.json only defines dev, build, preview. Don't suggest npm test/npm run lint.
Deployment is a two-stage Docker build (Dockerfile): Node builds dist/, then it's served by nginx (nginx.conf) with SPA fallback (try_files … /index.html).
Environment
VITE_API_URL (in .env / .env.production) points at the backend. It's the only runtime config — every API call in src/api/directus.js reads import.meta.env.VITE_API_URL. .env is gitignored.
Architecture
React 19 + Vite SPA, no router package. There are no DB cards/feed entries fetched directly — the app talks to a custom backend, not Directus directly (see below).
Routing is useState-based in src/App.jsx: a PAGES map (feed/game/pro/profil) swaps the active page component, driven by BottomNav. Game and Pro are placeholders.
Auth gating: App.jsx renders <AuthScreen /> unless the user has username, language_native_id, and language_target_id. AuthContext (src/context/AuthContext.jsx) holds the JWT (localStorage key hejyou_token), calls getMe() on mount, and clears the token on failure. Registration is two steps: RegisterStep1 (email+password) → RegisterStep2 (username + native/target language).
The API client is misnamed
src/api/directus.js is not a Directus client anymore — the module name is kept "aus historischen Gründen". It is a thin fetch wrapper around a custom backend (snakkimo-API) exposing /auth/* endpoints:
| Function | Endpoint | Notes |
|---|---|---|
login / registerUser |
POST /auth/login, /auth/register |
return { token, userId, needsProfile } |
getMe |
GET /auth/me |
auth check + progress: total_ep, streak_days, level, ep_into_level, ep_to_next_level, last_practice_at; also language_target_greeting (Profil-Anrede „Hej, …") |
checkUsername |
GET /auth/check-username |
|
createProfile |
POST /auth/profile |
username + native/target lang |
getLanguageOptions |
GET /auth/languages |
merged with local LANG_META (flag + Web Speech code) |
getFeedPairs |
GET /auth/feed?lang=&limit=&exclude= |
returns "pairs", the feed unit |
saveProgress |
POST /auth/progress |
books EP/streak; returns the milestone contract (see below) |
getStats |
GET /auth/stats |
Profil-Daten: daily/today/totals/skills + categories[] (Punkte je Kategorie) |
getAchievements |
GET /auth/achievements |
[{ key, label, icon, unlocked, unlocked_at }] für die Profil-Sektion |
setDailyGoal |
PUT /auth/goal |
Tagesziel (EP/Tag) setzen; Backend klemmt auf 5–500 |
saveProgress returns { total_ep, level, prev_level, streak_days, streak_increased, daily_ep, daily_goal_ep, goal_just_reached, unlocked_achievements } — Feed.jsx leitet daraus die Feier-Momente ab (Level-Up/Streak/Tagesziel/Achievement). Felder degradieren defensiv: fehlen sie (älteres Backend), greifen lokale Fallbacks.
src/pages/Profil.jsx rendert die Begrüßung (language_target_greeting), führt mit Momentum (% bis Level X + Capability-Satz), zeigt Kategorie-Stufen (stats.categories + categoryTier), Wochenvergleich, Streak-Status, Erfolge-Grid (getAchievements) und einen Sound-Toggle.
Fortschritts-/Feier-System (Momente)
Macht Fortschritt spürbar statt nur zählbar. Bausteine:
src/utils/leveling.js— spiegelt die Backend-Level-Kurve (levelForEp/levelInfo, Level 1 bei 20 EP). Backend ist Single Source of Truth; das ist Fallback + %-Anzeige.MilestoneOverlay(components/) — Vollbild-Feier, getriggert aus dersaveProgress-Response. Typen:level/streak(Schwellen 3/7/14/30/50/100/200/365) /goal/achievement. Konfetti viautils/confetti.js.EpFloat— „+N EP" schwebt am Bestätigen-Button auf; EP-Badge zählt hoch (hooks/useCountUp.js).SessionSummary— ersetzt die End-Sackgasse mit Zahlen + Story-Zeilen.- Combo + variables Lob/ermutigendes Fehler-Feedback (
utils/praise.js, dort auchcategoryTier/capabilitySentence). utils/sound.js— dezente WebAudio-Belohnung (Mute-Pref in localStorage).utils/streak.js+utils/streakReminder.js— Loss-Aversion-Nudge im Feed („Serie endet in X Std") und lokale Tages-Erinnerung via@capacitor/local-notifications(kein APNs nötig; nur nativ, web no-op — brauchtnpx cap sync ios).
Several content functions (getWords, getQuestions, getActiveLearningPair, assetUrl, …) are stubs returning empty/null — content endpoints are not built yet. Don't assume they fetch anything.
Feed = "pairs" → cards
src/pages/Feed.jsx fetches an array of pairs and maps each to one card by its answer_type:
answer_type |
Component | Points (POINTS map) |
|---|---|---|
text |
PairSentenceCard |
2 |
yes_no |
PairYesNoCard |
2 |
word |
PairWordCard |
3 |
question |
PairWordCard |
3 |
On completion, handleComplete adds the pair id to a local done set (cards are hidden, not removed) and POSTs to saveProgress. Target language comes from user.language_target_short (fallback de).
The Pair* cards (PairSentenceCard, PairYesNoCard, PairWordCard) are the live card components. The other card files (NewWord*, ImagePick/ImageQuiz, AudioQuiz, LetterOrder, SentenceFill, LanguageParentCard) are an earlier card model not wired into the current feed — check Feed.jsx before assuming a component is in use.
Several cards use the browser Web Speech API (SpeechRecognition / speechSynthesis) for the speak/listen flow; src/utils/confetti.js wraps canvas-confetti for correct-answer feedback.
knowledge/directus_struktur.md
A detailed reference for the backend data model (collections, fields, relations) and the original app concept. Useful for understanding the domain and DB schema, but note it predates the custom-backend migration: its "Frontend API-Funktionen" table describes direct Directus calls that no longer exist — trust src/api/directus.js for the actual frontend contract.