OrganizationPicker.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { ChevronDown } from 'lucide-react'
  2. import { useMemo, useRef, useState } from 'react'
  3. import {
  4. Badge,
  5. Button,
  6. cn,
  7. Command,
  8. CommandEmpty,
  9. CommandGroup,
  10. CommandInput,
  11. CommandItem,
  12. CommandList,
  13. Popover,
  14. PopoverContent,
  15. PopoverTrigger,
  16. } from 'ui'
  17. import { getHasInstalledObject } from '@/components/layouts/IntegrationsLayout/Integrations.utils'
  18. import PartnerIcon from '@/components/ui/PartnerIcon'
  19. import { useIntegrationsQuery } from '@/data/integrations/integrations-query'
  20. import type { IntegrationName } from '@/data/integrations/integrations.types'
  21. import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
  22. import type { Organization } from '@/types'
  23. export interface OrganizationPickerProps {
  24. integrationName: IntegrationName
  25. configurationId?: string
  26. selectedOrg: Organization | null
  27. onSelectedOrgChange: (organization: Organization) => void
  28. disabled?: boolean
  29. }
  30. const OrganizationPicker = ({
  31. integrationName,
  32. configurationId,
  33. selectedOrg,
  34. onSelectedOrgChange,
  35. disabled,
  36. }: OrganizationPickerProps) => {
  37. const [open, setOpen] = useState(false)
  38. const ref = useRef<HTMLButtonElement>(null)
  39. const { data: integrationData } = useIntegrationsQuery()
  40. const { data: organizationsData, isPending: isLoadingOrganization } = useOrganizationsQuery()
  41. const installed = useMemo(
  42. () =>
  43. integrationData && organizationsData
  44. ? getHasInstalledObject({
  45. integrationName,
  46. integrationData,
  47. organizationsData,
  48. installationId: configurationId,
  49. })
  50. : {},
  51. [configurationId, integrationData, integrationName, organizationsData]
  52. )
  53. return (
  54. <>
  55. <Popover open={open} onOpenChange={setOpen}>
  56. <PopoverTrigger asChild>
  57. <Button
  58. ref={ref}
  59. type="default"
  60. size="medium"
  61. block
  62. className="justify-start"
  63. loading={isLoadingOrganization}
  64. disabled={disabled}
  65. iconRight={
  66. <span className="grow flex justify-end">
  67. <ChevronDown />
  68. </span>
  69. }
  70. >
  71. <div className="flex gap-2">
  72. <span className={cn('truncate', !selectedOrg && 'text-foreground-light')}>
  73. {selectedOrg?.name ? selectedOrg?.name : 'Choose an organization'}
  74. </span>
  75. {selectedOrg && configurationId && installed[selectedOrg.slug] && (
  76. <Badge>Integration Installed</Badge>
  77. )}
  78. </div>
  79. </Button>
  80. </PopoverTrigger>
  81. <PopoverContent className="p-0 w-full" side="bottom" align="center" sameWidthAsTrigger>
  82. <Command>
  83. <CommandInput placeholder="Search organizations..." />
  84. <CommandList>
  85. <CommandEmpty>No results found.</CommandEmpty>
  86. <CommandGroup>
  87. {organizationsData?.map((org) => {
  88. return (
  89. <CommandItem
  90. value={org.slug}
  91. key={org.slug}
  92. className="flex gap-2 items-center"
  93. onSelect={(slug) => {
  94. const org = organizationsData?.find(
  95. (org) => org.slug.toLowerCase() === slug.toLowerCase()
  96. )
  97. if (org) {
  98. onSelectedOrgChange(org)
  99. }
  100. setOpen(false)
  101. }}
  102. >
  103. <PartnerIcon organization={org} />
  104. <span className="truncate">{org.name}</span>{' '}
  105. {configurationId && installed[org.slug] && (
  106. <Badge className="flex-none!">Integration Installed</Badge>
  107. )}
  108. </CommandItem>
  109. )
  110. })}
  111. </CommandGroup>
  112. </CommandList>
  113. </Command>
  114. </PopoverContent>
  115. </Popover>
  116. </>
  117. )
  118. }
  119. export default OrganizationPicker