OrganizationDropdown.tsx 3.2 KB

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