@@ -11,18 +11,20 @@ import {
1111 DropdownMenuItem ,
1212 DropdownMenuTrigger ,
1313} from '@/components/primitives/dropdown-menu' ;
14+ import { showErrorToast } from '@/components/primitives/sonner-helpers' ;
1415import { Tooltip , TooltipContent , TooltipTrigger } from '@/components/primitives/tooltip' ;
1516import { useRegion } from '@/context/region' ;
1617import { useFeatureFlag } from '@/hooks/use-feature-flag' ;
1718import { ROUTES } from '@/utils/routes' ;
1819import { cn } from '@/utils/ui' ;
1920
20- const SCROLL_THRESHOLD = 50 ;
21- const PAGE_SIZE = 8 ;
21+ const SCROLL_THRESHOLD = 100 ;
22+ const PAGE_SIZE = 10 ;
2223
2324function getOrganizationInitials ( name : string ) {
2425 return name
25- . split ( ' ' )
26+ . trim ( )
27+ . split ( / \s + / )
2628 . map ( ( word ) => word [ 0 ] )
2729 . join ( '' )
2830 . toUpperCase ( )
@@ -124,6 +126,8 @@ export function OrganizationDropdown() {
124126 setIsOpen ( false ) ;
125127 } catch ( error ) {
126128 console . error ( 'Failed to switch organization:' , error ) ;
129+ const errorMessage = error instanceof Error ? error . message : 'An unexpected error occurred' ;
130+ showErrorToast ( `Unable to switch organizations. ${ errorMessage } ` , 'Organization Switch Failed' ) ;
127131 } finally {
128132 setIsSwitching ( false ) ;
129133 setSwitchingToId ( null ) ;
@@ -173,75 +177,63 @@ export function OrganizationDropdown() {
173177 const filteredMemberships = userMemberships ?. data ?. filter ( filterMemberships ) || [ ] ;
174178
175179 return (
176- < >
177- < style >
178- { `
179- @keyframes shimmer {
180- from { transform: translateX(-100%); }
181- to { transform: translateX(100%); }
182- }
183- ` }
184- </ style >
185-
186- < DropdownMenu open = { isOpen } onOpenChange = { setIsOpen } >
187- < DropdownMenuTrigger asChild >
188- < button
189- className = { cn (
190- 'group relative flex w-full items-center gap-2 rounded-lg px-1.5 py-1.5 transition-all duration-300' ,
191- 'hover:bg-background hover:shadow-sm' ,
192- 'before:absolute before:bottom-0 before:left-0 before:h-0 before:w-full before:border-b before:border-neutral-alpha-100 before:transition-all before:duration-300 before:content-[""]' ,
193- 'hover:before:border-transparent' ,
194- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-background focus-visible:shadow-sm focus-visible:before:border-transparent'
195- ) }
196- >
197- < OrganizationAvatar imageUrl = { currentOrganization . imageUrl } name = { currentOrganization . name } showShimmer />
198- < span className = "text-sm font-medium text-foreground-950" > { currentOrganization . name } </ span >
199- < RiArrowDownSLine className = "ml-auto size-4 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus:opacity-100" />
200- </ button >
201- </ DropdownMenuTrigger >
202-
203- < DropdownMenuContent className = "w-64 p-0" align = "start" >
204- < div
205- ref = { scrollContainerRef }
206- className = "max-h-[200px] overflow-y-auto"
207- role = "group"
208- aria-label = "List of all organization memberships"
209- onScroll = { handleScroll }
210- >
211- < AnimatePresence mode = "popLayout" >
212- { filteredMemberships . map ( ( membership ) => (
213- < OrganizationListItem
214- key = { membership . id }
215- membership = { membership }
216- onSwitch = { handleOrganizationSwitch }
217- isSwitching = { isSwitching }
218- switchingToId = { switchingToId }
219- />
220- ) ) }
221- </ AnimatePresence >
222-
223- { userMemberships ?. isFetching && (
224- < div className = "flex items-center justify-center py-2" >
225- < RiLoader4Line className = "size-4 animate-spin text-foreground-600" />
226- </ div >
227- ) }
228- </ div >
229-
230- < DropdownMenuItem
231- className = { cn (
232- 'flex cursor-pointer items-center gap-2 rounded-none border-t border-neutral-alpha-200 px-3 py-1.5 text-sm transition-shadow focus:bg-accent hover:bg-accent' ,
233- isScrolled && 'shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]'
234- ) }
235- onSelect = { ( ) => {
236- window . location . href = ROUTES . SIGNUP_ORGANIZATION_LIST ;
237- } }
238- >
239- < RiAddCircleLine className = "size-4" />
240- < span className = "text-foreground-950" > Create organization</ span >
241- </ DropdownMenuItem >
242- </ DropdownMenuContent >
243- </ DropdownMenu >
244- </ >
180+ < DropdownMenu open = { isOpen } onOpenChange = { setIsOpen } >
181+ < DropdownMenuTrigger asChild >
182+ < button
183+ className = { cn (
184+ 'group relative flex w-full items-center gap-2 rounded-lg px-1.5 py-1.5 transition-all duration-300' ,
185+ 'hover:bg-background hover:shadow-sm' ,
186+ 'before:absolute before:bottom-0 before:left-0 before:h-0 before:w-full before:border-b before:border-neutral-alpha-100 before:transition-all before:duration-300 before:content-[""]' ,
187+ 'hover:before:border-transparent' ,
188+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-background focus-visible:shadow-sm focus-visible:before:border-transparent'
189+ ) }
190+ >
191+ < OrganizationAvatar imageUrl = { currentOrganization . imageUrl } name = { currentOrganization . name } showShimmer />
192+ < span className = "text-sm font-medium text-foreground-950" > { currentOrganization . name } </ span >
193+ < RiArrowDownSLine className = "ml-auto size-4 opacity-0 transition-opacity duration-300 group-hover:opacity-100 group-focus:opacity-100" />
194+ </ button >
195+ </ DropdownMenuTrigger >
196+
197+ < DropdownMenuContent className = "w-64 p-0" align = "start" >
198+ < div
199+ ref = { scrollContainerRef }
200+ className = "max-h-[200px] overflow-y-auto"
201+ role = "group"
202+ aria-label = "List of all organization memberships"
203+ onScroll = { handleScroll }
204+ >
205+ < AnimatePresence mode = "popLayout" >
206+ { filteredMemberships . map ( ( membership ) => (
207+ < OrganizationListItem
208+ key = { membership . id }
209+ membership = { membership }
210+ onSwitch = { handleOrganizationSwitch }
211+ isSwitching = { isSwitching }
212+ switchingToId = { switchingToId }
213+ />
214+ ) ) }
215+ </ AnimatePresence >
216+
217+ { userMemberships ?. isFetching && (
218+ < div className = "flex items-center justify-center py-2" >
219+ < RiLoader4Line className = "size-4 animate-spin text-foreground-600" />
220+ </ div >
221+ ) }
222+ </ div >
223+
224+ < DropdownMenuItem
225+ className = { cn (
226+ 'flex cursor-pointer items-center gap-2 rounded-none border-t border-neutral-alpha-200 px-3 py-1.5 text-sm transition-shadow focus:bg-accent hover:bg-accent' ,
227+ isScrolled && 'shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]'
228+ ) }
229+ onSelect = { ( ) => {
230+ window . location . href = ROUTES . SIGNUP_ORGANIZATION_LIST ;
231+ } }
232+ >
233+ < RiAddCircleLine className = "size-4" />
234+ < span className = "text-foreground-950" > Create organization</ span >
235+ </ DropdownMenuItem >
236+ </ DropdownMenuContent >
237+ </ DropdownMenu >
245238 ) ;
246239}
247-
0 commit comments