Skip to content

Report links

View as Markdown
import type {
ReportCellLink,
ReportCellLinkContext,
ReportCellLinkResolvers,
} from "@sapporta/frontend/report";

Links are frontend policy. They are passed to ReportGridDataset and are not serialized in GridDataset.

ReportCellLinkResolvers<TInput> is keyed by level name and then by cell column ID:

const links = {
project: {
cell: {
project: (context) => [
{
label: "Open project",
href: "/tables/projects?filter[id][eq]=1",
},
],
},
},
} satisfies ReportCellLinkResolvers<ProjectProgressQuery>;

Each cell resolver returns ReportCellLink[]. The current renderer follows only the first returned link; later entries are not rendered as secondary actions.

The public resolver type does not expose general row or footer resolver slots. Footer rows do not invoke cell resolvers. An ordinary synthetic node may invoke a configured cell resolver, so return [] when it lacks a safe identifier.

ReportCellLinkContext<TInput> contains:

  • dataset: the complete GridDataset;
  • node: the current Grid tree node;
  • levelName: the current level key;
  • input: TInput | undefined;
  • ancestors: ancestor dataset nodes;
  • column: the current GridDatasetColumn; and
  • value: the current cell value.

Pass report input with the renderer’s linkContext prop:

<ReportGridDataset
dataset={dataset}
links={links}
linkContext={{ input: query }}
/>

linkContext supplies only the report input. The renderer derives the other context fields from the dataset and current cell.

ReportCellLink has:

FieldTypeRequired
labelstringyes
hrefstringyes
kind"drill-down" | "record" | "route" | "external"no
icon"drill-up" | "drill-into" | "report" | "external"no
target"_self" | "_blank"no

Use target: "_blank" only for a deliberate new browsing context.

Hidden IDs can support a resolver, but they remain part of the report response and must already be authorized. Resolver checks prevent broken navigation; they do not create authority. Every destination applies its own ability and row-visibility rules.