mirror of
https://github.com/hanzo-apps/feedback.git
synced 2026-10-30 19:16:39 +00:00
Two-pane feedback inbox on the canonical Hanzo stack: @hanzo/gui UI, @hanzo/iam PKCE auth (hanzo.id), @hanzo/base org-scoped data. Views: inbox (list + detail), item detail (sentiment + theme tags, triage/archive actions), capture. Base collections: feedback, themes.
3.8 KiB
3.8 KiB
feedback-signal (Signal) — agent notes
Signal is a two-pane product-feedback triage inbox, and one of the real Hanzo
app templates forked on hanzo.app. 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 UI or metrics.
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_*. The IAM client id isVITE_IAM_CLIENT_ID(fallbackhanzo-app). - UI is one system —
@hanzo/guiprimitives only (no second kit, no Tailwind). The slate palette + helpers live insrc/theme.ts; shared atoms (Chip, ActionBtn, Caption) insrc/views/ui.tsx. - Three views —
signed-out(landing + PKCE), the inboxhome(two-pane shell),list/detail/capture.
Data model (schema.sql)
feedback—source,body,sentiment(positive|neutral|negative),theme,status(new|triaged|archived),received_at(ISO).themes—name,color.- Lifecycle: an item lands
new(unread weight in the list); triaging or archiving clears that weight. Base managesid/created/updated/owner/organd scopes rows to the org (@request.auth.org_id = org).
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.ButtonusesonPress;Input/TextAreausevalue/onChangeText.color/backgroundColor/borderColoraccept raw hex;placeholderTextColorwants aColorTokenstoken, so it is left to the theme default.- 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). Keep it in lockstep withsrc/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 the collections (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_IAM_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-appfeedback-signalclient).
Proven
tsc --noEmit clean · vite build → dist/ · 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).