index.tsx 4.9 KB

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