DiskManagementReviewAndSubmitDialog.hooks.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { useMemo } from 'react'
  2. import { UseFormReturn } from 'react-hook-form'
  3. import { DiskStorageSchemaType } from '../DiskManagement.schema'
  4. import {
  5. calculateComputeSizePrice,
  6. calculateDiskSizePrice,
  7. calculateIOPSPrice,
  8. calculateThroughputPrice,
  9. getAvailableComputeOptions,
  10. mapAddOnVariantIdToComputeSize,
  11. } from '../DiskManagement.utils'
  12. import { DiskType } from '../ui/DiskManagement.constants'
  13. import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query'
  14. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  15. import {
  16. useIsAwsNimbusCloudProvider,
  17. useSelectedProjectQuery,
  18. } from '@/hooks/misc/useSelectedProject'
  19. export function useDiskManagementReviewChanges(
  20. form: UseFormReturn<DiskStorageSchemaType>,
  21. numReplicas: number
  22. ) {
  23. const { data: project } = useSelectedProjectQuery()
  24. const { data: org } = useSelectedOrganizationQuery()
  25. const isAwsNimbus = useIsAwsNimbusCloudProvider()
  26. const { data: addons } = useProjectAddonsQuery({ projectRef: project?.ref })
  27. const isAwsK8sProject = project?.cloud_provider === 'AWS_K8S'
  28. const planId = org?.plan.id ?? 'free'
  29. const availableAddons = useMemo(() => addons?.available_addons ?? [], [addons])
  30. const availableOptions = useMemo(
  31. () => getAvailableComputeOptions(availableAddons, project?.cloud_provider),
  32. [availableAddons, project?.cloud_provider]
  33. )
  34. // --- Prices ---
  35. const computeSizePrice = calculateComputeSizePrice({
  36. availableOptions,
  37. oldComputeSize: form.formState.defaultValues?.computeSize || 'ci_micro',
  38. newComputeSize: form.getValues('computeSize'),
  39. plan: planId,
  40. })
  41. const diskSizePrice = calculateDiskSizePrice({
  42. planId,
  43. oldSize: form.formState.defaultValues?.totalSize || 0,
  44. oldStorageType: form.formState.defaultValues?.storageType as DiskType,
  45. newSize: form.getValues('totalSize'),
  46. newStorageType: form.getValues('storageType') as DiskType,
  47. numReplicas,
  48. })
  49. const iopsPrice = calculateIOPSPrice({
  50. oldStorageType: form.formState.defaultValues?.storageType as DiskType,
  51. oldProvisionedIOPS: form.formState.defaultValues?.provisionedIOPS || 0,
  52. newStorageType: form.getValues('storageType') as DiskType,
  53. newProvisionedIOPS: form.getValues('provisionedIOPS'),
  54. numReplicas,
  55. })
  56. const throughputPrice = calculateThroughputPrice({
  57. storageType: form.getValues('storageType') as DiskType,
  58. newThroughput: form.getValues('throughput') || 0,
  59. oldThroughput: form.formState.defaultValues?.throughput || 0,
  60. numReplicas,
  61. })
  62. const totalBeforePrice =
  63. Number(computeSizePrice.oldPrice) +
  64. Number(diskSizePrice.oldPrice) +
  65. Number(iopsPrice.oldPrice) +
  66. Number(throughputPrice.oldPrice)
  67. const totalAfterPrice =
  68. Number(computeSizePrice.newPrice) +
  69. Number(diskSizePrice.newPrice) +
  70. Number(iopsPrice.newPrice) +
  71. Number(throughputPrice.newPrice)
  72. // --- Change flags ---
  73. const hasComputeChanges =
  74. form.formState.defaultValues?.computeSize !== form.getValues('computeSize')
  75. const hasTotalSizeChanges =
  76. !isAwsK8sProject &&
  77. !isAwsNimbus &&
  78. form.formState.defaultValues?.totalSize !== form.getValues('totalSize')
  79. const hasStorageTypeChanges =
  80. !isAwsK8sProject &&
  81. !isAwsNimbus &&
  82. form.formState.defaultValues?.storageType !== form.getValues('storageType')
  83. const hasThroughputChanges =
  84. !isAwsK8sProject &&
  85. !isAwsNimbus &&
  86. form.formState.defaultValues?.throughput !== form.getValues('throughput')
  87. const hasIOPSChanges =
  88. !isAwsK8sProject &&
  89. !isAwsNimbus &&
  90. form.formState.defaultValues?.provisionedIOPS !== form.getValues('provisionedIOPS')
  91. const hasGrowthPercentChanges =
  92. !isAwsK8sProject &&
  93. !isAwsNimbus &&
  94. form.formState.defaultValues?.growthPercent !== form.getValues('growthPercent')
  95. const hasMinIncrementChanges =
  96. !isAwsK8sProject &&
  97. !isAwsNimbus &&
  98. form.formState.defaultValues?.minIncrementGb !== form.getValues('minIncrementGb')
  99. const hasMaxSizeChanges =
  100. !isAwsK8sProject &&
  101. !isAwsNimbus &&
  102. form.formState.defaultValues?.maxSizeGb !== form.getValues('maxSizeGb')
  103. // --- Derived predicates ---
  104. const storageTypeBefore = (form.formState.defaultValues?.storageType ?? '') as DiskType
  105. const storageTypeAfter = form.getValues('storageType') as DiskType
  106. // Show hero whenever any line-item price actually changes, not just compute
  107. const anyBillableDiskChange =
  108. Number(diskSizePrice.newPrice) !== Number(diskSizePrice.oldPrice) ||
  109. Number(iopsPrice.newPrice) !== Number(iopsPrice.oldPrice) ||
  110. Number(throughputPrice.newPrice) !== Number(throughputPrice.oldPrice)
  111. // Show cooldown warning whenever any disk attribute that enforces the 4-hour lock changes
  112. const anyDiskAttributeChange = hasIOPSChanges || hasStorageTypeChanges || hasTotalSizeChanges
  113. // Show throughput row whenever either the before or after storage type is GP3
  114. // (covers GP3→IO2 where the throughput charge drops to zero)
  115. const showThroughputRow =
  116. !isAwsK8sProject &&
  117. !isAwsNimbus &&
  118. (storageTypeBefore === 'gp3' || storageTypeAfter === 'gp3') &&
  119. (hasThroughputChanges || hasStorageTypeChanges)
  120. const hasAnyBreakdownRows =
  121. hasComputeChanges ||
  122. hasStorageTypeChanges ||
  123. hasIOPSChanges ||
  124. showThroughputRow ||
  125. hasTotalSizeChanges ||
  126. hasGrowthPercentChanges ||
  127. hasMinIncrementChanges ||
  128. hasMaxSizeChanges
  129. // --- Labels ---
  130. const oldComputeLabel = mapAddOnVariantIdToComputeSize(
  131. form.formState.defaultValues?.computeSize ?? 'ci_nano'
  132. )
  133. const newComputeLabel = mapAddOnVariantIdToComputeSize(form.getValues('computeSize'))
  134. return {
  135. // prices
  136. computeSizePrice,
  137. diskSizePrice,
  138. iopsPrice,
  139. throughputPrice,
  140. totalBeforePrice,
  141. totalAfterPrice,
  142. // change flags
  143. hasComputeChanges,
  144. hasTotalSizeChanges,
  145. hasStorageTypeChanges,
  146. hasThroughputChanges,
  147. hasIOPSChanges,
  148. hasGrowthPercentChanges,
  149. hasMinIncrementChanges,
  150. hasMaxSizeChanges,
  151. // derived predicates
  152. anyBillableDiskChange,
  153. anyDiskAttributeChange,
  154. showThroughputRow,
  155. hasAnyBreakdownRows,
  156. // labels
  157. oldComputeLabel,
  158. newComputeLabel,
  159. }
  160. }