8.8 KiB
Junior Developer — Frontend MVP Completion TODO
Owner: Junior developer
Status: Ready to implement (tackle after frontend_polish.md)
Goal: Close the gap between the bare happy path and the app described in ARCHITECTURE.md — a usable shell with navigation, a working settings page, calendar planning, the manual-data fallback, and partner planning.
Why this plan exists: the current dashboard has no header nav, no logged-in user, no child indicator, no logout, and no reachable settings — so features that already exist in code (Calendar, Tips, Settings, manual entry) are stranded. The backend endpoints for all of this already exist; this is almost entirely frontend wiring.
Work in tiers, top to bottom. Tier 1 is the highest value (it's what makes the app feel like an app). Keep
tsc/lint/tests green per task. CSS stays optional — structure and function over looks.
TIER 1 — App shell: navigation, identity, working settings
T1.1 — Real header (user, current child + switcher, nav, logout)
Problem: components/layout/Header.tsx already accepts onLogout / onWorkspaceSelect, but pages only pass { title }, so the nav is empty. There's no way to log out, switch pages, or see who/which child you're viewing.
Do — extend Header and wire it via Layout on every page:
- Logged-in identity: show the current parent's name. ⚠️
GET /api/auth/contextdoes not currently return the parent's display name (onlypersonal_data_uuid,workspace_id,children). Either show the email fromGET /api/auth/me, or ask the senior to addnameto the/auth/contextpayload (small, preferred). Don't hardcode. - Current child + switcher:
useWorkspace()exposeschildren,selectedChildId,selectChild(). Render a<select>of children; changing it callsselectChild(id). Show the selected child's name in the header. (If only one child, a static label is fine.) - Nav links: Översikt (
/), Kalender (/calendar), Tips (/tips), Inställningar (/settings) usingreact-router<Link>/NavLink. These routes already exist inApp.tsx. - Logout: a "Logga ut" button →
useAuth().logout()thennavigate('/auth', { replace: true }). - Apply the header consistently: pass the nav/user/logout props through
Layoutso every page gets the same shell (Dashboard, Calendar, Tips, Settings).
Acceptance: Every authenticated page shows a header with the parent's name, a child selector reflecting/affecting selectedChildId, working nav links to all four pages, and a logout button that returns you to /auth.
T1.2 — Make Settings reachable and actually save
Problem: pages/Settings.tsx renders the right inputs but most saves are no-ops or broken. Notably handleSaveVacationDays sends personalDataUuid: 'current-user' (a hardcoded string, not a UUID); the parent profile (salary_type / employment_status) and planning goal have no save handlers at all; child birth/preschool date in settings doesn't persist. The page is also unreachable (fixed by T1.1 nav).
Do — wire each section to its real endpoint (all exist; use the IDs from useWorkspace() context — personalDataUuid, workspace.id, selectedChildId):
| Setting | Endpoint | Notes |
|---|---|---|
| Parent profile (salary_type, employment_status) | PUT /api/settings/parent/{personal_data_uuid} |
Drives the salary-gated tips (§6). Add a save handler — currently local state only. |
| Planning goal (max_time / max_money / balanced) | PUT /api/workspace/{workspace_id} |
Add a save handler. Note the value strings the backend expects (check WorkspaceUpdate schema) — the page currently defaults to 'balance', confirm it's 'balanced'. |
| Vacation days / year | POST /api/vacation-days |
Fix the 'current-user' bug → pass the real personalDataUuid. |
| Child birth date + preschool start date | PUT /api/settings/child/{child_id} |
Preschool date powers the studiedagar tip. |
| Break periods | POST/PUT/DELETE /api/break-periods |
Mostly wired already — verify add/remove persist and reload. |
- Load current values into the form (don't always default to 25 vacation days /
'balance'— read from the fetched workspace/parent/vacation data). - Give each section its own save button + success/error message, and re-fetch after save so the UI reflects persisted state.
Acceptance: From Inställningar you can set salary type, employment status, planning goal, annual vacation days, and the child's birth/preschool dates; each persists (survives reload) and shows a success message. No 'current-user' placeholder.
TIER 2 — Planning & data entry
T2.1 — Calendar: add / edit / delete planned days
Problem: pages/Calendar.tsx renders a month grid but day clicks do nothing (per the round-3 report). Planning days is the core of the app.
Do:
- Clicking a day opens a small day-planner (reuse/extend the existing inputs): choose day type (sjukpenning / lägstanivå / vacation), and for two-parent workspaces, which parent + whether it's a dubbeldag.
- Persist via
POST /api/planned-days; allow delete viaDELETE /api/planned-days/{id}; edit via the PUT route. - Validate against remaining days before saving (ARCHITECTURE feature): don't let planned FP exceed
days_leftfrom the latest snapshot; surface the analytics/validation error (the backend has dubbeldagar validation — show its message). - Refresh the calendar + the dashboard breakdown after changes.
Acceptance: You can add, edit, and delete planned days from the calendar; the day shows its type; over-planning is blocked with a clear message; the dashboard's "Planerade dagar" reflects changes.
T2.2 — Manual data-entry fallback
Problem: ARCHITECTURE §2.3 mandates a manual-entry path that always works even if scraping is unavailable. The backend has it (POST /api/parental-leave/manual, api.saveManualFetch), but there's no UI — the only way to get data in is the scraper.
Do:
- Add a "Ange uppgifter manuellt" form (near the FK fetch button or in Settings) collecting the same fields the user sees on FK:
total_days,days_used, reserved total/left, transferable left, dubbeldagar left, and the tier breakdown. Use the field bounds from the backend schema (total_days0–480,dubbeldagar_left0–60, etc.). - Save via
api.saveManualFetch(source='manual'), then refresh the overview. The provenance line from P1 will then show "Manuellt angiven …".
Acceptance: A user can enter their FK numbers by hand and see the overview populate, with no scraper involved.
TIER 3 — Partner planning & tips (rounds it out)
T3.1 — Add a partner (solo planning path)
Problem: The product premise is planning with a partner, but there's no UI to add a second parent. The backend supports the no-login "solo planning" path: POST /api/workspace/{workspace_id}/parents adds a placeholder parent profile (just a name).
Do:
- In Settings, add "Lägg till partner" → creates a placeholder
PersonalDatavia that endpoint, then re-fetch context so the second parent appears. - Let per-parent settings (T1.2) and calendar planning (T2.1) target either parent.
- Optional / stretch: collaborative planning via invitations (
POST /api/workspace/{id}/invitations,POST /api/invitations/redeem) — list it but defer unless time allows.
Acceptance: A user can add a partner profile and plan/configure days for both parents in one workspace.
T3.2 — Verify Tips end-to-end
Problem: pages/Tips.tsx + TipCard exist and the backend tips engine is implemented, but the page was unreachable.
Do: Now reachable via T1.1 nav — verify it loads tips for the selected workspace/child (GET /api/tips/{workspace_id}), renders severity badges, and that the salary-/goal-gated tips actually appear once T1.2 lets users set salary type and planning goal. Fix any contract mismatches you find.
Acceptance: Tips page shows real tips driven by the user's configured settings.
Suggested order & notes
- T1.1 → T1.2 first: navigation + working settings unlock everything else and deliver the most visible improvement.
- Then T2.1 (calendar) — the biggest single chunk; budget accordingly.
- T2.2 (manual fallback) is small and satisfies an architectural must-have — good quick win.
- Tier 3 rounds out the partner story; invitations are optional.
One backend dependency to flag to the senior: the parent display name isn't in GET /api/auth/context (T1.1). Everything else uses endpoints that already exist — read each router's actual @router decorator and request/response schema before wiring, and don't invent paths (see the path-mismatch lesson from the last round).
Out of scope: real CSS/visual design, responsive/mobile, accessibility audit, real-time updates — separate passes once the functionality is complete.