dashboard
The authenticated landing page. Renders a small system overview: total users, active users in the last 7 days, installed module list, Python version, and health-check results.
It's intentionally simple — a place for new installs to land that proves the pipeline is wired up. Replace it (or set SM_MODULES_ENABLED without dashboard) once you have your own home page.
ModuleMeta
| Field | Value |
|---|---|
name | Dashboard |
route_prefix | /api/dashboard |
view_prefix | /dashboard |
depends_on | ["Users"] |
Routes
View
| Method + path | Inertia component | Permission |
|---|---|---|
GET /dashboard/ | Dashboard/Home | authenticated user (any role) |
GET /dashboard/doctor | Dashboard/Doctor | authenticated user (any role) |
/dashboard/doctor is a browser mirror of make doctor — it shows the same module list, static checks, dev-server and environment info from the stats payload. The route itself only requires login; its sidebar link is admin-only (see Menu).
API
| Method + path | Returns | Permission |
|---|---|---|
GET /api/dashboard/stats | dict (see below) | authenticated user (any role) |
/api/dashboard/stats is what the page itself calls; you can hit it from your own UI or scripts. Response shape:
{
"total_users": 12,
"active_users_7d": 3,
"module_count": 8,
"system_info": {
"python_version": "3.12.4",
"modules": ["Auth", "Users", ...],
"health_checks": {"database": "ok", "redis": "ok"}
}
}The result is cached process-wide for 30 seconds. If you mutate something that should change the numbers (e.g. seed users from a script), call dashboard.stats.invalidate_stats_cache() to clear it on the next call.
Menu
| Label | URL | Icon | Section | Order |
|---|---|---|---|---|
Dashboard | /dashboard/ | home | SIDEBAR | 10 |
Doctor | /dashboard/doctor | stethoscope | ADMIN_SIDEBAR | 90 |
Permissions
(none registered) — the page is gated by authentication only, via the users module's AuthMiddleware.
Inertia pages
Dashboard/Home.tsx— single page rendering the stats card, system info card, and welcome card. Kept simple on purpose so it's a useful starting template for a custom landing page.Dashboard/Doctor.tsx— themake doctormirror (module list, static checks, dev-server + environment info).
Locales
Top-level keys in dashboard/locales/en.json:
home.title,home.welcome_message,home.descriptionhome.stats.total_users,home.stats.active_users,home.stats.moduleshome.system_info_title,home.system_info.python_version,home.system_info.health_checks,home.system_info.moduleshome.welcome_card_title,home.description_body
Extending it
The bundled dashboard renders a fixed set of cards — dashboard/stats.py returns a hardcoded shape and the module exposes only register_routes + register_menu_items. This is intentional: there is no register_dashboard_cards hook or card/widget registry, and the module deliberately keeps no extension surface so it stays a small, predictable landing template rather than a framework subsystem to maintain.
To show app-specific tiles, build your own dashboard page in your module rather than contributing to this one:
- Add an Inertia view route + page (
register_routes→pages/Home.tsx) and a sidebar entry (register_menu_items), exactly like any other module page. - Make it the post-login landing page by pointing
users.login_redirect_urlat your route (see Replacing it). You can drop the bundled dashboard entirely viaSM_MODULES_ENABLEDminusdashboard. - Your page can still reuse this module's data — call
GET /api/dashboard/statsfor the system overview alongside your own module's endpoints.
Resolved as by design (GH #203): consumer modules build their own dashboard page; the bundled one is not a contribution point.
Replacing it
If you want a different post-login landing page, set users.login_redirect_url in the admin settings UI (or via smpy settings import-from-env from SM_USERS_LOGIN_REDIRECT_URL) to your route. You can keep the dashboard module installed for the menu entry, or set SM_MODULES_ENABLED without dashboard to drop it entirely. The users module auto-detects whether dashboard is installed; if not, it redirects to the first other module that exposes view routes (e.g. the GIS module on apps like smpy_gis), falling back to / only as a last resort.