Skip to content

fix: Revenue by Account chart renders empty — unified data extraction across all components#836

Merged
hotlong merged 3 commits intomainfrom
copilot/fix-revenue-by-account-chart
Feb 24, 2026
Merged

fix: Revenue by Account chart renders empty — unified data extraction across all components#836
hotlong merged 3 commits intomainfrom
copilot/fix-revenue-by-account-chart

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 24, 2026

"Revenue by Account" chart on the CRM Dashboard displays no data. Three independent bugs compound: ObjectChart only extracts from results.records (missing .data/.value formats), DashboardRenderer builds series with dataKey: yField which doesn't match aggregateRecords() output keyed by aggregate.field, and the CRM config has yField: 'total' while the aggregate field is 'amount'. Additionally, the same incomplete data extraction bug exists in 5 other data-fetching components.

Centralized extractRecords() utility (@object-ui/core)

Created a shared extractRecords() in packages/core/src/utils/extract-records.ts that handles all API response shapes, exported from @object-ui/core for use by all plugins:

export function extractRecords(results: unknown): any[] {
  if (Array.isArray(results)) return results;
  if (results && typeof results === 'object') {
    if (Array.isArray((results as any).records)) return (results as any).records;
    if (Array.isArray((results as any).data)) return (results as any).data;
    if (Array.isArray((results as any).value)) return (results as any).value;
  }
  return [];
}

Unified data extraction across all 6 data components

Refactored all data-fetching components to use the centralized extractRecords():

  • ObjectChart (plugin-charts) — was missing .data, .value
  • ObjectMap (plugin-map) — was missing .value
  • ObjectCalendar (plugin-calendar) — was missing .records
  • ObjectGantt (plugin-gantt) — was missing .value
  • ObjectTimeline (plugin-timeline) — was missing .value, had inconsistent pattern
  • ObjectKanban (plugin-kanban) — was missing .records

DashboardRenderer: auto-adapt series dataKey from aggregate config (DashboardRenderer.tsx)

  • When aggregate is present, derive dataKey from aggregate.field instead of yField — this matches aggregateRecords() output shape { [groupBy]: key, [field]: result }
  • Falls back to yField when no aggregate is configured

CRM dashboard config alignment (crm.dashboard.ts)

  • yField/valueField: 'total''amount' to match aggregate.field

Tests

  • 8 tests for extractRecords in @object-ui/core (all response formats, nulls, priority order)
  • 8 tests for extractRecords re-export from ObjectChart (backward compatibility)
  • 2 tests for DashboardRenderer aggregate auto-adaptation and no-aggregate fallback
  • Updated existing provider: 'object' test expectation
  • Full suite: 361 files, 6606 tests pass
Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug] Revenue by Account 图表不显示数据——聚合字段和数据结构不匹配,数据提取不全</issue_title>
<issue_description>## 问题描述

CRM Demo Dashboard页面,"Revenue by Account" 图表未显示任何数据。

Revenue by Account 无数据截图

问题排查过程

1. Dashboard Widget 的定义

  • examples/crm/src/dashboards/crm.dashboard.ts
    • Revenue by Account widget 配置为:
      options: {
        xField: 'account',
        yField: 'total',
        data: {
          provider: 'object',
          object: 'opportunity',
          aggregate: { field: 'amount', function: 'sum', groupBy: 'account' }
        }
      }
  • 这里聚合字段 field: 'amount', 但 yField 被写成 total

2. DashboardRenderer 生成 object-chart 的逻辑

packages/plugin-dashboard/src/DashboardRenderer.tsx:

  • 生成 series: [{ dataKey: yField }],即 dataKey: 'total'
  • 但实际聚合后生成的字段是 amount,不是 total

3. ObjectChart 数据提取及聚合逻辑

packages/plugin-charts/src/ObjectChart.tsx:

  • 只判断了 results.records,没有同时兼容 results.dataresults.value(ObjectStack/ MSW mock 可能返回的格式)。导致数据无法拿到。
  • 其 client-side 聚合函数 aggregateRecords 输出: { [groupBy]: key, [field]: result },所以聚合结果是 { account: 'xxxx', amount: 123 }
  • 但 series 期待数据 key 为 total,导致即使拿到数据也不显示。

4. 跟踪 Console/Provider 发现 Dashboard/ReportView/ListView 等组件获取数据时都会用到同时兼容多种格式的结果提取。

