OrganizationDropdown.tsx97 lines · main
1import { useParams } from 'common'
2import { Boxes } from 'lucide-react'
3import { useRouter } from 'next/router'
4import { useState } from 'react'
5import { Badge, cn } from 'ui'
6import { GenericSkeletonLoader, ShimmeringLoader } from 'ui-patterns'
7
8import { AppLayoutDropdownError, AppLayoutDropdownWithPopover } from './AppLayoutDropdown'
9import { OrganizationDropdownCommandContent } from './OrganizationDropdownCommandContent'
10import { useEmbeddedCloseHandler } from './useEmbeddedCloseHandler'
11import PartnerIcon from '@/components/ui/PartnerIcon'
12import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
13import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
14import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
15import { useTrack } from '@/lib/telemetry/track'
16
17interface OrganizationDropdownProps {
18 embedded?: boolean
19 className?: string
20 onClose?: () => void
21}
22
23export const OrganizationDropdown = ({
24 embedded = false,
25 className,
26 onClose,
27}: OrganizationDropdownProps = {}) => {
28 const router = useRouter()
29 const { slug: routeSlug } = useParams()
30 const { data: selectedOrganization } = useSelectedOrganizationQuery()
31 const {
32 data: organizations,
33 isPending: isLoadingOrganizations,
34 isError,
35 } = useOrganizationsQuery()
36
37 const organizationCreationEnabled = useIsFeatureEnabled('organizations:create')
38
39 const slug = selectedOrganization?.slug
40 const orgName = selectedOrganization?.name
41
42 const [open, setOpen] = useState(false)
43 const close = useEmbeddedCloseHandler(embedded, onClose, setOpen)
44 const track = useTrack()
45
46 const handleOpenChange = (next: boolean) => {
47 if (next) track('header_organization_dropdown_opened')
48 setOpen(next)
49 }
50
51 if (isLoadingOrganizations && !embedded)
52 return <ShimmeringLoader className="p-2 md:mr-2 w-[90px]" />
53
54 if (isError) return <AppLayoutDropdownError message="Failed to load organizations" />
55
56 const commandContent = (
57 <OrganizationDropdownCommandContent
58 embedded={embedded}
59 className={className}
60 organizations={organizations ?? []}
61 selectedSlug={slug}
62 routePathname={router.pathname}
63 hasRouteSlug={!!routeSlug}
64 organizationCreationEnabled={organizationCreationEnabled}
65 onClose={close}
66 />
67 )
68
69 if (embedded)
70 return isLoadingOrganizations ? <GenericSkeletonLoader className="p-2" /> : commandContent
71
72 return (
73 <AppLayoutDropdownWithPopover
74 linkHref={slug ? `/org/${slug}` : '/organizations'}
75 linkContent={
76 <>
77 <Boxes size={14} strokeWidth={1.5} className="text-foreground-lighter" />
78 <span
79 className={cn(
80 'md:max-w-32 lg:max-w-none truncate hidden md:block',
81 !!selectedOrganization ? 'text-foreground' : 'text-foreground-lighter'
82 )}
83 >
84 {orgName ?? 'Select an organization'}
85 </span>
86 {!!selectedOrganization && <PartnerIcon organization={selectedOrganization} />}
87 {!!selectedOrganization && (
88 <Badge variant="default">{selectedOrganization?.plan.name}</Badge>
89 )}
90 </>
91 }
92 commandContent={commandContent}
93 open={open}
94 onOpenChange={handleOpenChange}
95 />
96 )
97}