SPEC v0.40: K3's report recorded (D-201 to D-207), web card pages as built
Build Kvitt images / build (push) Failing after 3h14m38s
Build Kvitt images / build (push) Failing after 3h14m38s
K3 (card purchases on the web, landed faeb752; web gate 70 files, 684 tests, build ok; contracts-check 157): - SPEC §15.6 W-21 to W-23 as built, with W-5's ?cardPurchase= prefill and link, W-6's card line, W-13's "Anslut kort" row; §15.5 the card snapshot; §15.3 the shared card pieces (api/cards.ts, fixtures, dev proxy, three icons) and matchCardPurchase ported; §11.1, §11.2 K3 row, §11.3, §14.7, §15.9; v0.40 row. - decisions D-201 to D-207 (refresh while fetching, query names, https-only auth_url, attempt outcome, W-5 purchase errors, date forms, S-27 on the web); D-208 to D-210 unused. - HANDOFF: K0 to K3 landed, v1.0.4 deployed, v1.0.5 being built, kvitt-164 on the Pi; pending owner steps PLAN-K2 §8, PLAN-K3 §8, PLAN-K1 §8 steps 5 to 7; K3's open questions; next free D-212, v0.41. - web/README.md: card routes, the card snapshot, query names, the new icons; prompts/README.md: K2 and K3 done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
+24
-10
@@ -20,7 +20,7 @@ npm run build # web/dist/, what kvitt-server serves under /app/
|
||||
|
||||
The build splits libraries into `react` (React, React DOM), `charts` (Recharts and its d3 and Redux dependencies,
|
||||
loaded only with the analytics pages) and `vendor` (the rest), each under Vite's 500 kB warning. The dev proxy forwards
|
||||
every API prefix of `contracts/openapi.yaml`, `/analytics` included.
|
||||
every API prefix of `contracts/openapi.yaml`, `/analytics` and `/cards` included.
|
||||
|
||||
To sign in, create a "Pair a device" invite on the phone (Settings › Pair a device) and type the code on
|
||||
`/app/login`. The browser becomes a device named `Web · <browser>` and keeps its token in `localStorage`.
|
||||
@@ -60,7 +60,7 @@ web/
|
||||
useDocumentTitle
|
||||
api/ client.ts (openapi-fetch, headers, 401, ApiError, unwrap), schema.d.ts (generated),
|
||||
types.ts (wire objects), session.ts, ledger.ts (sync-pull ledger), me.ts, invites.ts,
|
||||
pictures.ts, push.ts (POST /sync), errors.ts (error and rejection texts),
|
||||
pictures.ts, push.ts (POST /sync), cards.ts (card snapshot, E-44), errors.ts (error and rejection texts),
|
||||
queryClient.ts, ApiProvider.tsx, useApi.ts
|
||||
i18n/ sv.json, en.json (generated), web-strings.csv, catalog.ts, I18nProvider, useI18n, language.ts
|
||||
design/ tokens.css, global.css, Icon.tsx, icons.generated.ts (generated)
|
||||
@@ -71,18 +71,21 @@ web/
|
||||
lib/ money, split, balances, settlement (kvitt-domain port), dates, effect, csv, ids,
|
||||
shortcuts, storage, categories (categoryName), timeline (audit lines),
|
||||
accountColors
|
||||
features/<name>/ auth (sign-in, built), home, accounts, expenses, settle, analytics (with insights), search,
|
||||
settings (placeholders for W1 to W5)
|
||||
test/ setup.ts, fakeServer.ts (hand-written fetch fake), fixtures.ts, render.tsx
|
||||
features/<name>/ auth (sign-in), home, accounts, expenses, settle, analytics (with insights), search,
|
||||
export, settings, cards (K3: W-21 to W-23)
|
||||
test/ setup.ts, fakeServer.ts (hand-written fetch fake), fixtures.ts, cards.ts (card fixtures),
|
||||
render.tsx
|
||||
```
|
||||
|
||||
## Routes
|
||||
|
||||
Fixed for the feature plans; always link with the builders in `src/app/routes.ts` (`paths.account(id)`,
|
||||
`paths.drillDown('bucket', key)`, `expensesNewPath({ account })`, `settlePath({ account })`,
|
||||
`settlementEditPath(id)`; the query parameter names are `QUERY.account` and `QUERY.settlement`, D-144). Each
|
||||
feature exports its route objects from `src/features/<name>/index.ts` (`homeRoutes`, `accountsRoutes`,
|
||||
`expensesRoutes`, `searchRoutes`, `settleRoutes`, `analyticsRoutes`, `settingsRoutes`, `authRoutes`), paths relative
|
||||
`settlementEditPath(id)`, `cardsPath({ account })`, `connectCardPath({ provider })`,
|
||||
`expensesNewPath({ cardPurchase, account })`, `paths.settingsCardConnection(id)`; the query parameter names are
|
||||
`QUERY.account` and `QUERY.settlement` (D-144), `QUERY.cardPurchase`, `QUERY.attempt` and `QUERY.provider` (D-202)).
|
||||
Each feature exports its route objects from `src/features/<name>/index.ts` (`homeRoutes`, `accountsRoutes`,
|
||||
`expensesRoutes`, `searchRoutes`, `settleRoutes`, `analyticsRoutes`, `settingsRoutes`, `cardsRoutes`, `authRoutes`), paths relative
|
||||
to `/app`, pages loaded lazily with `lazyPage(() => import('./Page'))` (`src/app/lazyPage.ts`). `src/app/router.tsx`
|
||||
only spreads those arrays, so a feature plan changes its pages and sub-routes without editing `src/app/`.
|
||||
|
||||
@@ -99,6 +102,9 @@ only spreads those arrays, so a feature plan changes its pages and sub-routes wi
|
||||
| `/analytics` | `features/analytics/AnalyticsPage` | S-11 |
|
||||
| `/analytics/:groupKind/:groupId` | `features/analytics/DrillDownPage` | S-12 (`groupKind` `category`, `account` or `bucket`) |
|
||||
| `/insights` | `features/analytics/InsightsPage` | web |
|
||||
| `/cards` (`?attempt=<id>`, `?account=<id>`) | `features/cards/CardsPage` | S-23, S-27 (W-21) |
|
||||
| `/settings/cards` (`?provider=<id>`) | `features/cards/ConnectCardPage` | S-24, S-25 (W-22) |
|
||||
| `/settings/cards/:id` | `features/cards/CardConnectionPage` | S-26 (W-23) |
|
||||
| `/search` | `features/search/SearchPage` | web |
|
||||
| `/settings` | `features/settings/SettingsPage` | S-13 (holds the web's language/theme card and sign-out) |
|
||||
| `/settings/categories`, `recurring`, `devices`, `profile`, `invite`, `about` | `features/settings/*Page` | S-15, S-19, S-20, S-14, S-21, S-22 |
|
||||
@@ -114,6 +120,13 @@ The web has no local database. What it reads:
|
||||
ported §9.3 rules; `live(map)` drops tombstones. Pass a `select` to `useLedger` for derived views.
|
||||
- **REST reads**: `useMe()` (E-7), and the feature plans add devices (E-15), invites (E-13), health (E-1) the same
|
||||
way (`useQuery` + `unwrap(api.GET(…))`). Profile pictures: `useUserPicture(userId, pictureVersion)`.
|
||||
- **The card snapshot** (SPEC §15.5, K3): the viewer's own card connections and purchases come through E-40
|
||||
`GET /cards/sync`, never `GET /sync`. `useCards(select?)` (`api/cards.ts`) holds them in memory beside the ledger:
|
||||
the first read pulls from cursor 0 in pages of 1000, later reads (focus, every 60 s, after every card write) from the
|
||||
cursor it holds; 409 `cursor_ahead` starts again from 0; rows apply by `card_version`. W-21 re-reads every 2 s while
|
||||
a connection is fetching or in its first fetch (D-201). `useCardWriter().refresh()` re-reads only the snapshot
|
||||
(`exact` key), not the catalogue (E-36) or an attempt (E-38). Card writes (E-44 through `useSetCardPurchase`) are
|
||||
sent at once, never queued.
|
||||
- **Refresh**: every query refetches when the window gets focus and every 60 s while visible
|
||||
(`api/queryClient.ts`). `UpdatedLine` shows "Uppdaterad för 3 min sedan" with a button that reads now.
|
||||
|
||||
@@ -184,7 +197,8 @@ web-only strings live in `src/i18n/web-strings.csv` (W0's).
|
||||
`propose_allocation`, `validate_settlement`, `over_settled_accounts`); `src/lib/vectors.test.ts` runs every case
|
||||
of `contracts/test-vectors/*.json` against them, like `server/crates/kvitt-domain/tests/vectors.rs`, and against
|
||||
the ports that live in features: `suggest_category` (W2, `features/expenses/suggest`), `resolve_range` and
|
||||
`spend_series` (W4, `features/analytics/domain/analytics`). Every vector file is listed as ported; a new file
|
||||
`spend_series` (W4, `features/analytics/domain/analytics`), and `match_card_purchase` against
|
||||
`lib/domain/matchCardPurchase` (K3). Every vector file is listed as ported; a new file
|
||||
fails the test until it is.
|
||||
- `lib/effect.ts` is the §9.2 effect line.
|
||||
|
||||
@@ -216,7 +230,7 @@ set is used. `scripts/gen-icons.mjs` copies the path data of the listed icons in
|
||||
`<Icon name="home" />` or `<Icon name="home" filled />` renders one inline. Five spec names have another file name
|
||||
in Material Symbols 0.48 (same glyph): `card_giftcard` → `redeem`, `expand_more`/`expand_less` →
|
||||
`keyboard_arrow_down`/`up`, `smartphone` → `mobile`, `phonelink_erase` → `mobile_cancel`; the tables keep the spec
|
||||
names. To add an icon: add its name to the list in the script and run `npm run icons:import`.
|
||||
names. K3 added `credit_card`, `link_off` and `visibility_off` for the card pages. To add an icon: add its name to the list in the script and run `npm run icons:import`.
|
||||
|
||||
## Adding a feature page (W1 to W5)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user