Files
app-hejyou/CLAUDE.md

84 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
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.