Appearance
A KPI dashboard page
When you need this
A page (or the top of a list page) showing several headline numbers computed server-side, fetched in one call, refreshed after writes.
The pieces
One count Data Service per metric
json
{
"name": "oeq-equipment-count-available",
"definition": {
"operation": "count",
"source": { "kind": "entity", "entityName": "oeq_equipment" },
"filters": [ { "field": "status", "operator": "eq", "value": "AVAILABLE" } ],
"parameters": []
}
}One composite Data Service wrapping them
metadata/data_service/oeq-equipment-kpis.json (real file):
json
{
"name": "oeq-equipment-kpis",
"definition": {
"operation": "composite",
"source": { "kind": "entity", "entityName": "oeq_equipment" },
"steps": [
{ "as": "total", "service": "oeq-equipment-count-total", "parameters": {} },
{ "as": "available", "service": "oeq-equipment-count-available", "parameters": {} },
{ "as": "assigned", "service": "oeq-equipment-count-assigned", "parameters": {} }
]
}
}The page mount loader
json
"events": { "mounted": { "source": "action-chain", "actions": [
{ "id": "a0", "order": 0, "type": "callApi",
"config": { "connectionRef": "self", "path": "/api/v1/data-services/oeq-equipment-kpis/execute", "httpMethod": "POST", "params": {} }, "output": "kpi" },
{ "id": "a1", "order": 1, "type": "setValue", "config": { "field": "page.kpiTotal", "value": "${kpi.results.total.value}" } },
{ "id": "a2", "order": 2, "type": "setValue", "config": { "field": "page.kpiAvailable", "value": "${kpi.results.available.value}" } },
{ "id": "a3", "order": 3, "type": "setValue", "config": { "field": "page.kpiAssigned", "value": "${kpi.results.assigned.value}" } }
] } }The cards
json
{
"blockType": "core.kpi-card",
"properties": {
"value": { "source": "binding", "binding": { "scope": "page", "key": "kpiTotal" } },
"labelKey": { "source": "static", "value": "office-equipment.equipment-register.kpi.total" },
"format": { "source": "static", "value": "number" },
"colorToken": { "source": "static", "value": "primary" }
}
}The rule that bites
count → ${out.value}. composite → ${out.results.<step>.value}. Never .total (that's the unrelated list-pagination envelope).
Real response from the tutorial:
json
{ "results": { "total": { "value": 0 }, "available": { "value": 0 }, "assigned": { "value": 0 } } }To refresh after a write, append the same callApi + setValue steps to the Save action chain.
Related
- Add a KPI or aggregation
- A cross-row aggregation into a summary entity — for numbers stored back into a table