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:

  1. Resolve parameters and relative dates.
  2. Add the chart filter.
  3. Add the list filter for same-entity charts.
  4. Add $search for same-entity charts.
  5. Add coreScope for cross-entity charts.
  6. Use $apply to filter, compute buckets, group, and aggregate.
  7. Add calculated measures with Core.SafeDivide when needed.
  8. 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.