# 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: