@@ -13,7 +13,7 @@ import { MembershipRole } from "@calcom/prisma/enums";
1313import { trpc } from "@calcom/trpc/react" ;
1414import { Avatar } from "@calcom/ui/components/avatar" ;
1515import { Form } from "@calcom/ui/components/form" ;
16- import { ToggleGroup } from "@calcom/ui/components/form" ;
16+ import { ToggleGroup , Select } from "@calcom/ui/components/form" ;
1717import { Icon } from "@calcom/ui/components/icon" ;
1818import { Sheet , SheetContent , SheetFooter , SheetHeader , SheetBody } from "@calcom/ui/components/sheet" ;
1919import { Skeleton , Loader } from "@calcom/ui/components/skeleton" ;
@@ -24,7 +24,7 @@ import { updateRoleInCache } from "./MemberChangeRoleModal";
2424import type { Action , State , User } from "./MemberList" ;
2525
2626const formSchema = z . object ( {
27- role : z . enum ( [ MembershipRole . MEMBER , MembershipRole . ADMIN , MembershipRole . OWNER ] ) ,
27+ role : z . union ( [ z . nativeEnum ( MembershipRole ) , z . string ( ) ] ) , // Support both traditional roles and custom role IDs
2828} ) ;
2929
3030type FormSchema = z . infer < typeof formSchema > ;
@@ -47,7 +47,7 @@ export function EditMemberSheet({
4747 ( state ) => [ state . editMode , state . setEditMode , state . setMutationLoading ] ,
4848 shallow
4949 ) ;
50- const [ role , setRole ] = useState ( selectedUser . role ) ;
50+ const [ role , setRole ] = useState < string > ( selectedUser . customRoleId || selectedUser . role ) ;
5151 const name =
5252 selectedUser . name ||
5353 ( ( ) => {
@@ -60,7 +60,25 @@ export function EditMemberSheet({
6060 const bookerUrlWithoutProtocol = bookerUrl . replace ( / ^ h t t p s ? : \/ \/ / , "" ) ;
6161 const bookingLink = ! ! selectedUser . username ? `${ bookerUrlWithoutProtocol } /${ selectedUser . username } ` : "" ;
6262
63+ // Load custom roles for the team
64+ const { data : customRoles , isPending : isLoadingRoles } = trpc . viewer . pbac . getTeamRoles . useQuery (
65+ { teamId } ,
66+ {
67+ enabled : ! ! teamId ,
68+ retry : false , // Don't retry if PBAC is not enabled
69+ }
70+ ) ;
71+
6372 const options = useMemo ( ( ) => {
73+ // If we have custom roles, only show custom roles
74+ if ( customRoles && customRoles . length > 0 ) {
75+ return customRoles . map ( ( customRole ) => ( {
76+ label : customRole . name ,
77+ value : customRole . id ,
78+ } ) ) ;
79+ }
80+
81+ // Otherwise, show traditional roles
6482 return [
6583 {
6684 label : t ( "member" ) ,
@@ -75,12 +93,16 @@ export function EditMemberSheet({
7593 value : MembershipRole . OWNER ,
7694 } ,
7795 ] . filter ( ( { value } ) => value !== MembershipRole . OWNER || currentMember === MembershipRole . OWNER ) ;
78- } , [ t , currentMember ] ) ;
96+ } , [ t , currentMember , customRoles ] ) ;
97+
98+ // Determine if we should use Select (when custom roles exist) or ToggleGroup (traditional only)
99+ const hasCustomRoles = customRoles && customRoles . length > 0 ;
100+ const shouldUseSelect = hasCustomRoles ; // Use Select for custom roles, ToggleGroup for traditional roles
79101
80102 const form = useForm ( {
81103 resolver : zodResolver ( formSchema ) ,
82104 defaultValues : {
83- role : selectedUser . role ,
105+ role : selectedUser . customRoleId || selectedUser . role , // Use custom role ID if available, otherwise traditional role
84106 } ,
85107 } ) ;
86108
@@ -101,13 +123,20 @@ export function EditMemberSheet({
101123 } ) ;
102124
103125 if ( previousValue ) {
104- updateRoleInCache ( { utils, teamId, memberId, role, searchTerm : undefined } ) ;
126+ updateRoleInCache ( {
127+ utils,
128+ teamId,
129+ memberId,
130+ role : role as MembershipRole | string ,
131+ searchTerm : undefined ,
132+ customRoles,
133+ } ) ;
105134 }
106135
107136 return { previousValue } ;
108137 } ,
109138 onSuccess : async ( _data , { role } ) => {
110- setRole ( role ) ;
139+ setRole ( role as string ) ;
111140 setMutationLoading ( false ) ;
112141 await utils . viewer . teams . get . invalidate ( ) ;
113142 await utils . viewer . teams . listMembers . invalidate ( ) ;
@@ -153,7 +182,7 @@ export function EditMemberSheet({
153182 dispatch ( { type : "CLOSE_MODAL" } ) ;
154183 } } >
155184 < SheetContent className = "bg-muted" >
156- { ! isPending ? (
185+ { ! isPending && ! isLoadingRoles ? (
157186 < Form form = { form } handleSubmit = { changeRole } className = "flex h-full flex-col" >
158187 < SheetHeader showCloseButton = { false } className = "w-full" >
159188 < div className = "border-sublte bg-default w-full rounded-xl border p-4" >
@@ -185,23 +214,42 @@ export function EditMemberSheet({
185214 < DisplayInfo label = "Cal" value = { bookingLink } icon = "external-link" />
186215 < DisplayInfo label = { t ( "email" ) } value = { selectedUser . email } icon = "at-sign" />
187216 { ! editMode ? (
188- < DisplayInfo label = { t ( "role" ) } value = { [ role ] } icon = "fingerprint" />
217+ < DisplayInfo
218+ label = { t ( "role" ) }
219+ value = { [ selectedUser . customRole ?. name || selectedUser . role ] }
220+ icon = "fingerprint"
221+ />
189222 ) : (
190223 < div className = "flex items-center gap-6" >
191224 < div className = "flex w-[110px] items-center gap-2" >
192225 < Icon className = "h-4 w-4" name = "fingerprint" />
193226 < label className = "text-sm font-medium" > { t ( "role" ) } </ label >
194227 </ div >
195228 < div className = "flex flex-1" >
196- < ToggleGroup
197- isFullWidth
198- defaultValue = { role }
199- value = { form . watch ( "role" ) }
200- options = { options }
201- onValueChange = { ( value : FormSchema [ "role" ] ) => {
202- form . setValue ( "role" , value ) ;
203- } }
204- />
229+ { shouldUseSelect ? (
230+ < Select
231+ value = { options . find ( ( option ) => option . value === form . watch ( "role" ) ) }
232+ onChange = { ( selectedOption : any ) => {
233+ if ( selectedOption ) {
234+ form . setValue ( "role" , selectedOption . value ) ;
235+ }
236+ } }
237+ options = { options }
238+ isDisabled = { isLoadingRoles }
239+ placeholder = { isLoadingRoles ? t ( "loading" ) : t ( "select_role" ) }
240+ className = "flex-1"
241+ />
242+ ) : (
243+ < ToggleGroup
244+ isFullWidth
245+ defaultValue = { role }
246+ value = { form . watch ( "role" ) }
247+ options = { options }
248+ onValueChange = { ( value : FormSchema [ "role" ] ) => {
249+ form . setValue ( "role" , value ) ;
250+ } }
251+ />
252+ ) }
205253 </ div >
206254 </ div >
207255 ) }
0 commit comments