@@ -35,10 +35,17 @@ import { addEmployeeWithoutInvite } from '../actions/addEmployeeWithoutInvite';
3535import { MultiRoleCombobox } from './MultiRoleCombobox' ;
3636
3737// --- Constants for Roles ---
38- const selectableRoles = [ 'admin' , 'auditor' , 'employee' ] as const satisfies Readonly < Role [ ] > ;
38+ const selectableRoles = [ 'admin' , 'auditor' , 'employee' ] as const satisfies Readonly <
39+ [ Role , ...Role [ ] ]
40+ > ;
3941type InviteRole = ( typeof selectableRoles ) [ number ] ;
4042const DEFAULT_ROLES : InviteRole [ ] = [ ] ;
4143
44+ // Type guard to check if a string is a valid InviteRole
45+ const isInviteRole = ( role : string ) : role is InviteRole => {
46+ return role === 'admin' || role === 'auditor' || role === 'employee' ;
47+ } ;
48+
4249// --- Schemas ---
4350const manualInviteSchema = z . object ( {
4451 email : z . string ( ) . email ( { message : 'Invalid email address.' } ) ,
@@ -151,12 +158,13 @@ export function InviteMembersModal({
151158
152159 // Process each invitation sequentially
153160 for ( const invite of values . manualInvites ) {
154- const isEmployeeOnly = invite . roles . length === 1 && invite . roles [ 0 ] === 'employee' ;
161+ const hasEmployeeRole = invite . roles . includes ( 'employee' ) ;
155162 try {
156- if ( isEmployeeOnly ) {
163+ if ( hasEmployeeRole ) {
157164 await addEmployeeWithoutInvite ( {
158165 organizationId,
159166 email : invite . email ,
167+ roles : invite . roles ,
160168 } ) ;
161169 } else {
162170 // Use authClient to send the invitation
@@ -276,6 +284,7 @@ export function InviteMembersModal({
276284 // Process each row
277285 for ( const row of dataRows ) {
278286 const columns = row . split ( ',' ) . map ( ( col ) => col . trim ( ) ) ;
287+
279288 if ( columns . length <= Math . max ( emailIndex , roleIndex ) ) {
280289 failedInvites . push ( {
281290 email : columns [ emailIndex ] || 'Invalid row' ,
@@ -296,9 +305,9 @@ export function InviteMembersModal({
296305 continue ;
297306 }
298307
299- // Validate role(s)
300- const roles = roleValue . split ( ', ' ) . map ( ( r ) => r . trim ( ) ) as Role [ ] ;
301- const validRoles = roles . filter ( ( role ) => selectableRoles . includes ( role as any ) ) ;
308+ // Validate role(s) - split by pipe for multiple roles
309+ const roles = roleValue . split ( '| ' ) . map ( ( r ) => r . trim ( ) ) ;
310+ const validRoles = roles . filter ( isInviteRole ) ;
302311
303312 if ( validRoles . length === 0 ) {
304313 failedInvites . push ( {
@@ -309,11 +318,20 @@ export function InviteMembersModal({
309318 }
310319
311320 // Attempt to invite
321+ const hasEmployeeRole = validRoles . includes ( 'employee' ) ;
312322 try {
313- await authClient . organization . inviteMember ( {
314- email,
315- role : validRoles . length === 1 ? validRoles [ 0 ] : validRoles ,
316- } ) ;
323+ if ( hasEmployeeRole ) {
324+ await addEmployeeWithoutInvite ( {
325+ organizationId,
326+ email,
327+ roles : validRoles ,
328+ } ) ;
329+ } else {
330+ await authClient . organization . inviteMember ( {
331+ email,
332+ role : validRoles ,
333+ } ) ;
334+ }
317335 successCount ++ ;
318336 } catch ( error ) {
319337 console . error ( `Failed to invite ${ email } :` , error ) ;
@@ -374,7 +392,12 @@ export function InviteMembersModal({
374392 }
375393 } ;
376394
377- const csvTemplate = 'email,role\[email protected] ,employee\[email protected] ,admin' ; 395+ const csvTemplate = `email,role
396+ 397+ 398+ 399+ [email protected] ,employee|auditor400+ 378401 const csvTemplateDataUri = `data:text/csv;charset=utf-8,${ encodeURIComponent ( csvTemplate ) } ` ;
379402
380403 return (
@@ -499,7 +522,9 @@ export function InviteMembersModal({
499522 />
500523 </ FormControl >
501524 < FormDescription >
502- { "Upload a CSV file with columns for 'email' and 'role'." }
525+ {
526+ "Upload a CSV file with 'email' and 'role' columns. Use pipe (|) to separate multiple roles (e.g., employee|admin)."
527+ }
503528 </ FormDescription >
504529 < a
505530 href = { csvTemplateDataUri }
0 commit comments