Files
F-r-ldraledigplannerare/frontend_mvp_complete.md
T
2026-06-14 09:58:30 +02:00

8.8 KiB
Raw Blame History

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/context does not currently return the parent's display name (only personal_data_uuid, workspace_id, children). Either show the email from GET /api/auth/me, or ask the senior to add name to the /auth/context payload (small, preferred). Don't hardcode.
  • Current child + switcher: useWorkspace() exposes children, selectedChildId, selectChild(). Render a <select> of children; changing it calls selectChild(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) using react-router <Link>/NavLink. These routes already exist in App.tsx.
  • Logout: a "Logga ut" button → useAuth().logout() then navigate('/auth', { replace: true }).
  • Apply the header consistently: pass the nav/user/logout props through Layout so 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 via DELETE /api/planned-days/{id}; edit via the PUT route.
  • Validate against remaining days before saving (ARCHITECTURE feature): don't let planned FP exceed days_left from 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_days 0–480, dubbeldagar_left 0–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 PersonalData via 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

  1. T1.1 → T1.2 first: navigation + working settings unlock everything else and deliver the most visible improvement.
  2. Then T2.1 (calendar) — the biggest single chunk; budget accordingly.
  3. T2.2 (manual fallback) is small and satisfies an architectural must-have — good quick win.
  4. 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.