Skip to content

feat(scorecard): add homepage sparkline charts for time-series metrics - #4596

Open
Eswaraiahsapram wants to merge 8 commits into
redhat-developer:mainfrom
Eswaraiahsapram:feat/scorecard-homepage-sparkline-cards-ui
Open

Eswaraiahsapram wants to merge 8 commits into
redhat-developer:mainfrom
Eswaraiahsapram:feat/scorecard-homepage-sparkline-cards-ui

Conversation

@Eswaraiahsapram

@Eswaraiahsapram Eswaraiahsapram commented Sep 7, 2026

Copy link
Copy Markdown
Member

Hey, I just made a Pull Request!

Follows #4573 (merged). Reuses the shared sparkline chart, legend, and time-series utilities from that PR on the Scorecard homepage.

What

Adds sparkline visualization on Scorecard homepage cards when aggregation metadata visualization is sparkline (for example DORA). Each card shows a 30-day trend and a threshold legend. Legend and line color come from the time-series threshold rules.

Screenshots

Screen.Recording.2026-09-17.at.1.49.33.PM.mov

What changed

Homepage rendering

  • ScorecardHomepageCard loads GET /aggregations/:aggregationId/metadata first. When metadata.visualization is sparkline, it fetches the aggregation time series instead of the snapshot and renders AggregatedSparklineCard. Other aggregations keep the existing pie, donut, and scalar cards.
  • AggregatedSparklineCard — homepage sparkline card using the shared SparklineChart / toSparklineChartModel path from feat(scorecard): add entity-page sparkline charts for time-series metrics #4573, including entity subheader (success count / total) and last-synced info.

New API hook

  • useAggregationTimeSeriesuseQuery hook for the default 30-day aggregation time series (GET /aggregations/:aggregationId/time-series).

Homepage widgets

Registers NFS and legacy homepage widgets for:

  • Average deployment frequency (avgDeploymentFrequency)
  • Average change failure rate (avgChangeFailureRate)
  • Average median lead time for changes (avgMedianLeadTimeForChanges)

Docs

  • README documents the metadata → time-series → AggregatedSparklineCard path for sparkline aggregations.

How to test

  1. Enable DORA (or any scalar aggregation whose metadata visualization is sparkline) and add a homepage Scorecard card with aggregationId such as avgDeploymentFrequency.
  2. Open the homepage and confirm the card shows a 30-day sparkline with a threshold legend.
  3. Confirm line color matches the latest successful point's threshold rule.
  4. Confirm non-sparkline homepage cards (GitHub open PRs, Jira, filecheck, weighted health) are unchanged.
  5. With no time-series points, confirm the existing empty state is shown instead of a chart.

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

@rhdh-gh-app

rhdh-gh-app Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-scorecard-backend

See CONTRIBUTING.md for more information about how to add changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app-legacy workspaces/scorecard/packages/app-legacy none v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard-backend workspaces/scorecard/plugins/scorecard-backend none v4.3.1
@red-hat-developer-hub/backstage-plugin-scorecard workspaces/scorecard/plugins/scorecard minor v4.3.1

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:04 AM UTC · Completed 9:11 AM UTC

Commit: 672f711 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $1.99

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.87755% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.64%. Comparing base (ef6ab36) to head (90374b3).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4596      +/-   ##
==========================================
+ Coverage   63.60%   63.64%   +0.03%     
==========================================
  Files        2685     2688       +3     
  Lines      107077   107214     +137     
  Branches    30010    30070      +60     
==========================================
+ Hits        68101    68231     +130     
- Misses      37165    37172       +7     
  Partials     1811     1811              