归纳(需修复的点)

  1. 数据提取不全ObjectChart 组件的 fetch 结果只检查 records,没有兼容 data, value 字段。
  2. 配置字段不匹配:聚合输出字段名为 amount,但 dashboard 配置的 yField/series 却是 total,导致数据 key 不一致。
  3. (高优先级)DashboardRenderer 构造 object-chart widget 时,对 series 的默认构造和聚合字段应保证一致性。

建议修复方案

(1)修复 ObjectChart 的数据提取:

let data: any[] = [];
if (Array.isArray(results)) {
    data = results;
} else if (results && typeof results === 'object') {
    if (Array.isArray((results as any).records)) {
        data = (results as any).records;
    } else if (Array.isArray((results as any).data)) {
        data = (results as any).data;
    } else if (Array.isArray((results as any).value)) {
        data = (results as any).value;
    }
}

(2)保证聚合输出字段与 series dataKey 一致:

  • 选项 A:dashboard 的 yField 和 aggregate 的 field 保持一致(都用 amount 或 total,二选一)。
  • 选项 BaggregateRecords 支持输出定制 key(即允许 key 映射,比如 { amount } → { total }),或者 DashboardRenderer 自动适配。

(3)建议在所有数据组件中统一结果提取(见 ObjectMap 和 ObjectCalendar 的实践)。


相关文件

  • packages/plugin-charts/src/ObjectChart.tsx
  • packages/plugin-dashboard/src/DashboardRenderer.tsx
  • examples/crm/src/dashboards/crm.dashboard.ts

优先级和建议

  • 优先级:高。
  • 建议处理:修复完应补充单元测试,确保 provider: 'object' 类型 dashboard widget 都能正常渲染(建议参考 DashboardRenderer.widgetData.test.tsx 的相关用例)。
  • 开发完成后请运行所有 test,并同步 Roadmap 状态。

/cc @objectstack-ai/objectui-maintainers</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 24, 2026 1:21pm
objectui-demo Ready Ready Preview, Comment Feb 24, 2026 1:21pm
objectui-storybook Ready Ready Preview, Comment Feb 24, 2026 1:21pm

Request Review

…adapt, and config alignment

- ObjectChart: extract records from results.data and results.value formats
- DashboardRenderer: auto-adapt series dataKey from aggregate.field
- CRM dashboard: align yField to aggregate field 'amount' (was 'total')
- Add extractRecords utility with 8 tests
- Add 2 DashboardRenderer tests for aggregate auto-adaptation
- Update ROADMAP.md

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue with revenue by account chart data display fix: Revenue by Account chart renders empty due to field mismatch and incomplete data extraction Feb 24, 2026
Copilot AI requested a review from hotlong February 24, 2026 12:32
@hotlong
Copy link
Copy Markdown
Collaborator

hotlong commented Feb 24, 2026

@copilot 仔细评估是否已经完成了任务中要求的所有开发,解决的所有相关的类似的问题

…action across all 6 components

- Create shared extractRecords() utility in packages/core/src/utils/extract-records.ts
- ObjectMap: was missing results.value extraction
- ObjectCalendar: was missing results.records extraction
- ObjectGantt: was missing results.value extraction
- ObjectTimeline: was missing results.value, had inconsistent pattern
- ObjectKanban: was missing results.records extraction
- ObjectChart: re-exports from @object-ui/core for backward compatibility
- Add 8 unit tests for extractRecords in @object-ui/core
- Update ROADMAP.md

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 24, 2026

@copilot 仔细评估是否已经完成了任务中要求的所有开发,解决的所有相关的类似的问题

经过全面审查,发现 issue 第 3 点提到的"在所有数据组件中统一结果提取"确实没有完成。审计后发现 5 个组件存在同类数据提取不全的 bug:

  • ObjectMap — 缺少 results.value 提取
  • ObjectCalendar — 缺少 results.records 提取
  • ObjectGantt — 缺少 results.value 提取
  • ObjectTimeline — 缺少 results.value,且提取模式不一致
  • ObjectKanban — 缺少 results.records 提取

已在 bd65d52 中:

  1. extractRecords() 提升到 @object-ui/corepackages/core/src/utils/extract-records.ts)作为共享工具函数
  2. 全部 6 个数据组件统一使用 extractRecords()
  3. 新增 8 个 @object-ui/core 单元测试
  4. 全量测试通过:361 文件,6606 测试,0 失败

