Files
kvitt/PLAN-W2.md
T
jacobh2 7bddc50a53 SPEC v0.28: web client programme (D-129 to D-134), plans W0 to W7
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
2026-10-21 20:49:00 +02:00

58 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.