mirror of
https://github.com/hanzo-apps/expense-spend.git
synced 2026-10-30 19:17:00 +00:00
Real Hanzo-stack app forked from hanzo-starter — Vite + React 19 + @hanzo/gui + @hanzo/iam (PKCE) + @hanzo/base. Three views (expense log, add expense, spend summary) over an org-scoped `expenses` collection provisioned from schema.sql. 100% @hanzo/gui primitives (longhand props), clean fintech surface with category chips and by-category / by-month charts. tsc + vite build green.
4.1 KiB
4.1 KiB
expense-spend — agent notes
A real Hanzo-stack app: log expenses by category and see where money goes.
Vite + React 19 + @hanzo/gui (UI) + @hanzo/iam (auth) + @hanzo/base (data).
Forked from the canonical hanzo-starter. Keep it minimal and REAL — every
surface must build and run, no fabricated UI.
The app
- Three views: expense log (add form + tabular ledger) and spend summary (KPI tiles + by-category proportion bars + by-month bar chart). The signed-out landing pitches it with a live sample of the by-category card.
- One collection,
expenses(amount, category, note, date), org-scoped. - Data lifted once:
src/views/home.tsxowns the singleuseQuery('expenses')and hands rows to both the log and the summary (and the hero total), so there is one source of truth.src/views/log.tsxowns the create/delete mutations. - Domain in one place:
src/lib/expense.ts— theExpensetype, the fixedCATEGORIES(id + label + accent colour),money/today/monthKey/monthLabelhelpers, andbyCategory/byMonth/sumaggregation. No JSX. - UI kit in one place:
src/ui.tsx—inkpalette +Panel,Chip,Bar,Action,Segmented, all composed from @hanzo/gui primitives.
One way, decomplected
- Providers (
src/providers.tsx) mount in the canonical order every Hanzo surface ships:GuiProvider→IamProvider→BaseProvider.BaseProvidergets aBaseClientcarrying the IAM access token; it is rebuilt when the token changes (src/lib/base.tsbaseAs). That single seam is what makes everyuseQuery/useMutationorg-scoped to the signed-in user. - Env is one place (
src/env.ts), read fromimport.meta.env.VITE_*.CLIENT_IDfalls back tohanzo-app. - UI is one system —
@hanzo/guiprimitives only (no second kit, no Tailwind).
Gotchas (do not regress)
@hanzo/guiunder Vite needs three things invite.config.ts(it is the Tamagui line; the in-browser builder runtime can't do this, which is the whole reason this ships as a real repo): (1) aliasreact-native→react-native-web, (2)defineprocess.env.TAMAGUI_TARGET/NODE_ENV/__DEV__, (3)dedupereact/react-dom/react-native-web. No Tamagui compiler, noone, no Expo — the optimizer is a perf pass, not a correctness one.@hanzo/guiprops are Tamagui LONGHAND with this v5 config:alignItems/justifyContent/backgroundColor/padding/alignSelf/borderRadius/textAlign— NOT theitems/justify/bg/p/self/rounded/textshorthands. Shorthands pass at runtime but FAILtsc.Inputusesvalue/onChangeText. The@hanzogui/buttonframe does not take a textcolorprop — that's why the accent CTA is a pressable stack (Actioninsrc/ui.tsx), not<Button>.- PKCE storage is
localStorage(not sessionStorage) so the verifier/state survive the round-trip to hanzo.id. schema.sqlis the data contract. It is thedatabaseSchemaDDL the deploy translates into Base collections (provisionBaseFromDDL, rule@request.auth.org_id = org). Keep it in lockstep withsrc/lib/expense.tsand the log/summary views.
Deploy contract (Hanzo Cloud)
- Static SPA:
npm run build→dist/, served at<slug>.hanzo.appfrom object storage (the*.hanzo.apppublished-sites edge). No server process. - On publish,
schema.sql→provisionBaseFromDDLcreates theexpensescollection (org-scoped, IAM-native). Runtime read/write is browser →VITE_HANZO_BASE_URLwith the IAM token. - IAM redirect registration is the one external requirement: the IAM client
(
VITE_HANZO_CLIENT_ID, defaulthanzo-app) must allow this origin's/auth/callback. Production needs ahttps://*.hanzo.app/auth/callbackwildcard on the shared client (or a per-apphanzo-expense-spendclient).
Build
CI (.github/workflows/ci.yml) runs npm ci && npm run typecheck && npm run build — build-verification only, NEVER a container image (Hanzo Cloud owns
deploys; do not build images locally). Prove changes with npm run build
(tsc + vite).