Files
Zauberwald/.planning/phases/03-komplettes-spielerlebnis/03-05-PLAN.md
T
gurixandClaude Opus 4.6 5819ade457 docs(03): create phase plan — komplettes spielerlebnis
5 plans across 3 waves covering all 17 requirements:
- Plan 01 (W1): Word lists, Progress extension, rate limits
- Plan 02 (W2): Lesson 3-phase state machine, review/repeat
- Plan 03 (W1): Forest visual scene with SVG + grid
- Plan 04 (W3): Reward screen, pre-generation, full flow wiring
- Plan 05 (W2): Parent area with stats and settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 13:37:08 +02:00

16 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-komplettes-spielerlebnis 05 execute 2
03-01
src/ui/parent.ts
index.html
src/styles/main.css
src/app.ts
true
PRNT-01
PRNT-02
PRNT-03
PRNT-04
truths artifacts key_links
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
path provides exports
src/ui/parent.ts initParentScreen() with stats display and settings panel
initParentScreen
showParentCodePrompt
path provides contains
index.html Parent screen HTML with stats overview and settings form parent-stats
from to via pattern
src/ui/parent.ts src/storage/db.ts getProgress, saveProgress, getSettings, saveSettings getProgress
from to via pattern
src/app.ts src/ui/parent.ts showParentCodePrompt on Ctrl+Shift+E 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.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.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<Progress | null>; export function saveProgress(db: IDBDatabase, progress: Progress): Promise; export function getSettings(db: IDBDatabase): Promise<Settings | null>; 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
<section id="screen-parent" class="screen">
  <div class="parent">
    <button class="parent__back-btn" id="parent-back-btn">&#x2190; Zurueck</button>
    <h1 class="parent__title">Elternbereich</h1>

    <!-- Code gate overlay -->
    <div class="parent__gate" id="parent-gate">
      <p>Bitte Code eingeben:</p>
      <input type="password" id="parent-code-input" class="parent__code-input" maxlength="4" placeholder="____" inputmode="numeric">
      <p class="parent__gate-error" id="parent-gate-error" style="display:none">Falscher Code</p>
    </div>

    <!-- Content (hidden until code entered) -->
    <div class="parent__content" id="parent-content" style="display:none">

      <section class="parent__section">
        <h2>Uebersicht</h2>
        <div class="parent__stats" id="parent-stats">
          <div class="parent__stat">
            <span class="parent__stat-label">Aktuelle Stufe</span>
            <span class="parent__stat-value" id="stat-level">-</span>
          </div>
          <div class="parent__stat">
            <span class="parent__stat-label">Einheiten</span>
            <span class="parent__stat-value" id="stat-sessions">-</span>
          </div>
          <div class="parent__stat">
            <span class="parent__stat-label">Genauigkeit</span>
            <span class="parent__stat-value" id="stat-accuracy">-</span>
          </div>
        </div>
        <div class="parent__calendar" id="parent-calendar">
          <h3>Uebungstage</h3>
          <div class="parent__calendar-dots" id="calendar-dots"></div>
        </div>
        <div class="parent__errors" id="parent-errors">
          <h3>Haeufigste Fehlertasten</h3>
          <div id="error-keys-list"></div>
        </div>
      </section>

      <section class="parent__section">
        <h2>Einstellungen</h2>
        <div class="parent__settings">
          <div class="parent__setting">
            <label>Tastatur-Layout</label>
            <select id="setting-layout">
              <option value="de">DE (Deutschland)</option>
              <option value="ch">CH (Schweiz)</option>
            </select>
          </div>
          <div class="parent__setting">
            <label>Audio</label>
            <label class="parent__toggle">
              <input type="checkbox" id="setting-audio">
              <span>An/Aus</span>
            </label>
          </div>
          <div class="parent__setting">
            <label>Begleitfigur</label>
            <select id="setting-companion"></select>
          </div>
          <div class="parent__setting">
            <label>API-Key</label>
            <input type="password" id="setting-apikey" class="parent__input" placeholder="Gemini API Key">
            <button class="parent__save-btn" id="save-apikey-btn">Speichern</button>
          </div>
        </div>
      </section>

    </div>
  </div>
</section>
```

**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: `<span class="parent__error-key">E (12x)</span>`
   - 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

<success_criteria>

  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 </success_criteria>
After completion, create `.planning/phases/03-komplettes-spielerlebnis/03-05-SUMMARY.md`