@@ -6,6 +6,8 @@ import { usePathname } from "next/navigation";
66import { useLocale } from "@calcom/lib/hooks/useLocale" ;
77import { trpc } from "@calcom/trpc/react" ;
88import { Avatar } from "@calcom/ui/components/avatar" ;
9+ import { Badge } from "@calcom/ui/components/badge" ;
10+ import classNames from "@calcom/ui/classNames" ;
911
1012import { useOnboardingStore , type Invite } from "../store/onboarding-store" ;
1113
@@ -26,6 +28,7 @@ type DisplayItem = {
2628 email : string ;
2729 team : string ;
2830 isReal ?: boolean ;
31+ isMigrated ?: boolean ;
2932} ;
3033
3134export const OnboardingInviteBrowserView = ( {
@@ -35,8 +38,16 @@ export const OnboardingInviteBrowserView = ({
3538} : OnboardingInviteBrowserViewProps ) => {
3639 const pathname = usePathname ( ) ;
3740 const { data : user } = trpc . viewer . me . get . useQuery ( ) ;
38- const { teamBrand, teamInvites, invites, teamDetails, organizationBrand, organizationDetails } =
39- useOnboardingStore ( ) ;
41+ const {
42+ teamBrand,
43+ teamInvites,
44+ invites,
45+ teamDetails,
46+ organizationBrand,
47+ organizationDetails,
48+ migratedMembers,
49+ teams,
50+ } = useOnboardingStore ( ) ;
4051 const { t } = useLocale ( ) ;
4152
4253 // Animation variants for entry and exit
@@ -83,6 +94,32 @@ export const OnboardingInviteBrowserView = ({
8394 // Filter out empty invites (where email is empty or just whitespace)
8495 const validInvites = actualInvites . filter ( ( invite ) => invite . email && invite . email . trim ( ) . length > 0 ) ;
8596
97+ // Add migrated members if using organization invites
98+ const migratedInvites : Invite [ ] = [ ] ;
99+ if ( useOrganizationInvites && migratedMembers . length > 0 ) {
100+ migratedInvites . push (
101+ ...migratedMembers . map ( ( member ) => {
102+ // Find team name from teamId
103+ const team = teams . find ( ( t ) => t . id === member . teamId ) ;
104+ return {
105+ email : member . email ,
106+ team : team ?. name || "" ,
107+ role : "MEMBER" as const ,
108+ } ;
109+ } )
110+ ) ;
111+ }
112+
113+ // Combine form invites with migrated members, avoiding duplicates
114+ const allInvites = [ ...validInvites ] ;
115+ const existingEmails = new Set ( validInvites . map ( ( inv ) => inv . email . toLowerCase ( ) ) ) ;
116+ migratedInvites . forEach ( ( migratedInvite ) => {
117+ if ( ! existingEmails . has ( migratedInvite . email . toLowerCase ( ) ) ) {
118+ allInvites . push ( migratedInvite ) ;
119+ existingEmails . add ( migratedInvite . email . toLowerCase ( ) ) ;
120+ }
121+ } ) ;
122+
86123 // Create empty state items
87124 const emptyStateItem = {
88125 name : t ( "team_member" ) ,
@@ -95,14 +132,19 @@ export const OnboardingInviteBrowserView = ({
95132 const displayItems : DisplayItem [ ] = [ ] ;
96133 const maxItems = 9 ;
97134
98- // Add actual invites first
99- for ( let i = 0 ; i < validInvites . length && i < maxItems ; i ++ ) {
100- const invite = validInvites [ i ] ;
135+ // Create a set of migrated member emails for quick lookup
136+ const migratedEmails = new Set ( migratedMembers . map ( ( member ) => member . email . toLowerCase ( ) ) ) ;
137+
138+ // Add all invites (form invites + migrated members)
139+ for ( let i = 0 ; i < allInvites . length && i < maxItems ; i ++ ) {
140+ const invite = allInvites [ i ] ;
141+ const isMigrated = migratedEmails . has ( invite . email . toLowerCase ( ) ) ;
101142 displayItems . push ( {
102143 name : invite . email . split ( "@" ) [ 0 ] || t ( "team_member" ) ,
103144 email : invite . email ,
104145 team : invite . team || t ( "team" ) ,
105146 isReal : true ,
147+ isMigrated,
106148 } ) ;
107149 }
108150
@@ -134,30 +176,65 @@ export const OnboardingInviteBrowserView = ({
134176 ease : "backOut" ,
135177 } } >
136178 < div className = "bg-default border-subtle flex flex-col rounded-2xl border" >
137- < div className = "relative p-1" >
138- { /* Banner Image */ }
139- { organizationBrand . banner && (
140- < div className = "border-subtle relative h-36 w-full overflow-hidden rounded-xl border" >
141- { /* eslint-disable-next-line @next/next/no-img-element */ }
142- < img
143- src = { organizationBrand . banner }
144- alt = { displayName }
145- className = "h-full w-full object-cover"
146- />
147- </ div >
148- ) }
179+ { useOrganizationInvites || organizationBrand . banner || avatar ? (
180+ < div className = "relative p-1" >
181+ { /* Banner Image */ }
182+ { useOrganizationInvites && (
183+ < div className = "border-subtle relative h-36 w-full overflow-hidden rounded-xl border" >
184+ { organizationBrand . banner ? (
185+ /* eslint-disable-next-line @next/next/no-img-element */
186+ < img
187+ src = { organizationBrand . banner }
188+ alt = { displayName }
189+ className = "h-full w-full object-cover"
190+ />
191+ ) : (
192+ < div className = "bg-emphasis h-full w-full" />
193+ ) }
194+ </ div >
195+ ) }
196+ { ! useOrganizationInvites && organizationBrand . banner && (
197+ < div className = "border-subtle relative h-36 w-full overflow-hidden rounded-xl border" >
198+ { /* eslint-disable-next-line @next/next/no-img-element */ }
199+ < img
200+ src = { organizationBrand . banner }
201+ alt = { displayName }
202+ className = "h-full w-full object-cover"
203+ />
204+ </ div >
205+ ) }
149206
150- { /* Organization Avatar - Overlaying the banner */ }
151- { organizationBrand . banner && avatar && (
152- < div className = "absolute -bottom-6 left-4" >
153- < Avatar size = "lg" imageSrc = { avatar } alt = { displayName } className = "h-12 w-12 border" />
154- </ div >
155- ) }
156- </ div >
207+ { /* Organization Avatar - Overlaying the banner */ }
208+ { useOrganizationInvites && avatar && (
209+ < div className = "absolute -bottom-6 left-4" >
210+ < Avatar
211+ size = "lg"
212+ imageSrc = { avatar }
213+ alt = { displayName }
214+ className = "h-12 w-12 border"
215+ />
216+ </ div >
217+ ) }
218+ { ! useOrganizationInvites && organizationBrand . banner && avatar && (
219+ < div className = "absolute -bottom-6 left-4" >
220+ < Avatar
221+ size = "lg"
222+ imageSrc = { avatar }
223+ alt = { displayName }
224+ className = "h-12 w-12 border"
225+ />
226+ </ div >
227+ ) }
228+ </ div >
229+ ) : null }
157230
158231 { /* Organization Info */ }
159- < div className = { `flex flex-col items-start gap-1 px-4 pb-4 pt-8` } >
160- { ! organizationBrand . banner && avatar && (
232+ < div
233+ className = { classNames (
234+ `flex flex-col items-start gap-1 px-4 pb-4` ,
235+ useOrganizationInvites || organizationBrand . banner || avatar ? "pt-8" : "pt-4"
236+ ) } >
237+ { ! useOrganizationInvites && ! organizationBrand . banner && avatar && (
161238 < Avatar
162239 size = "lg"
163240 imageSrc = { avatar }
@@ -192,11 +269,18 @@ export const OnboardingInviteBrowserView = ({
192269 { item . email }
193270 </ p >
194271 </ div >
195- { item . team && (
196- < div className = "bg-emphasis text-emphasis rounded-md px-2 py-0.5 text-xs" >
197- { item . team }
198- </ div >
199- ) }
272+ < div className = "flex flex-col items-center gap-1" >
273+ { item . team && ! item . isMigrated && (
274+ < div className = "bg-emphasis text-emphasis line-clamp-1 max-w-[86px] truncate rounded-md px-2 py-0.5 text-xs" >
275+ { item . team }
276+ </ div >
277+ ) }
278+ { item . isMigrated && (
279+ < Badge variant = "green" className = "text-xs" >
280+ { t ( "migrating" ) }
281+ </ Badge >
282+ ) }
283+ </ div >
200284 </ div >
201285 </ div >
202286 ) ) }
0 commit comments