https://github.com/user-attachments/assets/d012a013-2c90-49a1-a27e-b8e4b684a84f
Charts grouped by a relation without a sub-field rendered raw FK UUIDs
on axis ticks, legends and tooltips. The server now batch-resolves the
grouped record ids to their label identifier through a permission-scoped
query and formats every bucket with the record's display name.
Unresolvable records (deleted or not readable) render as Unknown and
their ids are stripped from the response payload. Same-named records get
an ordinal suffix so their buckets don't merge. Covers bar, line and
pie, plain and morph relations.
```mermaid
flowchart TD
A["Dashboard widget load"] --> B["Chart data service<br/>(bar / line / pie)"]
B --> C["executeGroupByQuery:<br/>group by relation FK id,<br/>ORDER BY target label identifier,<br/>scoped to source object permissions"]
C --> D["filterOutEmptyChartBuckets"]
D --> E{"Bare relation axis?<br/>(no sub-field)"}
subgraph RL["ChartRelationLabelService.resolveRelationLabels"]
direction TB
G1["Collect distinct record ids<br/>per target object"] --> G2["Batch SELECT label identifier columns,<br/>scoped to TARGET object permissions"]
G2 --> G3["buildRawLabelByRecordId:<br/>display name per record"]
G3 --> G4["buildUniqueRelationLabels:<br/>suffix duplicates, Unknown for unresolved"]
end
E -- No --> H["formatDimensionValue per bucket"]
E -- Yes --> G1
G4 --> H
H --> I["Strip unresolved ids from<br/>formattedToRawLookup"]
I --> J["Chart DTO to frontend"]
```
The chart settings sub-field dropdown gains a Record option to group by
the related record itself, and now only offers sub-fields the backend
accepts (system fields like a workspace member's updatedBy were
selectable but rejected at query time). Chart-data errors are now logged
server-side.
Also fixes two latent bugs on this path: sorting a bare-relation chart
by field threw `Cannot orderBy unknown field: agentId`, and the pie
chart truncated slices before sorting. The AI dashboard tool guidance
and the seeded dashboards no longer force the sub-field workaround.
The group-by query orders buckets by the related record's label
identifier at the database level (the engine now accepts ordering by a
target field when grouping by its id), so with more than 100 distinct
related records the surviving buckets match the label order.