Chapter 26 · Volume III — The Intelligence Layer

Reports, Views & Dashboards

26.1 Three Constructs

ADL provides three declarative analytics constructs, each serving a different purpose:

ConstructPurposeOutputThink of It As
ViewNamed, parameterized query with computed columnsTabular dataset (JSON, CSV)A saved query with access control
ReportFormatted, multi-section document with totalsJSON, CSV, or PDFA deliverable for stakeholders
DashboardCollection of real-time metric widgetsWidget values with thresholdsA KPI summary for executives

Views are the building block. Reports reference views and add formatting. Dashboards aggregate metrics across the domain. All three are declared in YAML, executed by the runtime, and accessible through the API.


26.2 Views

A view is a named query on a resource with computed columns, filtering, sorting, and access control.

views:
  activeGrants:
    description: "All active grants with budget utilization"
    source: Award
    filter: "status == 'active'"
    permissions: [spoOfficer, grantAccountant, admin]

    columns:
      - field: awardNumber
        label: "Award #"
      - field: title
        label: "Project Title"
      - field: totalAmount
        label: "Total Award"
        format: money
      - name: expendedToDate
        label: "Expended"
        computed: "sum(Expenditure, 'amount', awardId == $record.id && status == 'processed')"
        format: money
      - name: availableBalance
        label: "Available"
        computed: "totalAmount - expendedToDate"
        format: money
      - name: utilizationRate
        label: "Utilization %"
        computed: "expendedToDate / totalAmount * 100"
        format: percentage
      - field: endDate
        label: "End Date"
        format: date
      - name: daysRemaining
        label: "Days Left"
        computed: "daysUntil(endDate)"
        highlight:
          red: "< 30"
          yellow: "< 90"
          green: ">= 90"

    sort: daysRemaining asc
    defaultLimit: 50

View Properties

PropertyTypeRequiredDescription
descriptionstringnoHuman-readable description
sourcestringyesResource to query
filterstringnoAEL filter expression
permissionsstring[]noRoles that can access this view (default: [admin])
columnsarrayyesColumn definitions
sortstringnoDefault sort: fieldName asc or fieldName desc
defaultLimitintegernoDefault page size (default: 50)

Column Types

Field column — references an existing field on the resource:

- field: awardNumber
  label: "Award #"

Computed column — calculated using AEL expressions:

- name: utilizationRate
  label: "Utilization %"
  computed: "expendedToDate / totalAmount * 100"
  format: percentage

Computed columns can reference other columns by name, resource fields, and cross-resource aggregations.

Column Properties

PropertyTypeRequiredDescription
fieldstringif not computedResource field name
namestringif computedColumn identifier (for computed columns)
labelstringyesDisplay label
computedstringif no fieldAEL expression
formatstringnoOutput format (see §26.5)
highlightobjectnoConditional formatting thresholds

View API

GET /api/v1/{domain}/views/{name}
    ?format=json                    # json (default) or csv
    ?sort=-utilizationRate          # override default sort
    ?page=1&limit=25               # pagination

The response includes the formatted data with computed columns evaluated:

{
  "data": {
    "view": "activeGrants",
    "columns": [
      { "name": "awardNumber", "label": "Award #", "format": null },
      { "name": "utilizationRate", "label": "Utilization %", "format": "percentage" }
    ],
    "items": [
      {
        "awardNumber": "AWD-2026-001",
        "title": "Genomic Analysis Platform",
        "totalAmount": 500000,
        "expendedToDate": 312500,
        "availableBalance": 187500,
        "utilizationRate": 62.5,
        "daysRemaining": 45,
        "_highlights": { "daysRemaining": "yellow" }
      }
    ],
    "count": 1,
    "totalCount": 12
  }
}

26.3 Reports

A report is a multi-section document with parameters, metrics, tables, grouping, totals, and multi-format output.

