Dashboard Designer and Publication
Developer guide for the Enezaframework dashboard system. Part of the Dashboards series.
Table of Contents
- Overview
- Access and Prerequisites
- Routes
- Create a Dashboard
- Wireframe Catalogue
- The Designer Canvas
- The Widget Editor
- The Tab Editor
- Dashboard Settings
- Preview
- Publish
- Versions and Restore
- Ownership, Clone, Unpublish, Deactivate
- Viewer Experience and Preferences
- Operational Notes
- Related Guides
Overview
The designer is the browser workflow for building dashboards. It edits a draft version; viewers never see the draft until it is published.
flowchart LR
CREATE[Create dashboard] --> WIRE[Choose wireframe + data source]
WIRE --> DRAFT[Draft tabs and widgets]
DRAFT --> EDIT[Bind fields, layout, filters]
EDIT --> PREVIEW[Preview with real data]
PREVIEW --> PUBLISH[Publish]
PUBLISH --> LIVE[Published version]
PUBLISH --> NEXT[New draft clone]The designer and the declarative seeder write the same records and obey the same
publication validation. The difference is who owns the dashboard:
designer-created dashboards are admin-owned; seeded dashboards are
seed-owned.
Access and Prerequisites
To use the designer, a user needs:
- Login — all designer views require authentication.
- The manage action —
action.enezadashboard.manage, enforced byDesignerMixin(enezadashboard/views/designer.py:153-160). - Eligible reports — at least one report must pass data-source eligibility and the user's output permission.
- A module with
menu_verbose_name— the create form lists only apps that declare a menu label (enezadashboard/forms.py:78-84).
The action is registered at app startup
(enezadashboard/apps.py:9-16). Run:
python manage.py rbac_sync
after deployment so RBAC discovers it and the menu targets.
Designer views set current_app_override = "enezaadminstrator" by default so
their pages appear under the administrator shell
(enezadashboard/views/designer.py:156-160). The create form temporarily
switches the override to the selected business module while it renders
(enezadashboard/views/designer.py:184-201).
Routes
| URL | Name | Purpose |
|---|---|---|
/dashboards/ |
index |
Published dashboards by module. |
/dashboards/manage/ |
manage_list |
All dashboards for designers. |
/dashboards/manage/create/ |
create |
Create a dashboard. |
/dashboards/manage/report-meta/ |
report_meta |
JSON metadata for one report. |
/dashboards/manage/<uuid>/ |
designer |
Draft canvas. |
/dashboards/manage/<uuid>/edit/ |
edit |
Dashboard settings. |
/dashboards/manage/<uuid>/apply-wireframe/ |
apply_wireframe |
Replace the draft with a wireframe. |
/dashboards/manage/<uuid>/clone/ |
clone |
Clone as a new draft. |
/dashboards/manage/<uuid>/deactivate/ |
deactivate |
Deactivate. |
/dashboards/manage/<uuid>/unpublish/ |
unpublish |
Unpublish. |
/dashboards/manage/<uuid>/publish/ |
publish |
Validate and publish. |
/dashboards/manage/<uuid>/take-ownership/ |
take_ownership |
Protect from seed updates. |
/dashboards/manage/<uuid>/preview/ |
preview |
Render the draft with real data. |
/dashboards/manage/<uuid>/versions/ |
versions |
Version history. |
/dashboards/manage/<uuid>/versions/<uuid>/restore/ |
restore_version |
Restore as a new draft. |
/dashboards/manage/<uuid>/tabs/ |
tab_create |
Add a tab. |
/dashboards/manage/<uuid>/tabs/<uuid>/ |
tab_update |
Edit a tab. |
/dashboards/manage/<uuid>/tabs/<uuid>/delete/ |
tab_delete |
Delete a tab from the draft. |
/dashboards/manage/<uuid>/tabs/<uuid>/widgets/ |
widget_create |
Add a widget. |
/dashboards/manage/<uuid>/tabs/<uuid>/widgets/<uuid>/ |
widget_update |
Edit a widget. |
/dashboards/manage/<uuid>/tabs/<uuid>/widgets/<uuid>/delete/ |
widget_delete |
Delete a widget from the draft. |
/dashboards/manage/<uuid>/tabs/<uuid>/widgets/<uuid>/preview/ |
widget_preview |
Render one draft widget. |
/dashboards/manage/<uuid>/tabs/<uuid>/widgets/<uuid>/layout/ |
widget_layout |
Save a dragged layout. |
The route definitions are in
enezadashboard/urls.py:7-129.
Create a Dashboard
Open /dashboards/manage/create/ (optionally with ?module=<label>), or use
Create dashboard from a module's dashboard list.
The form is split into four tabs.
Basics
| Field | Required | Effect |
|---|---|---|
module |
Yes | Owning app. Fixed after the first publication. |
code |
Yes | Slug. Unique with module. Fixed after the first publication. |
name |
Yes | Display name. |
description |
No | Long description. |
Data source
| Field | Required | Effect |
|---|---|---|
default_report_ref |
No | Pre-binds new wireframe slots and supplies default measures, dimensions, and columns. |
default_period |
No | Dashboard-wide default date range. |
reference_date_field |
No | Date field used to apply periods. Overrides the report date field. |
data_limit |
No | Caps rows loaded by table, chart, and preview widgets. KPI/gauge aggregates are not capped. |
The data-source list contains only eligible reports the current user may execute
(enezadashboard/forms.py:168-174).
If you leave the data source blank, a wireframe still creates placeholder
widgets, but their report_ref is empty. They render an error and cannot be
published until each widget is bound.
Layout
| Field | Required | Effect |
|---|---|---|
layout_template |
No | A wireframe code, or blank for a blank dashboard. Disabled in this form after creation; use Apply wireframe to replace the draft later. |
A blank dashboard still receives an empty Overview tab, because the create flow always applies a template (an empty slot list when no wireframe is chosen).
Choosing a wireframe creates the initial tab and its placeholder slots. The wireframe is a starting point, not a constraint: every slot can be changed, added to, removed, moved, or resized afterwards.
Visibility
| Field | Required | Effect |
|---|---|---|
is_active |
No | Inactive dashboards are not visible. |
is_default |
No | Default dashboard for the module. Only one active default is allowed. |
sort_order |
No | List order. |
access_code |
No | Optional RBAC permission required to view the dashboard. |
Submitting creates the dashboard, its draft version, and the initial tab. The
designer opens the new tab
(enezadashboard/views/designer.py:211-239).
Wireframe Catalogue
Wireframes live in
enezadashboard/wireframes.py:53-276.
All ten use a 12-column grid.
| Code | Name | Family | Density | Tab | Slots | Best for |
|---|---|---|---|---|---|---|
command_center |
Command Center | Analyze | High | Overview | 7 | Operations teams watching queues, errors, and spend at once. |
executive_brief |
Executive Brief | Report | Low | Brief | 8 | Weekly leadership readouts where the headline is the point. |
data_explorer |
Data Explorer | Analyze | Very high | Explorer | 4 | Analysts slicing records before summarizing them. |
pipeline_board |
Pipeline Board | Operate | Medium | Pipeline | 5 | Sales, fulfillment, or review queues with named stages. |
geo_monitor |
Geo Monitor | Operate | Medium | Coverage | 5 | Logistics and field operations watching regional coverage. |
inbox_detail |
Inbox & Detail | Operate | High | Queue | 3 | Support and moderation queues with structured records. |
ops_calendar |
Ops Calendar | Operate | Medium | Schedule | 3 | Scheduling desks, staffing rotas, and campaign calendars. |
board_report |
Board Report | Report | Low | Report | 6 | Monthly board packs and printable performance reviews. |
monitoring_wall |
Monitoring Wall | Operate | High | Wall | 12 | NOC floors where the screen is read from ten feet away. |
growth_review |
Growth Review | Analyze | Medium | Growth | 8 | Growth and retention reviews built on cohorts. |
Slot composition:
- KPI slots become
kpiwidgets. - Trend/mix/distribution slots become
chartwidgets with a presetchart_type. - Grid/list slots become
tablewidgets. - Queue/agenda/watchlist slots become
report_previewwidgets.
Applying a wireframe is destructive to the draft: apply_wireframe_template(...,
replace=True) deletes every existing draft tab before creating the new one
(enezadashboard/services/wireframes.py:88-92). The published version is
untouched.
When a data source is chosen, slots are pre-bound where possible:
- KPIs use the first measure and its first supported aggregation;
- charts use the first dimension and the first measure;
- tables and previews use the first four or six columns
(
enezadashboard/services/wireframes.py:12-62).
The Designer Canvas
The canvas at /dashboards/manage/<uuid>/ shows the draft version.
Typical workflow:
- Select a tab, or add one.
- Add a widget, or open a placeholder slot.
- Bind the widget to a report and a field/measure.
- Drag or edit its grid position.
- Preview the tab.
- Publish.
Canvas actions:
| Action | Effect |
|---|---|
| Add tab / edit tab / delete tab | Changes the draft only. |
| Add widget / edit widget / delete widget | Changes the draft only. |
| Drag or resize | Saves x, y, w, h to the draft. |
| Preview widget | Renders one widget with the draft configuration. |
| Apply wireframe | Replaces all draft tabs with a new composition. |
| Publish | Validates and promotes the draft. |
| Versions | Lists history and restores a version as a new draft. |
The layout endpoint enforces x 0–11, y 0–200, w 2–12, h 2–8, and
x + w <= 12 (enezadashboard/views/designer.py:637-654). Publication uses the
same grid rules without the y cap
(enezadashboard/services/validation.py:28-49).
The Widget Editor
The widget form is split into five tabs
(enezadashboard/views/designer.py:39-80).
Basics
| Field | Required | Effect |
|---|---|---|
code |
Yes | Stable widget code, unique within the tab. |
title |
Yes | Card/chart/table title. |
description |
No | Supporting text. |
sort_order |
No | Order within the tab. |
is_active |
No | Inactive widgets are skipped. |
Data
| Field | Required | Effect |
|---|---|---|
widget_type |
Yes | kpi, chart, table, or report_preview. |
report_ref |
Yes | Eligible data source. The list is filtered by module and permission. |
Changing the report reloads its metadata so the field pickers show that report's columns and measures.
Display
The form shows the fields relevant to the selected widget type:
| Widget | Fields |
|---|---|
kpi |
measure, aggregation, format, decimals, prefix, suffix, sparkline category/value/limit. |
chart |
chart_type, category_field, value_field, series_field, comparison_field, line_field, size_field, chart_limit, target, scale_max, chart_sort, show_legend, plus measure/aggregation for gauges. |
table |
table_columns, page_size, sortable, dense, show_header. |
report_preview |
table_columns, preview_rows, show_header. |
Field pickers are populated from report metadata. You can type a value, but publication rejects values that are not part of the report.
Layout
layout_x, layout_y, layout_w, layout_h map directly to the stored
layout object. The same limits as dragging apply.
Advanced
| Field | Shape | Effect |
|---|---|---|
parameter_config |
JSON object | Report parameter values. |
filter_config |
JSON object | Query-builder filter rules for this widget. |
These are validated against the selected report on save and publication.
Per-type configuration details are in Dashboard Reference — Widget configuration.
The Tab Editor
| Field | Effect |
|---|---|
code |
Stable tab code, unique in the version. |
name |
Display name. |
description |
Description. |
icon |
One of the fixed tab icon keys. |
sort_order |
Tab order. |
default_period |
Date range loaded when the tab opens: inherit, a preset, or custom. |
default_start_date, default_end_date |
Required when the default is custom. |
reference_date_field |
Date field used to apply the period for this tab. |
data_limit |
Row cap for this tab. |
filter_config |
Advanced JSON. The tab form writes date_range and data keys from the fields above; other keys are preserved. |
Leaving default_period on Use dashboard default inherits the dashboard's
data_settings.default_period. Selecting Default explicitly means no date
range at all. A custom range requires both dates and validates that start is on
or before end (enezadashboard/forms.py:477-534).
Tab data settings live inside filter_config.data and override dashboard
settings for that tab
(enezadashboard/services/renderer.py:222-231).
Dashboard Settings
Edit settings changes the dashboard record, not the tabs:
- module and code (fixed after first publication);
- name, description;
- default data source;
- default period;
- reference date field;
- data load limit;
- active, default, sort order, access code;
- wireframe provenance (disabled in this form after creation; use Apply wireframe to replace the draft composition).
Changes to data settings take effect on the next render. Changes to module and
code are blocked after the first publication
(enezadashboard/forms.py:204-208).
Preview
Preview renders the draft with real report data, using the same renderer
as the live page (enezadashboard/views/designer.py:577-596).
Preview is the place to catch:
- a widget bound to the wrong report;
- a measure that is not numeric;
- a chart type that needs a series or size field;
- a date field that produces no rows;
- a filter that excludes everything.
Preview does not validate publication rules; only Publish does that.
Publish
Publishing runs the full validation:
- dashboard module and code;
- at least one tab;
- each tab has a name and a unique code;
- at least one active widget per tab;
- unique widget codes;
- known widget types;
- report reference resolves and is eligible;
- widget configuration passes the type's validation;
- layout is inside the 12-column grid;
- parameter names and filter fields are allowed.
The checks are in
enezadashboard/services/validation.py:79-146.
If validation fails, the designer shows the errors and the draft remains unchanged. If it passes:
- the draft becomes
published; published_at,published_by, and notes are stored;- the dashboard points at it and becomes
published; - a new draft clone is created for further work
(
enezadashboard/services/publication.py:68-101).
Published versions are immutable.
Versions and Restore
The versions page lists every version with its number, status, author, and publication time.
- Restore clones the selected version into a new draft. It does not change the live dashboard.
- The previous draft is archived unless it is the currently published version
(
enezadashboard/services/publication.py:104-118). - After restoring, review the draft and publish it to go live.
Ownership, Clone, Unpublish, Deactivate
| Action | Effect |
|---|---|
| Take ownership | Sets managed_by = "admin". Future seed_dashboards runs skip it unless --force. |
| Clone | Creates <code>_copy (or _copy_2, …), copies the published version if present (otherwise the draft), and leaves the clone as an admin-owned draft. |
| Unpublish | Sets status to unpublished, clears the published version, clears is_default, and keeps version history. |
| Deactivate | Sets is_active = False and clears is_default. |
Implementation: enezadashboard/services/publication.py:121-192.
Viewer Experience and Preferences
A viewer opens:
/dashboards/<module>/
The module entry selects the user's preferred dashboard, then the module
default, then the first visible dashboard
(enezadashboard/services/selection.py:9-25).
On a dashboard page, the viewer can:
- switch between visible dashboards for the module;
- switch tabs;
- set a period preset or custom range;
- enable comparison against the previous period or previous year;
- build per-report filters when the report supports them;
- page and sort tables;
- follow a widget's View report link, when the report has a URL.
Preferences are per user and per module (DashboardPreference):
- preferred dashboard;
- preferred tab.
Preferences never modify the published configuration. If a preference points to
something no longer visible, selection falls back
(enezadashboard/services/preferences.py:7-25).
Comparison behavior:
- KPI widgets compare the aggregate for the current and comparison periods and show a delta;
- line and area charts can overlay the comparison series;
- other widget types do not support period comparison
(
enezadashboard/services/renderer.py:604-619).
Operational Notes
- Run
python manage.py rbac_syncafter deployment. - Run
python manage.py seed_dashboards --dry-runbefore deploying definition changes. - Report discovery metadata is cached per process. Restart the process or call
clear_dashboard_report_cache()after changing report code. - Query results are reused only within one request; the cache key includes user
and entity identity (
enezadashboard/services/query_cache.py:7-39). - Dashboard visibility is not data protection. Every widget still checks the report permission and report access function.