PostgresVersionSelector.tsx149 lines · main
1import { useEffect } from 'react'
2import { ControllerRenderProps, UseFormReturn } from 'react-hook-form'
3import type { CloudProvider } from 'shared-data'
4import {
5 Badge,
6 Select,
7 SelectContent,
8 SelectGroup,
9 SelectItem,
10 SelectTrigger,
11 SelectValue,
12} from 'ui'
13import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
14
15import { smartRegionToExactRegion } from './ProjectCreation.utils'
16import { useProjectCreationPostgresVersionsQuery } from '@/data/config/project-creation-postgres-versions-query'
17import { useProjectUnpausePostgresVersionsQuery } from '@/data/config/project-unpause-postgres-versions-query'
18import { PostgresEngine, ReleaseChannel } from '@/data/projects/new-project.constants'
19import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
20
21interface PostgresVersionDetails {
22 postgresEngine?: Exclude<PostgresEngine, '13' | '14'>
23 releaseChannel?: ReleaseChannel
24}
25
26interface PostgresVersionSelectorProps {
27 cloudProvider: CloudProvider
28 dbRegion: string
29 organizationSlug?: string
30 field: ControllerRenderProps<any, 'postgresVersionSelection'>
31 form: UseFormReturn<any>
32 type?: 'create' | 'unpause'
33 layout?: 'vertical' | 'horizontal'
34 label?: string
35}
36
37const formatValue = ({
38 postgres_engine,
39 release_channel,
40}: {
41 postgres_engine: string
42 release_channel: string
43}) => {
44 return `${postgres_engine}|${release_channel}`
45}
46
47export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails => {
48 if (!value) {
49 return { postgresEngine: undefined, releaseChannel: undefined }
50 }
51
52 const [postgresEngine, releaseChannel] = value.split('|')
53 return { postgresEngine, releaseChannel } as PostgresVersionDetails
54}
55
56export const PostgresVersionSelector = ({
57 cloudProvider,
58 dbRegion,
59 organizationSlug,
60 field,
61 form,
62 type = 'create',
63 layout = 'horizontal',
64 label = 'Postgres version',
65}: PostgresVersionSelectorProps) => {
66 const { data: project } = useSelectedProjectQuery()
67
68 const dbRegionExact = smartRegionToExactRegion(dbRegion)
69
70 const {
71 data: createVersions,
72 isPending: isLoadingProjectCreateVersions,
73 isSuccess,
74 } = useProjectCreationPostgresVersionsQuery(
75 {
76 cloudProvider,
77 dbRegion: dbRegionExact,
78 organizationSlug,
79 },
80 { enabled: type === 'create' }
81 )
82
83 const { data: unpauseVersions, isPending: isLoadingProjectUnpauseVersions } =
84 useProjectUnpausePostgresVersionsQuery(
85 { projectRef: project?.ref },
86 { enabled: type === 'unpause' }
87 )
88
89 const versions =
90 type === 'create'
91 ? (createVersions?.available_versions ?? [])
92 : (unpauseVersions?.available_versions ?? [])
93 const availableVersions = versions.sort((a, b) => a.version.localeCompare(b.version)).reverse()
94 const { postgresVersionSelection } = form.watch()
95
96 useEffect(() => {
97 if (availableVersions.length > 0) {
98 const gaVersion = availableVersions.find((x) => x.release_channel === 'ga')
99 const defaultValue = gaVersion ? formatValue(gaVersion) : formatValue(availableVersions[0])
100 form.setValue('postgresVersionSelection', defaultValue)
101 }
102 }, [isSuccess, availableVersions, form])
103
104 return (
105 <FormItemLayout label={label} layout={layout}>
106 <Select
107 value={postgresVersionSelection}
108 onValueChange={field.onChange}
109 disabled={
110 availableVersions.length === 0 ||
111 (type === 'create' && isLoadingProjectCreateVersions) ||
112 (type === 'unpause' && isLoadingProjectUnpauseVersions)
113 }
114 >
115 <SelectTrigger className="[&>:nth-child(1)]:w-full [&>:nth-child(1)]:flex [&>:nth-child(1)]:items-start">
116 <SelectValue placeholder="Select a Postgres version for your project" />
117 </SelectTrigger>
118 <SelectContent>
119 <SelectGroup>
120 {availableVersions.map((value) => {
121 const postgresVersion = value.version
122 .split('briven-postgres-')[1]
123 .replace('-orioledb', '')
124 return (
125 <SelectItem
126 key={formatValue(value)}
127 value={formatValue(value)}
128 className="w-full [&>:nth-child(2)]:w-full"
129 >
130 <div className="flex flex-row items-center justify-between w-full">
131 <span className="text-foreground">{postgresVersion}</span>
132 <div className="flex flex-row gap-x-2">
133 {value.release_channel !== 'ga' && (
134 <Badge variant="warning">{value.release_channel}</Badge>
135 )}
136 {value.postgres_engine.includes('oriole') && (
137 <Badge variant="default">OrioleDB</Badge>
138 )}
139 </div>
140 </div>
141 </SelectItem>
142 )
143 })}
144 </SelectGroup>
145 </SelectContent>
146 </Select>
147 </FormItemLayout>
148 )
149}