Record pages
A page is one record with everything around it, keyed in pages by id and
opened wherever a record of its base table is. A good one has one shape:
pages: { opportunity: page('Opportunity', 'opportunity', [ header({ status: 'stage', facts: ['account_id', 'amount', 'close_date'] }), summary(['lines', 'units'], { workflow: workflow('stage', ['prospecting', 'proposal', 'closed_won'], { ordered: true }) }), related('Quote lines', 'opportunity_line', 'opportunity_id', { show: ['product_id', 'qty', 'unit_price'] }), related('Tasks', 'task', 'opportunity_id', { query: 'status = open', create: 'Add task' }), fields(['account_id', 'account_id.city'], { title: 'Account', presentation: 'card', region: 'aside' }), current('Next task', 'task', 'opportunity_id', { query: 'status = open', sort: 'due_on', region: 'aside' }), fields(['owner_id', 'probability', 'created_on'], { title: 'Deal', presentation: 'properties', region: 'aside' }), history('History', { region: 'aside' }), ], { listTabs: true }),},- The head:
header({ status, facts, eyebrow, primary, destructive })is drawn in the record’s head wherever it stands: the status column’s tone beside the name, up to six facts under it (units from display rules;{ column, label }says a word after a value, “3 beds”, andposition: 'before'ahead of it, “Born Apr 24, 2017”), and the verbs: one filled,primary: 'Renew'or else the first that is not destructive, and the destructive ones — named, confirming, or setting a value toned bad — last and quiet. - The figures: a
summaryof 3–4 rollups, with aworkflowfor a real pipeline. - The children: related lists of at most 4 columns, as tabs (
listTabs: true). - The aside: a
cardfor the parent,current(...)for the one child that matters now (the active lease, the latest visit), apropertiesrail edited in place, thenhistory()(the record history store must be provisioned). On a phone the aside follows the main flow; only a headline (the key figure and its actions) comes up after the first section.
A resource with no page opens on this anatomy, chosen from the catalog, and the scaffold writes it.
Sections
Section titled “Sections”| Section | Says |
|---|---|
{ kind: 'fields' }, fields(show, opts) |
The record’s own fields, label paths welcome (account_id.city). |
header(opts), summary(show, opts) |
The head’s status and facts; compact properties. |
related(title, from, whose, opts) |
Rows that point at the record: show, query, sort, limit, create, totals, empty. |
current(title, from, whose, opts) |
One of those rows, the first query and sort choose, as a card titled by its name without the page’s own, its show the facts; empty says what none means. |
timeline(title, streams) |
Rows of several tables in date order: stream(from, whose, at, show, { detail }). |
metric, breakdown, chart |
The dashboard helpers, narrowed by whose to rows about this record; about for another record. |
history() |
The record’s own changes. |
whose and about follow foreign keys only: relationships.
Tabs and which page opens
Section titled “Tabs and which page opens”listTabs: true draws each run of adjacent related lists (none with a
width) as one tab strip; the open tab rides the address
(/{app}/accounts/13/opportunities) and unopened tabs are never fetched.
Sections sharing a tab: 'Name' share one panel. With two pages over one
table, the resource’s page says which opens; a section’s
when: 'status = converted' shows it only while the record matches.
Every section’s keys: Lists, pages & forms.