mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## What kind of change does this PR introduce? Studio UI refactor. ## What is the current behavior? Pipeline table reset dialogs repeat explanatory content and use more layout than the reset decision needs. ## What is the new behavior? Simplifies the single-table and batch reset confirmations while preserving their cost estimate, destructive consequences, and existing reset mutations. | Before | After | | --- | --- | | <img width="854" height="1090" alt="CleanShot 2026-09-16 at 12 54 36@2x" src="https://github.com/user-attachments/assets/f9eef09b-89d1-4747-bc4c-e81fb64c584b" /> | <img width="840" height="742" alt="CleanShot 2026-09-16 at 17 01 11@2x" src="https://github.com/user-attachments/assets/fa45728c-ec66-45c8-9fef-9d2eb8310d4d" /> | This is an independent slice extracted from #49630. The related review series is this one, #50444, #50445, #50446, then #49630. ## To test 1. Open `/project/<ref>/database/replication` and select a pipeline. 2. Reset one replicated table and confirm the dialog explains that destination data will be deleted and resynchronised. 3. Choose **Reset all tables** and confirm the batch dialog shows the same concise treatment. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## UI Updates * **UI Updates** * Renamed replication “restart” actions to “reset” across dialogs, buttons, notifications, and cost estimates. * Updated messaging to clarify whether the pipeline will start or restart automatically after resetting. * Added clearer initial-sync guidance for all, some, or none of the affected tables. * Improved reset cost estimate messaging, including when no additional initial-sync charge applies. * Updated reset dialogs with clearer titles, descriptions, loading states, and error messages. * Disabled reset actions when pipeline status is unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
93 lines
3.4 KiB
TypeScript
93 lines
3.4 KiB
TypeScript
import { useMemo } from 'react'
|
||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||
|
||
import {
|
||
summarizeTableCopyEstimate,
|
||
type ReplicationTableIdentity,
|
||
type TableCopyEstimate,
|
||
} from './TableSyncCopy.utils'
|
||
import { useReplicationCostEstimateQuery } from '@/data/replication/cost-estimate-query'
|
||
import { formatBytes, formatCurrency } from '@/lib/helpers'
|
||
|
||
interface RestartCostEstimateProps {
|
||
open: boolean
|
||
projectRef?: string
|
||
sourceId?: number
|
||
publicationName?: string
|
||
tables: Pick<ReplicationTableIdentity, 'schema' | 'name'>[]
|
||
}
|
||
|
||
export const calculateRestartCostEstimate = (
|
||
estimates: TableCopyEstimate[],
|
||
tables: Pick<ReplicationTableIdentity, 'schema' | 'name'>[]
|
||
) => summarizeTableCopyEstimate(estimates, tables)
|
||
|
||
export const RestartCostEstimate = ({
|
||
open,
|
||
projectRef,
|
||
sourceId,
|
||
publicationName,
|
||
tables,
|
||
}: RestartCostEstimateProps) => {
|
||
const { data: estimate, isFetching } = useReplicationCostEstimateQuery(
|
||
{ projectRef, sourceId, publicationName },
|
||
{ enabled: open && tables.length > 0 }
|
||
)
|
||
const restartEstimate = useMemo(
|
||
() =>
|
||
estimate === undefined
|
||
? undefined
|
||
: calculateRestartCostEstimate(estimate.table_copy.tables, tables),
|
||
[estimate, tables]
|
||
)
|
||
return (
|
||
<div className="space-y-2 border-t px-5 py-4">
|
||
{tables.length === 0 ? (
|
||
<div className="flex items-center justify-between gap-x-6">
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-medium">No additional initial sync charge</p>
|
||
<p className="text-xs text-foreground-lighter">
|
||
This reset will skip initial sync based on the pipeline’s settings.
|
||
</p>
|
||
</div>
|
||
<span className="shrink-0 font-mono text-lg font-semibold" translate="no">
|
||
{formatCurrency(0)}
|
||
</span>
|
||
</div>
|
||
) : isFetching ? (
|
||
<GenericSkeletonLoader className="w-full" />
|
||
) : restartEstimate?.isComplete ? (
|
||
<div className="flex items-center justify-between gap-x-6">
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-medium">Estimated additional initial sync cost</p>
|
||
<p className="text-xs text-foreground-lighter">
|
||
Based on an estimated {formatBytes(restartEstimate.estimatedBytes)} of initial sync
|
||
data across {tables.length} {tables.length === 1 ? 'table' : 'tables'}
|
||
</p>
|
||
</div>
|
||
<span className="shrink-0 font-mono text-lg font-semibold" translate="no">
|
||
{formatCurrency(restartEstimate.estimatedCost)}
|
||
{restartEstimate.hasRowFilteredTables ? '*' : null}
|
||
</span>
|
||
</div>
|
||
) : (
|
||
<p className="text-sm text-foreground-light">
|
||
A cost estimate is unavailable. You can still reset the{' '}
|
||
{tables.length === 1 ? 'table' : 'tables'}.
|
||
</p>
|
||
)}
|
||
{restartEstimate?.isComplete && restartEstimate.hasRowFilteredTables && (
|
||
<p className="text-xs text-foreground-lighter">
|
||
*Row filters can reduce the data processed compared with this estimate.
|
||
</p>
|
||
)}
|
||
{restartEstimate?.isComplete && (
|
||
<p className="text-xs text-foreground-lighter">
|
||
Quick planning estimate; the final charge is based on successfully processed initial sync
|
||
data, which is billed again.
|
||
</p>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|