07df30d04d
K1 landed671c00d(card purchases on the server, schema 4, E-36 to E-44). Decisions: D-181 history counts today (6 windows for 180 days, revises CP-3 and CP-7); D-182 the server's CS-4 first token; D-183 Enable Banking errors to CP-14; D-184 callback (expired state stored as expired, revises CP-2; atomic claim; session without accounts revoked); D-185 fetch details; D-186 PSU headers; D-187 E-41, E-43, E-44 details, CardAttempt.failure a plain string; D-188 placement and the secrets/cards compose mount. D-189, D-190 unused. SPEC: §9.12 CP-2 to CP-5, CP-7, CP-8, CP-14; §10.2; §11.1, §11.2 K1 row with the landing gates, §11.3; §14.1.3; §14.3.3; §14.3.4 (E-1 schema_version 4, E-37, E-39, E-41, E-43, E-44); §14.3.5; §14.7; change log. PLAN-K2, PLAN-K3 and prompts/README.md name v0.39 and v0.40. HANDOFF: K1 landed, K2 and K3 running, the Android gate red until K2, K1 owner steps 1 to 4 pending, next free D-212 and v0.39. Pre-commit hook bypassed (--no-verify): docs only; its Android fast tests fail only in :domain VectorFilesTest, red on main by design until K2 lands (as for671c00d). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
51 lines
6.6 KiB
Markdown
51 lines
6.6 KiB
Markdown
# PLAN-K3: Card purchases, web
|
||
|
||
## 1. Goal
|
||
The card features of FR-22 in the web client (D-174): W-21 Kortköp, W-22 Anslut kort with BankID by QR in the browser, W-23 the connection's settings, the sidebar entry, "Skapa utgift" through W-5 with the prefill of CP-9, the card line on W-6, "Från kortköp" on W-3, and `matchCardPurchase` in TypeScript passing its vectors.
|
||
|
||
## 2. Read first
|
||
1. SPEC §15 (§15.3 strings and layout, §15.5 data, §15.6 rows W-3, W-5, W-6, W-21 to W-23, the navigation paragraph), §9.12 (CP-1 to CP-14, with K0's answers in v0.36), FR-22, §13.3 rows S-23 to S-27, the "Card callback page" and "Card purchase strings" tables, §14.3.3, §14.3.4 E-36 to E-44, §14.5.8, §14.7; D-174, D-169, D-170, D-172, D-177 to D-180 (K0's results and the artwork).
|
||
2. K1's `contracts/openapi.yaml` and `web/src/api/schema.d.ts` (regenerated by K1), `contracts/examples/E-36-*` to `E-44-*`, `contracts/test-vectors/match_card_purchase.json`.
|
||
3. `web/README.md`, `web/src/app/` (routes, layout, sidebar), `web/src/api/` (client, snapshot), `web/src/lib/domain/`, `web/src/components/`, `web/src/features/{expenses,accounts,settings}/`; `docs/ux/design-spec.md` §2 and the S-23 to S-27 design (K2's report, if landed).
|
||
4. `CLAUDE.md` (web gate, web layering).
|
||
|
||
## 3. Already established
|
||
- Card data is read through E-40 into an in-memory card snapshot next to the household snapshot (§15.5: pulled from 0 on load, from its cursor on focus and after every card write); nothing persisted across a reload; writes (E-44) go at once, no queue.
|
||
- Connect: E-37 with `client` `web`; the browser goes to the returned URL in the same tab; the bank shows its QR for BankID on the phone; the callback page's link returns to `/app/cards?attempt=<id>`, where W-21 polls E-38 every 2 seconds until the attempt ends, then drops the parameter (CP-2).
|
||
- "Skapa utgift" opens W-5 at `/app/expenses/new?cardPurchase=<id>` (plus `&account=<id>` from W-3's "Från kortköp", `/app/cards?account=<id>`); W-5 prefills per CP-9, runs the §9.11 suggestion as for any new expense, and after an accepted save sends E-44 with the new expense id.
|
||
- Artwork (D-174, D-180): `web/src/features/cards/assets/card-sj-prio-mastercard.png` is committed (the same file as Android's, 1313 × 829, cropped to the card, transparent only at its rounded corners and in the notch on the right edge). The cards feature imports it (Vite emits it under `/app/assets/` with a hashed name) and maps the image key `sj_prio_mastercard` to it from the first commit, drawn at its own aspect ratio (`object-fit: contain`, no clipping, no background), `alt` the card's name; for any unknown key, the drawn fallback of D-173 (CSS: aspect ratio 1.586, radius 16 px, the `green` category token's light value, the name in white). Served from the server's own origin (CSP `img-src 'self'`). Do not edit or re-encode the PNG.
|
||
- Strings: the keys of §13.3's table (K2 adds them to `docs/ux/strings.csv`; `npm run i18n:import` regenerates the dictionaries); web-only strings get `web_cards_` keys in `web/src/features/cards/i18n/{sv,en}.json`.
|
||
- The sidebar and bottom bar gain "Kortköp" after Home (D-172).
|
||
|
||
## 4. Owned paths
|
||
From SPEC §14.7 (row K3): `web/src/features/cards/**` (new); in shared code the route in `web/src/app/`, the sidebar entry and `web/src/lib/domain/` (`matchCardPurchase` and its vector test); `web/src/features/expenses/**` (W-5's prefill and link, W-6's card line), `web/src/features/accounts/**` (W-3's "Från kortköp"), `web/src/features/settings/**` (W-13's row); the regenerated i18n dictionaries. Not `web/src/api/schema.d.ts` (K1), `docs/ux/**`, `server/**`, `contracts/**`, `android/**`.
|
||
|
||
## 5. Deliverables
|
||
- The card snapshot and API calls; W-21 (header, filters, day groups, the purchase panel or dialog with every action of S-23's sheet, match chips, the `?attempt=` and `?account=` handling), W-22 (catalogue with artwork, history choice, "Anslut med BankID"), W-23 (as S-26 with the disconnect dialog); W-5, W-6, W-3 and W-13 changes; the sidebar entry.
|
||
- `matchCardPurchase` passing `match_card_purchase.json` in `web/src/lib/vectors.test.ts` (or the domain's vector test), listed as ported.
|
||
- Tests: pages against the fake server with every state of §13.3 (no connection, first fetch, loaded, empty filter, expiring, expired, fetch failed, long content), the attempt polling, E-44 outcomes (`expense_already_linked`, `not_found`), the prefill rules.
|
||
|
||
## 6. Steps
|
||
1. Worktree: from `/Users/jhag/projects/kvitt`, `git worktree add .claude/worktrees/agent-k3 -b agent-k3 main`; in it `git reset --hard main`; `npm ci` in `web/`; `tools/install-git-hooks.sh`.
|
||
2. Confirm SPEC v0.36 is on `main`. K3 may start before K1 lands, against the spec and the fake server; before its gate it rebases on K1 (the generated types and the vectors are K1's) and on K2 (the card strings in `docs/ux/strings.csv`; then `npm run i18n:import`). If K1 or K2 has not landed when the rest is done, stop and report.
|
||
3. Build; check manually against a throwaway server (`cargo run -p kvitt-server -- serve` with a temporary `KVITT_DATA_DIR` and `KVITT_EB_API_URL` pointed at a stub, as K1's tests do), never the owner's development or Pi data.
|
||
4. Gate (§7). Commit on `agent-k3`, `git rebase main`, rerun the gate. Never merge, push or remove the worktree.
|
||
|
||
## 7. Gate
|
||
The web gate, from `web/`: `npm run check`. Report the real result lines (test files, tests).
|
||
|
||
## 8. HUMAN steps (relayed by the orchestrator in chat, after K1 is deployed and an image with K3 is deployed)
|
||
1. On the laptop, `https://kvitt.suorra.duckdns.org/app/` › Inställningar › Anslut kort: the SJ Prio card shows "Ansluten" if the phone connected it (yes/no). Kortköp lists the same purchases as on the phone (yes/no).
|
||
2. Kortköp › a purchase › "Skapa utgift" › "Spara": the purchase is under "Kopplade" on the laptop and on the phone after a sync (yes/no).
|
||
3. If the card is not connected yet or after "Koppla från": Anslut kort › SJ Prio Mastercard › "Anslut med BankID": the bank's page shows a QR; scan it with BankID on the phone; "Tillbaka till Kvitt" opens Kortköp with your purchases (yes/no each).
|
||
|
||
## 9. Out of scope
|
||
- The server and the contract (K1), the app (K2); `docs/ux/strings.csv` (K2); a card notification and bulk add (D-171).
|
||
|
||
## 10. Report
|
||
- Branch `agent-k3`, head commit after rebasing; gate lines.
|
||
- `web_cards_` strings added; proposed decision entries and spec text for anything beyond §15.6 and §9.12 (numbered from the brief); HUMAN steps outstanding worded for relay; open questions with SPEC section numbers.
|
||
|
||
## 11. Numbering
|
||
Decisions from the start prompt (`TODO(spec)` if none was given). Proposed spec text goes into SPEC **v0.40** (v0.36 recorded K0's results, v0.38 K1's report, v0.39 K2's).
|