1
1
"use client" ;
2
2
3
- import { useSession } from "@/hooks/useSession" ;
3
+ import { useModal } from "@/hooks/use-modal" ;
4
+ import { CALLS_QUERY , TEAMS_QUERY } from "@/lib/QUERIES" ;
4
5
import type { Team } from "@/lib/types" ;
5
- import {
6
- AlertDialog ,
7
- AlertDialogContent ,
8
- AlertDialogHeader ,
9
- AlertDialogTitle ,
10
- } from "@call/ui/components/alert-dialog" ;
11
6
import { Button } from "@call/ui/components/button" ;
12
7
import {
13
8
Card ,
@@ -23,23 +18,15 @@ import {
23
18
DropdownMenuSeparator ,
24
19
DropdownMenuTrigger ,
25
20
} from "@call/ui/components/dropdown-menu" ;
21
+ import { LoadingButton } from "@call/ui/components/loading-button" ;
22
+ import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
26
23
import { MoreHorizontal , Users } from "lucide-react" ;
27
24
import { useRouter } from "next/navigation" ;
28
- import { useEffect , useState } from "react" ;
29
- import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
30
- import { TEAMS_QUERY } from "@/lib/QUERIES" ;
31
25
import { toast } from "sonner" ;
32
26
33
27
export const TeamSection = ( ) => {
34
28
const queryClient = useQueryClient ( ) ;
35
- const { session, isLoading : sessionLoading } = useSession ( ) ;
36
- const [ mounted , setMounted ] = useState ( false ) ;
37
- const [ addUsersOpen , setAddUsersOpen ] = useState < string | null > ( null ) ; // teamId or null
38
- const [ contacts , setContacts ] = useState < any [ ] > ( [ ] ) ;
39
- const [ selectedContacts , setSelectedContacts ] = useState < string [ ] > ( [ ] ) ;
40
- const [ addLoading , setAddLoading ] = useState ( false ) ;
41
- const [ addError , setAddError ] = useState < string | null > ( null ) ;
42
- const [ startingMeeting , setStartingMeeting ] = useState < string | null > ( null ) ;
29
+ const { onOpen } = useModal ( ) ;
43
30
const router = useRouter ( ) ;
44
31
45
32
const {
@@ -63,6 +50,7 @@ export const TeamSection = () => {
63
50
} ,
64
51
} ) ;
65
52
53
+
66
54
// Prevent hydration mismatch
67
55
useEffect ( ( ) => {
68
56
setMounted ( true ) ;
@@ -81,15 +69,8 @@ export const TeamSection = () => {
81
69
teamId : team . id ,
82
70
} ) ,
83
71
} ) ;
84
-
85
- if ( ! res . ok ) {
86
- const data = await res . json ( ) ;
87
- alert ( data . message || "Failed to start meeting" ) ;
88
- return ;
89
- }
90
-
91
- const data = await res . json ( ) ;
92
72
router . push ( `/app/call/${ data . callId } ` ) ;
73
+
93
74
} catch ( err ) {
94
75
alert ( "Network error starting meeting" ) ;
95
76
} finally {
@@ -118,52 +99,16 @@ export const TeamSection = () => {
118
99
body : JSON . stringify ( { emails : selectedContacts } ) ,
119
100
} ) ;
120
101
121
- const data = await res . json ( ) ;
122
- if ( ! res . ok ) {
123
- setAddError ( data . message || "Failed to add users" ) ;
124
- } else {
125
- setAddUsersOpen ( null ) ;
126
- setSelectedContacts ( [ ] ) ;
127
- // Invalidate teams query to refresh the list
128
- queryClient . invalidateQueries ( { queryKey : [ "teams" ] } ) ;
129
- }
130
- } catch ( err ) {
131
- setAddError ( "Network error adding users" ) ;
132
- } finally {
133
- setAddLoading ( false ) ;
134
- }
135
- } ;
136
102
137
- // Show loading state during initial mount to prevent hydration mismatch
138
- if ( ! mounted || sessionLoading ) {
139
- return (
140
- < div className = "space-y-6" >
141
- < div className = "flex items-center justify-between" >
142
- < div >
143
- < h1 className = "text-2xl font-bold" > Teams</ h1 >
144
- < p className = "text-muted-foreground" >
145
- Manage your teams and collaborate with others
146
- </ p >
147
- </ div >
148
- </ div >
149
- < div className = "flex h-32 items-center justify-center" >
150
- < p className = "text-muted-foreground" > Loading...</ p >
151
- </ div >
152
- </ div >
153
- ) ;
154
- }
155
-
156
- if ( ! session ?. user ) {
157
- return (
158
- < div className = "flex h-64 items-center justify-center" >
159
- < p className = "text-muted-foreground" > Please sign in to view teams</ p >
160
- </ div >
161
- ) ;
162
- }
103
+ const startTeamMeeting = async ( team : Team ) => {
104
+ createCall ( {
105
+ name : `${ team . name } Meeting` ,
106
+ members : team . members . map ( ( m ) => m . email ) ,
107
+ } ) ;
108
+ } ;
163
109
164
110
return (
165
111
< div className = "space-y-6 px-10" >
166
- { /* Header */ }
167
112
< div className = "flex items-center justify-between" >
168
113
< div >
169
114
< h1 className = "text-2xl font-bold" > Teams</ h1 >
@@ -220,7 +165,7 @@ export const TeamSection = () => {
220
165
Leave
221
166
</ DropdownMenuItem >
222
167
< DropdownMenuItem
223
- onClick = { ( ) => setAddUsersOpen ( team . id ) }
168
+ onClick = { ( ) => onOpen ( "add-member-to- team" , { team } ) }
224
169
>
225
170
Add users
226
171
</ DropdownMenuItem >
@@ -257,100 +202,18 @@ export const TeamSection = () => {
257
202
) }
258
203
</ div >
259
204
</ div >
260
-
261
- { /* Action Button */ }
262
- < Button
263
- className = "w-full"
264
- variant = "outline"
205
+ < LoadingButton
265
206
onClick = { ( ) => startTeamMeeting ( team ) }
266
- disabled = { startingMeeting === team . id }
207
+ loading = { createCallPending }
208
+ className = "w-full"
267
209
>
268
- { startingMeeting === team . id
269
- ? "Starting..."
270
- : "Start Meeting" }
271
- </ Button >
210
+ Start Meeting
211
+ </ LoadingButton >
272
212
</ CardContent >
273
213
</ Card >
274
214
) ) }
275
215
</ div >
276
216
) }
277
-
278
- { /* Modal for adding users to team */ }
279
- < AlertDialog
280
- open = { ! ! addUsersOpen }
281
- onOpenChange = { ( open : boolean ) => {
282
- if ( ! open ) setAddUsersOpen ( null ) ;
283
- } }
284
- >
285
- < AlertDialogContent >
286
- < AlertDialogHeader >
287
- < AlertDialogTitle > Add users to team</ AlertDialogTitle >
288
- </ AlertDialogHeader >
289
- < div className = "max-h-64 space-y-2 overflow-y-auto rounded-md border p-2" >
290
- { contacts . length === 0 ? (
291
- < p className = "text-muted-foreground py-4 text-center text-sm" >
292
- No contacts available
293
- </ p >
294
- ) : (
295
- contacts . map ( ( contact , idx ) => (
296
- < div
297
- key = { contact . id || contact . email || idx }
298
- className = "hover:bg-muted flex items-center justify-between rounded p-2"
299
- >
300
- < div className = "flex-1" >
301
- < p className = "text-sm font-medium" >
302
- { contact . name || contact . email }
303
- </ p >
304
- < p className = "text-muted-foreground text-xs" >
305
- { contact . email }
306
- </ p >
307
- </ div >
308
- < Button
309
- type = "button"
310
- variant = {
311
- selectedContacts . includes ( contact . email )
312
- ? "default"
313
- : "outline"
314
- }
315
- size = "sm"
316
- onClick = { ( ) =>
317
- setSelectedContacts ( ( prev ) =>
318
- prev . includes ( contact . email )
319
- ? prev . filter ( ( e ) => e !== contact . email )
320
- : [ ...prev , contact . email ]
321
- )
322
- }
323
- disabled = { addLoading }
324
- >
325
- { selectedContacts . includes ( contact . email ) ? "Added" : "Add" }
326
- </ Button >
327
- </ div >
328
- ) )
329
- ) }
330
- </ div >
331
- { addError && (
332
- < div className = "mt-2 text-sm text-red-500" > { addError } </ div >
333
- ) }
334
- < div className = "mt-4 flex flex-col gap-3" >
335
- < Button
336
- onClick = { handleAddUsers }
337
- disabled = { addLoading || selectedContacts . length === 0 }
338
- className = "w-full"
339
- >
340
- { addLoading ? "Adding..." : "Add Selected" }
341
- </ Button >
342
- < Button
343
- variant = "outline"
344
- onClick = { ( ) => setAddUsersOpen ( null ) }
345
- className = "w-full"
346
- type = "button"
347
- disabled = { addLoading }
348
- >
349
- Cancel
350
- </ Button >
351
- </ div >
352
- </ AlertDialogContent >
353
- </ AlertDialog >
354
217
</ div >
355
218
) ;
356
219
} ;
0 commit comments