Files
supabase/apps/studio/components/interfaces/Settings/Infrastructure/ReadReplicas/AddReadReplicaDialog.tsx
Danny White 34d6c2fbc5 feat(studio): move read replica creation to a dialog (#49516)
## What kind of change does this PR introduce?

Studio interface improvement.

## What is the current behavior?

Read replica creation uses an oversized sheet, with region selection,
eligibility guidance, and pricing all awkwardly competing for space. The
cost estimate can briefly show a compute-only subtotal while disk
pricing is still loading.

## What is the new behavior?

Read replica creation uses a focused, centred dialog with a vertical
region field, contextual eligibility guidance, and a separate cost
breakdown. Disabled forms omit redundant deployment-location text. The
additional monthly cost appears only after both compute and disk pricing
inputs are available.

| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Infrastructure Settings Chives
Pantry Supabase"
src="https://github.com/user-attachments/assets/afb0a9a6-3575-4d65-98a3-f21b23a032ac"
/> | <img width="1024" height="759" alt="Infrastructure Settings Chives
Pantry Supabase"
src="https://github.com/user-attachments/assets/89fd96f8-8f95-4b88-b1fd-505a261ff9a0"
/> |
| <img width="1024" height="759" alt="Infrastructure Settings Chisel
Toolshed Supabase"
src="https://github.com/user-attachments/assets/dd110b3a-6aca-4074-9a69-2dc711f4d1c7"
/> | <img width="1024" height="759" alt="Infrastructure Settings Chisel
Toolshed Supabase"
src="https://github.com/user-attachments/assets/894bbd81-01d5-411e-8279-1bd2e5839cff"
/> |
| <img width="1024" height="759" alt="Infrastructure Settings Chisel
Toolshed Supabase"
src="https://github.com/user-attachments/assets/09cbd315-c13d-4987-b274-daf4f91ea10d"
/> | <img width="1024" height="759" alt="Infrastructure Settings Chisel
Toolshed Supabase"
src="https://github.com/user-attachments/assets/a1effb33-1bf2-450a-8cd8-0de8675231c9"
/> |

## To test

- Open `/project/<ref>/settings/infrastructure` and select **Add read
replica** from the section header or empty state. Confirm the dialog
opens and closes using Close, Escape, backdrop, and Cancel.
- On an eligible project, confirm the pricing note initially reads
**Estimated additional cost**, then adds **of $X/month** once pricing
loads. **View breakdown** should remain disabled until then.
- Change the region and open **View breakdown**. Confirm the monthly
cost table has standard row borders and an estimated total.
- On a project below Small compute, confirm the region field is
disabled, its deployment-location description is hidden, and **Change
compute** returns to the compute controls.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Replaced the add read replica sheet with a dialog-based setup
experience.
* Added region details, eligibility guidance, compute recommendations,
and estimated pricing.
  * Added retry options when pricing information fails to load.

* **UI Improvements**
  * Updated warning messages, documentation links, and action labels.
  * Improved dialog behavior and deferred data loading until opened.

* **Tests**
* Expanded coverage for dialog behavior, eligibility warnings, pricing
errors, retries, and recommendations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-26 15:17:37 +08:00

63 lines
1.8 KiB
TypeScript

import { useRef } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogSectionSeparator, DialogTitle } from 'ui'
import { ReadReplicaForm } from './ReadReplicaForm'
import type { RecommendedComputeForReadReplicas } from './recommendCompute'
interface AddReadReplicaDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
onSuccess?: () => void
onRecommendCompute: (size: RecommendedComputeForReadReplicas) => void
}
export const AddReadReplicaDialog = ({
open,
onOpenChange,
onSuccess,
onRecommendCompute,
}: AddReadReplicaDialogProps) => {
const pendingRecommendationRef = useRef<RecommendedComputeForReadReplicas | null>(null)
const onClose = () => {
onOpenChange(false)
}
const closeWithRecommendation = (size: RecommendedComputeForReadReplicas) => {
pendingRecommendationRef.current = size
onClose()
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
size="small"
aria-describedby={undefined}
onCloseAutoFocus={(event) => {
const recommendation = pendingRecommendationRef.current
if (!recommendation) return
// The recommendation replaces the trigger as the close destination.
// Radix calls this after the close animation has completed.
event.preventDefault()
pendingRecommendationRef.current = null
onRecommendCompute(recommendation)
}}
>
<div className="flex min-h-0 flex-col" tabIndex={-1}>
<DialogHeader>
<DialogTitle>Add read replica</DialogTitle>
</DialogHeader>
<DialogSectionSeparator />
<ReadReplicaForm
onClose={onClose}
onSuccess={() => onSuccess?.()}
onRecommendCompute={closeWithRecommendation}
/>
</div>
</DialogContent>
</Dialog>
)
}