Flag Coverage Δ *Carryforward flag
adoption-insights 84.77% <ø> (ø) Carriedforward from 24d9955
ai-integrations 82.63% <ø> (ø) Carriedforward from 24d9955
app-defaults 54.82% <ø> (ø) Carriedforward from 24d9955
augment 46.67% <ø> (ø) Carriedforward from 24d9955
boost 84.97% <ø> (ø) Carriedforward from 24d9955
bulk-import 73.12% <ø> (ø) Carriedforward from 24d9955
cost-management 13.53% <ø> (ø) Carriedforward from 24d9955
dcm 73.47% <ø> (ø) Carriedforward from 24d9955
e2e-adoption-insights 60.00% <ø> (ø) Carriedforward from 24d9955
e2e-extensions 62.31% <ø> (ø) Carriedforward from 24d9955
e2e-global-header 51.82% <ø> (ø) Carriedforward from 24d9955
e2e-homepage 61.11% <ø> (ø) Carriedforward from 24d9955
e2e-intelligent-assistant 46.01% <ø> (ø) Carriedforward from 24d9955
e2e-orchestrator 49.49% <ø> (ø) Carriedforward from 24d9955
e2e-orchestrator-plugin 49.48% <ø> (ø) Carriedforward from 24d9955
e2e-quickstart 55.21% <ø> (ø) Carriedforward from 24d9955
e2e-scorecard 49.83% <ø> (-0.17%) ⬇️ Carriedforward from 24d9955
e2e-theme 16.36% <ø> (ø) Carriedforward from 24d9955
extensions 58.30% <ø> (ø) Carriedforward from 24d9955
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 24d9955
global-header 67.76% <ø> (ø) Carriedforward from 24d9955
homepage 55.16% <ø> (ø) Carriedforward from 24d9955
install-dynamic-plugins 71.77% <ø> (ø) Carriedforward from 24d9955
intelligent-assistant 78.04% <ø> (ø) Carriedforward from 24d9955
konflux 91.98% <ø> (ø) Carriedforward from 24d9955
lightspeed 69.02% <ø> (ø) Carriedforward from 24d9955
mcp-integrations 84.46% <ø> (ø) Carriedforward from 24d9955
orchestrator 77.69% <ø> (ø) Carriedforward from 24d9955
quickstart 63.74% <ø> (ø) Carriedforward from 24d9955
sandbox 79.56% <ø> (ø) Carriedforward from 24d9955
scorecard 88.91% <93.87%> (+0.20%) ⬆️
theme 87.94% <ø> (ø) Carriedforward from 24d9955
translations 5.12% <ø> (ø) Carriedforward from 24d9955
x2a 78.44% <ø> (ø) Carriedforward from 24d9955

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ef6ab36...90374b3. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fullsend-ai-review

Copy link
Copy Markdown

Review — Approve

PR: #4596 — Feat/scorecard homepage sparkline cards UI
Scope: workspaces/scorecard (67 files, +5463/−348)

Summary

This PR adds sparkline chart support to the scorecard plugin, enabling time-series visualization for DORA metrics on both entity pages and the homepage. The implementation is well-structured with clean separation across API client methods, React hooks, reusable chart components, and shared utility functions.

Key additions:

  • Three new API client methods (getAggregationTimeSeries, getMetricTimeSeries, getMetricCollectors) with input validation and response shape checking
  • Three new React hooks (useAggregationTimeSeries, useMetricTimeSeries, useMetricCollectors) with proper enabled gating
  • SparklineChart component using Recharts AreaChart with gradient fill, custom tooltip, error-point markers, and threshold legend
  • AggregatedSparklineCard for homepage sparkline KPIs and EntitySparklineCard for entity-page metrics
  • EntityMetricCard shared component that routes to sparkline or donut card based on defaultVisualization
  • ScorecardHomepageCard now loads aggregation metadata first and branches on visualization === 'sparkline' to fetch time-series data instead of snapshot data
  • DataSourcesDialog refactored to accept pre-computed SourceRow[] and optional buckets, enabling reuse for collector-based data sources
  • Three new homepage widgets (deployment frequency, change failure rate, median lead time)
  • Translations for 6 languages (en, de, es, fr, it, ja)
  • Two changesets (both minor, appropriate for new features)

Findings

No critical, high, or medium-severity issues found.

Low-severity observations

  1. Inconsistent URL path encoding (src/api/index.ts) — getMetricCollectors uses encodeURIComponent(metricId) for the URL path segment, but getAggregationTimeSeries interpolates aggregationId directly and getMetricTimeSeries interpolates entity kind/namespace/name directly. While current IDs are alphanumeric and safe, encoding should be consistent across all methods for defense in depth.

  2. Empty group behavior change (ScorecardEntityContentGridView.tsx) — The if (metricsInOrder.length > 0) guard around groupedMetrics.set() was removed. Empty groups are now added to the map but are correctly filtered out during rendering (if (metricsInGroup.length === 0) return null), so this is functionally equivalent. The simplification is fine.

  3. PR checklist — All checklist items are unchecked despite the PR including changesets, updated documentation (README), and comprehensive tests. No screenshots are attached for the new sparkline UI (checklist item 4).

