@@ -76,18 +76,18 @@ const statusLabelKeys: Record<SessionMetadata['status'], string> = {
7676 paused : 'sessions.status.paused' ,
7777} ;
7878
79- // Type variant configuration for session type badges
79+ // Type variant configuration for session type badges (unique colors for each type)
8080const typeVariantConfig : Record <
8181 SessionMetadata [ 'type' ] ,
82- { variant : 'default' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info' ; icon : React . ElementType }
82+ { variant : 'default' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info' | 'review' ; icon : React . ElementType }
8383> = {
84- review : { variant : 'info ' , icon : Search } ,
85- 'tdd' : { variant : 'success' , icon : TestTube } ,
86- test : { variant : 'default ' , icon : FileText } ,
87- docs : { variant : 'warning' , icon : File } ,
88- workflow : { variant : 'secondary ' , icon : Settings } ,
89- 'lite-plan' : { variant : 'default ' , icon : FileText } ,
90- 'lite-fix' : { variant : 'warning ' , icon : Zap } ,
84+ review : { variant : 'review ' , icon : Search } , // Purple
85+ 'tdd' : { variant : 'success' , icon : TestTube } , // Green
86+ test : { variant : 'info ' , icon : FileText } , // Blue
87+ docs : { variant : 'warning' , icon : File } , // Orange/Yellow
88+ workflow : { variant : 'default ' , icon : Settings } , // Primary (blue-violet)
89+ 'lite-plan' : { variant : 'secondary ' , icon : FileText } , // Gray/Neutral
90+ 'lite-fix' : { variant : 'destructive ' , icon : Zap } , // Red
9191} ;
9292
9393// Type label keys for i18n
@@ -149,6 +149,43 @@ function calculateProgress(tasks: SessionMetadata['tasks']): TaskStatusBreakdown
149149 return { total, completed, failed, pending, inProgress, percentage } ;
150150}
151151
152+ /**
153+ * Severity breakdown for review sessions
154+ */
155+ interface SeverityBreakdown {
156+ total : number ;
157+ critical : number ;
158+ high : number ;
159+ medium : number ;
160+ low : number ;
161+ }
162+
163+ /**
164+ * Calculate severity breakdown from review dimensions
165+ */
166+ function calculateSeverityBreakdown ( review : SessionMetadata [ 'review' ] ) : SeverityBreakdown {
167+ if ( ! review ?. dimensions || review . dimensions . length === 0 ) {
168+ return { total : 0 , critical : 0 , high : 0 , medium : 0 , low : 0 } ;
169+ }
170+
171+ let critical = 0 , high = 0 , medium = 0 , low = 0 ;
172+
173+ review . dimensions . forEach ( dim => {
174+ if ( dim . findings ) {
175+ dim . findings . forEach ( finding => {
176+ const severity = finding . severity ?. toLowerCase ( ) ;
177+ if ( severity === 'critical' ) critical ++ ;
178+ else if ( severity === 'high' ) high ++ ;
179+ else if ( severity === 'medium' ) medium ++ ;
180+ else if ( severity === 'low' ) low ++ ;
181+ } ) ;
182+ }
183+ } ) ;
184+
185+ const total = critical + high + medium + low ;
186+ return { total, critical, high, medium, low } ;
187+ }
188+
152189/**
153190 * SessionCard component for displaying session information
154191 *
@@ -188,6 +225,7 @@ export function SessionCard({
188225 : null ;
189226
190227 const progress = calculateProgress ( session . tasks ) ;
228+ const severity = calculateSeverityBreakdown ( session . review ) ;
191229 const isPlanning = session . status === 'planning' ;
192230 const isArchived = session . status === 'archived' || session . location === 'archived' ;
193231
@@ -227,21 +265,24 @@ export function SessionCard({
227265 onClick = { handleCardClick }
228266 >
229267 < CardContent className = "p-4" >
230- { /* Header - Session ID as title */ }
268+ { /* Header - Type badge + Session ID as title */ }
231269 < div className = "flex items-start justify-between gap-2 mb-2" >
232270 < div className = "flex-1 min-w-0" >
233- < h3 className = "font-bold text-card-foreground text-sm tracking-wide uppercase truncate" >
234- { session . session_id }
235- </ h3 >
271+ < div className = "flex items-center gap-2 mb-1" >
272+ { /* Type badge BEFORE title */ }
273+ { typeConfig && typeLabel && (
274+ < Badge variant = { typeConfig . variant } className = "gap-1 flex-shrink-0" >
275+ < typeConfig . icon className = "h-3 w-3" />
276+ { typeLabel }
277+ </ Badge >
278+ ) }
279+ < h3 className = "font-bold text-card-foreground text-sm tracking-wide uppercase truncate" >
280+ { session . session_id }
281+ </ h3 >
282+ </ div >
236283 </ div >
237284 < div className = "flex items-center gap-2 flex-shrink-0" >
238285 < Badge variant = { statusVariant } > { statusLabel } </ Badge >
239- { typeConfig && typeLabel && (
240- < Badge variant = { typeConfig . variant } className = "gap-1" >
241- < typeConfig . icon className = "h-3 w-3" />
242- { typeLabel }
243- </ Badge >
244- ) }
245286 { showActions && (
246287 < DropdownMenu >
247288 < DropdownMenuTrigger asChild >
@@ -291,21 +332,46 @@ export function SessionCard({
291332 </ p >
292333 ) }
293334
294- { /* Meta info - enriched */ }
335+ { /* Meta info - different based on session type */ }
295336 < div className = "flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-muted-foreground" >
296337 < span className = "flex items-center gap-1" >
297338 < Calendar className = "h-3.5 w-3.5" />
298339 { formatDate ( session . created_at ) }
299340 </ span >
300- < span className = "flex items-center gap-1" >
301- < ListChecks className = "h-3.5 w-3.5" />
302- { progress . total } { formatMessage ( { id : 'sessions.card.tasks' } ) }
303- </ span >
304- { progress . total > 0 && (
305- < span className = "flex items-center gap-1" >
306- < CheckCircle2 className = "h-3.5 w-3.5 text-success" />
307- { progress . completed } { formatMessage ( { id : 'sessions.card.completed' } ) }
308- </ span >
341+
342+ { /* Review sessions: Show findings and dimensions */ }
343+ { session . type === 'review' ? (
344+ < >
345+ { session . review ?. dimensions && session . review . dimensions . length > 0 && (
346+ < span className = "flex items-center gap-1" >
347+ < Search className = "h-3.5 w-3.5" />
348+ { session . review . dimensions . length } { formatMessage ( { id : 'sessions.card.dimensions' } ) }
349+ </ span >
350+ ) }
351+ { session . review ?. findings !== undefined && (
352+ < span className = "flex items-center gap-1" >
353+ < FileText className = "h-3.5 w-3.5" />
354+ { typeof session . review . findings === 'number'
355+ ? session . review . findings
356+ : session . review . dimensions ?. reduce ( ( sum , dim ) => sum + ( dim . findings ?. length || 0 ) , 0 ) || 0
357+ } { formatMessage ( { id : 'sessions.card.findings' } ) }
358+ </ span >
359+ ) }
360+ </ >
361+ ) : (
362+ < >
363+ { /* Workflow/other sessions: Show tasks */ }
364+ < span className = "flex items-center gap-1" >
365+ < ListChecks className = "h-3.5 w-3.5" />
366+ { progress . total } { formatMessage ( { id : 'sessions.card.tasks' } ) }
367+ </ span >
368+ { progress . total > 0 && (
369+ < span className = "flex items-center gap-1" >
370+ < CheckCircle2 className = "h-3.5 w-3.5 text-success" />
371+ { progress . completed } { formatMessage ( { id : 'sessions.card.completed' } ) }
372+ </ span >
373+ ) }
374+ </ >
309375 ) }
310376 { session . updated_at && session . updated_at !== session . created_at && (
311377 < span className = "flex items-center gap-1" >
@@ -315,15 +381,9 @@ export function SessionCard({
315381 ) }
316382 </ div >
317383
318- { /* Task status badges */ }
319- { progress . total > 0 && (
384+ { /* Task status badges - only for non-review sessions */ }
385+ { session . type !== 'review' && progress . total > 0 && (
320386 < div className = "flex flex-wrap items-center gap-1.5 mt-2" >
321- { progress . pending > 0 && (
322- < Badge variant = "warning" className = "gap-1 px-1.5 py-0 text-[10px]" >
323- < Clock className = "h-3 w-3" />
324- { progress . pending } { formatMessage ( { id : 'sessions.taskStatus.pending' } ) }
325- </ Badge >
326- ) }
327387 { progress . inProgress > 0 && (
328388 < Badge variant = "info" className = "gap-1 px-1.5 py-0 text-[10px]" >
329389 < RefreshCw className = "h-3 w-3" />
@@ -345,8 +405,38 @@ export function SessionCard({
345405 </ div >
346406 ) }
347407
348- { /* Progress bar (only show if not planning and has tasks) */ }
349- { progress . total > 0 && ! isPlanning && (
408+ { /* Severity badges - only for review sessions */ }
409+ { session . type === 'review' && severity . total > 0 && (
410+ < div className = "flex flex-wrap items-center gap-1.5 mt-2" >
411+ { severity . critical > 0 && (
412+ < Badge variant = "destructive" className = "gap-1 px-1.5 py-0 text-[10px]" >
413+ < AlertCircle className = "h-3 w-3" />
414+ { severity . critical } Critical
415+ </ Badge >
416+ ) }
417+ { severity . high > 0 && (
418+ < Badge variant = "warning" className = "gap-1 px-1.5 py-0 text-[10px]" >
419+ < AlertCircle className = "h-3 w-3" />
420+ { severity . high } High
421+ </ Badge >
422+ ) }
423+ { severity . medium > 0 && (
424+ < Badge variant = "info" className = "gap-1 px-1.5 py-0 text-[10px]" >
425+ < Search className = "h-3 w-3" />
426+ { severity . medium } Medium
427+ </ Badge >
428+ ) }
429+ { severity . low > 0 && (
430+ < Badge variant = "secondary" className = "gap-1 px-1.5 py-0 text-[10px]" >
431+ < FileText className = "h-3 w-3" />
432+ { severity . low } Low
433+ </ Badge >
434+ ) }
435+ </ div >
436+ ) }
437+
438+ { /* Progress bar (only show for non-review sessions with tasks) */ }
439+ { session . type !== 'review' && progress . total > 0 && ! isPlanning && (
350440 < div className = "mt-3" >
351441 < div className = "flex items-center justify-between text-xs mb-1" >
352442 < span className = "text-muted-foreground" > { formatMessage ( { id : 'sessions.card.progress' } ) } </ span >
0 commit comments