Admin ABM — the flagship back-office¶
A complete People & Access admin panel: login, dashboard, a rich data grid
over PostgreSQL, and full CRUD forms — all server-rendered over WebSockets,
dockerized, and internationalized ES/EN. It exercises the entire Fitz stack
(ORM + Postgres + HTTP + WebSockets + auth + Response{} export) in one
recognizable app, with zero JS build.
Source: examples/admin/
Why this example exists¶
The counter/chat/kanban/dashboard examples each isolate one idea. The Admin ABM is the opposite — it's the reference implementation of every component in the UI catalog, wired into a real business app. If you want to see how the DataGrid, the tabbed/stepped forms, the cascade selects, the modal, the toasts, the tree and the i18n all fit together, this is it.
It's also the app that drove three upstream fixes into Fitz core while it was
built (dogfooding): str.to_int(), a dynamic .order_by(ascending:), and
@header on @ws (so the live socket reads the locale straight from the
handshake cookie).
Run¶
The app reads/writes Postgres during requests (it never runs DDL at boot — the
schema lives in db/init.sql).
With Docker (Postgres + app together):
Local Postgres (schema loaded once, then fitz run):
psql "postgres://fitz:fitz@localhost:5432/fitz_admin" -f db/init.sql
cd examples/admin
$env:DATABASE_URL = "postgres://fitz:fitz@localhost:5432/fitz_admin?sslmode=disable"
fitz run
Open http://127.0.0.1:3000/, log in with admin@fitz.dev / admin1234, and click around: sort/filter/search the grid, expand a row, group by department, select rows and delete, export CSV, create an employee through the onboarding stepper, edit one through the tabs, and flip the language with the 🌐 switch in the topbar.
Architecture¶
One small module per concern — the same shape a real Fitz service takes:
| File | Role |
|---|---|
models.fitz |
@table types (User, Departamento, Empleado, ubicaciones, permisos, skills) |
config.fitz |
env-var helpers (db_url, jwt_secret, cookie names) |
session.fitz |
cookie-based session (user_from_cookie) — browser-style, redirects not 401 |
i18n.fitz |
t(locale, key) dictionary (ES/EN, ~120 keys) + locale_from_cookie |
shell.fitz |
the chrome: page_layout (sidebar + topbar + breadcrumbs + theme) |
auth.fitz |
GET/POST /login, /logout, /lang/{code} |
dashboard.fitz |
/ (stat cards + bar chart + progress) |
empleados.fitz |
the Empleados DataGrid + the rich forms, SSR first paint + @ws live layer |
departamentos.fitz |
the Departamentos ABM (grid + create/edit/delete), SSR + @ws; reuses the ConfirmDialog + Toast LiveComponents |
main.fitz |
imports the modules + @server(3000, "0.0.0.0") |
The grid is served twice: an SSR first paint on GET /empleados (embedded in the
shell with live_embed(...)) and a live layer over @ws("/live/empleados") that
re-queries Postgres on every event and diff-patches back to that socket only. All
grid state (page, search, filters, sort, selection, grouping, expanded rows,
active tab) is per-connection in the @ws handler loop.
The component tour¶
Every component here is documented in the UI catalog; this is where to read the real code:
- Shell — collapsible sidebar (nested
<details>menu), topbar with theme + language switch, breadcrumbs. - Auth — JWT login + Argon2id verify + HttpOnly cookie.
- Dashboard — stat cards, a pure-CSS bar chart and progress bars from ORM counts, an expansion panel, a spinner "live" indicator.
- DataGrid — sortable columns, search, estado + department filters,
pagination, row selection + multi-delete with a confirm modal, per-row expand
showing permits + skills as chips, group-by with collapsible sections, and CSV
export via
Response { body }. - Forms — the edit form as free Tabs, the create form as a guided Stepper; date picker, textarea, radio group, cascade select (país→provincia→ciudad), group select (reports-to), multiselect (skills), group-checkbox (permits), a star rating.
- Feedback — modal, toasts, badges, chips, tooltips.
- i18n — every string localized ES/EN; the
@wshandler reads the active locale from the handshake cookie (@header(name="cookie")), so the live grid and forms translate with no client-side handshake.
Where to go next¶
- UI catalog — the pattern + snippet for every component above.
- LiveViews — how the diff-and-patch engine works.