1+ import React from "react" ;
2+ import { useNavigate } from "react-router-dom" ;
3+ import { Button } from "../ui/button" ;
4+ import { Avatar , AvatarFallback , AvatarImage } from "../ui/avatar" ;
5+
6+ export interface ActiveCollabCardProps {
7+ id : number ;
8+ collaborator : {
9+ name : string ;
10+ avatar : string ;
11+ contentType : string ;
12+ } ;
13+ collabTitle : string ;
14+ status : string ;
15+ startDate : string ;
16+ dueDate : string ;
17+ messages : number ;
18+ deliverables : { completed : number ; total : number } ;
19+ lastActivity : string ;
20+ latestUpdate : string ;
21+ }
22+
23+ const statusColors : Record < string , string > = {
24+ "In Progress" : "bg-blue-100 text-blue-700" ,
25+ "Awaiting Response" : "bg-yellow-100 text-yellow-700" ,
26+ "Completed" : "bg-green-100 text-green-700"
27+ } ;
28+
29+ function getDaysBetween ( start : string , end : string ) {
30+ const s = new Date ( start ) ;
31+ const e = new Date ( end ) ;
32+ if ( isNaN ( s . getTime ( ) ) || isNaN ( e . getTime ( ) ) ) return 0 ;
33+ const diff = e . getTime ( ) - s . getTime ( ) ;
34+ if ( diff < 0 ) return 0 ;
35+ return Math . ceil ( diff / ( 1000 * 60 * 60 * 24 ) ) ;
36+ }
37+
38+ function getDaysLeft ( due : string ) {
39+ const now = new Date ( ) ;
40+ const d = new Date ( due ) ;
41+ if ( isNaN ( d . getTime ( ) ) ) return 0 ;
42+ const diff = d . getTime ( ) - now . getTime ( ) ;
43+ // Allow negative for overdue, but if invalid, return 0
44+ return Math . ceil ( diff / ( 1000 * 60 * 60 * 24 ) ) ;
45+ }
46+
47+ function getTimelineProgress ( start : string , due : string ) {
48+ const total = getDaysBetween ( start , due ) ;
49+ if ( total === 0 ) return 0 ;
50+ const elapsed = getDaysBetween ( start , new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) ) ;
51+ return Math . min ( 100 , Math . max ( 0 , Math . round ( ( elapsed / total ) * 100 ) ) ) ;
52+ }
53+
54+ const ActiveCollabCard : React . FC < ActiveCollabCardProps > = ( {
55+ id,
56+ collaborator,
57+ collabTitle,
58+ status,
59+ startDate,
60+ dueDate,
61+ messages,
62+ deliverables,
63+ lastActivity,
64+ latestUpdate
65+ } ) => {
66+ const navigate = useNavigate ( ) ;
67+ const deliverableProgress = Math . round ( ( deliverables . completed / deliverables . total ) * 100 ) ;
68+ const timelineProgress = getTimelineProgress ( startDate , dueDate ) ;
69+ const daysLeft = getDaysLeft ( dueDate ) ;
70+ const overdue = daysLeft < 0 && status !== "Completed" ;
71+
72+ return (
73+ < div className = "bg-white rounded-xl shadow p-5 flex flex-col gap-3 border border-gray-100 w-full max-w-xl mx-auto" >
74+ < div className = "flex items-center gap-4" >
75+ < Avatar className = "h-12 w-12" >
76+ < AvatarImage src = { collaborator . avatar } alt = { collaborator . name } />
77+ < AvatarFallback className = "bg-gray-200" > { collaborator . name . slice ( 0 , 2 ) . toUpperCase ( ) } </ AvatarFallback >
78+ </ Avatar >
79+ < div className = "flex-1" >
80+ < div className = "font-semibold text-lg text-gray-900" > { collaborator . name } </ div >
81+ < div className = "text-xs text-gray-500" > { collaborator . contentType } </ div >
82+ </ div >
83+ < span className = { `px-3 py-1 rounded-full text-xs font-semibold ${ statusColors [ status ] || "bg-gray-100 text-gray-700" } ` } > { status } </ span >
84+ </ div >
85+ < div className = "flex flex-wrap items-center gap-2 text-sm text-gray-700" >
86+ < span className = "font-semibold" > Collab:</ span > { collabTitle }
87+ < span className = "ml-4 font-semibold" > Start:</ span > { startDate }
88+ < span className = "ml-4 font-semibold" > Due:</ span > < span className = { overdue ? "text-red-600 font-bold" : "" } > { dueDate } </ span >
89+ < span className = "ml-4 font-semibold" > { overdue ? `Overdue by ${ Math . abs ( daysLeft ) } days` : daysLeft === 0 ? "Due today" : `${ daysLeft } days left` } </ span >
90+ </ div >
91+ { /* Timeline Progress Bar */ }
92+ < div className = "w-full flex flex-col gap-1" >
93+ < div className = "flex justify-between text-xs text-gray-500" >
94+ < span > Timeline</ span >
95+ < span > { timelineProgress } %</ span >
96+ </ div >
97+ < div className = "w-full h-2 bg-gray-200 rounded-full overflow-hidden" >
98+ < div className = "h-2 rounded-full bg-blue-400" style = { { width : `${ timelineProgress } %` } } />
99+ </ div >
100+ </ div >
101+ { /* Deliverables Progress Bar */ }
102+ < div className = "w-full flex flex-col gap-1" >
103+ < div className = "flex justify-between text-xs text-gray-500" >
104+ < span > Deliverables</ span >
105+ < span > { deliverables . completed } /{ deliverables . total } ({ deliverableProgress } %)</ span >
106+ </ div >
107+ < div className = "w-full h-2 bg-gray-200 rounded-full overflow-hidden" >
108+ < div className = "h-2 rounded-full bg-green-400" style = { { width : `${ deliverableProgress } %` } } />
109+ </ div >
110+ </ div >
111+ < div className = "flex flex-wrap items-center gap-4 text-xs text-gray-600" >
112+ < span > Messages: < span className = "font-semibold text-gray-900" > { messages } </ span > </ span >
113+ < span > Last activity: < span className = "font-semibold text-gray-900" > { lastActivity } </ span > </ span >
114+ </ div >
115+ < div className = "text-xs text-gray-700 italic bg-gray-50 rounded px-3 py-2 border border-gray-100" >
116+ < span className = "font-semibold text-gray-800" > Latest update:</ span > { latestUpdate }
117+ </ div >
118+ < div className = "flex gap-2 mt-2" >
119+ < Button
120+ className = "bg-gray-100 text-gray-900 hover:bg-gray-200 font-semibold rounded-full py-2 focus:outline-none focus:ring-2 focus:ring-blue-400"
121+ variant = "secondary"
122+ onClick = { ( ) => navigate ( `/dashboard/collaborations/${ id } ` ) }
123+ aria-label = "View collaboration details"
124+ >
125+ View Details
126+ </ Button >
127+ < Button
128+ className = "bg-blue-100 text-blue-700 hover:bg-blue-200 font-semibold rounded-full py-2 focus:outline-none focus:ring-2 focus:ring-blue-400"
129+ aria-label = "Send message to collaborator"
130+ >
131+ Message
132+ </ Button >
133+ { status !== "Completed" && (
134+ < Button
135+ className = "bg-green-100 text-green-700 hover:bg-green-200 font-semibold rounded-full py-2 focus:outline-none focus:ring-2 focus:ring-green-400"
136+ aria-label = "Mark collaboration as complete"
137+ >
138+ Mark Complete
139+ </ Button >
140+ ) }
141+ </ div >
142+ </ div >
143+ ) ;
144+ } ;
145+
146+ export default ActiveCollabCard ;
0 commit comments