Skip to content

Commit 94e9040

Browse files
authored
fix(spec): declare extended Gantt config fields the renderer reads (#2030)
GanttConfigSchema only declared the 5 core timeline fields (no passthrough), so parentField/typeField/colorField/groupByField/tooltipFields/baseline*/ resourceView/assignee*/quickFilters/autoZoomToFilter were stripped by parse() at compile time and on the runtime /meta/view re-validation, degrading the Gantt to a flat list. Declare them explicitly so the renderer contract round-trips through the spec.
1 parent 4331adb commit 94e9040

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): declare the extended Gantt config fields the renderer actually reads
6+
7+
`GanttConfigSchema` only declared the 5 core timeline fields as a plain
8+
`z.object` (no passthrough), so every other field the Gantt renderer consumes —
9+
`parentField`/`typeField` (two-level summary→step hierarchy), `colorField`,
10+
`groupByField`, `tooltipFields`, `baselineStartField`/`baselineEndField`,
11+
`resourceView`/`assigneeField`/`effortField`/`capacity`, `quickFilters`,
12+
`autoZoomToFilter` — was silently stripped by `.parse()` on both the compile-time
13+
protocol check and the runtime `GET /api/v1/meta/view/:object` re-validation. With
14+
the keys gone before render, the Gantt degraded to a flat list (no parent/child
15+
rows, no summary bars, no expand/collapse). These fields are now declared
16+
explicitly (with descriptions), so the renderer contract round-trips through the
17+
spec instead of requiring downstream patches.

packages/spec/src/ui/view.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,33 @@ describe('GanttConfigSchema', () => {
210210

211211
expect(() => GanttConfigSchema.parse(config)).not.toThrow();
212212
});
213+
214+
it('should accept extended renderer fields (hierarchy, baseline, grouping, resource, tooltip, quick filters)', () => {
215+
const config = {
216+
startDateField: 'start_date',
217+
endDateField: 'end_date',
218+
titleField: 'name',
219+
colorField: 'status',
220+
parentField: 'parent_id',
221+
typeField: 'row_type',
222+
baselineStartField: 'planned_start',
223+
baselineEndField: 'planned_end',
224+
groupByField: 'workshop',
225+
resourceView: true,
226+
assigneeField: 'owner',
227+
effortField: 'effort',
228+
capacity: 8,
229+
tooltipFields: ['owner', { field: 'effort', label: '工时' }],
230+
quickFilters: [
231+
{ field: 'status' },
232+
{ field: 'category', label: '类别', options: ['A', { value: 1, label: 'B' }] },
233+
],
234+
autoZoomToFilter: false,
235+
};
236+
237+
expect(() => GanttConfigSchema.parse(config)).not.toThrow();
238+
expect(GanttConfigSchema.parse(config)).toMatchObject({ parentField: 'parent_id', typeField: 'row_type' });
239+
});
213240
});
214241

215242
describe('ListViewSchema', () => {

packages/spec/src/ui/view.zod.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,55 @@ export const CalendarConfigSchema = lazySchema(() => z.object({
378378
colorField: z.string().optional(),
379379
}));
380380

381+
/**
382+
* Quick filter dimension for the Gantt toolbar.
383+
*/
384+
export const GanttQuickFilterSchema = lazySchema(() => z.object({
385+
field: z.string().describe('Record field / dot-path the dimension filters on'),
386+
label: z.string().optional().describe('Trigger label (falls back to the field label)'),
387+
options: z.array(z.union([
388+
z.string(),
389+
z.object({
390+
value: z.union([z.string(), z.number()]),
391+
label: z.string().optional(),
392+
}),
393+
])).optional().describe('Explicit option override for fixed enums'),
394+
}));
395+
381396
/**
382397
* Gantt Settings
398+
*
399+
* Beyond the core timeline fields, the renderer supports a two-level
400+
* parent/child hierarchy (parentField/typeField), planned-vs-actual baselines,
401+
* dynamic grouping, a resource/workload view, hover tooltips and quick filters.
383402
*/
384403
export const GanttConfigSchema = lazySchema(() => z.object({
385404
startDateField: z.string(),
386405
endDateField: z.string(),
387406
titleField: z.string(),
388407
progressField: z.string().optional(),
389408
dependenciesField: z.string().optional(),
409+
colorField: z.string().optional().describe('Field that drives the bar color'),
410+
// Two-level hierarchy: a parent task id (summary bar) and a row type.
411+
parentField: z.string().optional().describe('Field holding the parent task id (builds the summary → step tree)'),
412+
typeField: z.string().optional().describe('Field whose value maps to task/summary/milestone'),
413+
// Planned-vs-actual reference bars.
414+
baselineStartField: z.string().optional().describe('Baseline (planned) start field'),
415+
baselineEndField: z.string().optional().describe('Baseline (planned) end field'),
416+
// Dynamic grouping: bucket leaf tasks under one synthesized summary per value.
417+
groupByField: z.string().optional().describe('Field to group leaf tasks by (synthesized summary rows)'),
418+
// Resource / workload view.
419+
resourceView: z.boolean().optional().describe('Render a per-resource workload histogram instead of the timeline'),
420+
assigneeField: z.string().optional().describe('Resource field to bucket load by (resource view)'),
421+
effortField: z.string().optional().describe('Per-task load units (resource view; default 1)'),
422+
capacity: z.number().optional().describe('Per-resource capacity ceiling; loads above this flag overload'),
423+
// Hover tooltip + quick filters.
424+
tooltipFields: z.array(z.union([
425+
z.string(),
426+
z.object({ field: z.string(), label: z.string().optional() }),
427+
])).optional().describe('Fields to surface in the hover tooltip, in display order'),
428+
quickFilters: z.array(GanttQuickFilterSchema).optional().describe('Multi-select filter dropdowns rendered above the chart'),
429+
autoZoomToFilter: z.boolean().optional().describe('When true (default), filtering zooms the range to the filtered tasks'),
390430
}));
391431

392432
/**

0 commit comments

Comments
 (0)