-
-
Notifications
You must be signed in to change notification settings - Fork 280
Implement charts for health metrics #1633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement charts for health metrics #1633
Conversation
Summary by CodeRabbit
Summary by CodeRabbit
WalkthroughThis change introduces a comprehensive project health visualization section to the Project page. It adds new backend fields and properties for health requirements, extends the frontend data model, integrates ApexCharts for chart rendering, implements reusable chart components, updates the UI to display health metrics and trends, and includes new unit and end-to-end tests. Changes
Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNo out-of-scope changes found. Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (3)
✨ Finishing Touches
🧪 Generate Unit Tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
frontend/src/types/card.ts (1)
41-42: Synchronise prop name with callerIf you adopt the earlier renaming suggestion, remember to rename the prop here:
- healthMetricsData?: HealthMetricsProps[] + healthMetrics?: HealthMetricsProps[]
🧹 Nitpick comments (13)
frontend/src/app/projects/[projectKey]/page.tsx (2)
91-91: Keep TODOs actionable or create an issueThe inline TODO is easy to forget once the PR is merged. Prefer converting this into a GitHub issue (referencing the line) or tracking it in a ticket so it does not slip through the cracks.
93-99: Prop-name drift between caller and calleeYou pass
healthMetricsData={project.healthMetrics}, whereas the parent object useshealthMetricsand the type inProjectmirrors that. Maintaining two names for the same payload (healthMetricsvs.healthMetricsData) increases cognitive load and can lead to accidental typos.Consider normalising on a single prop/property name (
healthMetrics) throughout:- healthMetricsData={project.healthMetrics} + healthMetrics={project.healthMetrics}and update
DetailsCardPropsaccordingly.frontend/src/types/project.ts (1)
22-23: Make the collection read-onlyHealth metrics should be an immutable snapshot for the UI layer. Declare the property as a read-only tuple to prevent accidental in-place mutation by consumer components:
- healthMetrics?: HealthMetricsProps[] + readonly healthMetrics?: readonly HealthMetricsProps[]Helps TypeScript narrow immutability errors early.
backend/tests/apps/owasp/graphql/nodes/project_test.py (1)
47-51: Assertion could missListwrapper
field.type.of_typeworks whentypeisList[ProjectHealthMetricsNode]; if the schema later changes to a non-list field, this test will silently pass. A stricter assertion:assert getattr(field.type, "of_type", None) is ProjectHealthMetricsNode assert getattr(field.type, "_of_type", None) is None # guards against nested listsor use Strawberry’s
is_list_of_type.frontend/src/components/CardDetailsPage.tsx (2)
17-17: Consider lazy-loading heavy chart bundle
HealthMetricsChartslikely pulls in ApexCharts (~100 kB). Importing it eagerly increases the initial JS payload for every details page, even those that are not projects. A simpleReact.lazy/ dynamicimport()keeps non-project pages lighter.-import HealthMetricsCharts from 'components/HealthMetricsCharts' +const HealthMetricsCharts = React.lazy( + () => import('components/HealthMetricsCharts'), +)Remember to wrap the usage in
<Suspense>(or your app’s equivalent).
35-36: Prop should be optional in the component signature
healthMetricsDatais only relevant for projects. Make it optional (healthMetricsData?: …) inDetailsCardPropsto avoid forcing callers (e.g. chapter pages) to passundefined.backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py (2)
11-11: Magic number updated – assert the calculation insteadHard-coding
EXPECTED_SCORE = 34.0means every future tweak to the scoring formula will require manual test edits. Prefer deriving the expected score fromfields_weightsto keep the test self-maintaining.
76-81: Assertion covers onlyscorefield – include side-effectsYou assert
bulk_savecall and the finalscore. Consider also asserting thatmock_metric.updated_at(or similar timestamp) was modified if the command is expected to bump it, ensuring the full behaviour is protected.backend/apps/owasp/migrations/0041_merge_20250613_0336.py (1)
1-13: Silence Pylint false-positive for “too few public methods”Merge migrations are intentionally empty, but Pylint still complains.
A terse pragma keeps CI green without affecting Django:from django.db import migrations class Migration(migrations.Migration): + # pylint: disable=too-few-public-methodsfrontend/src/components/HealthMetricsCharts.tsx (2)
14-17: Reverse the dataset to chart chronological order
ProjectNode.health_metricsreturns records ordered descending (-nest_created_at).
When you map directly, Day 1 represents the most recent day and the chart runs right-to-left, which is counter-intuitive.-const openIssuesCountArray = data.map((item) => item.openIssuesCount) -const labels = data.map((item, index) => `Day ${index + 1}`) +const chronological = [...data].reverse() // oldest → newest +const openIssuesCountArray = chronological.map((i) => i.openIssuesCount) +const labels = chronological.map((_, idx) => `Day ${idx + 1}`)Optionally wrap those computations in
useMemoto avoid recalculation on each render.
86-99: Radial charts: one series per chart, not N seriesApexCharts’
radialBarexpectsseries: number[], one numeric value per radial bar.
Passing an array of many values causes a multi-series radial (concentric rings) which may not be what you want for “days since last commit / release”.If the intent is to display only the latest value, do:
-series={data.map((item) => item.lastCommitDays)} -labels={labels} +series={[chronological.at(-1)?.lastCommitDays ?? 0]} +labels={['Today']}Adjust similarly for
lastReleaseDays.backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py (1)
36-44: Dependence on Strawberry internals is brittleThe tests reach into
__strawberry_definition__, an internal implementation detail that may change across Strawberry releases.
A safer approach is to validate the schema via Strawberry’s public API or GraphQL introspection:schema = strawberry.Schema(query=Query) introspection = schema._schema # or use graphql-core introspectionThis keeps the test future-proof.
frontend/src/components/GeneralCharts.tsx (1)
20-28: Explicitly set the charttypeto avoid silent behavioural changesApexCharts currently defaults to
line, but being explicit guards against future default changes and improves readability.- <Chart + <Chart + type="line" options={{ xaxis: { categories: labels, }, }} series={series} height={450} />
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
backend/apps/owasp/graphql/nodes/project.py(2 hunks)backend/apps/owasp/graphql/nodes/project_health_metrics.py(1 hunks)backend/apps/owasp/management/commands/owasp_update_project_health_metrics_scores.py(1 hunks)backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py(1 hunks)backend/apps/owasp/migrations/0041_merge_20250613_0336.py(1 hunks)backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py(1 hunks)backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py(1 hunks)backend/apps/owasp/migrations/0044_merge_20250615_0346.py(1 hunks)backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py(1 hunks)backend/apps/owasp/models/project_health_metrics.py(1 hunks)backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py(1 hunks)backend/tests/apps/owasp/graphql/nodes/project_test.py(2 hunks)backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py(3 hunks)cspell/custom-dict.txt(1 hunks)frontend/package.json(2 hunks)frontend/src/app/projects/[projectKey]/page.tsx(1 hunks)frontend/src/components/CardDetailsPage.tsx(3 hunks)frontend/src/components/GeneralCharts.tsx(1 hunks)frontend/src/components/HealthMetricsCharts.tsx(1 hunks)frontend/src/server/queries/projectQueries.ts(1 hunks)frontend/src/types/card.ts(2 hunks)frontend/src/types/healthMetrics.ts(1 hunks)frontend/src/types/project.ts(2 hunks)
🧰 Additional context used
🪛 Pylint (3.3.7)
backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
backend/apps/owasp/migrations/0041_merge_20250613_0336.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
backend/apps/owasp/migrations/0044_merge_20250615_0346.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
backend/tests/apps/owasp/graphql/nodes/project_health_metrics_test.py
[error] 14-14: Class 'ProjectHealthMetricsNode' has no 'strawberry_definition' member
(E1101)
[error] 40-40: Class 'ProjectHealthMetricsNode' has no 'strawberry_definition' member
(E1101)
backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py
[refactor] 6-6: Too few public methods (0/2)
(R0903)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run backend tests
- GitHub Check: Run frontend unit tests
- GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (14)
cspell/custom-dict.txt (1)
34-34: Add apexcharts to custom dictionary
This inclusion ensures the spell checker recognizes the new charting library name without false positives.frontend/package.json (1)
39-39: Include ApexCharts dependencies
Addingapexchartsand its React wrapper independenciescorrectly equips the frontend for rendering health metric charts.Also applies to: 58-58
backend/apps/owasp/migrations/0038_projecthealthmetrics_has_long_open_issues_and_more.py (1)
12-31: Skip review on auto-generated migration
This migration file is Django-generated schema change code; no manual edits needed.backend/apps/owasp/models/project_health_metrics.py (1)
26-26: Skip trivial formatting change
The added blank line afterforks_countis a harmless style adjustment.backend/apps/owasp/management/commands/owasp_update_project_health_metrics_scores.py (1)
63-68: Approve refactoredbulk_savecall formatting
The multi-line argument format enhances readability without altering behavior.frontend/src/types/project.ts (1)
2-3: Import location looks off
HealthMetricsPropslives intypes/healthMetrics, but other domain-level types (Issue, Release, …) live in sibling directories. IfhealthMetrics.tsended up in a different folder by mistake, move it next to the othertypesto keep project structure flat and predictable.frontend/src/types/card.ts (1)
6-7: Circular dependency risk
Cardtypes now pullHealthMetricsProps, which itself likely references project-level concepts. Watch out for circular type imports (types/healthMetrics→types/project→types/card). If you notice slow TS compile or weird “circular structure” warnings, extract shared primitives to a new module (e.g.types/metrics).backend/apps/owasp/migrations/0045_remove_projecthealthmetrics_has_long_open_issues_and_more.py (1)
11-28: Data loss auditDropping four boolean columns permanently deletes historical data. Confirm that:
- No downstream analytics or dashboards still rely on them.
- A backup/ETL has archived the values if they are useful for future reporting.
If both checks pass, migration looks correct.
backend/apps/owasp/migrations/0042_remove_projecthealthmetrics_has_no_recent_commits_and_more.py (1)
11-21: ```shell
#!/bin/bashLocate and display the contents of migration 0043 in the owasp app
migration_files=$(fd -e py '0043_.*.py' backend/apps/owasp/migrations)
if [ -z "$migration_files" ]; then
echo "No 0043 migration file found."
exit 1
fifor file in $migration_files; do
echo "== $file =="
sed -n '1,200p' "$file"
done</details> <details> <summary>backend/apps/owasp/migrations/0043_remove_projecthealthmetrics_has_recent_commits_and_more.py (1)</summary> `11-21`: ```shell #!/bin/bash # List any migration files with “0042” in their name echo "Migration files containing '0042':" ls backend/apps/owasp/migrations | grep '0042' || echo "No 0042 files found" # For each matching file, print its leading content to inspect the field operations for file in backend/apps/owasp/migrations/*0042*.py; do echo echo "===== $file =====" sed -n '1,200p' "$file" donebackend/apps/owasp/migrations/0044_merge_20250615_0346.py (1)
6-12: Merge migration is fine but indicates branching painsThe merge itself is harmless, but it’s another hint that the migration chain is oscillating. After consolidating 0042/0043, this merge will likely become unnecessary and can be deleted.
backend/tests/apps/owasp/management/commands/owasp_update_project_health_metrics_scores_test.py (1)
39-54: Weights diverge: double-check with algorithmThe metric weights for
open_issues_count,unanswered_issues_count, andunassigned_issues_countare now higher (7). Confirm that the production scoring code uses the same values; otherwise the test will give false confidence.backend/apps/owasp/graphql/nodes/project.py (1)
39-44: ```shell
#!/bin/bash
set -erg "class BulkSaveModel" -A20 -n backend/apps/owasp/models
</details> <details> <summary>frontend/src/server/queries/projectQueries.ts (1)</summary> `15-25`: **Verify camel-casing conventions between backend & frontend** The query asks for `healthMetrics`, `forksCount`, etc., while the backend resolver defines `health_metrics` / `forks_count`. This only works when Strawberry’s `auto_camel_case` flag is enabled project-wide (it is by default, but can be disabled). Please double-check that: 1. `settings.STRAWBERRY_SETTINGS["auto_camel_case"]` (or equivalent) is not overridden; 2. No per-module override disables camel-casing. If camel-case is turned off, this query will fail at runtime. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
9fcac7b to
c033d35
Compare
c033d35 to
9623f53
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
cspell/custom-dict.txt(1 hunks)frontend/__tests__/unit/data/mockProjectDetailsData.ts(1 hunks)frontend/__tests__/unit/pages/ProjectDetails.test.tsx(1 hunks)frontend/package.json(2 hunks)frontend/src/app/projects/[projectKey]/page.tsx(1 hunks)frontend/src/components/CardDetailsPage.tsx(3 hunks)frontend/src/components/GeneralCharts.tsx(1 hunks)frontend/src/components/HealthMetricsCharts.tsx(1 hunks)frontend/src/server/queries/projectQueries.ts(1 hunks)frontend/src/types/card.ts(2 hunks)frontend/src/types/healthMetrics.ts(1 hunks)frontend/src/types/project.ts(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/tests/unit/data/mockProjectDetailsData.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- cspell/custom-dict.txt
- frontend/src/types/project.ts
- frontend/src/types/card.ts
- frontend/src/app/projects/[projectKey]/page.tsx
- frontend/package.json
- frontend/src/components/CardDetailsPage.tsx
- frontend/src/types/healthMetrics.ts
- frontend/src/server/queries/projectQueries.ts
- frontend/src/components/HealthMetricsCharts.tsx
- frontend/src/components/GeneralCharts.tsx
🧰 Additional context used
🪛 GitHub Actions: Run CI/CD
frontend/__tests__/unit/pages/ProjectDetails.test.tsx
[error] 25-25: CSpell: Unknown word 'apexchart' found during spelling check.
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: CodeQL (python)
a8afbfe to
88768dc
Compare
arkid15r
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great PoC 👍
We need to play w/ the style a bit and have enough data to visualize.
You can start with addressing any of these:
kasya
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!!! I know it's still in draft, but I also have a few comments 👍🏼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
backend/apps/owasp/graphql/nodes/project.py(1 hunks)frontend/package.json(2 hunks)frontend/src/components/CardDetailsPage.tsx(4 hunks)frontend/src/components/GradientRadialChart.tsx(1 hunks)frontend/src/components/HealthMetrics.tsx(1 hunks)frontend/src/components/LineChart.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/package.json
- frontend/src/components/HealthMetrics.tsx
- frontend/src/components/LineChart.tsx
- frontend/src/components/CardDetailsPage.tsx
🧰 Additional context used
🧠 Learnings (1)
frontend/src/components/GradientRadialChart.tsx (1)
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/GradientRadialChart.tsx:67-116
Timestamp: 2025-06-21T12:21:32.328Z
Learning: The react-apexcharts Chart component does not support ARIA attributes like aria-label and role as direct props. To add accessibility attributes to ApexCharts in React, wrap the Chart component in a container div with the appropriate ARIA attributes.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Run frontend unit tests
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run backend tests
🔇 Additional comments (2)
backend/apps/owasp/graphql/nodes/project.py (1)
45-45: LGTM! Ordering change aligns with chronological chart display.Changing from descending to ascending order makes sense for health metrics visualization where chronological progression (oldest to newest) is preferred for trend charts.
frontend/src/components/GradientRadialChart.tsx (1)
137-147: Excellent implementation of legend and pluralization features.The component properly addresses the feedback from kasya:
- ✅ Legend labels "Active" and "Stale" are clearly displayed
- ✅ Proper pluralization using the existing utility function
- ✅ Clean positioning with appropriate styling
| <div className="relative h-[200px] w-full"> | ||
| <Chart | ||
| key={theme} | ||
| options={{ | ||
| chart: { | ||
| animations: { | ||
| enabled: true, | ||
| speed: 1000, | ||
| }, | ||
| }, | ||
| plotOptions: { | ||
| radialBar: { | ||
| startAngle: -90, | ||
| endAngle: 90, | ||
| dataLabels: { | ||
| show: true, | ||
| name: { | ||
| show: false, | ||
| }, | ||
| value: { | ||
| formatter: () => `${days} ${pluralize(days, 'day', 'days')}`, | ||
| color: theme === 'dark' ? '#ececec' : '#1E1E2C', | ||
| fontSize: '20px', | ||
| show: true, | ||
| offsetY: 0, | ||
| }, | ||
| }, | ||
| track: { | ||
| background: theme === 'dark' ? '#1E1E2C' : '#ececec', | ||
| margin: 0, | ||
| }, | ||
| }, | ||
| }, | ||
| fill: { | ||
| type: 'gradient', | ||
| gradient: { | ||
| shade: theme, | ||
| type: 'horizontal', | ||
| shadeIntensity: 0.5, | ||
| stops: stops, | ||
| colorStops: colorStops, | ||
| }, | ||
| }, | ||
| }} | ||
| series={[requirement ? normalizedDays : 0]} | ||
| height={300} | ||
| type="radialBar" | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix height inconsistency and add accessibility wrapper.
Two issues to address:
-
Height mismatch: Container div has
h-[200px]but Chart height is set to 300px, which may cause overflow. -
Missing accessibility: Based on retrieved learnings, Chart component needs a wrapper div with ARIA attributes.
Apply this fix:
- <div className="relative h-[200px] w-full">
+ <div className="relative h-[300px] w-full">
+ <div
+ role="img"
+ aria-label={`Health metric chart showing ${days} ${pluralize(days, 'day', 'days')} out of ${requirement} required`}
+ >
<Chart
key={theme}
options={{
// ... existing options
}}
series={[requirement ? normalizedDays : 0]}
height={300}
type="radialBar"
/>
+ </div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="relative h-[200px] w-full"> | |
| <Chart | |
| key={theme} | |
| options={{ | |
| chart: { | |
| animations: { | |
| enabled: true, | |
| speed: 1000, | |
| }, | |
| }, | |
| plotOptions: { | |
| radialBar: { | |
| startAngle: -90, | |
| endAngle: 90, | |
| dataLabels: { | |
| show: true, | |
| name: { | |
| show: false, | |
| }, | |
| value: { | |
| formatter: () => `${days} ${pluralize(days, 'day', 'days')}`, | |
| color: theme === 'dark' ? '#ececec' : '#1E1E2C', | |
| fontSize: '20px', | |
| show: true, | |
| offsetY: 0, | |
| }, | |
| }, | |
| track: { | |
| background: theme === 'dark' ? '#1E1E2C' : '#ececec', | |
| margin: 0, | |
| }, | |
| }, | |
| }, | |
| fill: { | |
| type: 'gradient', | |
| gradient: { | |
| shade: theme, | |
| type: 'horizontal', | |
| shadeIntensity: 0.5, | |
| stops: stops, | |
| colorStops: colorStops, | |
| }, | |
| }, | |
| }} | |
| series={[requirement ? normalizedDays : 0]} | |
| height={300} | |
| type="radialBar" | |
| /> | |
| <div className="relative h-[300px] w-full"> | |
| + <div | |
| + role="img" | |
| + aria-label={`Health metric chart showing ${days} ${pluralize( | |
| + days, | |
| + 'day', | |
| + 'days' | |
| + )} out of ${requirement} required`} | |
| + > | |
| <Chart | |
| key={theme} | |
| options={{ | |
| chart: { | |
| animations: { | |
| enabled: true, | |
| speed: 1000, | |
| }, | |
| }, | |
| plotOptions: { | |
| radialBar: { | |
| startAngle: -90, | |
| endAngle: 90, | |
| dataLabels: { | |
| show: true, | |
| name: { show: false }, | |
| value: { | |
| formatter: () => | |
| `${days} ${pluralize(days, 'day', 'days')}`, | |
| color: theme === 'dark' ? '#ececec' : '#1E1E2C', | |
| fontSize: '20px', | |
| show: true, | |
| offsetY: 0, | |
| }, | |
| }, | |
| track: { | |
| background: theme === 'dark' ? '#1E1E2C' : '#ececec', | |
| margin: 0, | |
| }, | |
| }, | |
| }, | |
| fill: { | |
| type: 'gradient', | |
| gradient: { | |
| shade: theme, | |
| type: 'horizontal', | |
| shadeIntensity: 0.5, | |
| stops: stops, | |
| colorStops: colorStops, | |
| }, | |
| }, | |
| }} | |
| series={[requirement ? normalizedDays : 0]} | |
| height={300} | |
| type="radialBar" | |
| /> | |
| + </div> | |
| </div> |
🤖 Prompt for AI Agents
In frontend/src/components/GradientRadialChart.tsx between lines 89 and 136, fix
the height inconsistency by aligning the container div height with the Chart
height, either by changing the div's h-[200px] to h-[300px] or adjusting the
Chart height to 200px to prevent overflow. Additionally, wrap the Chart
component in a div with appropriate ARIA attributes (like role="img" and
aria-label) to improve accessibility.
| if (showRedAtEnd) { | ||
| stops.splice(stops.length - 2, 0, normalizedRequirement) | ||
| colorStops.push({ | ||
| offset: normalizedRequirement, | ||
| color: redColor, | ||
| opacity: 1, | ||
| }) | ||
| endColor = redColor | ||
| } | ||
|
|
||
| // Always add the end color at 100% if we have orange or red | ||
| if (showOrange || showRedAtEnd) { | ||
| stops.push(100) | ||
| colorStops.push({ | ||
| offset: 100, | ||
| color: endColor, | ||
| opacity: 1, | ||
| }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix gradient stops ordering and eliminate duplicate stops.
The gradient stops manipulation has two issues:
-
Incorrect insertion position: When both
showOrangeandshowRedAtEndare true,normalizedRequirementis inserted beforenormalizedOrangeStop, but it should come after for proper green→orange→red progression. -
Duplicate 100 stop: The final
push(100)creates a duplicate since 100 is already in the array.
Apply this fix:
if (showRedAtEnd) {
- stops.splice(stops.length - 2, 0, normalizedRequirement)
+ stops.splice(stops.length - 1, 0, normalizedRequirement)
colorStops.push({
offset: normalizedRequirement,
color: redColor,
opacity: 1,
})
endColor = redColor
}
// Always add the end color at 100% if we have orange or red
if (showOrange || showRedAtEnd) {
- stops.push(100)
colorStops.push({
offset: 100,
color: endColor,
opacity: 1,
})
}🤖 Prompt for AI Agents
In frontend/src/components/GradientRadialChart.tsx around lines 67 to 85, fix
the gradient stops by inserting normalizedRequirement after the orange stop when
both showOrange and showRedAtEnd are true to maintain correct color progression,
and avoid pushing 100 twice to stops to eliminate duplicate end stops. Adjust
the splice position accordingly and ensure 100 is only added once at the end.
|
kasya
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! @ahmedxgouda
p.s. I pushed small update for Health Score circle styling.
arkid15r
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @ahmedxgouda for another great contribution 👍



Resolves #1622
Screencast.from.2025-06-20.19-36-54.mp4