DevelopmentResource Framework
Drilldown Routes Advanced
Drilldown routes — deep dive
This page explains the RESOURCE_DRILLDOWN_ROUTES registry in detail: how titles, sections, and actions are defined; how custom paths are generated; and how the drilldown UI interprets your configuration.
The registry
File: packages/resource-framework/registries/resource-drilldown-routes.ts
export type ResourceDrilldownRoute = {
title?: (row: any) => string;
subtitle?: (row: any) => string;
backLabel?: string | ((resourceName: string) => string);
actions?: DrilldownAction[];
sections?: DrilldownSectionConfig[];
pathTemplate?: string; // e.g. "/v2/${name}/{{uuid}}"
};
export type DrilldownSectionConfig = {
title: string;
columns?: 1 | 2 | 3 | 4;
fields: DrilldownField[];
};
export type DrilldownField =
| string
| { key: string; label?: string; hidden?: boolean };
export type DrilldownAction =
| {
label: string;
onClick: (row: any) => void;
destructive?: boolean;
disabled?: boolean | ((row: any) => boolean);
}
| { type: "separator" };
```typescript
### Titles and subtitles
- `title(row)` should return a concise primary heading derived from the row (e.g., “Invoice INV-123”).
- `subtitle(row)` is optional secondary context (e.g., “Draft • EUR 250.00”).
- `backLabel` can be a string or a function; it labels the “back” link in the page shell.
### Sections
Sections group fields visually. Each section has:
- `title`: the section heading.
- `columns`: grid density (1–4). Defaults to 1.
- `fields`: field keys to render, in order. Each field can be:
- a string (the raw key to render), or
- an object `{ key, label?, hidden? }` to override the label or hide it.
Rendering rules:
- The drilldown uses the same column/renderer registry as list views. This keeps formatting consistent between list and detail pages.
- If a field key doesn’t exist on the row, it renders blank (or may be omitted based on UI heuristics).
- Use `hidden: true` for fields you want available to custom renderers but not displayed in the default layout.
Tips:
- Prefer human labels for ambiguous keys via `{ key: "sla_agreed_response", label: "SLA agreed response" }`.
- Group related fields into separate sections (“General”, “Address”, “Compliance”) to improve scannability.
### Actions
The `actions` array defines the menu in the drilldown header:
- Each action gets `label` and an `onClick(row)` handler.
- Use `destructive: true` to style actions like “Delete” accordingly.
- `disabled` can be a boolean or function per row.
- Insert `{ type: "separator" }` to group actions.
Examples:
```typescript
actions: [
{ label: "Open in external", onClick: (row) => window.open(row.url) },
{ type: "separator" },
{
label: "Delete",
destructive: true,
disabled: (row) => row.status === "locked",
onClick: async (row) => {/* ... */},
},
];
```typescript
### Custom paths
Use `pathTemplate` to define a custom drilldown path for a resource. Placeholders are replaced with values from a payload:
```typescript
pathTemplate: "/v2/invoices/{{invoice_id}}"
```typescript
Helper provided in the registry:
```typescript
getDrilldownPath(name, payload) // replaces {{key}} and nested {{a.b.c}}
```typescript
### How the drilldown integrates with routes
- The drilldown component reads the route from `RESOURCE_ROUTES` (static) or from the `resource_routes` table (when a static entry is missing).
- `categories` on the `ResourceRoute` control tab grouping when editing.
- `permanent_edit_state` forces the page into edit mode on load.
- Field rendering uses the same `defineColumns`/registry pipeline as the list tables.
### Best practices
- Keep the title short; put context in the subtitle.
- Use 2 columns for most sections; switch to 1 for long-form text.
- Ensure destructive actions confirm the intent before mutating.
- Avoid overloading the page with rarely used fields—hide or move them to a final “Other” section.
### Troubleshooting
- A field does not render: check the field key exists in the fetched row and isn’t filtered out by the column registry.
- Actions disabled inconsistently: ensure your `disabled(row)` logic is pure and fast (no async work).
- Custom path doesn’t navigate as expected: verify placeholders match payload keys exactly; use `getDrilldownPath` during development to inspect the resolved string.
*** End Patch