diff --git a/apps/studio/components/interfaces/Database/Warehouse/WarehouseEditorPanel.tsx b/apps/studio/components/interfaces/Database/Warehouse/WarehouseEditorPanel.tsx deleted file mode 100644 index fb0e379472f..00000000000 --- a/apps/studio/components/interfaces/Database/Warehouse/WarehouseEditorPanel.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { ChevronDown } from 'lucide-react' -import { useState, type ReactNode } from 'react' -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from 'ui' -import { TimestampInfo } from 'ui-patterns' -import { InfoTooltip } from 'ui-patterns/info-tooltip' - -import { - formatWarehouseSize, - useWarehouseTableState, - type WarehouseMode, - type WarehouseTableState, -} from './warehouseDemoStore' -import { WarehouseDetachModal } from './WarehouseDetachModal' -import { WarehouseEnablementModal, type EnablementVariant } from './WarehouseEnablementModal' -import { WarehouseSyncChip } from './WarehouseSyncChip' -import { WarehouseTimeTravelFlow } from './WarehouseTimeTravelFlow' -import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog' - -interface WarehouseEditorPanelProps { - tableKey: string - tableName: string - postgresSize?: string -} - -const MODE_LABELS: Record = { - postgres: 'Postgres', - has_warehouse_copy: 'Postgres + Warehouse', - warehouse_backed: 'Warehouse', -} - -const WAREHOUSE_MODE_TOOLTIPS: Partial> = { - has_warehouse_copy: ( - <> - Keeps this table in the Postgres heap and maintains a synced columnar copy in Warehouse. - Changes in Postgres propagate to the copy; sync is one-way, not bidirectional. - - ), - warehouse_backed: ( - <> - This table's storage was moved to Warehouse. The Postgres heap for this table no longer - exists. - - ), -} - -function StorageCard({ children }: { children: ReactNode }) { - return
{children}
-} - -function StorageMetaRow({ label, children }: { label: string; children: ReactNode }) { - return ( -
- {label} -
{children}
-
- ) -} - -function StorageModeRow({ mode }: { mode: WarehouseMode }) { - const tooltip = WAREHOUSE_MODE_TOOLTIPS[mode] - - return ( - -
- {MODE_LABELS[mode]} - {tooltip && ( - - {tooltip} - - )} -
-
- ) -} - -function StorageSyncRows({ state }: { state: WarehouseTableState }) { - return ( - <> - {state.syncState && ( - - - - )} - {state.lastSyncedAt !== undefined && ( - - - - )} - {state.lagSeconds !== undefined && ( - - {state.lagSeconds}s - - )} - - ) -} - -function CreateWarehouseCopyButton({ - onAttach, - onMove, -}: { - onAttach: () => void - onMove: () => void -}) { - return ( -
- - - -
- ) -} - -export function WarehouseEditorPanel({ - tableKey, - tableName, - postgresSize, -}: WarehouseEditorPanelProps) { - const state = useWarehouseTableState(tableKey) - const [enableModal, setEnableModal] = useState(null) - const [confirmDetach, setConfirmDetach] = useState(false) - const [detachProgress, setDetachProgress] = useState(false) - const [snapshotsOpen, setSnapshotsOpen] = useState(false) - - const copyName = state.copyName ?? `warehouse.${tableName}` - const warehouseSize = formatWarehouseSize(state.warehouseSizeBytes) - - return ( - <> -
-
Storage
- - {state.mode === 'postgres' && ( - <> - - - {postgresSize ?? '—'} - - setEnableModal('attach')} - onMove={() => setEnableModal('move')} - /> - - )} - - {state.mode === 'has_warehouse_copy' && ( - <> - - - - {copyName} - - {postgresSize ?? '—'} - {warehouseSize} - - - - - )} - - {state.mode === 'warehouse_backed' && ( - <> - - - {warehouseSize} - {state.migrationCompletedAt && ( - - - - )} - - - - )} -
- - {enableModal && ( - { - if (!open) setEnableModal(null) - }} - /> - )} - - setConfirmDetach(false)} - onClose={() => { - setConfirmDetach(false) - setDetachProgress(true) - }} - size="small" - title="Detach Warehouse copy" - description={ - <> - Detaching deletes the Warehouse copy{' '} - {copyName}. Your source table and - its data in Postgres are unaffected. - - } - confirmLabel="Detach" - cancelLabel="Cancel" - /> - - - - {state.mode === 'warehouse_backed' && ( - - )} - - ) -} diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx index 279b1f32a71..c368d9af8ac 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx @@ -25,7 +25,6 @@ import { validateFields, } from './TableEditor.utils' import { RLSToggleDialog } from '@/components/interfaces/Database/RLSToggleDialog' -import { WarehouseEditorPanel } from '@/components/interfaces/Database/Warehouse/WarehouseEditorPanel' import { DocsButton } from '@/components/ui/DocsButton' import { useDatabasePublicationsQuery } from '@/data/database-publications/database-publications-query' import { CONSTRAINT_TYPE, useTableConstraintsQuery } from '@/data/database/constraints-query' @@ -626,19 +625,6 @@ export const TableEditor = ({ handler={apiAccessToggleHandler} /> - - {!isNewRecord && !isDuplicating && ( - <> - - - - - - )} ) diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx index c6a13814147..8fe2348bcba 100644 --- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx @@ -1,7 +1,18 @@ import { useParams } from 'common' -import { Copy, Download, Edit, Globe, Lock, MoreVertical, Trash } from 'lucide-react' +import { + Box, + Copy, + Download, + Edit, + Globe, + History, + Lock, + MoreVertical, + Trash, + Unlink, +} from 'lucide-react' import Link from 'next/link' -import { type CSSProperties } from 'react' +import { useState, type CSSProperties } from 'react' import { toast } from 'sonner' import { Badge, @@ -21,11 +32,20 @@ import { TooltipTrigger, TreeViewItemVariant, } from 'ui' +import { useSnapshot } from 'valtio' import { useExportAllRowsAsCsv, useExportAllRowsAsSql } from './ExportAllRows' import { useTableFilter } from '@/components/grid/hooks/useTableFilter' import { buildTableEditorUrl } from '@/components/grid/SupabaseGrid.utils' +import { warehouseDemoStore } from '@/components/interfaces/Database/Warehouse/warehouseDemoStore' +import { WarehouseDetachModal } from '@/components/interfaces/Database/Warehouse/WarehouseDetachModal' +import { + WarehouseEnablementModal, + type EnablementVariant, +} from '@/components/interfaces/Database/Warehouse/WarehouseEnablementModal' +import { WarehouseTimeTravelFlow } from '@/components/interfaces/Database/Warehouse/WarehouseTimeTravelFlow' import { getEntityLintDetails } from '@/components/interfaces/TableGridEditor/TableEntity.utils' +import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog' import { EntityTypeIcon } from '@/components/ui/EntityTypeIcon' import { InlineLink } from '@/components/ui/InlineLink' import { getTableDefinition } from '@/data/database/table-definition-query' @@ -177,6 +197,18 @@ export const EntityListItem = ({ totalRows: rowCount, }) + // Warehouse storage (demo). Storage is an infra operation, so the actions live + // here in the table's menu rather than in the Edit Table form. + const warehouseSnap = useSnapshot(warehouseDemoStore) + const warehouseKey = `${entity.schema}.${entity.name}` + const warehouseState = warehouseSnap.tables[warehouseKey] + const warehouseMode = warehouseState?.mode ?? 'postgres' + const warehouseCopyName = warehouseState?.copyName ?? `warehouse.${entity.name}` + const [warehouseModal, setWarehouseModal] = useState(null) + const [detachConfirm, setDetachConfirm] = useState(false) + const [detachProgress, setDetachProgress] = useState(false) + const [snapshotsOpen, setSnapshotsOpen] = useState(false) + return ( + + {warehouseMode === 'postgres' && ( + + + + Storage + + + { + e.stopPropagation() + setWarehouseModal('attach') + }} + > + Copy to Warehouse + + { + e.stopPropagation() + setWarehouseModal('move') + }} + > + Move to Warehouse + + + + )} + {warehouseMode === 'has_warehouse_copy' && ( + { + e.stopPropagation() + setDetachConfirm(true) + }} + > + + Detach Warehouse copy + + )} + {warehouseMode === 'warehouse_backed' && ( + { + e.stopPropagation() + setSnapshotsOpen(true) + }} + > + + View snapshots + + )} + {exportCsvConfirmationModal} {exportSqlConfirmationModal} + + {warehouseModal && ( + { + if (!open) setWarehouseModal(null) + }} + /> + )} + setDetachConfirm(false)} + onClose={() => { + setDetachConfirm(false) + setDetachProgress(true) + }} + size="small" + title="Detach Warehouse copy" + description={ + <> + Detaching deletes the Warehouse copy{' '} + {warehouseCopyName}. Your source + table and its data in Postgres are unaffected. + + } + confirmLabel="Detach" + cancelLabel="Cancel" + /> + + {warehouseMode === 'warehouse_backed' && ( + + )} ) }