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>
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 |
|
|
true |
|
|
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 ````html
<section id="screen-parent" class="screen">
<div class="parent">
<button class="parent__back-btn" id="parent-back-btn">← 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
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.
<success_criteria>
- Ctrl+Shift+E opens code input overlay on parent screen
- Code "1234" reveals parent content, wrong code shows error
- Overview shows: current level, total sessions, accuracy percentage
- Calendar dots show last 30 days with green dots for practice days
- Error keys shown as badges, sorted by frequency, top 5
- Settings: layout change, audio toggle, companion change, API key save — all persist to IndexedDB
- Back button returns to forest screen </success_criteria>