Fork of hanzo-starter: Vite + React 19 + @hanzo/gui + @hanzo/iam + @hanzo/base. Board (status columns of vote-sorted requests), request detail (description + status control + comments), submit form. Base collections: requests, votes, comments (org-scoped). Builds green (tsc + vite).
3.7 KiB
Upvote — agent notes
A feature-request voting board, forked from the canonical Hanzo app starter.
Vite + React 19 + @hanzo/gui (UI) + @hanzo/iam (auth) + @hanzo/base (data).
Keep it minimal and REAL — every surface must build and run, no fabricated data.
What it is
Users submit feature requests and upvote existing ones; the product team moves
each request through open → planned → shipped and sees vote-sorted demand.
Three views: Board (status columns of vote-sorted cards), Request detail
(description + status control + comments), Submit (new-request form). Router
free — src/views/board.tsx holds a small View state (board · detail · submit).
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 makes everyuseQuery/useMutationorg-scoped to the signed-in user. Theme islight. - Data model + palette are one place (
src/lib/requests.ts):Request/Vote/Commenttypes, theSTATUSESmetadata (label + dot color), and theuiindigo-on-soft-white palette. - Board owns writes.
board.tsxis the single owner ofrequests+votes(list, create, vote toggle, status change);request-detail.tsxowns thecommentscollection for the open request. Voting is a toggle: avotesrow per principal enforces one vote per person and keeps the denormalizedrequests.votescounter in lockstep. - UI is one system —
@hanzo/guiprimitives only (no second kit, no Tailwind). The upvote pill (upvote-pill.tsx) is the one shared vote control.
Gotchas (do not regress)
@hanzo/guiunder Vite needs three things invite.config.ts(it is the Tamagui line): (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.@hanzo/guiprops are Tamagui LONGHAND with this v5 config:alignItems/justifyContent/backgroundColor/padding/paddingHorizontal/borderRadius/textAlign— NOT theitems/justify/bg/p/px/rounded/textshorthands. Shorthands pass at runtime but FAILtsc.ButtonusesonPress;Input/TextAreausevalue/onChangeText.- PKCE storage is
localStorageso the verifier/state survive the round-trip to hanzo.id. Client id readsimport.meta.env.VITE_IAM_CLIENT_ID(fallbackhanzo-app); deploy provisions a per-app client. schema.sqlis the data contract — thedatabaseSchemaDDL the deploy turns into Base collections. Keep it in lockstep withsrc/views/*.
Deploy contract (Hanzo Cloud)
- Static SPA:
npm run build→dist/, served at<slug>.hanzo.appfrom object storage. No server process. - On publish,
schema.sql→provisionBaseFromDDLcreates therequests,votes,commentscollections (org-scoped, IAM-native, rule@request.auth.org_id = org). Runtime read/write is browser →VITE_HANZO_BASE_URLwith the IAM token. - IAM redirect registration is the one external requirement: the IAM client
(
VITE_IAM_CLIENT_ID) must allow this origin’s/auth/callback.
Proven
tsc --noEmit clean · vite build → dist/ (899 modules) · renders under Vite.
login() performs a real PKCE S256 redirect to hanzo.id.
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).