Copilot AI changed the title fix: Revenue by Account chart renders empty due to field mismatch and incomplete data extraction fix: Revenue by Account chart renders empty — unified data extraction across all components Feb 24, 2026
@hotlong hotlong marked this pull request as ready for review February 24, 2026 13:24
Copilot AI review requested due to automatic review settings February 24, 2026 13:24
@hotlong hotlong merged commit c197734 into main Feb 24, 2026
5 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes the “Revenue by Account” CRM dashboard widget rendering empty by standardizing API response record extraction and ensuring aggregated chart series keys match the aggregation output shape.

Changes:

  • Added a centralized extractRecords() utility in @object-ui/core and refactored multiple data-fetching plugins to use it.
  • Updated DashboardRenderer to derive chart series dataKey from aggregate.field when aggregation is enabled (fallback to yField otherwise), with new/updated tests.
  • Aligned the CRM dashboard config (yField/valueField) to match the aggregate field used for “Revenue by Account”, and documented the fix in ROADMAP.md.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/core/src/utils/extract-records.ts Introduces shared record extraction for common response shapes (records/data/value/array).
packages/core/src/utils/tests/extract-records.test.ts Adds unit tests for the new core extractRecords() behavior and precedence rules.
packages/core/src/index.ts Exposes extractRecords() from the core package public API.
packages/plugin-charts/src/ObjectChart.tsx Switches ObjectChart fetching to use core extractRecords() and re-exports it for backwards compatibility.
packages/plugin-charts/src/tests/ObjectChart.aggregation.test.ts Adds tests validating extractRecords behavior via the ObjectChart re-export.
packages/plugin-dashboard/src/DashboardRenderer.tsx Uses aggregate.field as effective dataKey for provider:'object' aggregated charts.
packages/plugin-dashboard/src/tests/DashboardRenderer.widgetData.test.tsx Updates expectations and adds coverage for aggregate vs non-aggregate dataKey selection.
packages/plugin-map/src/ObjectMap.tsx Replaces local response-shape handling with extractRecords().
packages/plugin-calendar/src/ObjectCalendar.tsx Replaces local response-shape handling with extractRecords().
packages/plugin-gantt/src/ObjectGantt.tsx Replaces local response-shape handling with extractRecords().
packages/plugin-timeline/src/ObjectTimeline.tsx Replaces local response-shape handling with extractRecords().
packages/plugin-kanban/src/ObjectKanban.tsx Replaces local response-shape handling with extractRecords().
examples/crm/src/dashboards/crm.dashboard.ts Aligns yField/valueField to amount to match aggregate.field.
ROADMAP.md Marks the chart bugfix work as completed and summarizes the fix scope/tests.

Comment on lines +17 to +31
export function extractRecords(results: unknown): any[] {
if (Array.isArray(results)) {
return results;
}
if (results && typeof results === 'object') {
if (Array.isArray((results as any).records)) {
return (results as any).records;
}
if (Array.isArray((results as any).data)) {
return (results as any).data;
}
if (Array.isArray((results as any).value)) {
return (results as any).value;
}
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractRecords is introduced in @object-ui/core but uses any[] and multiple (results as any) casts, which triggers the repo’s @typescript-eslint/no-explicit-any warnings and weakens type safety for all consumers. Consider making it generic (e.g., <T = unknown>(): T[]) and narrowing results to a typed shape like { records?: unknown; data?: unknown; value?: unknown } to avoid any while keeping the same runtime behavior.

Suggested change
export function extractRecords(results: unknown): any[] {
if (Array.isArray(results)) {
return results;
}
if (results && typeof results === 'object') {
if (Array.isArray((results as any).records)) {
return (results as any).records;
}
if (Array.isArray((results as any).data)) {
return (results as any).data;
}
if (Array.isArray((results as any).value)) {
return (results as any).value;
}
}
interface RecordsContainer {
records?: unknown;
data?: unknown;
value?: unknown;
}
export function extractRecords<T = unknown>(results: unknown): T[] {
if (Array.isArray(results)) {
return results as T[];
}
if (results && typeof results === 'object') {
const container = results as RecordsContainer;
if (Array.isArray(container.records)) {
return container.records as T[];
}
if (Array.isArray(container.data)) {
return container.data as T[];
}
if (Array.isArray(container.value)) {
return container.value as T[];
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Revenue by Account 图表不显示数据——聚合字段和数据结构不匹配,数据提取不全

3 participants