Dashboard Module¶
The dashboard surface collects operational metrics for the currently scoped organization. The goal is to keep the implementation incremental-friendly so we can ship additional widgets without revisiting the foundations each time.
Architecture Overview¶
- Views –
validibot.dashboard.views.MyDashboardViewbuilds the landing page, resolves the selectedtime_range, and queues the registered widgets for HTMX loading.WidgetDetailViewis the single HTMX endpoint that renders widget bodies on demand usingTemplateResponseso tests can easily inspect context. - Widget registry –
validibot.dashboard.widgets.basedefines theDashboardWidgetbase-class, the registry, and validation during registration. Each widget subclass provides a slug, title, template, andget_context_dataimplementation. Built-ins live inmetrics.py(scalar cards) andtimeseries.py(line charts). - Time ranges –
dashboard.time_rangesexposes curated presets (1h → 90d) and resolves them to concreteResolvedTimeRangeinstances. Bucket selection (hour vs. day) happens here so individual widgets do not need to repeat the logic. - Data services –
dashboard.services.generate_time_seriesandbuild_chart_payloadshape ORM results into Chart.js config dictionaries and ensure gaps are zero-filled so the charts stay readable.
The Django app auto-imports validibot.dashboard.widgets in
DashboardConfig.ready() to populate the registry during startup.
Data Sources & Scoping¶
Widgets resolve the organization with request.user.get_current_org() and
apply it to each query:
TotalValidationsWidgetcountsValidationRunrows per org/time window.TotalErrorsWidgetcountsValidationFindingrows with severityERROR.EventsTimeSeriesWidgetaggregatesTrackingEventvolume.UsersTimeSeriesWidgetcounts distinctTrackingEvent.user_idvalues by interval.- Login/logout signals record activity as
user.logged_in/user.logged_outevents so the user chart has data even before validation runs exist.
Every query filters on created__gte/start and created__lt/end using the
resolved time range. When you add new widgets reuse the helper functions to
avoid cross-tenant leaks.
HTMX Flow & Loading Experience¶
my_dashboard.htmlrenders lightweight placeholders for each registered widget. Every placeholder carrieshx-getattributes pointing to the widget detail endpoint and triggers both onloadand the customdashboard:refreshevent.- The inline script rewrites the
hx-getURL when a new time range is selected and dispatchesdashboard:refreshso every widget reloads without a full-page refresh. The history state updates to keep URLs shareable. WidgetDetailViewsends back the fully styled widget markup. Because the outer wrapper retains the HTMX attributes (minus the initialloadtrigger) subsequent refreshes work transparently.- If a widget has no data, the template shows an empty-state panel instead of an empty chart.
Front-end Integration¶
- Chart.js is now shipped via
package.json; bundling happens instatic/src/ts/project.ts. We register the default chart set, exposewindow.Chart, and add aninitializeChartshelper that runs onDOMContentLoadedand everyhtmx:afterSwapevent. - Widget templates embed the JSON chart config in a lightweight
<script type="application/json">tag. The TypeScript helper parses the payload and mounts the chart, destroying any previous instance bound to that canvas. - The global bundle owns other progressive enhancements (tooltips, HTMX cleanup) so chart bootstrapping slots neatly into the existing life-cycle.
Extending the Dashboard¶
To add a new widget:
- Subclass
DashboardWidget, populate the metadata, and implementget_context_data. - Decorate the class with
@register_widgetinside a module that is imported byvalidibot.dashboard.widgets. - Create a template that extends
dashboard/widgets/base_widget.html. Use the card structure to keep styling consistent. - Prefer
dashboard.services.generate_time_seriesandbuild_chart_payloadfor line/bar charts. - Add tests that cover both the context data and the HTMX response.
To add new time ranges, edit dashboard.time_ranges and the select element in
my_dashboard.html will render the new option automatically.
Testing¶
validibot/dashboard/tests/test_views.py exercises the HTMX endpoint
behaviour, org scoping, and aggregation logic. Use the existing factories and
helpers when writing additional cases so test flows mirror production data
relationships.
Seeding Demo Data¶
- Use
validibot.tracking.sample_data.seed_sample_tracking_data()when tests need a handful of events without building full validation runs. - The
seed_tracking_eventsmanagement command wraps the helper so local development environments can populate meaningful dashboard data:
Supply --runs-per-day, --logins-per-day, or --no-failures to tune the
generated mix. The command reuses existing org/projects/users when possible
and falls back to lightweight sample instances.
Follow-up Work¶
- Expand tracking coverage to include workflow CRUD and submission lifecycle events so operators can correlate chart spikes with configuration changes.
- Capture warning-level findings in a future widget so teams can spot noisy validators before they fail.
- Evaluate whether we need rollups larger than daily once the 90-day window carries more volume; the bucket helper is ready for additional granularities.