reports:
  budgetStatusReport:
    description: "Grant budget status by category"
    permissions: [grantAccountant, spoOfficer, admin]
    header:
      title: "Budget Status Report"
      subtitle: "Fiscal Year {$params.fiscalYear}"
    parameters:
      - name: fiscalYear
        type: integer
        default: "$currentYear"
        label: "Fiscal Year"
      - name: sponsor
        type: string
        optional: true
        label: "Sponsor"

    sections:
      - name: summary
        type: metrics
        metrics:
          - label: "Total Awards"
            value: "count(Award, status == 'active')"
            format: integer
          - label: "Total Budget"
            value: "sum(Award, 'totalAmount', status == 'active')"
            format: money
          - label: "Total Expended"
            value: "sum(Expenditure, 'amount', status == 'processed')"
            format: money
          - label: "Overall Utilization"
            value: "sum(Expenditure, 'amount', status == 'processed') / sum(Award, 'totalAmount', status == 'active') * 100"
            format: percentage

      - name: byCategory
        type: table
        view: activeGrants
        groupBy: sponsor
        totals: [totalAmount, expendedToDate, availableBalance]
        emptyMessage: "No active grants for the selected criteria"

    footer:
      text: "Generated {$now} by {$actor.name}"
      confidentiality: "INTERNAL — Do not distribute outside the institution"

Report Properties

PropertyTypeRequiredDescription
descriptionstringnoHuman-readable description
permissionsstring[]noRoles that can access (default: [admin])
headerobjectnoReport title and subtitle
parametersarraynoUser-supplied query parameters
sectionsarrayyesReport content sections
footerobjectnoFooter text and confidentiality notice

Section Types

Metrics — aggregate KPI values:

- name: summary
  type: metrics
  metrics:
    - label: "Total Orders"
      value: "count(Order, status != 'cancelled')"
      format: integer

Table — tabular data from a view or inline query:

- name: orderDetails
  type: table
  view: activeOrders
  groupBy: status
  totals: [orderTotal]

Parameters

Reports accept parameters at query time. Parameters are substituted as $params.{name} in filters and computations:

parameters:
  - name: tradeDate
    type: date
    default: "$today"
    label: "Trade Date"
  - name: venue
    type: string
    optional: true
    label: "Venue"
    enum: "distinct(Trade, 'venue')"

The enum property populates a dropdown from live data.

Report API

GET /api/v1/{domain}/reports/{name}
    ?format=json                    # json, csv, pdf
    ?fiscalYear=2026                # declared parameter

PDF Generation

When format=pdf is requested:

  • header renders as the title page or header block
  • metrics sections render as KPI cards
  • table sections render as formatted tables with totals rows
  • highlight conditions drive cell background colors
  • footer renders at the bottom of each page
  • confidentiality renders as a watermark or footer stamp

The PDF generator is a Kalos plugin. The default implementation produces a clean text-based layout. Production deployments MAY use a rich PDF library for advanced formatting.


26.4 Dashboards

A dashboard is a collection of widgets that aggregate metrics from across the domain.

dashboards:
  operationsOverview:
    description: "Real-time operational status"
    permissions: [operationsManager, plantManager, admin]
    refresh: 5m
    layout:
      columns: 3

    widgets:
      - name: assetAvailability
        type: gauge
        position: { row: 1, col: 1 }
        query:
          value: "count(Asset, status == 'operational') / count(Asset, status != 'decommissioned') * 100"
        label: "Asset Availability"
        format: percentage
        thresholds:
          green: ">= 95"
          yellow: ">= 85"
          red: "< 85"
        target: 98

      - name: overdueInspections
        type: counter
        position: { row: 1, col: 2 }
        query:
          value: "count(Asset, status == 'operational' && daysUntil(nextInspectionDue) < 0)"
        label: "Overdue Inspections"
        icon: lucide:alert-triangle
        thresholds:
          green: "== 0"
          yellow: "<= 3"
          red: "> 3"

      - name: openWorkOrders
        type: breakdown
        position: { row: 1, col: 3 }
        query:
          resource: WorkOrder
          filter: "status != 'closed' && status != 'rejected'"
          groupBy: priority
          metric: count
        label: "Open Work Orders"
        colorMap:
          emergency: "#ef4444"
          urgent: "#f97316"
          high: "#f59e0b"
          normal: "#3b82f6"
          low: "#94a3b8"

      - name: workOrderTrend
        type: timeSeries
        position: { row: 2, col: 1, span: 2 }
        query:
          resource: WorkOrder
          metric: count
          groupBy: "$dateGroup(createdAt, 'week')"
          filter: "createdAt > dateAdd(now(), -90, 'day')"
        label: "Work Orders — Last 90 Days"

      - name: recentActivity
        type: timeline
        position: { row: 2, col: 3 }
        query:
          resource: "*"
          history: true
          filter: "severity == 'error' || severity == 'warning'"
          limit: 10
        label: "Recent Alerts"

