Commit Graph

9 Commits

Author SHA1 Message Date
jacobh2 07c36d24a2 L-10: E-16 answers not_found for another user's device (D-156)
A member naming the other user's device now gets the same 404 not_found
body as an unknown id, so E-16 no longer reveals whose a device id is.
The admin keeps revoking any device except the current one (AC-3.2, §8.4).
contracts/openapi.yaml drops the 403 on E-16 and states the rule;
web/src/api/schema.d.ts regenerated. The forbidden test becomes a
not_found test, plus one asserting the body equals the unknown-id body
and that nothing was revoked.

Also bumps ctutils 0.4.3 -> 0.4.4 (lockfile only) for RUSTSEC-2026-0385,
published 2026-10-23, which failed cargo audit on main.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-24 11:03:19 +02:00
jacobh2 83f52937cf Web fix: deterministic feature strings, suggest_category vectors, raw bodies in the fake server
- Root cause of the raw keys (web_accounts_load_failed, the settle missing state) on main:
  Vitest 5's file-system module cache (on by default) skips only modules whose source
  contains the literal `import.meta.glob(`. catalog.ts wrote `import.meta.glob<…>(`, so
  its transform was cached by its own content and kept the glob result from before a
  feature added its i18n/*.json; the main checkout's node_modules/.vitest-cache served
  that stale catalog until a config change invalidated it. Fresh worktrees passed.
- catalog.ts: glob without a type argument (typed by annotation), with the reason.
- catalog.test.ts: the loaded feature catalogs must equal the i18n files on disk.
- A string two features render moves to the shared catalog: web_accounts_load_failed
  (accounts/PageStates, also rendered by Home) is now web_ledger_load_failed in
  src/i18n/web-strings.csv; sv.json/en.json regenerated.
- vectors.test.ts: suggest_category is PORTED and runs against features/expenses/suggest.
- fakeServer: bodies by Content-Type (JSON parsed, text/* as text, else raw bytes);
  ProfilePage's picture test asserts the uploaded bytes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:44:30 +02:00
jacobh2 1aecf5172f W4 web analytics: W-9, W-10 with the D-128 line chart, W-11 Insights on E-34/E-35
- domain/: TypeScript port of resolveRange and spendSeries (contract vectors 13 + 13),
  AnalyticsRules (averages, moving average, caption, filter rules, drill-down URL),
  S-11/S-12 model from the sync snapshot, W-11 mapping of the E-34/E-35 responses.
- W-9 /app/analytics: period menu with custom range dialog, interval, measure, group-by;
  total card; Recharts stacked bars (4/2 corners, 2 px segment gaps) with bucket links
  as keyboard stops, legend, table view; category and account breakdowns.
- W-10 /app/analytics/:groupKind/:groupId (category, account, bucket): own period and
  interval in the query string, header card, line chart with range average, 3-bucket
  moving average and caption, expense rows.
- W-11 /app/insights: household and per-category trends, two-month forecast per account
  with its recurring and average parts; online only, loading/error/empty states.
- Strings in features/analytics/i18n; render tests per page state, domain and chart tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:39:35 +02:00
jacobh2 a65ab711ef W2 web expenses, search and CSV export
W-5 expense editor (S-7): amount, description, category quick row and
"Alla" grid with the §9.11 suggestion for a new expense (CS-1 to CS-7),
the four fixed option rows (D-102), split modes default, ratio, exact and
full with live shares, notes, inline §9.6 validation, one POST /sync per
save, delete with confirmation, the discard dialog on Cancel, Esc and
in-app navigation (D-080), rejection and network failures shown on the
page with the form kept (§15.5). Defaults per AC-8.6 with this browser's
last account and category; ?account= preselects the opener's account.

W-6 expense detail (S-8): hero, facts, shares and mode, recurring
template badge, the AC-12.4 timeline, deleted and missing states.

W-12 search with filters in the URL query, count and sum, rows a hundred
at a time, and the CSV export (UTF-8 BOM, semicolons, CRLF, decimal
comma, ISO dates, header in the page language), built in the browser.

The §9.11 port passes contracts/test-vectors/suggest_category.json (65
cases); the keyword table is a Vite raw import of
contracts/category-keywords.csv, which needs ../contracts in
server.fs.allow (vite.config.ts).

Shared editor fields of design-spec §3.7 in web/src/components/editor/
for W5's template editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:38:09 +02:00
jacobh2 3ebe57d8b9 W5 web settings: W-13 hub, W-14 profile, W-15 categories, W-16 recurring, W-17 devices, W-18 invites, W-19 about
- W-13: household card (roles, partner missing), rows with category and recurring summaries, Invites for the
  admin only, language and theme (W0's card), log out that forgets the token only (§15.2).
- W-14: picture upload (E-9, raw bytes, JPEG/PNG/WebP up to 5 MiB) and remove (E-10), name validation and save
  (E-8); the S-2 step once per browser after a partner login (AC-2.10).
- W-15: list in sort_order with drag and keyboard reorder (arrow keys on the handle, move up/down) saved through
  E-24 sort_order with restore on failure; archived section; editor with reset name, 33 icons, 14 colours,
  archive/unarchive, delete or the reason from the snapshot (AC-7.6).
- W-16: list (account, payer, schedule, next date, paused badge); editor with D-072/AC-9.8 defaults, schedule,
  pause/resume hints, create (E-28), patch of changed fields only (E-29), delete (E-30), discard dialog (D-080).
  Uses minimal local fields until W2's shared editor fields land.
- W-17: devices (E-15) with this browser marked, revoke with confirmation (E-16), never the current device.
- W-18: create partner or device invites (E-12), code in groups of 4, link, expiry, copy to clipboard, pending
  invites (E-13) with cancel (E-14); `/settings/invites` aliases W0's `/settings/invite`.
- W-19: web build version, server origin, server version from /health or the X-Kvitt-Server-Version header.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:30:50 +02:00
jacobh2 f9a9bfb472 W3 web settle up: W-7 settle up and W-8 settlement detail
Build Kvitt images / build (push) Has been cancelled
W-7 (/app/settle, mirrors S-9): global, per account (?account=<id>) and
editing (?settlement=<id>). Defaults per AC-11.3/11.4/11.6, proposal from
proposeAllocation over all non-deleted accounts with nets excluding the
edited payment, rows follow the proposal until one is edited, "Föreslå
igen", swap, over-settle warnings and confirm dialog (AC-11.5), remaining
indicator, add account, untouched info line (D-093), date and note,
§14.5.3 errors live, one POST /sync mutation, §14.4.6 rejections shown
with a re-pull, discard dialog on leaving with changes (D-080).

W-8 (/app/settlements/:id, mirrors S-10): hero, date, note, allocations,
audit timeline (AC-12.4), deleted and missing states, edit link and soft
delete with confirmation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:28:43 +02:00
jacobh2 934eee793c W1 web Home and accounts: W-2 Home, W-3 account detail, W-4 account editor
Home (mirrors S-4, S-3 while the partner has not joined): global balance hero
with the seesaw and §9.3 wording, Settle up, active accounts with balance and
this month's count, collapsed "Arkiverade (n)" kept for the session, empty,
loading and error states, New expense when an active account exists.

Account detail (S-5): header with default split, split bar, archived badge and
balance; expenses and settlements interleaved by date, grouped by month with
household spend (settlement rows carry the allocation, AC-11.7); overflow menu
with Edit, Archive/Unarchive (undo snackbar, D-071) and Delete only when
allowed (AC-6.5); not-found, loading and error states.

Account editor (S-6): name (1-60 code points, name_taken on the field), two
percent fields that always sum to 100 with two decimals, slider and presets,
the "Av 100,00 kr" preview; discard dialog with a title and no body (D-080)
on every navigation away; E-18/E-19 apply the returned row.

The account preset for W-5 and W-7 is the query parameter `account`
(proposed D-144). Tests over a shared snapshot fixture and the fake server.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:28:43 +02:00
jacobh2 aaa09d9073 SPEC v0.30 and v0.31: manual update check (D-140), Android Insights (D-141 to D-143)
Build Kvitt images / build (push) Failing after 3h14m40s
v0.30: D-140 S-22 "Check for update" calls UpdateRepository.checkNow(), which always asks
GET /apk/latest and restarts the 6-hour window (AC-16.2, AC-16.6, S-22, §14.2.4).
v0.31: D-141 InsightsRepository UI contract (§14.2.4); D-142 Insights placement and loading
on S-11; D-143 display rules; design-spec S-11 "Insights" and §3.10; strings.csv 588 rows
(16 analytics_insights_* keys), web dictionaries regenerated; §11.2 W0 and W6 landed.
HANDOFF: state at 1c9856b, Pi image deploy step, numbering (D-144..D-155 reserved, next v0.32).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:23:29 +02:00
jacobh2 2ae65a5a9f W0 web foundation: Vite + React SPA under /app/, sync-pull ledger, auth, tokens, shared components
Build Kvitt images / build (push) Failing after 1h14m5s
web/ is the browser client's scaffold: Vite 8, React 19, TypeScript 5.9 strict, React Router 7 (basename /app, every
fixed route with a lazy placeholder page), TanStack Query 5 (refetch on focus and every 60 s), openapi-typescript
types generated from contracts/openapi.yaml with openapi-fetch. Reads go through GET /sync (no REST reads exist for
the ledger, SPEC §7): useLedger() pulls from cursor 0 and then incrementally, restarting on a new epoch or
cursor_ahead. Login pairs the browser as "Web · <browser>" with an invite code (E-3); every call sends the SPEC
§14.3.1 headers, a 401 signs out. kvitt-domain's split, balances, allocation and settlement rules are ported to
web/src/lib and pass every case of contracts/test-vectors. Design tokens from design-spec §2 in tokens.css
(light-dark()), Material Symbols Rounded (Apache-2.0) as generated inline SVG, sv/en catalogs generated from
docs/ux/strings.csv plus web-strings.csv. Gate: cd web && npm run check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-21 21:03:25 +02:00