Assessment

The code quality is high throughout. Specific strengths:

  • Error handling: Loading, error, and empty states are handled consistently across all new components and hooks. The useMetricCollectors hook correctly suppresses loading/error state when disabled.
  • Test coverage: Extensive — API client methods, hooks, components, and utility functions all have dedicated test suites with edge-case coverage (null values, empty arrays, error points, interpolation).
  • Architecture: The toSparklineChartModel view-model pattern cleanly separates data transformation from rendering. The DataSourcesDialog refactoring properly decouples row computation from the dialog component.
  • Backward compatibility: The ScorecardHomepageCard gracefully handles the deprecated metricId prop path and gates the new metadata/time-series fetches behind hasAggregationId.
  • i18n: All user-visible strings use the translation framework. Collector-specific translations are added consistently across all 6 language files.

Note: This PR declares a dependency on PR #4573 and has the do-not-merge/hold label.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Sep 7, 2026
@Eswaraiahsapram Eswaraiahsapram changed the title Feat/scorecard homepage sparkline cards UI feat(scorecard): add homepage sparkline charts for time-series metrics Sep 8, 2026
@Eswaraiahsapram
Eswaraiahsapram force-pushed the feat/scorecard-homepage-sparkline-cards-ui branch 2 times, most recently from 2d8ea7d to fb15906 Compare September 17, 2026 07:37

@dzemanov dzemanov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for the PR.
Looks good, just a couple of quick comments:

Comment thread workspaces/scorecard/plugins/scorecard/src/extensions/homePageCards.tsx Outdated
Comment thread workspaces/scorecard/packages/app-legacy/src/App.tsx Outdated
- `AggregatedCardWithTotalOpenBugs` (title: **Scorecard: Total open bugs**)
- `AggregatedCardWithEntitiesWithOpenPrs` (title: **Scorecard: Entities with open PRs**)
- `AggregatedCardWithAvgOpenPrs` (title: **Scorecard: Average open PRs**)
- `AggregatedCardWithAvgDeploymentFrequency` (title: **Scorecard: Average deployment frequency**)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When you will add the last DORA metric for MedianTimeToRestore, please update also docs, thank you!