Widget Types

TypeDescriptionOutput
counterSingle numeric valueNumber with threshold coloring
gaugePercentage with targetValue with green/yellow/red zones
scorecardValue with trend comparisonNumber with delta indicator
breakdownGrouped counts or sumsCategorized values with colors
timeSeriesMetrics over timeGrouped values by date period
tableTabular data from a viewFormatted data table
timelineRecent history eventsEvent feed with icons and severity

Dashboard API

GET /api/v1/{domain}/dashboards/{name}

Returns all widget values pre-computed:

{
  "data": {
    "dashboard": "operationsOverview",
    "computedAt": "2026-05-17T14:30:00Z",
    "widgets": {
      "assetAvailability": {
        "type": "gauge",
        "value": 96.3,
        "format": "percentage",
        "threshold": "green",
        "target": 98
      },
      "overdueInspections": {
        "type": "counter",
        "value": 4,
        "threshold": "red"
      },
      "openWorkOrders": {
        "type": "breakdown",
        "groups": [
          { "label": "emergency", "value": 1, "color": "#ef4444" },
          { "label": "urgent", "value": 3, "color": "#f97316" },
          { "label": "high", "value": 7, "color": "#f59e0b" },
          { "label": "normal", "value": 12, "color": "#3b82f6" }
        ]
      }
    }
  }
}

The frontend renders directly from the response — no business logic on the client. The widget definition carries the value, the type, the threshold state, the formatting, and the colors.

Per-Widget Refresh

GET /api/v1/{domain}/dashboards/{name}/widgets/{widget}

Returns a single widget value for individual refresh without recomputing the entire dashboard.

Caching

refresh: 5m

The runtime caches the computed dashboard result and returns the cached version for requests within the refresh window. computedAt in the response shows when the data was last computed.

ValueBehavior
noneCompute on every request
1mNear-real-time (one-minute cache)
5mStandard operational dashboard
1hHourly summary

26.5 Format System

Built-In Formats

FormatDescriptionExample InputExample Output
moneyCurrency with symbol1234567.89$1,234,567.89
percentagePercentage with 1 decimal96.396.3%
integerWhole number with thousands12345671,234,567
decimal2Two decimal places3.141593.14
decimal4Four decimal places0.123450.1235
dateLocale date2026-03-07Mar 7, 2026
datetimeLocale date and time2026-03-07T14:30ZMar 7, 2026 2:30 PM
durationHuman-readable duration172800 (seconds)2 days
booleanYes/NotrueYes

Highlight Conditions

highlight:
  red: "< 30"
  yellow: "< 90"
  green: ">= 90"

Highlights are AEL conditions evaluated against the column value. The API response includes a _highlights object mapping column names to threshold states. The frontend renders conditional formatting — identical to Excel conditional formatting but declared in the domain model.

Locale Configuration

Currency symbol and number formatting follow the domain’s locale:

domain:
  locale:
    currency: USD
    dateFormat: "MMM d, yyyy"
    numberFormat:
      thousandsSeparator: ","
      decimalSeparator: "."

26.6 Integration Points

Reports + Schedules

Scheduled tasks (§24) can generate and deliver reports:

schedules:
  weeklyComplianceReport:
    recurrence: weekly on Monday at 06:00
    action: report
    report: complianceWeeklySummary
    recipients:
      roles: [complianceOfficer, admin]
    channels: [email]
    format: pdf

Reports + Notifications

Reports can be attached to notification emails. When a scheduled report generates a PDF, it is delivered as an email attachment through the notification channel.

Dashboards + History

The timeline widget type queries history events directly, providing a real-time activity feed within the dashboard. The timeSeries widget can aggregate history event counts over time for trend analysis.

Views in Reports and Dashboards

Reports reference views in their table sections. Dashboards reference views in their table widgets. Views are reusable across multiple reports and dashboards.


End of Chapter 26.

Next: Chapter 27 — Templates & Documents