diff --git a/frontend/cc-ui/src/pages/EcosystemReportTabs.tsx b/frontend/cc-ui/src/pages/EcosystemReportTabs.tsx index eabc19c2..6204dfd6 100644 --- a/frontend/cc-ui/src/pages/EcosystemReportTabs.tsx +++ b/frontend/cc-ui/src/pages/EcosystemReportTabs.tsx @@ -64,6 +64,7 @@ export const LANG: Record= 1000 ? (n / 1000).toFixed(0) + 'k' : String(n) } +function round1(n: number) { return Math.round(n * 10) / 10 } // ── Shared UI pieces ─────────────────────────────────────────────────────────── export function KpiCard({ label, value, sub, color }: { label: string; value: string | number; sub?: string; color?: string }) { @@ -208,8 +209,21 @@ export const thStyle = (active: boolean, right = false): React.CSSProperties => // ── Tab: Projects ─────────────────────────────────────────────────────────── export function ProjectsTab({ data }: { data: ReportData }) { - const { projects, grand } = data - const tbl = useTable(projects as unknown as Record[], 'code') + const { grand } = data + const totalAll = grand.code + grand.comment + grand.blank || 1 + + // Rank/size everything on this tab by total lines (code + comment + + // blank) rather than code-only -- overrides the backend's code-based + // `pct` with a total-based one so the KPI, donut, bars, and table + // share column all agree on the same denominator. + const projects = useMemo(() => + data.projects + .map(r => ({ ...r, total: r.code + r.comment + r.blank })) + .map(r => ({ ...r, pct: round1(100 * r.total / totalAll) })) + .sort((a, b) => b.total - a.total), + [data.projects, totalAll] + ) + const tbl = useTable(projects as unknown as Record[], 'total') const filtered = useMemo(() => applyTable(projects as unknown as Record[], tbl, ['name', 'catLabel'], 'cat'), @@ -218,12 +232,11 @@ export function ProjectsTab({ data }: { data: ReportData }) { const pages = Math.ceil(filtered.length / tbl.perPage) const paged = filtered.slice((tbl.page - 1) * tbl.perPage, tbl.page * tbl.perPage) - const totalCode = grand.code || 1 const catTotals: Record = {} - projects.forEach(r => { catTotals[r.cat] = (catTotals[r.cat] || 0) + r.code }) + projects.forEach(r => { catTotals[r.cat] = (catTotals[r.cat] || 0) + r.total }) const catOrder = Object.keys(CAT).sort((a, b) => (catTotals[b] || 0) - (catTotals[a] || 0)) const donutData = catOrder.map(k => ({ name: CAT[k].label, value: catTotals[k] || 0, color: CAT[k].color })) - const maxCode = projects[0]?.code || 1 + const maxTotal = projects[0]?.total || 1 const SortTh = ({ col, label, right = false }: { col: string; label: string; right?: boolean }) => ( { tbl.toggleSort(col); }} style={thStyle(tbl.sortKey === col, right)}> @@ -235,35 +248,35 @@ export function ProjectsTab({ data }: { data: ReportData }) {
- - + +
- +
- +
{catOrder.map(cat => (
{CAT[cat].label} - {((catTotals[cat] || 0) / totalCode * 100).toFixed(1)}% + {((catTotals[cat] || 0) / totalAll * 100).toFixed(1)}%
))}
{projects.slice(0, 16).map(r => { - const pct = Math.round(r.code / maxCode * 100) + const pct = Math.round(r.total / maxTotal * 100) const meta = CAT[r.cat] || CAT.infra return (
{r.name}
- {k(r.code)} + {k(r.total)}
@@ -296,6 +309,7 @@ export function ProjectsTab({ data }: { data: ReportData }) { + @@ -307,9 +321,10 @@ export function ProjectsTab({ data }: { data: ReportData }) { {r.name} {fmt(r.files)} - {fmt(r.code)} + {fmt(r.code)} {fmt(r.comment)} {fmt(r.blank)} + {fmt(r.total)} {r.pct.toFixed(1)}%