Skip to content

feat: add packages health ranking chart#146

Merged
MatteoGabriele merged 3 commits into
MatteoGabriele:mainfrom
graphieros:package-health-ranking-chart
May 28, 2026
Merged

feat: add packages health ranking chart#146
MatteoGabriele merged 3 commits into
MatteoGabriele:mainfrom
graphieros:package-health-ranking-chart

Conversation

@graphieros

@graphieros graphieros commented May 28, 2026

Copy link
Copy Markdown
Collaborator

This adds a horizontal bar chart in the /heatmap page laboratory:

image

I also reordered some stuff (utilities) and updated the EcosystemHealthItem type.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added date selector functionality to filter ecosystem health metrics by specific calendar dates
    • Introduced Featured Package Health Ranking chart with color-coded health score visualization to the heatmap dashboard
  • Improvements

    • Enhanced heatmap page layout for clearer data presentation

Review Change Stack

@netlify

netlify Bot commented May 28, 2026

Copy link
Copy Markdown

Deploy Preview for agentscan ready!

Name Link
🔨 Latest commit 3275fad
🔍 Latest deploy log https://app.netlify.com/projects/agentscan/deploys/6a17cebea8faaf000868ae1d
😎 Deploy Preview https://deploy-preview-146--agentscan.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@graphieros, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 576fd340-fee6-4e72-a744-a0b1efb81175

📥 Commits

Reviewing files that changed from the base of the PR and between 17d0d8c and 3275fad.

📒 Files selected for processing (3)
  • app/components/Chart/FeaturedPackageHealthRanking.vue
  • app/components/Common/DateSelector.vue
  • app/utils/charts.ts
📝 Walkthrough

Walkthrough

This 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.

Changes

Package Health Chart Feature

Layer / File(s) Summary
Type contract and color utilities
shared/types/ecosystem-health.ts, app/utils/colors.ts
EcosystemHealthItem adds user_id, pr_number, repo_name fields (replacing hash) to enable repository grouping. Color utilities provide RGB type, hex-RGB conversions, channel interpolation, and palette-based color interpolation for bar gradient styling.
Chart utilities refactoring and wiring
app/utils/charts.ts, app/components/Chart/AccountEventsTimeline.vue, nuxt.config.ts
getCompleteDayRange moves from local component module to shared utils. New convertToHorizontalBarDataset function groups EcosystemHealthItem records by repo_name and averages scores. AccountEventsTimeline import updated. Nuxt dependency optimization includes vue-data-ui/vue-ui-horizontal-bar.
Date selector component
app/components/Common/DateSelector.vue
Derives available dates from EcosystemHealthItem[].created_at, renders a select with "all" option, maintains selection state, and emits select-date with null or date string. Watcher resets selection when available dates change.
Featured package health chart
app/components/Chart/FeaturedPackageHealthRanking.vue
New component using useEcosystemHealth() hook, integrates DateSelector for filtering, converts data via convertToHorizontalBarDataset, computes horizontal bar chart config with vue-data-ui, and interpolates bar colors based on item values relative to maximum. Renders in ClientOnly wrapper.
Heatmap page integration
app/pages/heatmap.vue
Changes global events heatmap layout from h-screen to centered my-6 container and adds new ChartFeaturedPackageHealthRanking component in a second centered container below the heatmap.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • MatteoGabriele

Poem

🐰 A rabbit charts the health of repos bright,
With dates that filter, colors that interpolate,
Package metrics grouped in bars of light,
From scores we average, gradients we create,
The heatmap now has ranking to delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a horizontal bar chart component for displaying package health rankings on the heatmap page.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between db6424e and 17d0d8c.

📒 Files selected for processing (9)
  • app/components/Chart/AccountEventsTimeline.vue
  • app/components/Chart/FeaturedPackageHealthRanking.vue
  • app/components/Chart/chart.ts
  • app/components/Common/DateSelector.vue
  • app/pages/heatmap.vue
  • app/utils/charts.ts
  • app/utils/colors.ts
  • nuxt.config.ts
  • shared/types/ecosystem-health.ts
💤 Files with no reviewable changes (1)
  • app/components/Chart/chart.ts

Comment on lines +22 to +24
const source = computed(() => {
return convertToHorizontalBarDataset(data.value, selectedDate.value);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 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:


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".

Comment thread app/components/Chart/FeaturedPackageHealthRanking.vue Outdated
Comment thread app/utils/charts.ts
Comment on lines +24 to +27
export function convertToHorizontalBarDataset(
source: EcosystemHealthItem[],
date?: Date | string | null,
): VueUiHorizontalBarDatasetItem[] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 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.ts

Repository: 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 . || true

Repository: 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" || true

Repository: 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 . || true

Repository: 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.

Comment thread app/utils/charts.ts Outdated
Comment thread app/utils/colors.ts
Comment on lines +49 to +53
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]!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
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 [].

@MatteoGabriele MatteoGabriele left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@MatteoGabriele MatteoGabriele merged commit fcb07b1 into MatteoGabriele:main May 28, 2026
5 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.

2 participants