Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…elegation and client-side aggregation - DashboardRenderer.getComponentSchema() now detects provider: 'object' and produces type: 'object-chart' schema with objectName/aggregate config instead of empty data[] - Same pattern applied to table and pivot widget types (pass through dataProvider) - ObjectChart.aggregateRecords() performs client-side sum/count/avg/min/max grouping - Added 4 new DashboardRenderer tests (chart/fallback/table/pivot with provider: object) - Added 8 new ObjectChart aggregation unit tests - Updated ROADMAP.md Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…e review Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a critical bug where dashboard widgets with provider: 'object' were rendering blank. The root cause was that DashboardRenderer.getComponentSchema() only extracted static data arrays (widgetData?.items || []) but didn't handle the async object provider pattern where data configuration includes provider metadata instead of actual data items.
Changes:
- Added
isObjectProvider()type guard and delegation logic inDashboardRendererto route object provider widgets to async-capable components (object-chartfor charts,data-tableandpivotwithdataProviderpassthrough) - Implemented
aggregateRecords()inObjectChartfor client-side aggregation (sum/count/avg/min/max by groupBy field) when server-side aggregation isn't available - Added comprehensive test coverage (4 DashboardRenderer tests, 8 aggregateRecords unit tests) validating the new data flow
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/plugin-dashboard/src/DashboardRenderer.tsx | Added isObjectProvider() helper and conditional logic in getComponentSchema() to detect provider: 'object' and delegate to type: 'object-chart' for charts, type: 'data-table' with dataProvider for tables, and type: 'pivot' with dataProvider for pivot widgets, passing through objectName and aggregate config instead of empty data arrays |
| packages/plugin-charts/src/ObjectChart.tsx | Added aggregateRecords() function for client-side groupBy + aggregation (sum/count/avg/min/max) and integrated it into the data fetch flow when schema.aggregate is present; updated useEffect dependency array to include schema.aggregate; added aggregate field to ComponentRegistry metadata |
| packages/plugin-dashboard/src/tests/DashboardRenderer.widgetData.test.tsx | Added 4 new tests validating object provider handling: chart with aggregate, objectName fallback from widget.object, table with provider:object, pivot with provider:object |
| packages/plugin-charts/src/tests/ObjectChart.aggregation.test.ts | Added 8 comprehensive unit tests for aggregateRecords covering all aggregation functions (sum/count/avg/min/max), edge cases (missing groupBy field, empty records, non-numeric values) |
| ROADMAP.md | Updated P2: Dashboard Dynamic Data entry to reflect completion with implementation details |
| export function aggregateRecords( | ||
| records: any[], | ||
| aggregate: { field: string; function: string; groupBy: string } | ||
| ): any[] { | ||
| const { field, function: aggFn, groupBy } = aggregate; | ||
| const groups: Record<string, any[]> = {}; | ||
|
|
||
| for (const record of records) { | ||
| const key = String(record[groupBy] ?? 'Unknown'); | ||
| if (!groups[key]) groups[key] = []; | ||
| groups[key].push(record); | ||
| } | ||
|
|
||
| return Object.entries(groups).map(([key, group]) => { | ||
| const values = group.map(r => Number(r[field]) || 0); | ||
| let result: number; | ||
|
|
||
| switch (aggFn) { | ||
| case 'count': | ||
| result = group.length; | ||
| break; | ||
| case 'avg': | ||
| result = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0; | ||
| break; | ||
| case 'min': | ||
| result = values.length > 0 ? Math.min(...values) : 0; | ||
| break; | ||
| case 'max': | ||
| result = values.length > 0 ? Math.max(...values) : 0; | ||
| break; | ||
| case 'sum': | ||
| default: | ||
| result = values.reduce((a, b) => a + b, 0); | ||
| break; | ||
| } | ||
|
|
||
| return { [groupBy]: key, [field]: result }; | ||
| }); | ||
| } |
There was a problem hiding this comment.
Consider adding JSDoc type annotations for better type safety and IDE support. The aggregate parameter should be typed more strictly to match the expected shape with literal types for the function field (e.g., 'sum' | 'count' | 'avg' | 'min' | 'max'). This would prevent typos and provide better autocomplete support.
DashboardRenderer.getComponentSchema()only extractswidgetData?.items || []for chart/table/pivot widgets. Whenprovider: 'object', the data config is{ provider: 'object', object: 'opportunity', aggregate: {...} }— noitemsproperty exists, so charts always receive[]and render blank.Changes
DashboardRenderer.tsx— AddisObjectProvider()type guard. For chart widgets withprovider: 'object', emittype: 'object-chart'schema withobjectName/aggregatepassthrough instead oftype: 'chart'with emptydata: []. Same pattern for table and pivot widget types.ObjectChart.tsx— AddaggregateRecords()for client-side groupBy + aggregation (sum/count/avg/min/max) applied afterdataSource.find()whenschema.aggregateis present.Tests — 4 new
DashboardRenderer.widgetDatatests (chart, objectName fallback, table, pivot withprovider: 'object'). 8 newaggregateRecordsunit tests.Example: before vs after schema generation
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.