Skip to content

Fix dashboard to not display hardcoded data on server errors#1152

Merged
hotlong merged 5 commits intomainfrom
copilot/fix-dashboard-static-data-display
Mar 31, 2026
Merged

Fix dashboard to not display hardcoded data on server errors#1152
hotlong merged 5 commits intomainfrom
copilot/fix-dashboard-static-data-display

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 31, 2026

When the analytics API (/api/v1/analytics/query) returns errors (e.g. "Cube name is required"), dashboard widgets previously continued displaying hardcoded static values, masking real data failures. This PR implements proper three-state rendering (Loading → Error → Data/Fallback) across all dashboard widget types.

Changes Made

ObjectChart (@object-ui/plugin-charts)

  • Added error state tracking via useState. When dataSource.aggregate() or dataSource.find() fails, the chart now renders a prominent error alert with a red AlertCircle icon and the error message, instead of silently swallowing errors.
  • Added data-testid attributes for loading, error, and no-datasource states.
  • Added lucide-react as a dependency for the AlertCircle icon.

MetricWidget / MetricCard (@object-ui/plugin-dashboard)

  • Added loading and error props to both components. When loading is true, a spinner is shown. When error is set, a destructive-colored error message is displayed instead of the metric value. The label/title remains visible in all states.

ObjectMetricWidget (new component, @object-ui/plugin-dashboard)

  • New data-bound metric widget that fetches live values from the server via dataSource.aggregate() or dataSource.find().
  • Shows explicit loading/error states when data fetching is in progress or fails.
  • Falls back to a static fallbackValue only when no dataSource is available (demo/storybook mode).
  • Resets fetchedValue and error state when dataSource becomes unavailable or changes, preventing stale server data from masking fallback values.
  • Registered as object-metric in the ComponentRegistry.

DashboardRenderer (@object-ui/plugin-dashboard)

  • Metric widgets with widget.object binding are now routed to ObjectMetricWidget (object-metric type) for async data loading with proper error/loading states, instead of always rendering static hardcoded values.
  • Static-only metric widgets (no object binding) continue to work as before.
  • Metric aggregation groupBy defaults to _all (single bucket) instead of name, so shorthand aggregate configs return correct single-value results for metric widgets.

CRM Dashboard Example

  • Documented that options.value fields in metric widgets are demo/fallback values that only display when no dataSource is connected.

CHANGELOG

  • Updated CHANGELOG.md with all dashboard error state improvements.

Testing

  • ✅ 210 tests passing across 15 test files in @object-ui/plugin-dashboard
  • ✅ 14 new tests covering ObjectMetricWidget (error states, loading, fallback, aggregate), MetricCard (loading/error props), DashboardRenderer (object-metric routing, static metric passthrough, error state with failing dataSource)
  • ✅ CodeQL security scan passes with 0 alerts
  • ✅ No regressions in existing tests

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

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

Project Deployment Actions Updated (UTC)
objectui-demo Ready Ready Preview, Comment Mar 31, 2026 5:44am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Mar 31, 2026 5:44am
objectui-storybook Ignored Ignored Mar 31, 2026 5:44am

Request Review

Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/27c667be-572a-4498-9b2d-2e9a96ab381a

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

@copilot 继续

- Fix DashboardRenderer.widgetData tests to verify rendered output instead
  of relying on getRenderedSchemas (object-metric is registered, so it
  renders directly rather than falling through to <pre> JSON dump)
- Remove debug_metric.test.tsx that was accidentally committed
- Update CHANGELOG.md documenting all dashboard error state improvements

Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/2c052023-f69e-4edb-a3a1-c4f4394c4b70

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 31, 2026

@copilot 继续

All checklist items are now complete in commit 2ae211a:

Changes made:

  • ObjectChart: Now tracks and displays error state when aggregate()/find() fails, instead of silently swallowing errors
  • MetricWidget / MetricCard: Added loading and error props for explicit loading/error state display
  • ObjectMetricWidget (new): Data-bound metric that fetches live data from server; shows error state on API failure; falls back to static fallbackValue only when no dataSource is available
  • DashboardRenderer: Metric widgets with widget.object binding are routed to ObjectMetricWidget for async data loading with proper error states
  • CRM dashboard example: Documented that options.value fields are demo/fallback values
  • 13 new tests covering error states, loading states, fallback behavior, and routing
  • CHANGELOG.md updated

All 209 tests pass across 15 test files.

Copilot AI changed the title [WIP] Fix dashboard to not display hardcoded data on server errors Fix dashboard to not display hardcoded data on server errors Mar 31, 2026
Copilot AI requested a review from xuyushun441-sys March 31, 2026 05:12
@hotlong
Copy link
Copy Markdown
Contributor

hotlong commented Mar 31, 2026

@copilot 13:10:38.160 �[31merror during build:
13:10:38.160 �[31mBuild failed with 1 error:
13:10:38.160 Error: [vite]: Rolldown failed to resolve import "lucide-react" from "/vercel/path0/packages/plugin-charts/src/ObjectChart.tsx".
13:10:38.213 Error: Command "pnpm msw:init && NODE_OPTIONS=--max-old-space-size=4096 VITE_BASE_PATH=/ VITE_USE_MOCK_SERVER=true vite build" exited with 1

The ObjectChart error state UI uses AlertCircle from lucide-react, but
the package was not listed as a dependency. This caused the Vercel build
to fail with: "Rolldown failed to resolve import lucide-react".

Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/049d7802-348f-46aa-8587-3d6e7f67f8ae

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

Copilot AI commented Mar 31, 2026

@copilot 13:10:38.160 �[31merror during build:
13:10:38.160 �[31mBuild failed with 1 error:
13:10:38.160 Error: [vite]: Rolldown failed to resolve import "lucide-react" from "/vercel/path0/packages/pl...

Fixed in 930d802. Added lucide-react: ^0.577.0 to @object-ui/plugin-charts dependencies. The AlertCircle icon import was added for the ObjectChart error state UI but the package wasn't listed as a dependency, causing the Vite/Rolldown build to fail.

@hotlong hotlong marked this pull request as ready for review March 31, 2026 05:27
Copilot AI review requested due to automatic review settings March 31, 2026 05:27
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

This PR ensures dashboard widgets stop masking server/API failures by introducing consistent three-state rendering (Loading → Error → Data/Fallback) for data-bound charts and metrics, so dashboards no longer display misleading hardcoded/static values when live queries fail.

Changes:

  • Added async, data-bound ObjectMetricWidget and routed object-bound metric widgets to it from DashboardRenderer.
  • Extended MetricWidget and MetricCard with loading/error states and updated ObjectChart to surface fetch errors with explicit UI.
  • Added/updated tests and documentation notes; updated dependencies and changelog.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks lucide-react addition for workspace dependency resolution.
packages/plugin-dashboard/src/ObjectMetricWidget.tsx New data-bound metric widget with loading/error/fallback behavior.
packages/plugin-dashboard/src/MetricWidget.tsx Adds loading + error rendering paths (spinner / alert) for metrics.
packages/plugin-dashboard/src/MetricCard.tsx Adds loading + error rendering paths for metric cards.
packages/plugin-dashboard/src/index.tsx Exports and registers object-metric in the component registry.
packages/plugin-dashboard/src/DashboardRenderer.tsx Routes object-bound metric widgets to object-metric and passes fallback props.
packages/plugin-dashboard/src/tests/ObjectMetricWidget.test.tsx New unit tests for ObjectMetricWidget loading/error/fallback/aggregate behavior.
packages/plugin-dashboard/src/tests/MetricCard.test.tsx Adds tests for MetricCard loading/error props behavior.
packages/plugin-dashboard/src/tests/DashboardRenderer.widgetData.test.tsx Adds tests around metric routing behavior for object-bound vs static metrics.
packages/plugin-charts/src/ObjectChart.tsx Adds chart fetch error state + test ids; stops swallowing fetch errors.
packages/plugin-charts/package.json Adds lucide-react dependency for chart error icon usage.
examples/crm/src/dashboards/crm.dashboard.ts Documents that metric options.value is fallback/demo-only when no dataSource.
CHANGELOG.md Documents dashboard error-state improvements and new widget behavior.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +138 to +144
groupBy: widget.categoryField || widgetData.aggregate.groupBy,
}
: widget.aggregate ? {
field: widget.valueField || 'value',
function: widget.aggregate,
groupBy: widget.categoryField || 'name',
} : undefined;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

For object-bound metric widgets using the widget.aggregate shorthand, groupBy defaults to 'name'. That will typically return multiple groups, but ObjectMetricWidget displays only the first row for non-count aggregates, yielding incorrect metrics. Consider defaulting metric aggregations to a single group (e.g., '_all') unless the user explicitly provides categoryField/groupBy.

Suggested change
groupBy: widget.categoryField || widgetData.aggregate.groupBy,
}
: widget.aggregate ? {
field: widget.valueField || 'value',
function: widget.aggregate,
groupBy: widget.categoryField || 'name',
} : undefined;
// Prefer explicit categoryField or aggregate.groupBy; otherwise, default to a single bucket.
groupBy: widget.categoryField ?? widgetData.aggregate.groupBy ?? '_all',
}
: widget.aggregate
? {
field: widget.valueField || 'value',
function: widget.aggregate,
// Default to a single group unless the user explicitly configures a categoryField.
groupBy: widget.categoryField || '_all',
}
: undefined;

Copilot uses AI. Check for mistakes.
Comment on lines +138 to +140
const displayValue = fetchedValue !== null
? fetchedValue
: (!dataSource ? (fallbackValue ?? '—') : '—');
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

If dataSource becomes unavailable (or changes) after a successful fetch, fetchedValue stays non-null and displayValue will keep showing the stale server value instead of falling back to fallbackValue/placeholder. Consider resetting fetchedValue (and possibly error) when dataSource is falsy or changes.

Copilot uses AI. Check for mistakes.
Comment on lines +1289 to +1293
it('should route metric widgets with object binding to object-metric (renders fallback without dataSource)', () => {
const schema = {
type: 'dashboard' as const,
name: 'test',
title: 'Test',
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

This test only asserts label/value rendering without a dataSource, which would also pass with the legacy metric rendering (pre-change), so it doesn’t verify routing to object-metric or the new error behavior. Consider adding an assertion using a failing dataSource (via SchemaRendererProvider) and checking for the error state (e.g., data-testid="metric-error") and absence of the fallback value.

Copilot uses AI. Check for mistakes.
@xuyushun441-sys
Copy link
Copy Markdown
Contributor

@copilot fix all

1. Default metric aggregation groupBy to '_all' instead of 'name' for
   single-bucket results in DashboardRenderer.
2. Reset fetchedValue/error in ObjectMetricWidget when dataSource becomes
   unavailable, preventing stale server data from masking fallback values.
3. Add test with failing dataSource to verify error state routing in
   DashboardRenderer object-metric path.

Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/7a05b366-541b-4772-8f1f-91bcb9a25ff7

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 31, 2026

@copilot fix all

All 3 review comments addressed in fd8f2f6:

  1. groupBy default: Changed from 'name' to '_all' for both aggregate paths in DashboardRenderer, so metric aggregations return a single bucket by default.
  2. Stale fetchedValue: ObjectMetricWidget now resets fetchedValue and error when dataSource becomes unavailable, falling back to fallbackValue instead of stale data.
  3. Error state test: Added test with a failing dataSource (aggregate rejects with "Cube name is required") that verifies error UI is shown and fallback value $999 is NOT displayed.

All 210 tests pass, CodeQL clean.

@hotlong hotlong merged commit fdd42ec into main Mar 31, 2026
4 checks passed
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.

Dashboard 页面无服务端数据时仍显示硬编码数据,导致诊断与体验问题

4 participants