components: lazyScorecardWidget(
ScorecardHomepageCardWithProvider => () =>
(
<ScorecardHomepageCardWithProvider aggregationId="avgChangeFailureRate" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When testing with default configuration, I get:

Screen.Recording.2026-09-17.at.11.23.11.mov

All avg dora widgets except for deployment frequency are always loading.

This is because default app-config configuration is missing all avg dora configurations except for avgDeploymentFrequency: https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/scorecard/app-config.yaml#L373

I am not familiar much with NFS blueprints in RHDH. If we are providing only these blueprint widgets, this means that in NFS for example for filecheck, users are unable to create aggregation cards except for checking for codeowners and license file because that is the only configuration we provide here and here in blueprints?
This also means if they forget to add that key to their app-config configuration, that they are always in load state?

It would be good to fix that if metadata API returns not found, widgets are not in always loading state.

Going back to dora, maybe it makes sense to provide at least 1 dora metric with Deprecated metric id (at least for legacy - so we can easier test it is working) - so for example dora.changeFailureRate. We can keep avgDeploymentFrequency as we already have it in app-config. But maybe we really want to provide all 4 dora metrics also in blueprints with deprecated metric id, as otherwise users will need to have them configured in their app-config?

For aggregationId, I am thinking we might want to make the ids general? So instead of avgChangeFailureRate we have 'changeFailureRateKpi' similiar to openIssuesKpi? Just because you can specify different type: sum etc in your scorecard configuration (avg is default), what do you think? Using custom aggregationId instead of deprecated metric id means you will need to add all those configurations to default app config.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks @dzemanov.

  1. Default configurations: I'll add the missing KPI config to the default app-config.

  2. NFS blueprints: The available homepage cards depend on the blueprints we provide. Users cannot create new widget with custom aggregation IDs unless we/they provide a blueprint widget. Custom KPI IDs also require matching app-config entries

  3. I'll add at least one DORA widget using deprecated metricId to test

  4. Aggregation ID naming: I agree that avgChangeFailureRate is not a good name because the aggregation type can be changed. I'll use names like changeFailureRateKpi and deploymentFrequencyKpi, following the existing openIssuesKpi pattern.

@Eswaraiahsapram
Eswaraiahsapram force-pushed the feat/scorecard-homepage-sparkline-cards-ui branch from c0284dd to fbbff9d Compare September 17, 2026 10:01
Comment thread workspaces/scorecard/plugins/scorecard/src/extensions/homePageCards.tsx Outdated
@Eswaraiahsapram
Eswaraiahsapram force-pushed the feat/scorecard-homepage-sparkline-cards-ui branch from fbbff9d to 8a7bac9 Compare September 17, 2026 15:58

@dzemanov dzemanov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Retested,

default configuration works as expected.

No configuration of aggregation kpi is no longer loading infinitely:

Image

It doesn't look nice, but is in line with how other errors are shown, thanks for the update.

I don't know if this is as a result of fixing loading, but first time when I add scorecard, I get auth error. When I reload, everything works.

Screen.Recording.2026-09-17.at.22.04.47.mov

No data works fine:

Image

Single points:

Image Drill down with single points: Image

2 points:

Image

Different values:

Screen.Recording.2026-09-17.at.21.30.26.mov

Translation:

Screen.Recording.2026-09-17.at.21.37.03.mov

Just wanted to mention, title and description translations are currently unused for
dora aggregation KPIs in NFS blueprints (and also all other KPIs that don't use as aggregation id metric id), as the app-config always overrides
them (this is expected behavior as user configured those). For a future release, we could explore removing the need for the app-config configuration by adding blueprints that use aggregationId=metricId and using as aggregation thresholds by default metric-defined thresholds instead of DEFAULT_NUMBER_THRESHOLDS.

Would be good to truncate the values for graphs:

Image

@Eswaraiahsapram, adding current value is to be done within a different ticket?

Design:

Image

View datasources settings are missing as well for home page, can be added in a different PR as well.

Comment thread workspaces/scorecard/plugins/scorecard/src/extensions/homePageCards.tsx Outdated
Comment thread workspaces/scorecard/plugins/scorecard/src/extensions/homePageCards.tsx Outdated
Comment thread workspaces/scorecard/app-config.yaml Outdated
Comment thread workspaces/scorecard/app-config.yaml Outdated
Comment thread workspaces/scorecard/app-config.yaml Outdated
Comment thread workspaces/scorecard/packages/app-legacy/src/App.tsx Outdated
Comment on lines +57 to +60
const matchingThresholdKey = chartColorToken
? thresholdRules?.find(
rule =>
getThresholdRuleColor(thresholdRules, rule.key) === chartColorToken,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah, this reverse engineering of matched threshold by color to get stroke is not great. But backend doesn't provide it so thank you for getting around this. We can fix that in future if needed.

Comment thread workspaces/scorecard/app-config.yaml Outdated

@dzemanov dzemanov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Permissions work fine:

Screen.Recording.2026-09-17.at.22.10.02.mov

@Eswaraiahsapram
Eswaraiahsapram force-pushed the feat/scorecard-homepage-sparkline-cards-ui branch from 70c4be9 to 2d6f7e8 Compare September 18, 2026 04:20
@sonarqubecloud

Copy link
Copy Markdown

@Eswaraiahsapram

Copy link
Copy Markdown
Member Author

Thanks a lot @dzemanov for the feedback.

It doesn't look nice, but is in line with how other errors are shown, thanks for the update.

Thanks. We have a jira bug for similar issue - https://redhat.atlassian.net/browse/RHDHBUGS-3263

I don't know if this is as a result of fixing loading, but first time when I add scorecard, I get auth error. When I reload, everything works.

I haven't been able to reproduce this locally. Could you please share the steps? If it still reproduces, I’m happy to file a follow-up bug.

Screen.Recording.2026-09-18.at.10.23.26.AM.mov

Would be good to truncate the values for graphs

Fixed - graph values are truncated to 4 decimal places.

@Eswaraiahsapram, adding current value is to be done within a different ticket?

Thanks for flagging this. I’ve added the current value in this PR on the drill-down page. Homepage cards still show only the chart and legend.

View datasources settings are missing as well for home page, can be added in a different PR as well.

Agreed, we'll pick this up in a follow-up PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-merge All reviewers approved — ready to merge workspace/scorecard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants