@@ -8,6 +8,7 @@ import type { Control, Task } from '@db';
88import { BarChart3 , Clock } from 'lucide-react' ;
99import Link from 'next/link' ;
1010import { useParams } from 'next/navigation' ;
11+ import { computeFrameworkStats } from '../lib/compute' ;
1112import type { FrameworkInstanceWithControls } from '../types' ;
1213
1314interface FrameworkCardProps {
@@ -51,44 +52,13 @@ export function FrameworkCard({
5152 return 'text-red-600 dark:text-red-400' ;
5253 } ;
5354
54- const controlsCount = frameworkInstance . controls ?. length || 0 ;
55- const compliantControlsCount = Math . round ( ( complianceScore / 100 ) * controlsCount ) ;
56-
57- // Calculate not started controls: controls where all policies are draft or non-existent AND all tasks are todo or non-existent
58- const notStartedControlsCount =
59- frameworkInstance . controls ?. filter ( ( control ) => {
60- // If a control has no policies and no tasks, it's not started.
61- const controlTasks = tasks . filter ( ( task ) => task . controls . some ( ( c ) => c . id === control . id ) ) ;
62-
63- if ( ( ! control . policies || control . policies . length === 0 ) && controlTasks . length === 0 ) {
64- return true ;
65- }
66-
67- // Check if ALL policies are in draft state or non-existent
68- const policiesNotStarted =
69- ! control . policies ||
70- control . policies . length === 0 ||
71- control . policies . every ( ( policy ) => policy . status === 'draft' ) ;
72-
73- // Check if ALL tasks are in todo state or there are no tasks
74- const tasksNotStarted =
75- controlTasks . length === 0 || controlTasks . every ( ( task ) => task . status === 'todo' ) ;
76-
77- return policiesNotStarted && tasksNotStarted ;
78- // If either any policy is not draft or any task is not todo, it's in progress
79- } ) . length || 0 ;
80-
81- // Calculate in progress controls: Total - Compliant - Not Started
82- const inProgressCount = Math . max (
83- 0 , // Ensure count doesn't go below zero
84- controlsCount - compliantControlsCount - notStartedControlsCount ,
55+ const { totalPolicies, publishedPolicies, totalTasks, doneTasks } = computeFrameworkStats (
56+ frameworkInstance ,
57+ tasks ,
8558 ) ;
8659
87- // Use direct framework data:
8860 const frameworkDetails = frameworkInstance . framework ;
8961 const statusBadge = getStatusBadge ( complianceScore ) ;
90-
91- // Calculate last activity date - use current date as fallback
9262 const lastActivityDate = new Date ( ) . toLocaleDateString ( 'en-US' , {
9363 year : 'numeric' ,
9464 month : 'short' ,
@@ -129,11 +99,21 @@ export function FrameworkCard({
12999 < Progress value = { complianceScore } className = "h-1" />
130100 </ div >
131101
132- { /* Stats */ }
133- < div className = "text-muted-foreground flex items-center justify-between text-xs" >
134- < span > { compliantControlsCount } complete</ span >
135- < span > { inProgressCount } active</ span >
136- < span > { controlsCount } total</ span >
102+ { /* Breakdown */ }
103+ < div className = "text-muted-foreground space-y-1.5 text-xs" >
104+ < div className = "flex items-center justify-between" >
105+ < span > Policies</ span >
106+ < span className = "tabular-nums" >
107+ { publishedPolicies } /{ totalPolicies } published
108+ </ span >
109+ </ div >
110+ < div className = "flex items-center justify-between" >
111+ < span > Tasks</ span >
112+ < span className = "tabular-nums" >
113+ { doneTasks } /{ totalTasks } done
114+ </ span >
115+ </ div >
116+ { /* Intentionally omit controls in card to reduce noise */ }
137117 </ div >
138118
139119 { /* Footer */ }
0 commit comments