Skip to main content

Dashboard

Added in 1.0.1 Changed in 1.0.9

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,
live_refresh_interval_seconds=30
),
DashboardBlockDTO(
slug='revenue',
title=_('Revenue'),
block_type=DashboardBlockType.CHART,
component_slugs=('revenue',),
row=2,
size=2,
),
)

DashboardBlockDTO​

ParameterTypeDefault
slugunique block idrequired
titlelocalized stringrequired
block_typeDashboardBlockTyperequired
component_slugsslugs of components in this blockrequired
rowgrid row1
grouplayout group'default'
size1–4 columns4
ordersort inside the row100
is_globalspan the full widthFalse
live_refresh_interval_secondspositive integer or NoneNone — no polling

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_slugs fail validation.

Live endpoint​

Only blocks with live_refresh_interval_seconds set are polled. Each block uses its own timer, request, and ETag; successful reads animate only that block and respect prefers-reduced-motion.

GET {admin}/dashboard/live/?section=<block-slug> returns the current snapshot for one live-enabled block. Omitting section returns every enabled live block. 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, status
  • check_view(request) / check_use(request)
  • is_visible / is_available — superusers see and use everything; HIDDEN hides the widget; non-ACTIVE status blocks use

See Dashboard metrics and charts for the concrete component contracts.

See also​

Donut chart​

Added in 1.0.20

Use DashboardChartType.DONUT for a distribution. Each DashboardChartSeriesDTO represents one named slice and contains exactly one non-negative point. The existing DTO, live refresh, permissions and theme contracts remain the same.