@@ -15,7 +15,7 @@ import { useCurrentUser } from '@/modules/auth/AuthProvider';
15
15
import { api } from '@workspace/backend/convex/_generated/api' ;
16
16
import type { Doc } from '@workspace/backend/convex/_generated/dataModel' ;
17
17
import { useSessionQuery } from 'convex-helpers/react/sessions' ;
18
- import { CheckCircle2 , ChevronDown , LogIn , XCircle } from 'lucide-react' ;
18
+ import { CheckCircle2 , ChevronDown , UserPlus , XCircle } from 'lucide-react' ;
19
19
import { useRouter , useSearchParams } from 'next/navigation' ;
20
20
import { Suspense , useCallback , useEffect , useState } from 'react' ;
21
21
import { AttendanceDialog } from './AttendanceDialog' ;
@@ -25,13 +25,15 @@ interface AttendanceModuleProps {
25
25
attendanceKey : string ;
26
26
title : string ;
27
27
expectedNames ?: string [ ] ;
28
+ remarksPlaceholder ?: string ;
28
29
}
29
30
30
31
// Internal component that uses useSearchParams
31
32
const AttendanceContent = ( {
32
33
attendanceKey,
33
34
title = 'Attendance' ,
34
35
expectedNames = [ ] ,
36
+ remarksPlaceholder,
35
37
} : AttendanceModuleProps ) => {
36
38
const router = useRouter ( ) ;
37
39
const searchParams = useSearchParams ( ) ;
@@ -42,8 +44,8 @@ const AttendanceContent = ({
42
44
const [ selectedPerson , setSelectedPerson ] = useState < string > ( '' ) ;
43
45
const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
44
46
const [ showFullListModal , setShowFullListModal ] = useState ( false ) ;
45
- const [ loginDialogOpen , setLoginDialogOpen ] = useState ( false ) ;
46
47
const [ modalSearchQuery , setModalSearchQuery ] = useState ( '' ) ;
48
+ const [ isManualJoin , setIsManualJoin ] = useState ( false ) ;
47
49
const attendanceData = useSessionQuery ( api . attendance . getAttendanceData , {
48
50
attendanceKey,
49
51
} ) ;
@@ -102,13 +104,10 @@ const AttendanceContent = ({
102
104
}
103
105
104
106
const handleJoin = ( ) => {
105
- if ( ! isAuthenticated || ! currentUser ) {
106
- setLoginDialogOpen ( true ) ;
107
- return ;
108
- }
109
-
110
- // Open the dialog with the current user's name pre-selected
111
- setSelectedPerson ( currentUser . name ) ;
107
+ // If user is authenticated, pre-fill their name, otherwise open with empty name
108
+ const defaultName = isAuthenticated && currentUser ? currentUser . name : '' ;
109
+ setSelectedPerson ( defaultName ) ;
110
+ setIsManualJoin ( true ) ; // Always true when using the join button
112
111
setDialogOpen ( true ) ;
113
112
} ;
114
113
@@ -118,12 +117,17 @@ const AttendanceContent = ({
118
117
119
118
const handlePersonClick = ( name : string ) => {
120
119
setSelectedPerson ( name ) ;
120
+ // If the name is not in the expected list, consider it a manual join
121
+ // This handles cases where someone manually added themselves but we're now clicking on their name
122
+ const wasInExpectedList = expectedNames ?. includes ( name ) ;
123
+ setIsManualJoin ( ! wasInExpectedList ) ;
121
124
setDialogOpen ( true ) ;
122
125
} ;
123
126
124
127
const handleDialogClose = ( ) => {
125
128
setDialogOpen ( false ) ;
126
129
setSelectedPerson ( '' ) ;
130
+ setIsManualJoin ( false ) ;
127
131
} ;
128
132
129
133
// Handle successful attendance submission
@@ -250,7 +254,11 @@ const AttendanceContent = ({
250
254
251
255
< TabsContent value = "responded" >
252
256
{ respondedNames . length === 0 ? (
253
- < AttendanceEmptyState message = "No results found" onJoin = { handleJoin } />
257
+ < AttendanceEmptyState
258
+ message = "No results found"
259
+ onJoin = { handleJoin }
260
+ showJoinButton = { ! searchQuery . trim ( ) }
261
+ />
254
262
) : (
255
263
< div className = "space-y-2" >
256
264
{ respondedNames . slice ( 0 , 7 ) . map ( ( name ) => {
@@ -316,7 +324,11 @@ const AttendanceContent = ({
316
324
317
325
< TabsContent value = "pending" >
318
326
{ pendingNames . length === 0 ? (
319
- < AttendanceEmptyState message = "No results found" onJoin = { handleJoin } />
327
+ < AttendanceEmptyState
328
+ message = "No results found"
329
+ onJoin = { handleJoin }
330
+ showJoinButton = { ! searchQuery . trim ( ) }
331
+ />
320
332
) : (
321
333
< div className = "space-y-2" >
322
334
{ pendingNames . slice ( 0 , 7 ) . map ( ( name ) => {
@@ -360,28 +372,25 @@ const AttendanceContent = ({
360
372
) }
361
373
</ TabsContent >
362
374
</ Tabs >
375
+
376
+ { /* Join button below component */ }
377
+ { ( ! isAuthenticated || ! isCurrentUserRegistered ) && (
378
+ < div className = "mt-6 text-center" >
379
+ < p className = "text-muted-foreground mb-2" > Don't see your name?</ p >
380
+ < Button
381
+ onClick = { handleJoin }
382
+ className = "flex items-center justify-center mx-auto w-fit"
383
+ variant = "outline"
384
+ >
385
+ < UserPlus className = "h-4 w-4 mr-2" />
386
+ Join the list
387
+ </ Button >
388
+ </ div >
389
+ ) }
363
390
</ >
364
391
) }
365
392
</ div >
366
393
367
- { /* Login dialog */ }
368
- < Dialog open = { loginDialogOpen } onOpenChange = { setLoginDialogOpen } >
369
- < DialogContent >
370
- < DialogHeader >
371
- < DialogTitle > Login Required</ DialogTitle >
372
- < DialogDescription > You need to be logged in to mark your attendance.</ DialogDescription >
373
- </ DialogHeader >
374
- < DialogFooter className = "flex justify-end gap-2 mt-4" >
375
- < Button variant = "outline" onClick = { ( ) => setLoginDialogOpen ( false ) } >
376
- Cancel
377
- </ Button >
378
- < Button onClick = { ( ) => router . push ( '/login' ) } className = "flex items-center" >
379
- < LogIn className = "h-4 w-4 mr-2" /> Go to Login
380
- </ Button >
381
- </ DialogFooter >
382
- </ DialogContent >
383
- </ Dialog >
384
-
385
394
{ /* Full list modal - update to show based on active tab */ }
386
395
< Dialog open = { showFullListModal } onOpenChange = { setShowFullListModal } >
387
396
< DialogContent className = "sm:max-w-[425px]" >
@@ -418,12 +427,16 @@ const AttendanceContent = ({
418
427
type = "button"
419
428
onClick = { ( ) => {
420
429
setSelectedPerson ( name ) ;
430
+ const wasInExpectedList = expectedNames ?. includes ( name ) ;
431
+ setIsManualJoin ( ! wasInExpectedList ) ;
421
432
setDialogOpen ( true ) ;
422
433
setShowFullListModal ( false ) ;
423
434
} }
424
435
onKeyDown = { ( e ) => {
425
436
if ( e . key === 'Enter' ) {
426
437
setSelectedPerson ( name ) ;
438
+ const wasInExpectedList = expectedNames ?. includes ( name ) ;
439
+ setIsManualJoin ( ! wasInExpectedList ) ;
427
440
setDialogOpen ( true ) ;
428
441
setShowFullListModal ( false ) ;
429
442
}
@@ -465,6 +478,7 @@ const AttendanceContent = ({
465
478
setShowFullListModal ( false ) ;
466
479
handleJoin ( ) ;
467
480
} }
481
+ showJoinButton = { ! modalSearchQuery . trim ( ) }
468
482
/>
469
483
) }
470
484
</ ScrollArea >
@@ -480,6 +494,8 @@ const AttendanceContent = ({
480
494
attendanceKey = { attendanceKey }
481
495
attendanceRecords = { attendanceRecords }
482
496
onSuccess = { handleAttendanceSuccess }
497
+ isManuallyJoined = { isManualJoin }
498
+ remarksPlaceholder = { remarksPlaceholder }
483
499
/>
484
500
) }
485
501
</ >
0 commit comments