Dashboards
Maintainers
Overview
Core dashboards store chart definitions. A chart definition says which entity to read, how to filter it, how to group it, and which values to calculate.
The Blazor dashboard client turns each chart into one OData $apply query. It sends all chart queries in one read-only OData batch.
Batching reduces HTTP requests. It does not reduce database work. Each chart still runs as one database query.
Registration
Register dashboards in the API host:
using Benevia.Core.Dashboards.DependencyInjection;
services.AddCoreDashboards();
Register dashboard components in the Blazor host:
using Benevia.Core.Dashboards.Configuration;
using Benevia.Core.Dashboards.Blazor.DependencyInjection;
services.AddCoreDashboardsBlazor(options =>
{
options.StandardDates["fiscalYearStart"] =
new DashboardRelativeDateDefinition(MonthsOffset: -6, StartOfMonth: true);
});
AddCoreDashboardsBlazor(Action<DashboardOptions>?) registers dashboard options and the default UTC time zone provider.
Blazor Management Navigation
Hosts can expose dashboard management from each visible dashboard without coupling Core to an application-specific route:
<DashboardSidebar ManagementUrl="/settings/dashboards" />
When the current user has Dashboard Manage access, the sidebar shows a compact settings link for
each dashboard. The link is omitted when ManagementUrl is not set or the user has view-only
access. Hosts using DashboardPanelFrame can provide the same route through
DashboardManagementUrl.
Dashboard editor selection belongs in the host URL so refresh, bookmarks, and browser history keep working. The Core components expose callbacks for this purpose; the host owns its route and should return from save or cancel through browser history when it opened the editor from the management list.
Persisted Dashboard
The Dashboard entity contains:
| Field | Meaning |
|---|---|
Key |
Stable unique key. |
Name |
Name shown to users. |
Description |
Optional help text. |
Entity |
Host entity name used for discovery. |
Type |
List or Single. |
JsonDefinition |
Stored dashboard definition text. |
Standard |
Product seed data owns this dashboard. |
Hide |
Hide from normal panels. |
Saving a dashboard does not parse the JSON definition. Normal required, length, key, and standard-dashboard rules still apply.
Definitions contain parameters and charts. The persisted entity supplies the dashboard name and key.
{
"parameters": [
{
"id": "fromDate",
"name": "From date",
"type": "date",
"default": "thirtyDaysAgo"
}
],
"charts": [
{
"id": "salesByMonth",
"name": "Sales by month",
"type": "Bar",
"order": 10,
"query": {
"entity": "SalesInvoice",
"context": null,
"filters": {
"kind": "comparison",
"left": { "property": "InvoiceDate" },
"operator": "GreaterThanOrEqual",
"right": { "parameterId": "fromDate" }
},
"dimensions": [
{ "id": "month", "property": "InvoiceDate", "bucket": "Month" }
],
"measures": [
{
"kind": "aggregate",
"id": "sales",
"label": "Net sales",
"property": "NetAmount",
"aggregate": "Sum"
}
],
"sort": [
{ "dimension": "month", "direction": "Ascending" }
],
"top": 12
},
"display": {
"showLegend": false,
"showValues": true
}
}
]
}
How Data Loads
For each chart, the client builds one OData query:
- Resolve parameters and relative dates.
- Add the chart filter.
- Add the list filter for same-entity charts.
- Add
$searchfor same-entity charts. - Add
coreScopefor cross-entity charts. - Use
$applyto filter, compute buckets, group, and aggregate. - Add calculated measures with
Core.SafeDividewhen needed. - Sort and request one extra row.
The extra row lets the client set DashboardChartResult.IsTruncated.
Context
DashboardQuery.Context connects a chart to the host page.
If the chart reads the same entity as the ListView, the dashboard can reuse the ListView filter and search directly.
If the chart reads another entity, the query must include a to-one reference path back to the host entity. The client sends the host filter, search, and active row in coreScope. The API applies that scope before aggregation.
Dimensions
A dimension groups rows by a property.
Date-like properties support Day, Week, Month, Quarter, and Year. Weeks start on Monday. DateTimeOffset values use UTC before grouping.
AgingBucket groups a date against an anchor parameter. Null dates use DashboardOptions.NullAgingBucketLabel, which defaults to No due date.
The default aging buckets are:
| Label | Offset |
|---|---|
Current |
0 |
1-30 days |
-30 |
31-60 days |
-60 |
61-90 days |
-90 |
90+ days |
none |
The last bucket needs a null offset. It catches values older than the other buckets.
Measures
Aggregate measures support Count, Sum, Average, Minimum, and Maximum.
Calculated measures combine earlier measure IDs with Add, Subtract, Multiply, or SafeDivide.
Filtered measures use Core.When(condition, value) before aggregation.
Relative Dates
Relative parameter values come from DashboardOptions.StandardDates.
Built-in values are:
| Key | Meaning |
|---|---|
today |
Current date. |
thirtyDaysAgo |
30 days before today. |
sixtyDaysAgo |
60 days before today. |
ninetyDaysAgo |
90 days before today. |
twelveMonthsAgo |
12 months before today. |
lastCompletedMonthStart |
First day of the previous month. |
currentMonthStart |
First day of the current month. |
Relative dates use IDashboardTimeZoneProvider. The default provider uses UTC.
Security
The dashboard definition is not the security boundary. The OData query is.
When a chart query runs, the API checks:
- tenant model trimming,
- disabled features,
- entity permissions,
- property permissions,
- root row filters,
- row filters on referenced to-one navigations,
- host filters and search from
coreScope.
Users without read access to the Dashboard entity do not see dashboard panels or sidebar entries.
A chart denied by entity or property permissions shows Not available. Other chart failures return a safe chart-local error, and later charts can still render.
Dashboard.JsonDefinition can expose property names in saved JSON. Data access is still enforced when the chart query runs.
Limits
Important defaults:
| Limit | Value |
|---|---|
| Rows per chart | 200 |
| Dashboard definition text | 1 MiB |
| OData batch parts | 64 |
ApiOptions.MaxTop = 0 means there is no configured maximum. Requesting $top=0 still returns no rows.