Dashboard
The Admin index is a dashboard. You declare components and place them in
blocks on a four-column grid. DashboardService validates the layout and
renders SSR context. Live widgets refresh through dashboard/live/.
Index template: admin/chassis/dashboard/index.html (extends
admin/index.html). Set index_template only if you replace the whole
page.
Page anatomy
┌─ navbar (theme, custom buttons, page-group dropdowns, user menu) ─┐
├─ sidebar (declared sidebar_items) ─────────────────────────────────┤
├─ alert strip (`nav-breadcrumbs` override) ─────────────────────────┤
│ global / alert blocks (`is_global=True`, ALERTS only) │
├─ main grid (`admins-dashboard-layout`) ────────────────────────────┤
│ row 1: block size 2 + block size 2 │
│ row 2: block size 4 (full width) │
│ … │
│ optional aside: Django "Recent actions" (`show_recent_actions`) │
└────────────────────────────────────────────────────────────────────┘
Each block includes templates/admin/chassis/dashboard/sections/*.html
for its DashboardBlockType. Charts load ApexCharts. Live widgets poll
{% url 'admin:dashboard_live' %} with If-None-Match.
If dashboard_component_classes is empty, the shell still renders: brand,
sidebar, navbar, and an empty grid.
Components and blocks
from django.utils.translation import gettext_lazy as _
from django_chassis.dto import DashboardBlockDTO
from django_chassis.enums import DashboardBlockType
class CustomAdminSite(ChassisAdminSiteMixin, AdminSite):
dashboard_component_classes = (OpenOrdersMetric, RevenueChart, NightlyImportAlert, RebuildSearchAction)
dashboard_blocks = (
DashboardBlockDTO(
slug='alerts',
title=_('Alerts'),
block_type=DashboardBlockType.ALERTS,
component_slugs=('nightly-import',),
row=1,
size=4,
is_global=True,
),
DashboardBlockDTO(
slug='kpis',
title=_('Overview'),
block_type=DashboardBlockType.METRICS,
component_slugs=('open-orders',),
row=2,
size=2,
),
DashboardBlockDTO(
slug='revenue',
title=_('Revenue'),
block_type=DashboardBlockType.CHART,
component_slugs=('revenue',),
row=2,
size=2,
),
)
DashboardBlockDTO
| Parameter | Type | Default |
|---|---|---|
slug | unique block id | required |
title | localized string | required |
block_type | DashboardBlockType | required |
component_slugs | slugs of components in this block | required |
row | grid row | 1 |
group | layout group | 'default' |
size | 1–4 columns | 4 |
order | sort inside the row | 100 |
is_global | span the full width | False |
DashboardBlockType: alerts, metrics, actions, metrics_and_actions,
chart.
Layout rules
- The grid is four columns.
- A chart block contains exactly one chart.
- A component slug cannot be assigned twice.
- Only alert blocks may set
is_global=True. - Unknown slugs and empty
component_slugsfail validation.
Live endpoint
GET {admin}/dashboard/live/ returns the current widget snapshot. Send
If-None-Match to receive 304 when the ETag has not changed.
DashboardLiveSerializationService builds deterministic ETags.
Widgets carry etag, fetched_at, and next_refresh_at when the component
provides them.
Visibility
Every component inherits:
slug,label,order,statuscheck_view(request)/check_use(request)is_visible/is_available— superusers see and use everything;HIDDENhides the widget; non-ACTIVEstatus blocks use
See Dashboard metrics and charts for the concrete component contracts.