11'use client' ;
2+ import { useMemo } from 'react' ;
23import type { TeamProject } from '@/lib/team-pulse/types' ;
34
5+ // Segment colors used in both the legend swatches and the bar segments.
6+ const SEGMENT_COLORS = {
7+ prs : '#06B6D4' ,
8+ jiras : '#A855F7' ,
9+ commits : 'rgba(255,255,255,0.10)' ,
10+ } as const ;
11+
412/** Older shape used by the home-page LLM project insights — superset of TeamProject.
513 * Allows the same component to serve both surfaces without a refactor of the home payload. */
614export interface ProjectsCardItem {
@@ -70,35 +78,40 @@ function ProjectsBody({
7078 return < p className = { `text-sm text-gray-500 ${ variant === 'collapsible' ? 'py-4' : 'mt-2' } ` } > { emptyMessage } </ p > ;
7179 }
7280
73- // Sort by meaningful output (PRs + Jiras). Commits are squashed into PRs so
74- // using raw commit count would inflate commit-heavy, low-PR projects.
75- const sorted = [ ...projects ] . sort (
76- ( a , b ) => ( b . estimated_prs + b . jira_count ) - ( a . estimated_prs + a . jira_count ) ,
81+ // Sort by meaningful output (PRs + Jiras). Commits excluded from sort key
82+ // because they are squashed into PRs — including them would inflate rank for
83+ // commit-heavy, low-PR projects. Commits are still shown in the bar for context.
84+ const sorted = useMemo (
85+ ( ) => [ ...projects ] . sort ( ( a , b ) => ( b . estimated_prs + b . jira_count ) - ( a . estimated_prs + a . jira_count ) ) ,
86+ [ projects ] ,
7787 ) ;
7888
79- // Max total volume across all projects — used to normalise bar widths.
80- const maxVolume = Math . max (
81- ...sorted . map ( p => p . estimated_prs + p . jira_count + p . estimated_commits ) ,
82- 1 ,
89+ // Bar width = total volume (PRs + Jiras + Commits) / max across all projects.
90+ // Commits are intentionally included here even though they are excluded from the
91+ // sort key — the bar shows the full activity footprint of each project (sort rank
92+ // reflects quality output, bar width reflects overall size).
93+ const maxVolume = useMemo (
94+ ( ) => sorted . reduce ( ( max , p ) => Math . max ( max , p . estimated_prs + p . jira_count + p . estimated_commits ) , 1 ) ,
95+ [ sorted ] ,
8396 ) ;
8497
8598 // Hide Jira legend entry when Jira is disabled (all counts are 0).
86- const hasJira = sorted . some ( p => p . jira_count > 0 ) ;
99+ const hasJira = useMemo ( ( ) => sorted . some ( p => p . jira_count > 0 ) , [ sorted ] ) ;
87100
88101 return (
89102 < div className = { `space-y-3 ${ variant === 'collapsible' ? 'mt-3' : 'mt-4' } ` } >
90103 { /* Legend */ }
91104 < div className = "flex gap-3 text-[10px] text-gray-600 pl-6" >
92105 < span className = "flex items-center gap-1" >
93- < span className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : '#06B6D4' } } /> PRs
106+ < span aria-hidden = "true" className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : SEGMENT_COLORS . prs } } /> PRs
94107 </ span >
95108 { hasJira && (
96109 < span className = "flex items-center gap-1" >
97- < span className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : '#A855F7' } } /> Jiras
110+ < span aria-hidden = "true" className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : SEGMENT_COLORS . jiras } } /> Jiras
98111 </ span >
99112 ) }
100113 < span className = "flex items-center gap-1" >
101- < span className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : 'rgba(255,255,255,0.18)' } } /> Commits
114+ < span aria-hidden = "true" className = "inline-block w-2 h-2 rounded-[2px]" style = { { background : 'rgba(255,255,255,0.18)' } } /> Commits
102115 </ span >
103116 </ div >
104117
@@ -107,7 +120,7 @@ function ProjectsBody({
107120 const totalVol = p . estimated_prs + p . jira_count + p . estimated_commits ;
108121 const barPct = ( totalVol / maxVolume ) * 100 ;
109122 return (
110- < div key = { p . name } className = "bg-white/[0.02] rounded-lg p-3" >
123+ < div key = { ` ${ p . name } - ${ i } ` } className = "bg-white/[0.02] rounded-lg p-3" >
111124 < div className = "flex items-start justify-between gap-3 mb-1" >
112125 < div className = "flex items-center gap-2 min-w-0" >
113126 < span className = "text-xs text-gray-600 w-4 shrink-0 text-right" > { i + 1 } </ span >
@@ -121,16 +134,23 @@ function ProjectsBody({
121134 </ div >
122135 </ div >
123136
124- { /* Volume bar: width = total / max, segments = PRs (cyan) + Jiras (purple) + Commits (ghost) */ }
137+ { /* Volume bar: width = total / max, segments = PRs + Jiras + Commits (ghost) */ }
138+ { totalVol > 0 && (
125139 < div className = "pl-6 mb-1.5" >
126- < div className = "h-[5px] rounded-sm overflow-hidden" style = { { background : 'rgba(255,255,255,0.05)' } } >
140+ < div
141+ className = "h-[5px] rounded-sm overflow-hidden"
142+ style = { { background : 'rgba(255,255,255,0.05)' } }
143+ role = "img"
144+ aria-label = { `Volume: ${ p . estimated_prs } PRs, ${ p . jira_count } Jiras, ${ p . estimated_commits } commits` }
145+ >
127146 < div className = "h-full flex" style = { { width : `${ barPct } %` } } >
128- < div style = { { flex : p . estimated_prs , background : '#06B6D4' } } />
129- < div style = { { flex : p . jira_count , background : '#A855F7' } } />
130- < div style = { { flex : p . estimated_commits , background : 'rgba(255,255,255,0.10)' } } />
147+ < div style = { { flex : p . estimated_prs , background : SEGMENT_COLORS . prs } } />
148+ < div style = { { flex : p . jira_count , background : SEGMENT_COLORS . jiras } } />
149+ < div style = { { flex : p . estimated_commits , background : SEGMENT_COLORS . commits } } />
131150 </ div >
132151 </ div >
133152 </ div >
153+ ) }
134154
135155 < p className = "text-xs text-gray-500 pl-6 mb-1.5" > { p . summary } </ p >
136156 < div className = "flex gap-1 pl-6 flex-wrap" >
0 commit comments