7bddc50a53
New SPEC §15 Web client and FR-21; §1, §2, G-5, AC-14.1 revised; §8.5 and §10.5.2 gate lines for /app/, the invite accept and bearer requests (except /health, /setup/, /i/, /apk/) plus the /app/ CSP via $kvitt_csp, identical in infra/npm/kvitt.conf and infra/README.md step 4 (checked with nginx 1.31.7); §10.3, §10.6, §11.1 to §11.3 (rows W0 to W7), §14.7 ownership; change log is now §16. PLAN-W0..W7 and prompts/W0..W7; HANDOFF numbering D-139 / v0.30. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
58 lines
6.8 KiB
Markdown
58 lines
6.8 KiB
Markdown
# PLAN-W2: Web expenses, search and export
|
||
|
||
## 1. Goal
|
||
Build the web client's expense editor (W-5) with the category suggestion of §9.11, expense detail with the audit timeline (W-6), and the search page (W-12) with its CSV export, in `web/src/features/expenses/`, `web/src/features/search/` and `web/src/features/export/`, on W0's foundation (SPEC §15.6, §15.7), with tests.
|
||
|
||
## 2. Read first
|
||
1. SPEC §15 (all), FR-21, FR-8 (expenses), FR-12 (audit, AC-12.4), §9.1 (splits), §9.2, §9.6 (limits), §9.8, §9.11 (CS-1 to CS-7) and §14.5.6, §14.4.3 to §14.4.6 (push, rejections, audit diff), §13.3 rows S-7 and S-8, D-049, D-055, D-080, D-102.
|
||
2. `docs/ux/design-spec.md` §3.7 (editor fields) and the S-7 and S-8 sections; `docs/ux/category-suggestion.md`; `docs/ux/strings.csv` keys `expense_*`, `editor_*` and the shared ones.
|
||
3. `contracts/test-vectors/suggest_category.json`, `contracts/category-keywords.csv`, `contracts/examples/sync/*.json`.
|
||
4. `web/README.md` and W0's report (module map).
|
||
5. Files: `web/src/{app,api,lib,components,i18n}/**` (read only), `web/src/features/{expenses,search,export}/**` (W0's placeholders).
|
||
6. `CLAUDE.md`.
|
||
|
||
## 3. Already established
|
||
- W0 owns everything shared; this plan edits only its three feature folders. Missing shared pieces are requests to the orchestrator.
|
||
- Editor (W-5, mirrors S-7), built from W0's shared editor fields (design-spec §3.7, D-019; W5's recurring editor uses the same): amount (kr, two decimals, §9.6 range), description, category grid (active categories, the S-7 quick row of `frequentCategoryIds`' rule computed in the browser from the snapshot: the five most used active categories of the account, ties by `sort_order`, D-060), account (active accounts), date (today by default), payer, split modes `default`, `ratio`, `exact`, `full` with live shares from W0's `computeSplit`, notes; account, date, payer and split as four fixed rows (D-102); validation inline; Save writes one `POST /sync` mutation (§15.5); Delete in edit mode (soft delete, with confirmation); Cancel with the discard dialog (title, no body, D-080). After saving, the page returned to shows "Utgiften sparades" (D-071) without undo.
|
||
- Category suggestion (§9.11, §15.7): a TypeScript port of `suggestCategory` and its normalisation in `web/src/features/expenses/suggest/`, passing `contracts/test-vectors/suggest_category.json` in a Vitest test; the keyword table imported at build time from `contracts/category-keywords.csv` (Vite raw import of the repository file, never a copy), parsed with the §14.8 rules. For a new expense the category follows the description until the user picks one (CS-2, CS-3, CS-7), with no marker. The history for CS-1 comes from the snapshot. Unicode normalisation with `String.prototype.normalize('NFC')` and `\p{L}`/`\p{Nd}` classes (the `UnicodeText` roles of §14.5.6).
|
||
- Detail (W-6, mirrors S-8): every field, each user's share and the split mode, "Recurring: <template>" for a generated expense, the timeline from the snapshot's audit entries (AC-12.4; AC-12.5's pending line does not apply on the web), a deleted state; Edit (`/app/expenses/:id` with an edit sub-route of this plan's choice) and Delete.
|
||
- A rejection from `POST /sync` (§14.4.6, for example `stale`) shows its reason with the copy of S-16's rejection texts and reloads the row from the server; nothing is queued (§15.5).
|
||
- Search (W-12, §15.7): in the browser over the snapshot; non-deleted expenses whose description or notes contain the query (case-insensitive); filters amount from–to, date from–to, account, category, payer, combined with AND; result count and sum; ledger rows (W0's) opening W-6; newest first. The filter state may live in the URL query so a search can be bookmarked (this plan's choice).
|
||
- CSV export (§15.7): downloads the current filtered list, one row per expense: date, description, category, account, payer, amount, each user's share, notes; a header row in the page's language. Choose separator, decimal mark, quoting, byte-order mark and file name so that the file opens correctly in Numbers and in Excel with a Swedish locale; test the output byte for byte in Vitest; propose the format as a decision in the report. Built in the browser (`Blob`, a download link); no server endpoint.
|
||
- Keyboard: W0's shell binds `N` to `/app/expenses/new` and `/` to `/app/search`; this plan focuses the amount field on W-5 and the search field on W-12 when they open, and handles `Esc` (discard rule) inside its pages.
|
||
- Receipt pictures do not exist (§2): no picture field.
|
||
|
||
## 4. Owned paths
|
||
From SPEC §14.7: `web/src/features/expenses/**`, `web/src/features/search/**`, `web/src/features/export/**`. `web/package.json` only for a dependency line. Nothing else; `contracts/**` is read only.
|
||
|
||
## 5. Deliverables
|
||
- W-5, W-6 and W-12 with every state of §15.6 (loaded, empty, loading, error, long content), the editor's validation and split states, the detail's deleted state.
|
||
- The suggestion port with the vector test (65 cases); the CSV writer with its tests; tests of every split mode's shares, the quick row, the discard dialog, the rejection path, the search filters and their combination.
|
||
|
||
## 6. Steps
|
||
1. Worktree: from `/Users/jhag/projects/kvitt`, `git worktree add .claude/worktrees/agent-w2 -b agent-w2 main`; in it `git reset --hard main`, `tools/install-git-hooks.sh`, `cd web && npm ci`.
|
||
2. Read §2. Confirm W0 has landed (placeholders in this plan's folders on `main`); if not, stop and report.
|
||
3. `npm outdated`; record it. No new library is expected (a CSV library is not needed).
|
||
4. Suggestion port and vector test; editor; detail; search; export.
|
||
5. Manual check (optional): a throwaway server, `npm run dev`, add, edit and delete an expense, search, export and open the file in Numbers. Never the owner's development stack or the Pi.
|
||
6. Gate (§7). Commit on `agent-w2`, `git rebase main`, rerun the gate. Never merge, push or remove the worktree.
|
||
|
||
## 7. Gate
|
||
The web gate, from `web/`: `npm run check`. Green: every step passed in this session; Vitest summary quoted, including the suggestion vector test.
|
||
|
||
## 8. HUMAN steps
|
||
From SPEC §11.2: add, edit and delete an expense in a browser; open an exported CSV in Numbers (yes/no), relayed after landing.
|
||
|
||
## 9. Out of scope
|
||
- Home and accounts (W1), settle up (W3), analytics (W4), settings (W5).
|
||
- A server search or export endpoint; receipt pictures; edits outside the three folders.
|
||
|
||
## 10. Report
|
||
- Branch `agent-w2`, head commit after rebasing; gate lines; Vitest counts (suggestion vectors: 65).
|
||
- The CSV format chosen (bytes of a two-row sample) as a proposed decision; pages × states covered.
|
||
- `web_` keys added (en, sv); strings missing from `strings.csv`; design-spec mismatches.
|
||
- Requests for W0's modules; proposed decision entries (numbered from the brief); open questions with SPEC section numbers.
|
||
|
||
## 11. Numbering
|
||
Assigned in the start prompt; use `TODO(spec)` if none was given.
|