8.8 KiB
Junior Developer — MVP Happy-Path TODO
Owner: Junior developer Status: Ready to implement Goal: Make the core happy path work end-to-end: register → land logged-in on the overview → log in → land on the overview → register a child → see the overview → fetch the child's data from Försäkringskassan. Date: 2026-06-12
Context: The backend is in good shape and the FK scraper is now implemented (Playwright, real captured selectors). The breakages are almost all in frontend wiring. FK scraping is now legally cleared, so the overview's day numbers should come from the scraper.
Do NOT undo or rewrite the scraper (
backend/app/scrapers/fk_scraper.py), its captured selectors, or theFetchDataServicecontract. Reuse what exists.Work top-to-bottom; tasks are ordered so each unblocks the next. Run
npx tsc --noEmitandnpm run lintafter each task — keep them green.
J1 — Redirect after login & register (the #1 visible bug)
Problem: pages/Auth.tsx calls login() / register(), which do authenticate you (AuthContext sets isAuthenticated: true), but the page never navigates, and nothing bounces an already-authenticated user off /auth. So you're silently logged in but stuck on the form — this is why "after registering I'm not logged in" and "login doesn't take me anywhere."
Do:
- In
pages/Auth.tsx, after a successfullogin/register, navigate to/(useuseNavigate()fromreact-router-dom,navigate('/', { replace: true })). - Also guard
/authitself: ifisAuthenticatedis already true, redirect to/. Either insideAuth.tsx(early<Navigate to="/" replace />) or inApp.tsx's/authroute. This handles the "I manually went to /auth while logged in" case cleanly.
Acceptance:
- Registering a brand-new account lands you on the dashboard, logged in, with no manual navigation.
- Logging in lands you on the dashboard.
- Visiting
/authwhile logged in redirects to/.
J2 — Bootstrap the workspace after auth (why the dashboard is empty)
Problem: context/WorkspaceContext.tsx only reads workspaceId from localStorage, which nothing ever sets. So after login there is no workspace → the dashboard shows defaults. The backend already exposes everything you need at GET /api/auth/context (api.getUserContext()), which returns { personal_data_uuid, workspace_id, children[] }.
Do:
- When the user becomes authenticated, call
api.getUserContext()and use itsworkspace_idto driveWorkspaceContext(set it via the existingsetWorkspaceId, and keep persisting tolocalStorageis fine as a cache — just don't depend on it being pre-populated). - Expose the children and a
selectedChildIdfrom context (or a smallChildContext). For MVP, auto-select the first child if any exist. Add aselectChild(id)setter for later. - Also expose
personal_data_uuid(the overview + FK fetch need it). - Re-fetch context after creating a child (see J4) so the new child appears and gets selected.
Acceptance:
- After logging in (fresh browser, empty localStorage), the dashboard loads the user's workspace automatically.
- If the workspace has a child, it's auto-selected and the overview renders for it.
(Senior will review this context seam — keep the change small and readable.)
J3 — Fix the broken API client paths (several calls 404 today)
Problem: services/api.ts invents /workspaces/{id}/… REST paths that do not exist on the backend. Confirmed mismatches:
api.ts call (wrong) |
Real backend route |
|---|---|
GET /workspaces/{id}/break-periods |
prefix is /api/break-periods (see routers/break_periods.py + routers/settings.py) |
POST /workspaces/{id}/break-periods |
/api/break-periods |
GET /workspaces/{id}/tips |
/api/tips/{workspace_id} (prefix /api/tips, see routers/settings.py) |
GET /workspaces/{id}/planned-days |
prefix /api/planned-days (see routers/planned_days.py) |
POST /workspaces/{id}/planned-days |
/api/planned-days |
GET /workspaces/{id}/parental-leave |
/api/parental-leave/{personal_data_uuid}/{child_id} — different shape; see note |
Do:
- Open
backend/app/routers/*.py, read the actual@routerdecorators under each prefix, and correct each path + its params/body inapi.ts. The analytics overview path (/analytics/{ws}/overview?child_id=…) is already correct — use it as your reference for "right". - For parental-leave, the backend route needs
personal_data_uuid+child_id, not a workspace id. Don't guess — the senior is adding/clarifying a workspace-friendly read route (Senior task S2). Until that lands, wiregetParentalLeaveto the contract the senior specifies (likely/api/parental-leave/{personal_data_uuid}/{child_id}using the IDs from context). Coordinate; don't invent a path.
Acceptance:
- No 404s in the network tab when the dashboard loads.
tsc/lint green; existing hook tests updated to the corrected paths and passing.
J4 — Child registration on the overview (empty-state happy path)
Problem: There's no way to add a child from the overview. The pieces exist: api.createChild() → POST /api/children, and the ChildBirthdayInput component.
Do:
- On the Dashboard, when the selected workspace has no children, render an empty state with an "Lägg till barn" form (reuse
ChildBirthdayInput; collect name + birth date). - On submit: call
api.createChild({ workspace_id, name, birth_date }), then re-fetch context (J2) so the child appears and is auto-selected, and the overview re-renders for it. - Show a clear success/error message; disable the submit button while in flight.
- Keep it simple — one child is enough for MVP. A child switcher can come later.
Acceptance:
- A freshly registered user sees "no child yet → add one", can add a child, and immediately sees the overview populate for that child.
J5 — "Hämta från Försäkringskassan" on the overview
Problem: The FK fetch flow currently only lives in the dev harness (pages/DevFetch.tsx). The real overview should let the user pull their data for the selected child.
Do:
- Extract the harness's session logic (start → poll every ~1s → render QR
data:image/png;base64,…→ show result/error → cancel) out ofDevFetch.tsxinto a reusable hook (e.g.useScraperFetch) and a smallQRCodeModalcomponent.DevFetchshould then use these too (don't duplicate). - On the Dashboard, add a "Hämta från Försäkringskassan" button (enabled only when a child is selected). Clicking it opens the
QRCodeModal, starts a session (the backend auto-resolves IDs from the logged-in user — send an empty body, or pass the selectedchild_id), shows the live QR, and ondonecloses the modal and refreshes the overview (re-fetch parental-leave for the selected child). - Handle the readable error states the backend can return (e.g. "No child in workspace — add one before fetching"). Show the message; don't crash.
Acceptance:
- From the overview, clicking the button shows the BankID QR, and after a successful scan the overview updates with the fetched numbers.
Depends on Senior S1 (persist the scraped snapshot) for the refreshed read to actually return data. You can build and demo the modal/QR against the live
doneresult before S1 lands; full overview refresh works once S1 is in.
J6 — Small dev-harness auth fix (stop the bare "Not authenticated")
Problem: npm run login opens /dev/fetch directly. The auth token lives only in memory after an app login, so opening the harness cold makes getUserContext() / start return "Not authenticated" with no guidance.
Do (minimal):
- In
pages/DevFetch.tsx, whengetUserContext()(or any scraper call) returns a 401/unauthorized, render a friendly banner with a "Logga in först" link to/auth(useisUnauthorizedfromservices/errors.ts). Optionally auto-redirect to/authand back. - Keep the existing dev-only mount and the "real BankID" warning. This is a small UX fix, not a redesign.
Acceptance:
- Opening
/dev/fetchwhile logged out shows a clear "log in first" link instead of a raw error. After logging in (J1 now redirects properly), the harness works.
Definition of done (the demo to run)
- Register a new account → you land on the dashboard, logged in.
- Dashboard shows the empty state → add a child → overview renders for that child.
- Click "Hämta från Försäkringskassan" → scan QR → overview updates with FK numbers.
- Log out, log back in → you land on the dashboard with the child still there.
npx tsc --noEmit,npm run lint,npm testall green.
Escalate to senior (don't guess): the parental-leave read contract (J3/S2), and anything about how/where scraped data is persisted (S1).