The database already permitted all three (forms admin manage is FOR ALL); this was purely a missing UI. Create and edit now share one editor, since the only differences are the initial values and insert vs update. Three hazards in the existing data model shaped this rather than just adding buttons: - form_responses.form_id is ON DELETE CASCADE, so deleting a form permanently destroys its submissions. The confirmation names the response count and points to deactivating instead. Verified: deleting a form with two responses leaves zero. - The list filtered active = true for everyone, so deactivating a form hid it from the only people who could reactivate it. Admins now see inactive forms with a badge; families still see only active ones. - Responses key their answers by field *label* in data_json, so renaming a field strands existing answers under the old key. The editor warns when the form already has responses. Deliberately not migrating old answers — guessing which old label maps to which new one would risk silently rewriting submitted data. Also strips fields with blank labels on save, shows a response count per form, and gives the response list an empty state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Routes
TanStack Start uses file-based routing. Every .tsx file in this directory
defines a route. Do not create src/pages/, src/routes/_app/index.tsx, or
app/layout.tsx — those are Next.js / Remix conventions. The only root layout
is src/routes/__root.tsx.
Conventions
| File | URL |
|---|---|
index.tsx |
/ |
about.tsx |
/about |
users/index.tsx |
/users |
users/$id.tsx |
/users/:id (dynamic — bare $, no curly braces) |
posts/{-$category}.tsx |
/posts/:category? (optional segment) |
files/$.tsx |
/files/* (splat — read via _splat param, never *) |
_layout.tsx |
layout route (renders children via <Outlet />) |
__root.tsx |
app shell — wraps every page; preserve <Outlet /> |
routeTree.gen.ts is auto-generated. Don't edit it by hand.