1+ "use client" ;
2+
3+ import { useState , useEffect } from "react" ;
4+ import { CheckCircle2 , Circle , Calendar } from "lucide-react" ;
5+
6+ interface ChecklistItem {
7+ id : string ;
8+ title : string ;
9+ description : string ;
10+ completed : boolean ;
11+ dueDate ?: string ;
12+ priority : "low" | "medium" | "high" ;
13+ }
14+
15+ interface MemberChecklistProps {
16+ memberId : string ;
17+ projectId : string ;
18+ }
19+
20+ const MemberChecklist = ( { memberId, projectId } : MemberChecklistProps ) => {
21+ const [ checklistItems , setChecklistItems ] = useState < ChecklistItem [ ] > ( [ ] ) ;
22+ const [ isLoading , setIsLoading ] = useState ( true ) ;
23+
24+ useEffect ( ( ) => {
25+ loadChecklistItems ( ) ;
26+ } , [ memberId , projectId ] ) ;
27+
28+ const loadChecklistItems = async ( ) => {
29+ if ( ! memberId || ! projectId ) return ;
30+
31+ setIsLoading ( true ) ;
32+ try {
33+ // Mock data for now - in real implementation, this would fetch from API
34+ // You can replace this with actual API call: const response = await fetch(`/api/projects/${projectId}/members/${memberId}/checklist`);
35+
36+ // Simulate API delay
37+ await new Promise ( resolve => setTimeout ( resolve , 300 ) ) ;
38+
39+ setChecklistItems ( [
40+ {
41+ id : "1" ,
42+ title : "Complete onboarding documentation" ,
43+ description : "Fill out all required forms and complete the onboarding checklist" ,
44+ completed : true ,
45+ priority : "high"
46+ } ,
47+ {
48+ id : "2" ,
49+ title : "Set up development environment" ,
50+ description : "Install required tools and configure local development setup" ,
51+ completed : true ,
52+ priority : "high"
53+ } ,
54+ {
55+ id : "3" ,
56+ title : "Review project documentation" ,
57+ description : "Go through the project README, API docs, and architecture overview" ,
58+ completed : true ,
59+ priority : "medium"
60+ } ,
61+ {
62+ id : "4" ,
63+ title : "Implement user authentication feature" ,
64+ description : "Create login/logout functionality with proper validation" ,
65+ completed : false ,
66+ dueDate : "2025-10-20" ,
67+ priority : "high"
68+ } ,
69+ {
70+ id : "5" ,
71+ title : "Write unit tests for API endpoints" ,
72+ description : "Add comprehensive test coverage for all authentication endpoints" ,
73+ completed : false ,
74+ dueDate : "2025-10-25" ,
75+ priority : "medium"
76+ } ,
77+ {
78+ id : "6" ,
79+ title : "Code review training session" ,
80+ description : "Attend the team code review best practices workshop" ,
81+ completed : false ,
82+ dueDate : "2025-10-30" ,
83+ priority : "low"
84+ }
85+ ] ) ;
86+ } catch ( error ) {
87+ console . error ( "Error loading checklist items:" , error ) ;
88+ setChecklistItems ( [ ] ) ;
89+ } finally {
90+ setIsLoading ( false ) ;
91+ }
92+ } ;
93+ const completedTasks = checklistItems . filter ( item => item . completed ) . length ;
94+ const totalTasks = checklistItems . length ;
95+ const completionPercentage = totalTasks > 0 ? Math . round ( ( completedTasks / totalTasks ) * 100 ) : 0 ;
96+
97+ const getPriorityColor = ( priority : string ) => {
98+ switch ( priority ) {
99+ case "high" : return "text-red-600 bg-red-100 border-red-200" ;
100+ case "medium" : return "text-yellow-600 bg-yellow-100 border-yellow-200" ;
101+ case "low" : return "text-green-600 bg-green-100 border-green-200" ;
102+ default : return "text-gray-600 bg-gray-100 border-gray-200" ;
103+ }
104+ } ;
105+
106+ if ( isLoading ) {
107+ return (
108+ < div className = "flex items-center justify-center py-8" >
109+ < div className = "text-center" >
110+ < div className = "animate-spin rounded-full h-8 w-8 border-b-2 border-green-500 mx-auto mb-4" > </ div >
111+ < p className = "text-gray-600 dark:text-gray-300" > Loading checklist...</ p >
112+ </ div >
113+ </ div >
114+ ) ;
115+ }
116+
117+ if ( checklistItems . length === 0 ) {
118+ return (
119+ < div className = "flex items-center justify-center py-8" >
120+ < div className = "text-center" >
121+ < CheckCircle2 className = "h-12 w-12 text-gray-400 mx-auto mb-4" />
122+ < p className = "text-gray-600 dark:text-gray-300" > No checklist items available</ p >
123+ </ div >
124+ </ div >
125+ ) ;
126+ }
127+
128+ return (
129+ < div className = "space-y-6" >
130+ { /* Progress Overview */ }
131+ < Section title = "Progress Overview" >
132+ < div className = "flex items-center gap-4 p-4 rounded-lg bg-white/20 backdrop-blur-sm dark:bg-white/10 border border-white/30 dark:border-white/20" >
133+ < div className = "text-center" >
134+ < div className = "text-3xl font-bold text-green-600 dark:text-green-400" >
135+ { completionPercentage } %
136+ </ div >
137+ < div className = "text-sm text-gray-500" > Complete</ div >
138+ </ div >
139+ < div className = "flex-1" >
140+ < div className = "flex justify-between text-sm text-gray-600 dark:text-gray-400 mb-2" >
141+ < span > { completedTasks } of { totalTasks } tasks completed</ span >
142+ </ div >
143+ < div className = "w-full bg-gray-200 rounded-full h-2" >
144+ < div
145+ className = "bg-green-500 h-2 rounded-full transition-all duration-300"
146+ style = { { width : `${ completionPercentage } %` } }
147+ > </ div >
148+ </ div >
149+ </ div >
150+ </ div >
151+ </ Section >
152+
153+ { /* Task List */ }
154+ < Section title = "Task List" >
155+ < div className = "space-y-3" >
156+ { checklistItems . map ( ( item ) => (
157+ < div
158+ key = { item . id }
159+ className = { `p-4 rounded-lg border transition-all duration-200 ${
160+ item . completed
161+ ? 'bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-800'
162+ : 'bg-white/20 backdrop-blur-sm dark:bg-white/10 border-white/30 dark:border-white/20'
163+ } `}
164+ >
165+ < div className = "flex items-start gap-3" >
166+ < div className = "flex-shrink-0 mt-0.5" >
167+ { item . completed ? (
168+ < CheckCircle2 className = "h-5 w-5 text-green-600" />
169+ ) : (
170+ < Circle className = "h-5 w-5 text-gray-400" />
171+ ) }
172+ </ div >
173+ < div className = "flex-1 min-w-0" >
174+ < div className = "flex items-start justify-between gap-2" >
175+ < h4 className = { `font-medium ${
176+ item . completed
177+ ? 'text-green-800 dark:text-green-200 line-through'
178+ : 'text-gray-900 dark:text-white'
179+ } `} >
180+ { item . title }
181+ </ h4 >
182+ < span className = { `px-2 py-1 text-xs font-medium rounded-full border ${ getPriorityColor ( item . priority ) } ` } >
183+ { item . priority }
184+ </ span >
185+ </ div >
186+ < p className = { `text-sm mt-1 ${
187+ item . completed
188+ ? 'text-green-700 dark:text-green-300'
189+ : 'text-gray-600 dark:text-gray-400'
190+ } `} >
191+ { item . description }
192+ </ p >
193+ { item . dueDate && (
194+ < p className = "text-xs text-gray-500 dark:text-gray-400 mt-2 flex items-center gap-1" >
195+ < Calendar className = "h-3 w-3" />
196+ Due: { new Date ( item . dueDate ) . toLocaleDateString ( ) }
197+ </ p >
198+ ) }
199+ </ div >
200+ </ div >
201+ </ div >
202+ ) ) }
203+ </ div >
204+ </ Section >
205+ </ div >
206+ ) ;
207+ } ;
208+
209+ // Helper Component
210+ const Section = ( {
211+ title,
212+ children,
213+ } : {
214+ title : string ;
215+ children : React . ReactNode ;
216+ } ) => (
217+ < div className = "space-y-3" >
218+ < h3 className = "text-lg font-medium" > { title } </ h3 >
219+ { children }
220+ </ div >
221+ ) ;
222+
223+ export default MemberChecklist ;
0 commit comments