Skip to content

Commit 8ef2a96

Browse files
fix(app-showcase): scope the command center work queue to in-review tasks (#25534)
<!-- claude-projects-attribution: {"github_login":"os-sales"} --> _Requested by **Sales** · [project thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuACdPWFJTLDcTrByFCHLzqb)_ Fixes #25503 Clause-②: no Metadata-protocol: none ## What changed On the showcase command center (`examples/app-showcase/src/ui/pages/command-center.page.ts`), the bottom panel `cc_queue_g` is titled 待审核列表 · Work Queue and carries the badge 审批中, but its `object-grid` had no filter and listed all 10 seeded tasks, done ones included. The KPI tile `cc_k3` (待复审 Review) above it counts only `status = in_review`. Both blocks now read one module constant, `IN_REVIEW = [{ field: 'status', operator: 'equals', value: 'in_review' }]`. The tile passes it to `kpi()` as before, and the grid gets `filter: IN_REVIEW`, so the panel lists the set the tile counts (2 rows on the stock seed). Landing: the file the claim names. No `packages/spec` edit. ## Which key object-grid reads `filter`, singular, as a `ViewFilterRule` array: - **Spec:** `ObjectGridPropsSchema` in `packages/spec/src/ui/component.zod.ts` (the `filter: z.array(ViewFilterRuleSchema, …)` member, line 5055 at base `1ff43b67ea`) is described as "lowered to the wire `$filter`. THE key, singular — not the plural misspelling". `filters` is declared only as an alias of `filter` (`FILTERS_TO_FILTER`), and `defaultFilters` is a retired key. - **Renderer inputs:** the committed `sdui.manifest.json`, generated from objectui's built tree at the `.objectui-sha` pin `6fb31dc`, lists `filter` (type `array`) among `object-grid`'s inputs. It has no `filters` input. - **Showcase precedent:** `examples/app-showcase/src/ui/pages/my-work.page.ts` filters its `object-grid` the same way. That page is the one #7750 was about: the plural `filters:` was written there but never read. The renderer's own read point (objectui `plugin-grid/src/ObjectGrid.tsx` reading `schema.filter`) is cited from that page's comment. This session can only read this repository, so I did not open it at the pin myself. ## Pin `examples/app-showcase/test/command-center-metric-filter.test.ts` gains a `describe` block. It finds `cc_queue_g` and `cc_k3` anywhere in the page as authored, helpers included, and asserts four things: - the queue's `filter` equals the in_review rule array; - the queue's `filter` equals the tile's `filter`; - both blocks read the same `objectName`; - the whole `cc_queue_g` properties bag passes `ComponentPropsMap['object-grid'].safeParse`. ## Tests (head `15ef279240`) - `pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2 test/command-center-metric-filter.test.ts`: Test Files 1 passed, Tests 7 passed. - `pnpm --filter @objectstack/example-showcase test`: Test Files 38 passed (38), Tests 465 passed (465), lock VERDICT command-exit 0. - `pnpm --filter @objectstack/example-showcase typecheck`: exit 0. `tsc --listFiles` includes the test file (count 1). - `pnpm --filter @objectstack/example-showcase validate` (`os validate`): Validation passed, exit 0. - **Ablation:** `node scripts/ablation-replace.mjs` replaced the anchor `'due_date'], filter: IN_REVIEW } },` with `'due_date'] } },` (anchor x1 to x0, blob a98af74 to 8af132bc2407). - Red: 2 of 7 failed, "scopes the queue to in_review tasks through `filter`" and "carries the same rules as the tile `cc_k3`". - Restore: blob == HEAD (a98af74), `git diff HEAD` empty. - No build leg: the test imports the page from the package's own `src`. ## Gates `node scripts/pm/dispatch-gates.mjs --commands` derived 45 commands at `15ef279240`. 44 exited 0. `pnpm check:dual-build-cjs-loads` exited 3 with PREREQUISITE NOT MET because the worktree built only the showcase's dependency closure, so that one is NOT MEASURED locally and CI's Lint & Repo Gates covers it. `--ran` reconciliation: "45 derived famil(ies) accounted for — 44 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)". ## Changeset None, with `skip-changeset`. `@objectstack/example-showcase` is `private: true`, so nothing in this diff publishes. ## Acceptance notes - `cc_k3`'s filter is now read through the shared constant rather than written inline. The value it counts is unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016g7nYP9wdNx1GDa5XDr7Uy Co-authored-by: Claude <noreply@anthropic.com>
1 parent 74496c6 commit 8ef2a96

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

‎examples/app-showcase/src/ui/pages/command-center.page.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ function chart(id: string, chartType: string, dataset: string, dimensions: strin
9898

9999
const CHART_H = '376px';
100100

101+
/**
102+
* The review set — tasks whose `status` is `in_review`. ONE value read by two
103+
* blocks: the KPI tile `cc_k3` counts it, and the work queue `cc_queue_g`
104+
* under its 待审核列表 / 审批中 heading lists it, so the panel shows exactly
105+
* the rows the tile counts. `object-grid` reads `filter` (singular, the
106+
* `ViewFilterRule` array — `ComponentPropsMap['object-grid']`) and lowers it
107+
* to `$filter`.
108+
*/
109+
const IN_REVIEW = [{ field: 'status', operator: 'equals', value: 'in_review' }];
110+
101111
export const CommandCenterPage = definePage({
102112
name: 'showcase_command_center',
103113
label: 'Command Center (大屏)',
@@ -146,7 +156,7 @@ export const CommandCenterPage = definePage({
146156
child: band('cc_kpi_grid', 6, [
147157
kpi('cc_k1', 'showcase_project', '活跃项目 Active', 'blue', { field: 'id', function: 'count' }, [{ field: 'status', operator: 'equals', value: 'active' }]),
148158
kpi('cc_k2', 'showcase_task', '待办任务 Open', 'teal', { field: 'id', function: 'count' }, [{ field: 'status', operator: 'not_equals', value: 'done' }]),
149-
kpi('cc_k3', 'showcase_task', '待复审 Review', 'purple', { field: 'id', function: 'count' }, [{ field: 'status', operator: 'equals', value: 'in_review' }]),
159+
kpi('cc_k3', 'showcase_task', '待复审 Review', 'purple', { field: 'id', function: 'count' }, IN_REVIEW),
150160
kpi('cc_k4', 'showcase_project', '风险项目 At-Risk', 'danger', { field: 'id', function: 'count' }, [{ field: 'health', operator: 'equals', value: 'red' }]),
151161
kpi('cc_k5', 'showcase_account', '客户 Accounts', 'orange', { field: 'id', function: 'count' }),
152162
kpi('cc_k6', 'showcase_project', '总预算 Budget', 'success', { field: 'budget', function: 'sum' }, undefined, '0.0a'),
@@ -169,7 +179,7 @@ export const CommandCenterPage = definePage({
169179
// ── Work queue — its own full-width row at the bottom ────────
170180
panel({
171181
id: 'cc_queue', title: '待审核列表 · Work Queue', accent: A.c1, badge: '审批中', minHeight: '0px',
172-
child: { id: 'cc_queue_g', type: 'object-grid', responsiveStyles: { large: { minWidth: '0', display: 'block' } }, properties: { objectName: 'showcase_task', columns: ['title', 'project', 'status', 'priority', 'due_date'] } },
182+
child: { id: 'cc_queue_g', type: 'object-grid', responsiveStyles: { large: { minWidth: '0', display: 'block' } }, properties: { objectName: 'showcase_task', columns: ['title', 'project', 'status', 'priority', 'due_date'], filter: IN_REVIEW } },
173183
}),
174184
],
175185
},

‎examples/app-showcase/test/command-center-metric-filter.test.ts‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,65 @@ describe('Command Center — object-metric properties parse against their Compon
7777
}
7878
});
7979
});
80+
81+
/**
82+
* The work queue lists the set the KPI tile above it counts.
83+
*
84+
* `cc_queue_g` sits under the heading 待审核列表 · Work Queue with the badge
85+
* 审批中, and the tile `cc_k3` (待复审 Review) counts tasks whose `status` is
86+
* `in_review`. The grid carried no filter, so it listed every task, done ones
87+
* included, while the tile beside it counted two.
88+
*
89+
* `filter` is the key `object-grid` declares (`ComponentPropsMap['object-grid']`,
90+
* the `ViewFilterRule` array) and the one the committed `sdui.manifest.json`
91+
* lists among the grid's inputs; the renderer lowers it to `$filter`. The pin
92+
* asserts that key, parses the whole bag against the grid's row, and holds the
93+
* queue's rules equal to the tile's so the two cannot drift apart.
94+
*/
95+
function nodeById(root: unknown, id: string): AnyRec | undefined {
96+
let found: AnyRec | undefined;
97+
const visit = (v: unknown): void => {
98+
if (found) return;
99+
if (Array.isArray(v)) {
100+
for (const item of v) visit(item);
101+
return;
102+
}
103+
if (!isRec(v)) return;
104+
if (v.id === id) {
105+
found = v;
106+
return;
107+
}
108+
for (const child of Object.values(v)) visit(child);
109+
};
110+
visit(root);
111+
return found;
112+
}
113+
114+
describe('Command Center — the work queue lists the review set its KPI counts', () => {
115+
const queue = nodeById(CommandCenterPage.regions, 'cc_queue_g');
116+
const tile = nodeById(CommandCenterPage.regions, 'cc_k3');
117+
const queueProps = isRec(queue?.properties) ? queue.properties : {};
118+
const tileProps = isRec(tile?.properties) ? tile.properties : {};
119+
120+
it('finds the queue grid and the Review tile', () => {
121+
expect(queue?.type).toBe('object-grid');
122+
expect(tile?.type).toBe('object-metric');
123+
expect(queueProps.objectName).toBe(tileProps.objectName);
124+
});
125+
126+
it('scopes the queue to in_review tasks through `filter`', () => {
127+
expect(queueProps.filter).toEqual([{ field: 'status', operator: 'equals', value: 'in_review' }]);
128+
});
129+
130+
it("carries the same rules as the tile `cc_k3`", () => {
131+
expect(queueProps.filter).toEqual(tileProps.filter);
132+
});
133+
134+
it('the queue properties bag parses clean against its ComponentPropsMap row', () => {
135+
const result = ComponentPropsMap['object-grid'].safeParse(queueProps);
136+
const failures = result.success
137+
? []
138+
: result.error.issues.map((issue) => `properties.${issue.path.join('.')}: ${issue.code}`);
139+
expect(failures).toEqual([]);
140+
});
141+
});

0 commit comments

Comments
 (0)