mirror of
https://github.com/hanzo-apps/roster.git
synced 2026-10-30 19:05:40 +00:00
Vite + React 19 + @hanzo/gui + @hanzo/iam + @hanzo/base, forked from hanzo-starter (identical provider stack, vite config, auth + deploy contract). - one org-scoped people(name,role,team,email,avatar,bio) Base collection - three views: directory grid (avatar cards + quick search) / person profile / edit - friendly light surface built entirely from @hanzo/gui primitives - PKCE sign-in via hanzo.id; useQuery/useMutation carry the IAM bearer token
3.9 KiB
3.9 KiB
team-roster — agent notes
Roster: a searchable team directory on Hanzo. Vite + React 19 + @hanzo/gui (UI)
@hanzo/iam(auth) +@hanzo/base(data). Forked from the canonical hanzo-starter — same provider stack, vite config, auth, and deploy contract. Keep it minimal and REAL — every surface must build and run, no fabricated UI.
One way, decomplected
- Providers (
src/providers.tsx, unchanged from the starter) mount in the canonical order:GuiProvider→IamProvider→BaseProvider.BaseProvidergets aBaseClientcarrying the IAM access token, rebuilt when the token changes (src/lib/base.tsbaseAs). That single seam makes everyuseQuery/useMutationorg-scoped to the signed-in user. - One collection —
people(name, role, team, email, avatar, bio).teamis free text; the directory derives its filter set and each team's color from the distinct values (teamNames/colorForTeaminsrc/lib/roster.ts). No second table. - One Route value drives the signed-in shell (
src/views/home.tsx):{ k:'list' } | { k:'profile', id } | { k:'edit', person }. No router. - UI is one system —
@hanzo/guiprimitives only. The light surface is one explicit palette (UIinroster.ts); atoms live insrc/views/kit.tsx, and the views compose them.
Gotchas (do not regress)
@hanzo/guiunder Vite needs three things invite.config.ts(unchanged): (1) aliasreact-native→react-native-web, (2)defineprocess.env.TAMAGUI_TARGET/NODE_ENV/__DEV__, (3)dedupereact/react-dom/react-native-web.@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.- This
Buttondoes NOT takecolor/fontWeight. Those props are not on the button frame — the label must be a<Text color… fontWeight…>child.kit.tsxwraps this once asPrimary/Ghost; use them (or a<Text>child) rather than stylingButtontext directly, ortscfails. - Light everywhere via
<Theme name="light">wrapping the tree inapp.tsx, so component defaults (input placeholders, etc.) read on the light surface. The providers keep the starter'sdefaultTheme="dark"; the app-level Theme wins. - Avatars render offline.
PersonAvatarshows theavatarURL when set, else a colored initials circle — so the landing preview and cards always draw. - PKCE storage is
localStorage(not sessionStorage) so the verifier/state survive the round-trip to hanzo.id. schema.sqlis the data contract — thedatabaseSchemaDDL the deploy translates into thepeopleBase collection (provisionBaseFromDDL). Keep it in lockstep withdirectory.tsx·profile.tsx·edit.tsx.
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 thepeoplecollection (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-team-rosterclient).
Proven
tsc --noEmit clean · vite build → dist/ · renders under Vite with zero
console errors · 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).