--- phase: 03-komplettes-spielerlebnis plan: 05 type: execute wave: 2 depends_on: [03-01] files_modified: - src/ui/parent.ts - index.html - src/styles/main.css - src/app.ts autonomous: true requirements: [PRNT-01, PRNT-02, PRNT-03, PRNT-04] must_haves: truths: - "Ctrl+Shift+E opens a code input prompt" - "Entering '1234' navigates to parent area screen" - "Parent area shows current level, completed sessions, practice days as calendar dots" - "Parent area shows average accuracy and most frequent error keys" - "Settings allow changing layout, toggling audio, changing companion, entering API key" artifacts: - path: "src/ui/parent.ts" provides: "initParentScreen() with stats display and settings panel" exports: ["initParentScreen", "showParentCodePrompt"] - path: "index.html" provides: "Parent screen HTML with stats overview and settings form" contains: "parent-stats" key_links: - from: "src/ui/parent.ts" to: "src/storage/db.ts" via: "getProgress, saveProgress, getSettings, saveSettings" pattern: "getProgress" - from: "src/app.ts" to: "src/ui/parent.ts" via: "showParentCodePrompt on Ctrl+Shift+E" pattern: "showParentCodePrompt" --- Build the parent area: gated access via Ctrl+Shift+E + code "1234", stats overview (level, sessions, practice days, accuracy, error keys), and settings (layout, audio, companion, API key). Purpose: Parents can see progress and adjust settings without the child being exposed to performance metrics. Output: Parent screen with stats and settings, access gate wired into app.ts. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-komplettes-spielerlebnis/03-01-SUMMARY.md @src/app.ts @src/storage/db.ts @src/types.ts @src/companion/characters.ts @index.html @src/styles/main.css export interface Progress { id: 1; currentLevel: number; completedLevels: number[]; totalSessions: number; sessionDates: string[]; // ISO date strings "YYYY-MM-DD" selectedCharacter: CompanionType; selectedLayout: KeyboardLayout; totalCorrect: number; totalErrors: number; errorKeyCounts: Record; lastPlayedLevels: number[]; } export interface Settings { id: 1; audioEnabled: boolean; apiKey: string; apiCallsToday: { text: number; image: number }; lastApiCallDate: string; } export function getProgress(db: IDBDatabase): Promise; export function saveProgress(db: IDBDatabase, progress: Progress): Promise; export function getSettings(db: IDBDatabase): Promise; export function saveSettings(db: IDBDatabase, settings: Settings): Promise; export interface CompanionDefinition { type: CompanionType; name: string; emoji: string; personality: string; avatarUrl: string; } export const companions: CompanionDefinition[]; export function showScreen(name: ScreenName): void; export function getDB(): IDBDatabase; Task 1: Parent area HTML, CSS, and access gate index.html, src/styles/main.css, src/ui/parent.ts index.html, src/styles/main.css, src/types.ts, src/storage/db.ts, src/companion/characters.ts **Update index.html** — replace empty `` (per D-14, D-17, D-18): ```html ← Zurueck Elternbereich Bitte Code eingeben: Falscher Code Uebersicht Aktuelle Stufe - Einheiten - Genauigkeit - Uebungstage Haeufigste Fehlertasten Einstellungen Tastatur-Layout DE (Deutschland) CH (Schweiz) Audio An/Aus Begleitfigur API-Key Speichern ``` **Add CSS to `src/styles/main.css`:** ```css /* Parent Area */ .parent { max-width: 600px; margin: 0 auto; padding: 1.5rem; } .parent__back-btn { background: none; border: none; font-family: var(--font-heading); font-size: 1rem; color: var(--text-warm); cursor: pointer; margin-bottom: 1rem; } .parent__title { font-family: var(--font-heading); color: var(--text-dark); margin-bottom: 1.5rem; } .parent__gate { text-align: center; padding: 2rem; font-family: var(--font-body); } .parent__code-input { font-size: 2rem; text-align: center; width: 120px; padding: 0.5rem; border: 2px solid var(--accent-purple); border-radius: 8px; font-family: var(--font-heading); letter-spacing: 0.5rem; margin-top: 0.5rem; } .parent__gate-error { color: var(--accent-pink); margin-top: 0.5rem; font-size: 0.9rem; } .parent__section { margin-bottom: 2rem; } .parent__section h2 { font-family: var(--font-heading); color: var(--text-dark); border-bottom: 2px solid var(--bg-forest); padding-bottom: 0.5rem; margin-bottom: 1rem; } .parent__stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; } .parent__stat { background: var(--bg-forest); border-radius: 12px; padding: 1rem; text-align: center; } .parent__stat-label { display: block; font-family: var(--font-body); font-size: 0.85rem; color: var(--text-warm); margin-bottom: 0.25rem; } .parent__stat-value { display: block; font-family: var(--font-heading); font-size: 1.5rem; font-weight: 700; color: var(--text-dark); } .parent__calendar-dots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 0.5rem; } .parent__calendar-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent-green); } .parent__calendar-dot--empty { background: var(--bg-cream); border: 1px solid var(--text-light); } .parent__errors h3, .parent__calendar h3 { font-family: var(--font-heading); font-size: 1rem; color: var(--text-warm); margin-bottom: 0.5rem; } .parent__error-key { display: inline-block; background: var(--accent-pink); color: white; padding: 0.25rem 0.75rem; border-radius: 8px; margin: 0.25rem; font-family: var(--font-heading); font-size: 0.9rem; } .parent__settings { display: flex; flex-direction: column; gap: 1rem; } .parent__setting { display: flex; align-items: center; gap: 1rem; } .parent__setting label:first-child { min-width: 120px; font-family: var(--font-body); color: var(--text-warm); } .parent__setting select, .parent__input { font-family: var(--font-body); padding: 0.5rem; border: 1px solid var(--text-light); border-radius: 8px; font-size: 0.95rem; } .parent__save-btn { font-family: var(--font-heading); padding: 0.5rem 1rem; background: var(--accent-green); color: white; border: none; border-radius: 8px; cursor: pointer; } ``` **Create `src/ui/parent.ts`:** ```typescript import { getProgress, saveProgress, getSettings, saveSettings } from '../storage/db'; import { companions } from '../companion/characters'; import type { CompanionType, KeyboardLayout } from '../types'; import { showScreen, getDB } from '../app'; ``` **`showParentCodePrompt()`** (per D-14, PRNT-01): - Show parent screen: `showScreen('parent')` - Show gate, hide content - Focus code input - On input change, if value === '1234': hide gate, show content, call `loadParentData()` - If value.length === 4 but wrong: show error, clear after 1.5s **`initParentScreen(db: IDBDatabase, onBack: () => void)`:** Wire back button to `onBack` callback. **`loadParentData()`** (per D-17, PRNT-02, PRNT-03): 1. Get progress from IndexedDB 2. Set `#stat-level` to `progress.currentLevel` 3. Set `#stat-sessions` to `progress.totalSessions` 4. Calculate accuracy: `progress.totalCorrect + progress.totalErrors > 0 ? Math.round(progress.totalCorrect / (progress.totalCorrect + progress.totalErrors) * 100) : 0` 5. Set `#stat-accuracy` to `${accuracy}%` 6. Render calendar dots in `#calendar-dots`: - Get last 30 days as date strings - For each day: if in `progress.sessionDates`, render green dot, else empty dot 7. Render error keys in `#error-keys-list`: - Sort `progress.errorKeyCounts` by value descending - Show top 5 keys as colored badges: `E (12x)` - If no errors: show "Noch keine Fehler erfasst" **Settings (per D-18, PRNT-04):** 1. Populate companion select from `companions` array 2. Set current values from progress (layout, companion) and settings (audio, apiKey) 3. On layout change: `saveProgress(db, { ...progress, selectedLayout: value })` 4. On companion change: `saveProgress(db, { ...progress, selectedCharacter: value })` 5. On audio toggle: `saveSettings(db, { ...settings, audioEnabled: checked })` 6. On save API key: `saveSettings(db, { ...settings, apiKey: value })` 7. Each save shows brief "Gespeichert!" feedback npx tsc --noEmit && grep -c "showParentCodePrompt" src/ui/parent.ts && grep -c "parent-gate" index.html && grep -c "parent__calendar-dot" src/styles/main.css - grep "showParentCodePrompt" src/ui/parent.ts returns export - grep "1234" src/ui/parent.ts returns match (access code) - grep "parent-gate" index.html returns match (gate overlay) - grep "stat-level" index.html returns match (stats display) - grep "stat-accuracy" index.html returns match - grep "calendar-dots" index.html returns match - grep "error-keys-list" index.html returns match - grep "setting-layout" index.html returns match (layout setting) - grep "setting-companion" index.html returns match (companion setting) - grep "setting-apikey" index.html returns match (API key setting) - grep "parent__calendar-dot" src/styles/main.css returns match - grep "parent__error-key" src/styles/main.css returns match - npx tsc --noEmit exits 0 Parent area gate with code "1234", stats overview with level/sessions/accuracy/calendar-dots/error-keys, settings for layout/audio/companion/API-key. All styled and functional. Task 2: Wire parent area into app.ts src/app.ts src/app.ts, src/ui/parent.ts **Wire parent area access in `src/app.ts`** (per D-14, PRNT-01): 1. Import: `import { showParentCodePrompt, initParentScreen } from './ui/parent';` 2. In `initApp()`, after DB is opened, call: ```typescript initParentScreen(db, async () => { // Back from parent -> return to forest await initForestScreen(db, startLessonFromForest); showScreen('forest'); }); ``` 3. Replace the stub `showParentCodePrompt` reference (if Plan 04 added a stub) or add the global keydown listener: ```typescript document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'E') { e.preventDefault(); showParentCodePrompt(); } }); ``` Add this in `initApp()` after DB setup. Ensure it's only added once. 4. Make sure `showParentCodePrompt` properly uses the DB reference. Since `parent.ts` imports `getDB` from `app.ts`, it can access the DB. npx tsc --noEmit && grep -c "showParentCodePrompt" src/app.ts && grep -c "initParentScreen" src/app.ts - grep "import.*showParentCodePrompt" src/app.ts returns match - grep "import.*initParentScreen" src/app.ts returns match - grep "ctrlKey.*shiftKey" src/app.ts returns match (keyboard shortcut) - grep "initParentScreen" src/app.ts shows it's called in initApp - npx tsc --noEmit exits 0 Parent area fully wired: Ctrl+Shift+E triggers code prompt, "1234" opens parent screen, back button returns to forest. Global listener registered once in initApp. - `npx tsc --noEmit` — no type errors - `npx vitest run` — all tests pass - Parent area accessible via Ctrl+Shift+E + "1234" - Stats show current data, settings persist changes 1. Ctrl+Shift+E opens code input overlay on parent screen 2. Code "1234" reveals parent content, wrong code shows error 3. Overview shows: current level, total sessions, accuracy percentage 4. Calendar dots show last 30 days with green dots for practice days 5. Error keys shown as badges, sorted by frequency, top 5 6. Settings: layout change, audio toggle, companion change, API key save — all persist to IndexedDB 7. Back button returns to forest screen After completion, create `.planning/phases/03-komplettes-spielerlebnis/03-05-SUMMARY.md`
Bitte Code eingeben:
Falscher Code