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