@@ -6,16 +6,16 @@ import { definePage } from '@objectstack/spec/ui';
66 * Renewals Pipeline — a `kind:'react'` business scenario (ADR-0081).
77 *
88 * A renewals manager works a list of accounts by lifecycle stage; selecting one
9- * drives a 360° panel (highlights + invoices + a value-by-status chart) and a
10- * pre-styled `<ObjectForm formType="drawer">` to update the account in place.
9+ * drives a 360° panel (account summary + invoices + a value-by-status chart) and
10+ * a pre-styled `<ObjectForm formType="drawer">` to update the account in place.
1111 * Every block prop is taken straight from the react-tier contract
1212 * (skills/objectstack-ui/references/react-blocks.md).
1313 *
1414 * The 360 panel deliberately shows BOTH rollup styles side by side:
1515 * • hand-rolled — a `useAdapter()` effect counts related projects/invoices
1616 * into a KPI strip (full control, you own loading/refresh), vs
17- * • framework blocks — `<ObjectChart>`/`<RecordRelatedList >` do the same
18- * cross-object reads declaratively (zero data code).
17+ * • framework blocks — `<ObjectChart>`/`<ListView >` do the same cross-object
18+ * reads declaratively (zero data code).
1919 * (This comparison absorbed the former Account Cockpit page.)
2020 *
2121 * The chart is written in the spec `ChartConfig` shape (#3729) and its axes are
@@ -24,14 +24,20 @@ import { definePage } from '@objectstack/spec/ui';
2424 * `groupBy`) and `total` (its `field`) — not by a dataset-style measure name.
2525 * `os validate` checks both halves.
2626 *
27- * `<RecordRelatedList>` binds the CHILD object it lists (`showcase_invoice`),
28- * not the parent — the parent is `recordId`, and `relationshipField="account"`
29- * is the invoice's lookup back to it. This page used to pass the parent, which
30- * is what #4340 found: the react contract had glossed `objectName` as "the
31- * parent object" while the schema (and the renderer behind both surfaces) read
32- * it as the related one, so the list resolved `total` against an account and
33- * came back empty. Every field-bearing prop on the page is now checked against
34- * the object it actually names.
27+ * The selected account is bound BY REACT STATE, not by a record context: `sel`
28+ * is the parent id, so the invoice list is an ordinary `<ListView>` filtered on
29+ * the child's lookup (`['account', '=', sel]`) and the summary is an
30+ * `<ObjectForm mode="view">`. Both read their binding from their own props,
31+ * which is what makes them work on this tier.
32+ *
33+ * This panel used to be `<RecordHighlights>` + `<RecordRelatedList>`, and both
34+ * rendered EMPTY here (#4413): every `record:*` block takes its record from the
35+ * context a record page mounts, and a react page mounts none — the
36+ * `objectName`/`recordId` the contract published for them were read by no
37+ * renderer. They are out of the react tier now, and `os validate` rejects them
38+ * on this surface rather than letting the next author rediscover it at runtime.
39+ * (#4340's finding still holds where it applies: on a RECORD page
40+ * `<RecordRelatedList objectName>` is the CHILD object, never the parent.)
3541 *
3642 * Styling (ADR-0065): no Tailwind — inline `style={{}}` with `hsl(var(--token))`;
3743 * data blocks and the drawer bring their own compiled styling. The drawer sets
@@ -121,7 +127,7 @@ function Page() {
121127 <button onClick={() => setEditing(true)} style={{ borderRadius: 'var(--radius)', border: '1px solid hsl(var(--border))', background: 'transparent', color: 'hsl(var(--foreground))', padding: '6px 12px', fontSize: 14, cursor: 'pointer' }}>Edit account</button>
122128 </div>
123129
124- <RecordHighlights objectName="showcase_account" recordId={sel} fields={['name', 'status']} layout="horizontal" />
130+ <ObjectForm objectName="showcase_account" mode="view" recordId={sel} fields={['name', 'status']} />
125131
126132 <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
127133 <Stat label="Projects" value={related.projects} />
@@ -131,7 +137,8 @@ function Page() {
131137
132138 <ObjectChart objectName="showcase_invoice" type="bar" aggregate={{ field: 'total', function: 'sum', groupBy: 'status' }} xAxis={{ field: 'status' }} yAxis={[{ field: 'total', format: '$0,0' }]} series={[{ name: 'total', label: 'Invoice value' }]} title="Invoice value by status" showLegend={true} />
133139
134- <RecordRelatedList objectName="showcase_invoice" recordId={sel} relationshipField="account" columns={['name', 'status', 'total']} limit={5} showViewAll={true} title="Invoices" />
140+ <h3 style={{ margin: 0, fontSize: 14, fontWeight: 600, color: 'hsl(var(--foreground))' }}>Invoices</h3>
141+ <ListView objectName="showcase_invoice" filters={['account', '=', sel]} columns={['name', 'status', 'total']} navigation={{ mode: 'none' }} />
135142
136143 {editing ? (
137144 <ObjectForm objectName="showcase_account" mode="edit" recordId={sel}
0 commit comments