Files
Danny White a4be167491 fix(studio): clarify Warehouse status and navigation (#50554)
## What kind of change does this PR introduce?

Bug fix and UI polish.

## What is the current behavior?

The Warehouse Connect option repeats its own name, and a configured
Warehouse has no direct route back to its management page. Warehouse
table states also use badges instead of the status-dot pattern used by
Replication, and a backfilling table can misleadingly appear as “Caught
up”.

## What is the new behavior?

- Describes Warehouse as an analytical endpoint and adds a low-emphasis
“Manage Warehouse” link from the Connect sheet.
- Shares Replication’s status-dot presentation with Warehouse while
keeping feature-specific state mapping separate.
- Shows replication lag only for live tables, so backfilling and “Caught
up” are never presented together.

| Before | After |
| --- | --- |
| <img width="1244" height="1156" alt="CleanShot 2026-09-18 at 14 03
49@2x"
src="https://github.com/user-attachments/assets/60aa3496-6930-491a-af9e-9ffcfb035a0f"
/> | <img width="1216" height="1214" alt="CleanShot 2026-09-18 at 14 04
33@2x"
src="https://github.com/user-attachments/assets/84a8c80e-d8d1-4ee5-9cce-352d1f6477c2"
/> |
| <img width="1314" height="1414" alt="CleanShot 2026-09-18 at 14 02
50@2x"
src="https://github.com/user-attachments/assets/8871a4a5-0543-4290-91a7-9da949ec4c49"
/> | <img width="1308" height="1498" alt="CleanShot 2026-09-18 at 14 02
43@2x"
src="https://github.com/user-attachments/assets/ac3fc6d1-5b4d-4a3a-8ab7-bb54025e2145"
/> |

## To test

1. Open `/project/<ref>?showConnect=true&connectTab=warehouse` for a
project with Warehouse configured. Confirm the mode subtitle says
“Analytical endpoint”. Confirm the new “Manage Warehouse” button opens
`/project/<ref>/integrations/warehouse/overview`.
2. Open `/project/<ref>/integrations/warehouse/overview` while a table
is backfilling. Confirm it has a pulsing amber status dot and does not
show “Caught up”.
3. Once the table is live, confirm it has a green “Live” status dot and
its lag appears normally.

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

- **New Features**
- Added clearer Warehouse setup progress messaging with a “View
progress” action while setup is running.
- Connection details are shown once the Warehouse is provisioned or has
live tables.
- Added a Cancel action when editing changed Warehouse table selections.
- Updated table statuses with live, syncing, and warning indicators,
including animated syncing states.
  - Lag details are displayed for live tables when available.
  - Renamed the Warehouse connection option to “Analytical endpoint.”

- **Style**
  - Improved Warehouse management controls and table name readability.
  - Removed the table Size column from the Warehouse overview.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 12:21:40 +10:00

153 lines
5.5 KiB
TypeScript

import { useParams } from 'common'
import { TableEditor } from 'icons'
import { MoreVertical, RotateCcw } from 'lucide-react'
import Link from 'next/link'
import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
TableCell,
TableRow,
} from 'ui'
import { ErroredTableDetails } from '../ErroredTableDetails'
import { SlotLagMetrics as SlotLagMetricsType, TableState } from './ReplicationPipelineStatus.types'
import { getStatusConfig, getTableSyncLagLabel } from './ReplicationPipelineStatus.utils'
import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip'
import { StateDot } from '@/components/ui/StateDot'
import { ReplicationPipelineTableStatus } from '@/data/replication/pipeline-replication-status-query'
interface TableReplicationRowProps {
table: ReplicationPipelineTableStatus
isRestarting: boolean
showDisabledState: boolean
disabledStateMessage: string
isAnyRestartInProgress: boolean
isPipelineStopped: boolean
onSelectRestart: () => void
onSelectShowError: () => void
}
export const TableReplicationRow = ({
table,
isRestarting,
showDisabledState,
disabledStateMessage,
isAnyRestartInProgress,
isPipelineStopped,
onSelectRestart,
onSelectShowError,
}: TableReplicationRowProps) => {
const { ref } = useParams()
const statusConfig = getStatusConfig(table.state as TableState['state'])
const tableName = `${table.schema}.${table.name}`
const canRestart = !showDisabledState && !isRestarting && !isAnyRestartInProgress
const pipelineAction = isPipelineStopped ? 'start' : 'restart'
const isErrorState = table.state.name === 'error'
const canShowError =
isErrorState && 'reason' in table.state && !showDisabledState && !isRestarting
// A table copying during the initial sync reports its own slot metrics. Shown as one line rather
// than a grid, so the detail survives without a table cell turning into a dashboard.
const syncLag = table.table_sync_lag as SlotLagMetricsType | null | undefined
const syncLagParts = syncLag == null ? undefined : getTableSyncLagLabel(syncLag)
const syncLagLabel =
syncLagParts !== undefined && syncLagParts.length > 0 ? syncLagParts.join(' · ') : undefined
// Status column already names the state (Copying, Queued, …). Prefer the sync line when we have
// one; keep the description only when it adds something the status label doesn't say.
const detailsLine =
syncLagLabel !== undefined ? syncLagLabel : isErrorState ? undefined : statusConfig.description
return (
<TableRow>
<TableCell>
<span className="text-foreground-lighter">{table.schema}.</span>
<span className="text-foreground">{table.name}</span>
</TableCell>
<TableCell>
{isRestarting ? (
<StateDot variant="warning" isPulsing>
Resetting
</StateDot>
) : showDisabledState ? (
<StateDot variant="default">Not available</StateDot>
) : (
<StateDot
variant={statusConfig.variant}
isPulsing={statusConfig.isPulsing}
pulseDelayMs={statusConfig.isPulsing ? (table.id % 8) * 55 : undefined}
>
{statusConfig.label}
</StateDot>
)}
</TableCell>
<TableCell>
{isRestarting ? (
<p className="text-sm text-foreground-lighter">
Resetting. The pipeline will {pipelineAction} automatically…
</p>
) : showDisabledState ? (
<p className="text-sm text-foreground-lighter">{disabledStateMessage}</p>
) : isErrorState ? (
<div className="flex flex-col gap-y-3 text-sm text-foreground-lighter">
<p>{statusConfig.description}.</p>
<ErroredTableDetails table={table} />
</div>
) : (
<p className="text-sm text-foreground-lighter">{detailsLine}</p>
)}
</TableCell>
<TableCell>
<div className="flex items-center justify-end gap-x-2">
{canShowError && (
<Button variant="default" onClick={onSelectShowError}>
View error
</Button>
)}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="default"
className="px-1.25 hit-area-2"
aria-label={`Options for ${tableName}`}
icon={<MoreVertical />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end" className="w-44">
<DropdownMenuItemTooltip
className="gap-x-2"
disabled={!canRestart}
onClick={onSelectRestart}
tooltip={{
content: {
side: 'left',
text: canRestart ? undefined : disabledStateMessage,
},
}}
>
<RotateCcw size={14} />
<span>Reset table</span>
</DropdownMenuItemTooltip>
<DropdownMenuItem className="gap-x-2" asChild>
<Link
target="_blank"
rel="noopener noreferrer"
href={`/project/${ref}/editor/${table.id}`}
>
<TableEditor size={14} />
<span>View in Table Editor</span>
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TableCell>
</TableRow>
)
}