@@ -32,6 +32,7 @@ interface MeetingSchedule {
3232interface MeetingBasedAttendanceProps {
3333 teamMembers : SimpleMemberData [ ] ;
3434 teamLead : SimpleMemberData | null ;
35+ readOnly ?: boolean ; // ✅ ADDED: Controls edit access
3536 projectId : string ;
3637 meetingSchedule : MeetingSchedule [ ] | null ;
3738 onHasChangesUpdate ?: ( hasChanges : boolean ) => void ;
@@ -41,6 +42,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
4142 teamMembers,
4243 teamLead,
4344 projectId,
45+ readOnly = false ,
4446 meetingSchedule,
4547 onHasChangesUpdate
4648} , ref ) => {
@@ -72,7 +74,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
7274 // Get day name from day of week
7375 const getDayName = ( dayOfWeek : number ) : string => {
7476 const days = [ "sunday" , "monday" , "tuesday" , "wednesday" , "thursday" , "friday" , "saturday" ] ;
75- return days [ dayOfWeek ] ;
77+ return days [ dayOfWeek ] ! ;
7678 } ;
7779
7880 // Month names
@@ -144,6 +146,12 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
144146
145147 // Toggle attendance status
146148 const toggleAttendance = ( memberId : string , day : number ) => {
149+ // Prevent editing in read-only mode
150+ if ( readOnly ) {
151+ toast . error ( "You don't have permission to edit attendance." ) ;
152+ return ;
153+ }
154+
147155 const scheduledMeeting = hasScheduledMeeting ( day ) ;
148156
149157 // Only allow changes for scheduled meeting days
@@ -167,6 +175,12 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
167175
168176 // Save all attendance changes
169177 const saveAllAttendance = useCallback ( async ( ) => {
178+ // Prevent saving in read-only mode
179+ if ( readOnly ) {
180+ toast . error ( "You don't have permission to save attendance." ) ;
181+ return ;
182+ }
183+
170184 setIsSaving ( true ) ;
171185 try {
172186 const records : any [ ] = [ ] ;
@@ -186,14 +200,20 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
186200
187201 const dateStr = `${ selectedYear } -${ String ( selectedMonth + 1 ) . padStart ( 2 , '0' ) } -${ String ( day ) . padStart ( 2 , '0' ) } ` ;
188202
203+ const checkOutTime = ( status === "present" || status === "excused" ) && scheduledMeeting . time
204+ ? ( ( ) => {
205+ const [ hours , minutes ] = scheduledMeeting . time . split ( ':' ) ;
206+ // ...
207+ } ) ( )
208+ : undefined ;
209+
189210 records . push ( {
190211 codev_id : member . id ,
191212 project_id : projectId ,
192213 date : dateStr ,
193214 status : status as any ,
194215 check_in : status === "present" || status === "excused" ? scheduledMeeting . time : undefined ,
195- check_out : status === "present" || status === "excused" ?
196- `${ parseInt ( scheduledMeeting . time . split ( ':' ) [ 0 ] ) + 1 } :${ scheduledMeeting . time . split ( ':' ) [ 1 ] } ` : undefined
216+ check_out : checkOutTime
197217 } ) ;
198218 } ) ;
199219 } ) ;
@@ -209,7 +229,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
209229 if ( warningResult . success && warningResult . warnings && warningResult . warnings . length > 0 ) {
210230 const warningCount = warningResult . warnings . filter ( w => w . notificationSent ) . length ;
211231 if ( warningCount > 0 ) {
212- toast . warning ( ` ${ warningCount } member${ warningCount > 1 ? 's' : '' } received attendance warnings`) ;
232+ toast . error ( `⚠️ ${ warningCount } member${ warningCount > 1 ? 's' : '' } received attendance warnings`) ;
213233 }
214234 }
215235 } else {
@@ -221,7 +241,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
221241 } finally {
222242 setIsSaving ( false ) ;
223243 }
224- } , [ allMembers , monthDays , selectedYear , selectedMonth , attendanceData , projectId , meetingSchedule ] ) ;
244+ } , [ readOnly , allMembers , monthDays , selectedYear , selectedMonth , attendanceData , projectId , meetingSchedule ] ) ;
225245
226246 // Expose save function to parent via ref
227247 useImperativeHandle ( ref , ( ) => ( {
@@ -383,9 +403,17 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
383403 { /* Header */ }
384404 < div className = "bg-gray-50 dark:bg-gray-900 p-2 sm:p-3 border-b border-gray-200 dark:border-gray-700" >
385405 < div className = "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2" >
386- < h3 className = "text-sm sm:text-base font-semibold text-gray-900 dark:text-white" >
387- Meeting Attendance Tracker
388- </ h3 >
406+ < div className = "flex items-center gap-3" >
407+ < h3 className = "text-sm sm:text-base font-semibold text-gray-900 dark:text-white" >
408+ Meeting Attendance Tracker
409+ </ h3 >
410+ { /* Read-only indicator badge */ }
411+ { readOnly && (
412+ < span className = "text-xs bg-amber-100 text-amber-700 dark:bg-amber-900/20 dark:text-amber-300 px-2 py-0.5 rounded-full" >
413+ View Only
414+ </ span >
415+ ) }
416+ </ div >
389417 < div className = "flex items-center gap-1 sm:gap-2" >
390418 < Button
391419 variant = "ghost"
@@ -439,6 +467,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
439467
440468 { /* Attendance Grid */ }
441469 < div className = "overflow-x-auto max-w-full" >
470+ < TooltipProvider >
442471 < table className = "w-full table-auto" >
443472 < thead >
444473 < tr className = "border-b border-gray-200 dark:border-gray-700" >
@@ -452,8 +481,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
452481 const dayName = getDayName ( dayOfWeek ) ;
453482
454483 return (
455- < TooltipProvider key = { day } >
456- < Tooltip >
484+ < Tooltip key = { day } >
457485 < TooltipTrigger asChild >
458486 < th
459487 className = { `p-0 text-center text-[9px] font-medium w-7 sm:w-8 ${
@@ -477,7 +505,6 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
477505 </ p >
478506 </ TooltipContent >
479507 </ Tooltip >
480- </ TooltipProvider >
481508 ) ;
482509 } ) }
483510 < th className = "sticky right-0 z-10 bg-green-50 dark:bg-green-900/20 text-center p-1 text-[9px] font-medium text-gray-700 dark:text-gray-300 w-14 sm:w-16" >
@@ -540,7 +567,8 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
540567 } `}
541568 >
542569 < div className = "flex items-center justify-center p-1" >
543- { scheduledMeeting ? (
570+ { /* Disable interaction in read-only mode */ }
571+ { scheduledMeeting && ! readOnly ? (
544572 < button
545573 onClick = { ( ) => toggleAttendance ( member . id , day ) }
546574 className = "hover:scale-110 transition-transform p-1"
@@ -549,8 +577,8 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
549577 { getStatusIcon ( status , true ) }
550578 </ button >
551579 ) : (
552- < div className = "cursor-not-allowed" >
553- { getStatusIcon ( "not_scheduled" , false ) }
580+ < div className = { readOnly && scheduledMeeting ? "cursor-not-allowed opacity-70" : "cursor-not-allowed" } >
581+ { getStatusIcon ( scheduledMeeting ? status : "not_scheduled" , ! ! scheduledMeeting ) }
554582 </ div >
555583 ) }
556584 </ div >
@@ -577,6 +605,7 @@ const MeetingBasedAttendance = forwardRef<any, MeetingBasedAttendanceProps>(({
577605 } ) }
578606 </ tbody >
579607 </ table >
608+ </ TooltipProvider >
580609 </ div >
581610
582611 { /* Legend */ }
0 commit comments