feat: add packages health ranking chart#146
Conversation
✅ Deploy Preview for agentscan ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
More reviews will be available in 17 minutes and 7 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR introduces a new featured package health ranking visualization by adding a horizontal bar chart component with date-based filtering to the heatmap page. The change includes type updates to support repository grouping, color utilities for gradient bar styling, chart utility refactoring, and a reusable date selector component. ChangesPackage Health Chart Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/components/Chart/FeaturedPackageHealthRanking.vue`:
- Line 110: The UI copy in the FeaturedPackageHealthRanking.vue component
contains a typo in the template slot label; update the template fragment with
the slot named "label" (the line currently showing "<template `#label`> Chose a
date </template>") to read "Choose a date" instead of "Chose a date" so the
displayed label is correct.
- Around line 22-24: The date filtering in convertToHorizontalBarDataset is
timezone-unsafe because new Date("YYYY-MM-DD") is treated as UTC; change the
comparison to be timezone-stable by either comparing the raw YYYY-MM-DD strings
(e.g., format both the data date and selectedDate as ISO date strings) or by
constructing dates with new Date(year, monthIndex, day) / using UTC getters,
ensuring convertToHorizontalBarDataset uses the stable comparison; additionally
guard the CommonDateSelector and chart transform consumers so they always
receive an array while useEcosystemHealth() is loading (return [] by default or
null-check before mapping) and fix the UI typo "Chose a date" → "Choose a date".
In `@app/utils/charts.ts`:
- Around line 24-27: The file is missing the EcosystemHealthItem type import
used in the convertToHorizontalBarDataset signature; add an import for
EcosystemHealthItem alongside the existing VueUiHorizontalBarDatasetItem import
so the type is in scope (i.e., update the import statement that currently brings
in VueUiHorizontalBarDatasetItem to also import EcosystemHealthItem) to resolve
the TypeScript compilation error.
- Around line 28-41: The date filtering is mixing local and UTC dates causing
off-by-one-day errors; update convertToHorizontalBarDataset so targetDay and
isSameDay use UTC consistently: construct targetDay from the incoming YYYY-MM-DD
string as new Date(date + 'T00:00:00Z') (or use Date.UTC) and change isSameDay
to compare getUTCFullYear(), getUTCMonth(), and getUTCDate() when comparing
createdAt and targetDay (or alternatively compare
createdAt.toISOString().slice(0,10) === date); adjust the grouping reduce that
uses targetDay and isSameDay accordingly (identify symbols: targetDay,
isSameDay, grouped, item.created_at).
In `@app/utils/colors.ts`:
- Around line 49-53: The current code only checks Array.isArray(colors) but
allows an empty array to proceed and later crash; add an explicit empty-length
guard right after the Array.isArray check (e.g. if (colors.length === 0) throw
new Error("colors must be a non-empty array of hex colors")); locate the check
that uses the colors parameter (the Array.isArray(colors) block and the
subsequent if (colors.length === 1) return colors[0]!) and insert the
empty-array throw so the function fails deterministically for [].
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0227467c-ae32-4141-ac1b-a9d0fd10eee2
📒 Files selected for processing (9)
app/components/Chart/AccountEventsTimeline.vueapp/components/Chart/FeaturedPackageHealthRanking.vueapp/components/Chart/chart.tsapp/components/Common/DateSelector.vueapp/pages/heatmap.vueapp/utils/charts.tsapp/utils/colors.tsnuxt.config.tsshared/types/ecosystem-health.ts
💤 Files with no reviewable changes (1)
- app/components/Chart/chart.ts
| const source = computed(() => { | ||
| return convertToHorizontalBarDataset(data.value, selectedDate.value); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In JavaScript, how is new Date("YYYY-MM-DD")parsed, and why can comparing it withgetFullYear()/getMonth()/getDate() cause day shifts across time zones?
💡 Result:
In JavaScript, parsing a date-only string like new Date("YYYY-MM-DD") is treated as an ISO 8601 format, which the ECMAScript specification mandates be parsed in the UTC time zone [1][2]. Specifically, if the string lacks a time component or a time zone offset, the engine assumes the time is midnight UTC (00:00:00Z) [3][4][5]. When you subsequently call methods like getFullYear, getMonth, or getDate, the JavaScript Date object converts that internal UTC timestamp into the local time zone of the environment (the browser or server running the code) [4][5][6]. This behavior causes "day shifts" because of the time zone offset: 1. If your local time zone is behind UTC (e.g., UTC-5), midnight UTC on the specified date will be converted to the previous day (e.g., 19:00 on the day before) in your local time [7][5]. 2. Because getDate and getMonth return components based on this local conversion, the value returned will reflect the local time zone's date, not the UTC date originally passed [3][5]. To avoid this, you should avoid passing pure date strings to the new Date constructor [5]. Instead, you can parse the string components manually or use a library that handles date-only objects [1][3][5]. A common approach for local-timezone interpretation is to use the multi-argument constructor new Date(year, monthIndex, day), which interprets the components as local time (not UTC) [8][5][9]: const [year, month, day] = "2026-05-28".split("-"); // Note: monthIndex is 0-indexed (January=0, May=4) const date = new Date(year, month - 1, day);
Citations:
- 1: https://stackoverflow.com/questions/17893896/parse-yyyy-mm-dd-dates-using-the-local-timezone
- 2: https://news.ycombinator.com/item?id=46589658
- 3: https://apisurfer.com/js-date-constructor-timezone-oddity
- 4: https://stackoverflow.com/questions/7556591/is-the-javascript-date-object-always-one-day-off
- 5: https://medium.com/@TheCraftedDev/why-new-date-yyyy-mm-dd-causes-timezone-bugs-in-react-native-and-the-safe-fix-a11eb086a7fb
- 6: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
- 7: https://dev.to/bharath_kumar_39293/how-i-replaced-react-calendar-with-a-timezone-safe-unifieddatepicker-in-a-production-oss-codebase-4n5e
- 8: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
- 9: https://stackoverflow.com/questions/29789577/javascript-getfullyear-getmonth-getdate-not-working
Fix timezone-unsafe day filtering (and guard async data)
new Date("YYYY-MM-DD") is parsed as UTC midnight, while getFullYear()/getMonth()/getDate() read in local time—this can shift the intended day and break filtering. Update the date-only parsing/comparison in convertToHorizontalBarDataset/the selector logic to be timezone-stable (e.g., compare YYYY-MM-DD strings directly, or use UTC-based getters / parse with new Date(year, monthIndex, day)).
Also ensure CommonDateSelector/chart transform always receive an array while useEcosystemHealth() is still loading, and fix the typo “Chose a date” → “Choose a date”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/components/Chart/FeaturedPackageHealthRanking.vue` around lines 22 - 24,
The date filtering in convertToHorizontalBarDataset is timezone-unsafe because
new Date("YYYY-MM-DD") is treated as UTC; change the comparison to be
timezone-stable by either comparing the raw YYYY-MM-DD strings (e.g., format
both the data date and selectedDate as ISO date strings) or by constructing
dates with new Date(year, monthIndex, day) / using UTC getters, ensuring
convertToHorizontalBarDataset uses the stable comparison; additionally guard the
CommonDateSelector and chart transform consumers so they always receive an array
while useEcosystemHealth() is loading (return [] by default or null-check before
mapping) and fix the UI typo "Chose a date" → "Choose a date".
| export function convertToHorizontalBarDataset( | ||
| source: EcosystemHealthItem[], | ||
| date?: Date | string | null, | ||
| ): VueUiHorizontalBarDatasetItem[] { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the type is referenced and whether it's imported.
sed -n '1,80p' app/utils/charts.ts
rg -n '\bEcosystemHealthItem\b' app/utils/charts.ts
rg -n 'from "~~/shared/types/ecosystem-health"' app/utils/charts.tsRepository: MatteoGabriele/agentscan
Length of output: 1704
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show full import section of the file under review
sed -n '1,60p' app/utils/charts.ts
# Find where EcosystemHealthItem is declared/defined
rg -n "EcosystemHealthItem" -S . | head -n 200
# Specifically look for its type declaration/export and any global/ambient declaration
rg -n "declare global|global namespace|interface EcosystemHealthItem|type EcosystemHealthItem|export (type|interface) EcosystemHealthItem" -S . || trueRepository: MatteoGabriele/agentscan
Length of output: 2463
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="app/utils/charts.ts"
echo "== imports in $FILE =="
rg -n '^import ' "$FILE" || true
echo "== EcosystemHealthItem occurrences in $FILE =="
rg -n '\bEcosystemHealthItem\b' "$FILE" || true
echo "== any shared/types/ecosystem-health import in $FILE =="
rg -n 'ecosystem-health' "$FILE" || trueRepository: MatteoGabriele/agentscan
Length of output: 362
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the type definition file
sed -n '1,120p' shared/types/ecosystem-health.ts
# Look for any ambient/global declarations in the repo
rg -n "declare global|global namespace" -S . || true
# Look for any ambient references to EcosystemHealthItem (beyond normal imports)
rg -n "declare global.*EcosystemHealthItem|global namespace.*EcosystemHealthItem|interface EcosystemHealthItem|type EcosystemHealthItem" -S . || trueRepository: MatteoGabriele/agentscan
Length of output: 366
Fix missing EcosystemHealthItem import in app/utils/charts.ts
EcosystemHealthItem is used in the convertToHorizontalBarDataset signature, but app/utils/charts.ts only imports VueUiHorizontalBarDatasetItem, so the type isn’t in scope and will block TS compilation.
Proposed fix
import type { VueUiHorizontalBarDatasetItem } from "vue-data-ui/vue-ui-horizontal-bar";
+import type { EcosystemHealthItem } from "~~/shared/types/ecosystem-health";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/utils/charts.ts` around lines 24 - 27, The file is missing the
EcosystemHealthItem type import used in the convertToHorizontalBarDataset
signature; add an import for EcosystemHealthItem alongside the existing
VueUiHorizontalBarDatasetItem import so the type is in scope (i.e., update the
import statement that currently brings in VueUiHorizontalBarDatasetItem to also
import EcosystemHealthItem) to resolve the TypeScript compilation error.
| if (!Array.isArray(colors)) { | ||
| throw new Error("colors must be an array with at least 2 hex colors"); | ||
| } | ||
|
|
||
| if (colors.length === 1) return colors[0]!; |
There was a problem hiding this comment.
Handle empty palettes explicitly.
Line 49 only checks Array.isArray(colors). colors = [] currently falls through and crashes later with an out-of-bounds access. Add an explicit length guard so the function fails deterministically.
Proposed fix
export function interpolateHexColors({
colors,
ratio,
}: {
colors: string[];
ratio: number;
}): string {
- if (!Array.isArray(colors)) {
- throw new Error("colors must be an array with at least 2 hex colors");
+ if (!Array.isArray(colors) || colors.length === 0) {
+ throw new Error("colors must be a non-empty array of hex colors");
}
if (colors.length === 1) return colors[0]!;📝 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.
| if (!Array.isArray(colors)) { | |
| throw new Error("colors must be an array with at least 2 hex colors"); | |
| } | |
| if (colors.length === 1) return colors[0]!; | |
| if (!Array.isArray(colors) || colors.length === 0) { | |
| throw new Error("colors must be a non-empty array of hex colors"); | |
| } | |
| if (colors.length === 1) return colors[0]!; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/utils/colors.ts` around lines 49 - 53, The current code only checks
Array.isArray(colors) but allows an empty array to proceed and later crash; add
an explicit empty-length guard right after the Array.isArray check (e.g. if
(colors.length === 0) throw new Error("colors must be a non-empty array of hex
colors")); locate the check that uses the colors parameter (the
Array.isArray(colors) block and the subsequent if (colors.length === 1) return
colors[0]!) and insert the empty-array throw so the function fails
deterministically for [].

This adds a horizontal bar chart in the /heatmap page laboratory:
I also reordered some stuff (utilities) and updated the
EcosystemHealthItemtype.Summary by CodeRabbit
Release Notes
New Features
Improvements