124 lines
8.8 KiB
Markdown
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).
|