From e25f13cbfb70d2f3bc75cf0da143c128add3fa03 Mon Sep 17 00:00:00 2001 From: Mallya Date: Tue, 9 Jun 2026 06:54:13 +0530 Subject: [PATCH] feat(languages): upgrade language breakdown to Recharts donut chart (#938) --- src/components/LanguageBreakdown.tsx | 160 ++++++++++++++++++--------- 1 file changed, 108 insertions(+), 52 deletions(-) diff --git a/src/components/LanguageBreakdown.tsx b/src/components/LanguageBreakdown.tsx index 93966cef6..d266fdafe 100644 --- a/src/components/LanguageBreakdown.tsx +++ b/src/components/LanguageBreakdown.tsx @@ -2,6 +2,14 @@ import { useEffect, useState } from "react"; import { useAccount } from "@/components/AccountContext"; +import { + PieChart, + Pie, + Cell, + Tooltip, + ResponsiveContainer, + type TooltipProps, +} from "recharts"; interface Language { name: string; @@ -20,6 +28,16 @@ const LANG_COLORS: Record = { HTML: "#e34c26", Ruby: "#701516", Shell: "#89e051", + Swift: "#F05138", + Kotlin: "#7F52FF", + "C++": "#f34b7d", + C: "#555555", + "C#": "#178600", + PHP: "#4F5D95", + Dart: "#00B4AB", + Scala: "#c22d40", + Vue: "#41b883", + Other: "#6b7280", }; const FALLBACK_COLOR = "#6b7280"; @@ -28,18 +46,17 @@ function getColor(name: string): string { return LANG_COLORS[name] ?? FALLBACK_COLOR; } -function LanguageDot({ color, label }: { color: string; label: string }) { +function LanguageTooltip({ active, payload }: TooltipProps) { + if (!active || !payload || payload.length === 0) return null; + const entry = payload[0]; + if (!entry) return null; return ( - - - +
+
{entry.name}
+
+ {entry.value}% +
+
); } @@ -52,15 +69,13 @@ export default function LanguageBreakdown() { useEffect(() => { setLoading(true); setError(null); - const url = selectedAccount !== null - ? `/api/metrics/languages?accountId=${encodeURIComponent(selectedAccount)}` - : "/api/metrics/languages"; + const url = + selectedAccount !== null + ? `/api/metrics/languages?accountId=${encodeURIComponent(selectedAccount)}` + : "/api/metrics/languages"; fetch(url) .then((r) => { - if (!r.ok) { - throw new Error("API error"); - } - + if (!r.ok) throw new Error("API error"); return r.json(); }) .then((d: { languages: Language[] }) => setLanguages(d.languages ?? [])) @@ -70,12 +85,15 @@ export default function LanguageBreakdown() { .finally(() => setLoading(false)); }, [selectedAccount]); - const totalPercentage = languages.reduce((sum, lang) => sum + lang.percentage, 0); + const totalPercentage = languages.reduce( + (sum, lang) => sum + lang.percentage, + 0 + ); const roundedTotal = Math.round(totalPercentage * 10) / 10; - - const displayLanguages = [...languages]; - if (roundedTotal < 100 && languages.length > 0) { - displayLanguages.push({ + + const chartData: Language[] = [...languages]; + if (roundedTotal < 99.5 && languages.length > 0) { + chartData.push({ name: "Other", bytes: 0, percentage: Math.round((100 - roundedTotal) * 10) / 10, @@ -98,9 +116,12 @@ export default function LanguageBreakdown() { Loading language breakdown