Files
app-hejyou/CLAUDE.md

6.4 KiB
Raw Permalink Blame History

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 5500

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 der saveProgress-Response. Typen: level / streak (Schwellen 3/7/14/30/50/100/200/365) / goal / achievement. Konfetti via utils/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 auch categoryTier/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 — braucht npx 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.