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

124 lines
8.8 KiB
Markdown

# 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 the `FetchDataService` contract. Reuse what exists.
>
> Work top-to-bottom; tasks are ordered so each unblocks the next. Run `npx tsc --noEmit` and `npm run lint` after 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 successful `login`/`register`, navigate to `/` (use `useNavigate()` from `react-router-dom`, `navigate('/', { replace: true })`).
- Also guard `/auth` itself: if `isAuthenticated` is already true, redirect to `/`. Either inside `Auth.tsx` (early `<Navigate to="/" replace />`) or in `App.tsx`'s `/auth` route. 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 `/auth` while 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 its `workspace_id` to drive `WorkspaceContext` (set it via the existing `setWorkspaceId`, and keep persisting to `localStorage` is fine as a cache — just don't *depend* on it being pre-populated).
- Expose the **children** and a **`selectedChildId`** from context (or a small `ChildContext`). For MVP, **auto-select the first child** if any exist. Add a `selectChild(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** `@router` decorators under each prefix, and correct each path + its params/body in `api.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, wire `getParentalLeave` to 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 of `DevFetch.tsx` into a **reusable hook** (e.g. `useScraperFetch`) and a small **`QRCodeModal`** component. `DevFetch` should 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 selected `child_id`), shows the live QR, and on `done` closes 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 `done` result 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`, when `getUserContext()` (or any scraper call) returns a 401/unauthorized, render a friendly banner with a **"Logga in först"** link to `/auth` (use `isUnauthorized` from `services/errors.ts`). Optionally auto-redirect to `/auth` and back.
- Keep the existing dev-only mount and the "real BankID" warning. This is a small UX fix, not a redesign.
**Acceptance:**
- Opening `/dev/fetch` while 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)
1. Register a new account → you land on the dashboard, logged in.
2. Dashboard shows the empty state → add a child → overview renders for that child.
3. Click "Hämta från Försäkringskassan" → scan QR → overview updates with FK numbers.
4. Log out, log back in → you land on the dashboard with the child still there.
5. `npx tsc --noEmit`, `npm run lint`, `npm test` all green.
**Escalate to senior** (don't guess): the parental-leave read contract (J3/S2), and anything about how/where scraped data is persisted (S1).