# Local FamilyDashboard iPad feedback polish

Use this reference when the user has tested the FamilyDashboard PWA on an iPad and reports usability issues around the morning status area, task-board movement, or admin PIN changes.

## Morning status as a child-readable progress bar

User feedback: the upper status area should not be a row of pill chips. Children need to understand:

- how much time has passed since wake-up
- where they are right now in the morning routine
- what they must do now and next
- how much time remains until leaving for school

Implementation pattern:

1. Backend timeline payload should include derived fields in addition to segments:
   - `progress_percent`
   - `elapsed_minutes`
   - `remaining_minutes`
   - `current_step`
   - `next_step`
   - `segments[]` with `label`, `state`, and `percent`
2. Derive timing from persisted family settings where possible:
   - `ChildSettings.default_wake_time`
   - `ScheduleDay.school_start` or fallback `ChildSettings.default_school_start`
   - `ChildSettings.commute_minutes`
3. Represent the UI as a horizontal progress/loading bar with labelled markers, not as equal-width chips.
4. Status copy should be action-oriented and child-readable, e.g. `Jetzt: Morgenessen · als nächstes: Zähneputzen · noch 43 Min.`
5. Keep weekend/schulfrei mode graceful with harmless progress defaults and friendly labels.

## Stable task-row layout

User feedback: when a Pflicht/Lernen task is checked off, the Bonus block must not jump upward. Required tasks should appear to move horizontally into the Erledigt column while staying on the same vertical row; undo should move the same row back left.

Implementation pattern:

1. Build a combined task list from `open + done`, then split by kind (`required/study` vs `bonus`) after combining.
2. Render each section as stable rows with two slots:
   - left slot: open task card or placeholder
   - right slot: done task card or placeholder
3. Use placeholders with the same approximate min-height as task cards so rows retain height.
4. Sort rows by a stable key (`id`, template sort order if available) so task movement does not reorder neighbouring tasks.
5. Keep Bonus as a separate section below the required/study rows with a visual divider; it should not be positioned directly by the shrinking open-task column.

## Changing seeded admin PINs in an existing SQLite DB

If the user asks to change the Admin PIN, changing only the seed default is insufficient for an already-created DB. Add a migration-like reseed guard:

1. Keep storing only a PBKDF2 hash, never the raw PIN.
2. On seed/init, compute the target PIN from `FAMILYDASHBOARD_INITIAL_ADMIN_PIN` or the new requested default.
3. Store a non-secret marker such as `admin_pin_marker` in `AppSetting`.
4. If the marker is missing or stale, overwrite the existing admin credential hash with a fresh hash for the target PIN.
5. Update tests and helper login fixtures to use the new PIN.

## Tailnet/iPad preview when Docker cannot be rebuilt immediately

If Docker is blocked but the user needs to test UI changes now, use a temporary dev preview instead of claiming production Docker is updated:

1. Start backend on an alternate port, e.g. `8001`.
2. Start Vite on an alternate Tailnet-reachable port, e.g. `5175`.
3. Make Vite proxy configurable via `VITE_API_TARGET`, defaulting to the Docker service name for normal Compose usage.
4. Report clearly that the temporary preview URL is separate from the existing Docker URL and still needs Docker/GitHub finalization.
5. Do not persist environment-specific failure claims; capture only the preview pattern and the honest reporting boundary.

## Verification checklist

- Backend tests pass after changing seed counts, default PIN, and timeline payload.
- Frontend TypeScript build passes after widening timeline types.
- Smoke the new PIN via login and ensure the old PIN is rejected if possible.
- Confirm dashboard payload includes the new required task and timeline progress fields.
- If final Docker verification is blocked, explicitly label the running preview as temporary and do not push/claim final deployment until the normal gate is completed.
