index.tsx135 lines · main
1import { useParams } from 'common'
2import { useState } from 'react'
3import { AWS_REGIONS, AWS_REGIONS_KEYS } from 'shared-data'
4import { toast } from 'sonner'
5import {
6 Button,
7 InfoIcon,
8 Select,
9 SelectContent,
10 SelectItem,
11 SelectTrigger,
12 SelectValue,
13 SheetFooter,
14 SheetSection,
15} from 'ui'
16import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
17
18import { ReadReplicaEligibilityWarnings } from './ReadReplicaEligibilityWarnings'
19import { ReadReplicaPricingDialog } from './ReadReplicaPricingDialog'
20import { useCheckEligibilityDeployReplica } from './useCheckEligibilityDeployReplica'
21import { useGetReplicaCost } from './useGetReplicaCost'
22import { AVAILABLE_REPLICA_REGIONS } from '@/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'
23import { Region, useReadReplicaSetUpMutation } from '@/data/read-replicas/replica-setup-mutation'
24import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
25import { AWS_REGIONS_DEFAULT, BASE_PATH } from '@/lib/constants'
26
27interface ReadReplicaFormProps {
28 onSuccess: () => void
29 onClose: () => void
30}
31
32export const ReadReplicaForm = ({ onSuccess, onClose }: ReadReplicaFormProps) => {
33 const { ref: projectRef } = useParams()
34 const { data } = useReadReplicasQuery({ projectRef })
35
36 const [defaultRegion] = Object.entries(AWS_REGIONS).find(
37 ([_, name]) => name === AWS_REGIONS_DEFAULT
38 ) ?? ['ap-southeast-1']
39 const { totalCost } = useGetReplicaCost()
40 const { can: canDeployReplica } = useCheckEligibilityDeployReplica()
41
42 const [selectedRegion, setSelectedRegion] = useState<string>(defaultRegion)
43
44 const { mutate: setUpReplica, isPending: isSettingUp } = useReadReplicaSetUpMutation({
45 onSuccess: () => {
46 const region = AVAILABLE_REPLICA_REGIONS.find((r) => r.key === selectedRegion)?.name
47 toast.success(`Spinning up new replica in ${region ?? ' Unknown'}...`)
48 onSuccess?.()
49 onClose()
50 },
51 })
52
53 const availableRegions =
54 process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging'
55 ? AVAILABLE_REPLICA_REGIONS.filter((x) =>
56 ['SOUTHEAST_ASIA', 'CENTRAL_EU', 'EAST_US'].includes(x.key)
57 )
58 : AVAILABLE_REPLICA_REGIONS
59
60 const onSubmit = async () => {
61 const regionKey = AWS_REGIONS[selectedRegion as AWS_REGIONS_KEYS].code
62 if (!projectRef) return console.error('Project is required')
63 if (!regionKey) return toast.error('Unable to deploy replica: Unsupported region selected')
64
65 const primary = data?.find((db) => db.identifier === projectRef)
66 setUpReplica({ projectRef, region: regionKey as Region, size: primary?.size ?? 't4g.small' })
67 }
68
69 return (
70 <>
71 {!canDeployReplica && (
72 <SheetSection>
73 <ReadReplicaEligibilityWarnings />
74 </SheetSection>
75 )}
76
77 <SheetSection className="grow overflow-auto px-0 py-0">
78 <FormItemLayout
79 isReactForm={false}
80 layout="horizontal"
81 className="p-5 [&>div]:gap-y-1 [&>div>span]:text-foreground-lighter"
82 label="Region"
83 labelOptional="Select a region to deploy your replica in"
84 >
85 <Select
86 value={selectedRegion}
87 onValueChange={setSelectedRegion}
88 disabled={!canDeployReplica}
89 >
90 <SelectTrigger>
91 <SelectValue placeholder="Select a region" />
92 </SelectTrigger>
93 <SelectContent>
94 {availableRegions.map((region) => (
95 <SelectItem key={region.key} value={region.key}>
96 <div className="flex gap-x-3 items-center">
97 <img
98 alt="region icon"
99 className="w-5 rounded-xs"
100 src={`${BASE_PATH}/img/regions/${region.region}.svg`}
101 />
102 <p className="flex items-center gap-x-2">
103 <span>{region.name}</span>
104 <span className="text-xs text-foreground-lighter font-mono">
105 {region.region}
106 </span>
107 </p>
108 </div>
109 </SelectItem>
110 ))}
111 </SelectContent>
112 </Select>
113 </FormItemLayout>
114 </SheetSection>
115 <SheetFooter className="justify-between!">
116 <div className="flex items-center gap-x-4">
117 <InfoIcon className="h-5 w-5" />
118 <p className="text-sm">
119 New replica will cost an additional <span translate="no">{totalCost}/month</span>
120 </p>
121 <ReadReplicaPricingDialog />
122 </div>
123
124 <div className="flex items-center gap-x-2">
125 <Button disabled={isSettingUp} type="default" onClick={onClose}>
126 Cancel
127 </Button>
128 <Button disabled={!canDeployReplica} loading={isSettingUp} onClick={onSubmit}>
129 Deploy replica
130 </Button>
131 </div>
132 </SheetFooter>
133 </>
134 )
135}