OrganizationSelector.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { PermissionAction } from '@supabase/shared-types/out/constants'
  2. import { useQueryClient } from '@tanstack/react-query'
  3. import { useParams } from 'common'
  4. import { useRouter } from 'next/router'
  5. import { useEffect } from 'react'
  6. import { UseFormReturn } from 'react-hook-form'
  7. import {
  8. Badge,
  9. FormControl,
  10. FormField,
  11. Select,
  12. SelectContent,
  13. SelectGroup,
  14. SelectItem,
  15. SelectTrigger,
  16. SelectValue,
  17. } from 'ui'
  18. import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
  19. import { OrgNotFound } from '../Organization/OrgNotFound'
  20. import { CreateProjectForm } from './ProjectCreation.schema'
  21. import { NoPermission } from '@/components/ui/NoPermission'
  22. import Panel from '@/components/ui/Panel'
  23. import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
  24. import { permissionKeys } from '@/data/permissions/keys'
  25. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  26. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  27. interface OrganizationSelectorProps {
  28. form: UseFormReturn<CreateProjectForm>
  29. }
  30. export const OrganizationSelector = ({ form }: OrganizationSelectorProps) => {
  31. const router = useRouter()
  32. const { slug } = useParams()
  33. const queryClient = useQueryClient()
  34. const { data: currentOrg } = useSelectedOrganizationQuery()
  35. const { can: isAdmin } = useAsyncCheckPermissions(PermissionAction.CREATE, 'projects')
  36. // Permissions may be stale for newly created accounts due to replication lag between
  37. // org setup and the permissions endpoint. Invalidate in the background on mount so the
  38. // check reflects the latest state before the user tries to create a project.
  39. useEffect(() => {
  40. queryClient.invalidateQueries({ queryKey: permissionKeys.list() })
  41. }, [queryClient])
  42. const { data: organizations, isSuccess: isOrganizationsSuccess } = useOrganizationsQuery()
  43. const isInvalidSlug = isOrganizationsSuccess && currentOrg === undefined
  44. const orgNotFound = (organizations?.length ?? 0) > 0 && isInvalidSlug
  45. return (
  46. <Panel.Content className="space-y-4">
  47. <FormField
  48. control={form.control}
  49. name="organization"
  50. render={({ field }) => (
  51. <FormItemLayout label="Organization" layout="horizontal">
  52. {(organizations?.length ?? 0) > 0 && (
  53. <Select
  54. onValueChange={(slug) => {
  55. field.onChange(slug)
  56. router.push(`/new/${slug}`)
  57. }}
  58. value={field.value}
  59. defaultValue={field.value}
  60. >
  61. <FormControl>
  62. <SelectTrigger>
  63. <SelectValue placeholder="Select an organization" />
  64. </SelectTrigger>
  65. </FormControl>
  66. <SelectContent>
  67. <SelectGroup>
  68. {organizations?.map((x) => (
  69. <SelectItem key={x.id} value={x.slug}>
  70. <div className="flex justify-between items-center gap-2 w-full">
  71. <span>{x.name}</span>
  72. <Badge className="mt-px">{x.plan.name}</Badge>
  73. </div>
  74. </SelectItem>
  75. ))}
  76. </SelectGroup>
  77. </SelectContent>
  78. </Select>
  79. )}
  80. </FormItemLayout>
  81. )}
  82. />
  83. {isOrganizationsSuccess && !isAdmin && !orgNotFound && (
  84. <NoPermission resourceText="create a project" />
  85. )}
  86. {orgNotFound && <OrgNotFound slug={slug} />}
  87. </Panel.Content>
  88. )
  89. }