1
+ import { useState } from "react" ;
1
2
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider" ;
2
3
import { useAuthStore } from "@/lib/providers/auth-store-provider" ;
3
4
import { useUserMutation } from "@/lib/api/organizations/users" ;
4
5
import { Button } from "@/components/ui/button" ;
5
- import { Trash2 } from "lucide-react" ;
6
- import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship_with_user_names" ;
6
+ import {
7
+ DropdownMenu ,
8
+ DropdownMenuTrigger ,
9
+ DropdownMenuContent ,
10
+ DropdownMenuItem ,
11
+ DropdownMenuSeparator ,
12
+ } from "@/components/ui/dropdown-menu" ;
13
+ import { MoreHorizontal , Trash2 } from "lucide-react" ;
14
+ import {
15
+ Dialog ,
16
+ DialogContent ,
17
+ DialogDescription ,
18
+ DialogHeader ,
19
+ DialogFooter ,
20
+ DialogTitle ,
21
+ } from "@/components/ui/dialog" ;
22
+ import {
23
+ Select ,
24
+ SelectTrigger ,
25
+ SelectValue ,
26
+ SelectContent ,
27
+ SelectItem ,
28
+ } from "@/components/ui/select" ;
29
+ import { CoachingRelationshipWithUserNames } from "@/types/coaching_relationship" ;
7
30
import { OrganizationStateStore } from "@/lib/stores/organization-state-store" ;
8
31
import { AuthStore } from "@/lib/stores/auth-store" ;
9
32
import { Id } from "@/types/general" ;
33
+ import { User , Role } from "@/types/user" ;
34
+ import { useCoachingRelationshipMutation } from "@/lib/api/coaching-relationships" ;
35
+ import { toast } from "sonner" ;
10
36
11
37
interface MemberCardProps {
12
38
firstName : string ;
@@ -15,6 +41,13 @@ interface MemberCardProps {
15
41
userId : Id ;
16
42
userRelationships : CoachingRelationshipWithUserNames [ ] ;
17
43
onRefresh : ( ) => void ;
44
+ users : User [ ] ;
45
+ }
46
+
47
+ interface Member {
48
+ id : Id ;
49
+ first_name : string ;
50
+ last_name : string ;
18
51
}
19
52
20
53
export function MemberCard ( {
@@ -24,31 +57,84 @@ export function MemberCard({
24
57
userId,
25
58
userRelationships,
26
59
onRefresh,
60
+ users,
27
61
} : MemberCardProps ) {
28
62
const currentOrganizationId = useOrganizationStateStore (
29
63
( state : OrganizationStateStore ) => state . currentOrganizationId
30
64
) ;
31
- const { userSession } = useAuthStore ( ( state : AuthStore ) => state ) ;
32
- const { deleteNested : deleteUser } = useUserMutation ( currentOrganizationId ) ;
65
+ const { isACoach, userSession } = useAuthStore ( ( state : AuthStore ) => state ) ;
66
+ const { error : deleteError , deleteNested : deleteUser } = useUserMutation ( currentOrganizationId ) ;
67
+ const { error : createError , createNested : createRelationship } = useCoachingRelationshipMutation ( currentOrganizationId ) ;
68
+
69
+ console . log ( "is a coach" , isACoach ) ;
33
70
34
71
// Check if current user is a coach in any of this user's relationships
35
- // and make sure we can't delete ourselves
36
- const canDeleteUser = userRelationships . some (
72
+ // and make sure we can't delete ourselves. Admins can delete any user.
73
+ const canDeleteUser = ( userRelationships ? .some (
37
74
( rel ) => rel . coach_id === userSession . id && userId !== userSession . id
38
- ) ;
75
+ ) || ( userSession . role === Role . Admin ) ) && userSession . id !== userId ;
39
76
40
77
const handleDelete = async ( ) => {
41
78
if ( ! confirm ( "Are you sure you want to delete this member?" ) ) {
42
79
return ;
43
80
}
81
+ await deleteUser ( currentOrganizationId , userId ) ;
82
+ onRefresh ( ) ;
44
83
45
- try {
46
- await deleteUser ( currentOrganizationId , userId ) ;
84
+ if ( deleteError ) {
85
+ console . error ( "Error deleting member:" , deleteError ) ;
86
+ toast . error ( "Error deleting member" ) ;
47
87
onRefresh ( ) ;
48
- } catch ( error ) {
49
- console . error ( "Error deleting user:" , error ) ;
50
- // TODO: Show an error toast here once we start using toasts for showing operation results.
88
+ return ;
89
+ }
90
+ toast . success ( "Member deleted successfully" ) ;
91
+ onRefresh ( ) ;
92
+ } ;
93
+
94
+ const handleAssignMember = ( val : string ) => {
95
+ const user = users . find ( ( m ) => m . id === val ) ;
96
+ if ( ! user ) return ;
97
+ const member : Member = {
98
+ id : user . id ,
99
+ first_name : user . first_name ,
100
+ last_name : user . last_name ,
101
+ } ;
102
+ setAssignedMember ( member ) ;
103
+ } ;
104
+
105
+ // Placeholder – actual UI flows will be implemented later
106
+ const [ assignDialogOpen , setAssignDialogOpen ] = useState ( false ) ;
107
+ const [ assignMode , setAssignMode ] = useState < "coach" | "coachee" > ( "coach" ) ;
108
+ const [ selectedMember , setSelectedMember ] = useState < Member | null > ( null ) ;
109
+ const [ assignedMember , setAssignedMember ] = useState < Member | null > ( null ) ;
110
+
111
+ const handleCreateCoachingRelationship = ( ) => {
112
+ if ( ! selectedMember || ! assignedMember ) return ;
113
+
114
+ if ( assignMode === "coach" ) {
115
+ console . log ( "Assign" , selectedMember . id , "as coach for" , userId ) ;
116
+ createRelationship ( currentOrganizationId , {
117
+ coach_id : assignedMember . id ,
118
+ coachee_id : selectedMember . id ,
119
+ } ) ;
120
+ } else {
121
+ console . log ( "Assign" , selectedMember . id , "as coachee for" , userId ) ;
122
+ createRelationship ( currentOrganizationId , {
123
+ coach_id : selectedMember . id ,
124
+ coachee_id : assignedMember . id ,
125
+ } ) ;
51
126
}
127
+
128
+ if ( createError ) {
129
+ toast . error ( `Error assigning ${ assignMode } ` ) ;
130
+ return ;
131
+ }
132
+
133
+ toast . success ( `Successfully assigned ${ assignedMember . first_name } ${ assignedMember . last_name } as ${ assignMode } for ${ selectedMember . first_name } ${ selectedMember . last_name } ` ) ;
134
+ onRefresh ( ) ;
135
+ setAssignDialogOpen ( false ) ;
136
+ setSelectedMember ( null ) ;
137
+ setAssignedMember ( null ) ;
52
138
} ;
53
139
54
140
return (
@@ -59,16 +145,82 @@ export function MemberCard({
59
145
</ h3 >
60
146
{ email && < p className = "text-sm text-muted-foreground" > { email } </ p > }
61
147
</ div >
62
- { canDeleteUser && (
63
- < Button
64
- variant = "ghost"
65
- size = "icon"
66
- onClick = { handleDelete }
67
- className = "text-destructive hover:text-destructive"
68
- >
69
- < Trash2 className = "h-4 w-4" />
70
- </ Button >
71
- ) }
148
+ { ( isACoach || userSession . role === Role . Admin ) && (
149
+ < DropdownMenu >
150
+ < DropdownMenuTrigger asChild >
151
+ < Button variant = "ghost" size = "icon" className = "text-muted-foreground" >
152
+ < MoreHorizontal className = "h-4 w-4" />
153
+ </ Button >
154
+ </ DropdownMenuTrigger >
155
+ < DropdownMenuContent align = "end" >
156
+ { userSession . role === Role . Admin && (
157
+ < >
158
+ < DropdownMenuItem
159
+ onClick = { ( ) => {
160
+ setAssignMode ( "coach" ) ;
161
+ setAssignDialogOpen ( true ) ;
162
+ setSelectedMember ( { id : userId , first_name : firstName , last_name : lastName } ) ;
163
+ } }
164
+ >
165
+ Assign Coach
166
+ </ DropdownMenuItem >
167
+ < DropdownMenuItem
168
+ onClick = { ( ) => {
169
+ setAssignMode ( "coachee" ) ;
170
+ setAssignDialogOpen ( true ) ;
171
+ setSelectedMember ( { id : userId , first_name : firstName , last_name : lastName } ) ;
172
+ } }
173
+ >
174
+ Assign Coachee
175
+ </ DropdownMenuItem >
176
+ </ >
177
+ ) }
178
+ { canDeleteUser && (
179
+ < >
180
+ < DropdownMenuSeparator />
181
+ < DropdownMenuItem
182
+ onClick = { handleDelete }
183
+ className = "text-destructive focus:text-destructive"
184
+ >
185
+ < Trash2 className = "mr-2 h-4 w-4" /> Delete
186
+ </ DropdownMenuItem >
187
+ </ >
188
+ ) }
189
+ </ DropdownMenuContent >
190
+ </ DropdownMenu >
191
+ ) }
192
+
193
+ { /* Assign Coach/Coachee Modal */ }
194
+ < Dialog open = { assignDialogOpen } onOpenChange = { setAssignDialogOpen } >
195
+ < DialogContent >
196
+ < DialogHeader >
197
+ < DialogTitle >
198
+ { assignMode === "coach" ? "Assign Coach" : "Assign Coachee" }
199
+ </ DialogTitle >
200
+ < DialogDescription >
201
+ Select a member to be their { assignMode === "coach" ? "coach" : "coachee" }
202
+ </ DialogDescription >
203
+ </ DialogHeader >
204
+ < Select
205
+ onValueChange = { ( val ) => handleAssignMember ( val ) }
206
+ value = { assignedMember ?. id ?. toString ( ) }
207
+ >
208
+ < SelectTrigger className = "w-full" >
209
+ < SelectValue placeholder = "Select a member" />
210
+ </ SelectTrigger >
211
+ < SelectContent >
212
+ { users
213
+ . filter ( ( m ) => m . id !== userId )
214
+ . map ( ( m ) => (
215
+ < SelectItem key = { m . id } value = { m . id . toString ( ) } > { `${ m . first_name } ${ m . last_name } ` } </ SelectItem >
216
+ ) ) }
217
+ </ SelectContent >
218
+ </ Select >
219
+ < DialogFooter >
220
+ < Button onClick = { handleCreateCoachingRelationship } > { assignMode === "coach" ? "Assign as Coach" : "Assign as Coachee" } </ Button >
221
+ </ DialogFooter >
222
+ </ DialogContent >
223
+ </ Dialog >
72
224
</ div >
73
225
) ;
74
226
}
0 commit comments