From a2b40468d00c0f5ba334594da6198b1db1af45cd Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 4 Mar 2026 15:28:01 +0800 Subject: [PATCH] Joshen/fe 2453 post rollout dashboard clean up (#43325) ## Context Just cleaning up the `unifiedReplication` feature flag now that this is fully rolled out Also addresses some bugs that I came across while cleaning up, specifically for the new home page - Add enabled features flag check for read replicas in database replication - Infra diagram, read replicas - View connection string CTA to open connect UI with the right database selected - Replaced all other actions with a "Manage replica" CTA which links to the replica details page --- .../studio/.github/eslint-rule-baselines.json | 3 - .../Database/Replication/ComingSoon.tsx | 232 ------- .../DestinationPanel/DestinationPanel.tsx | 12 +- .../DestinationTypeSelection.tsx | 12 +- .../Database/Replication/Destinations.tsx | 103 ++-- .../Replication/ReplicationDiagram/index.tsx | 2 + .../interfaces/HomeNew/TopSection.tsx | 2 +- .../DeployNewReplicaPanel.tsx | 573 ------------------ .../InstanceConfiguration.tsx | 33 +- .../InstanceNode.tsx | 65 +- .../use-show-new-replica.tsx | 13 - .../Infrastructure/InfrastructureInfo.tsx | 50 +- .../Infrastructure/UpgradeWarnings.tsx | 17 +- .../studio/components/ui/DatabaseSelector.tsx | 22 +- .../[ref]/database/replication/index.tsx | 67 +- 15 files changed, 133 insertions(+), 1073 deletions(-) delete mode 100644 apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx delete mode 100644 apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx delete mode 100644 apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/use-show-new-replica.tsx diff --git a/apps/studio/.github/eslint-rule-baselines.json b/apps/studio/.github/eslint-rule-baselines.json index 0dd5b3fe2f1..3137e6c6f54 100644 --- a/apps/studio/.github/eslint-rule-baselines.json +++ b/apps/studio/.github/eslint-rule-baselines.json @@ -87,7 +87,6 @@ "components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx": 1, "components/interfaces/Settings/Database/SSLConfiguration.tsx": 1, "components/interfaces/Settings/General/ComplianceConfig/ProjectComplianceMode.tsx": 1, - "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx": 1, "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx": 1, "components/interfaces/Settings/Logs/LogTable.tsx": 2, "components/interfaces/Settings/Logs/LogsPreviewer.tsx": 2, @@ -948,7 +947,6 @@ "components/interfaces/Settings/General/CustomDomainConfig/DNSRecord.tsx": 1, "components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx": 1, "components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx": 1, - "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx": 1, "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DropAllReplicasConfirmationModal.tsx": 1, "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/MapView.tsx": 1, "components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx": 1, @@ -1110,7 +1108,6 @@ "components/interfaces/Organization/Usage/UsageBarChart.tsx": 1, "components/interfaces/ProjectAPIDocs/FirstLevelNav.tsx": 1, "components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx": 1, - "components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx": 1, "components/interfaces/Settings/Logs/LogSelection.tsx": 1, "components/interfaces/Settings/Logs/LogTable.tsx": 3, "components/interfaces/Settings/Logs/LogsFormatters.tsx": 2, diff --git a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx deleted file mode 100644 index 0816a5cd1fe..00000000000 --- a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { ArrowRight, ArrowUpRight, Circle, Database, Plus } from 'lucide-react' -import { useTheme } from 'next-themes' -import Link from 'next/link' -import { useMemo } from 'react' -import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reactflow' - -import 'reactflow/dist/style.css' - -import { useParams } from 'common' -import { BASE_PATH } from 'lib/constants' -import { Button, Card, CardContent } from 'ui' - -import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' - -const STATIC_NODES = [ - { - id: '1', - type: 'primary', - data: { - label: 'Primary Database', - region: 'East US (Ohio)', - provider: 'AWS', - regionIcon: 'us-east-1', - }, - position: { x: 825, y: 0 }, - }, - { - id: '2', - type: 'replica', - data: { - label: 'Iceberg', - details: '3 tables', - regionIcon: 'us-west-1', - }, - position: { x: 875, y: 110 }, - }, - { - id: '3', - type: 'replica', - data: { - label: 'BigQuery', - details: '5 tables', - regionIcon: 'us-west-1', - }, - position: { x: 875, y: 200 }, - }, - { - id: '4', - type: 'blank', - position: { x: 875, y: 290 }, - data: {}, - }, - { - id: '5', - type: 'cta', - position: { x: 125, y: 20 }, - data: {}, - }, -] - -const STATIC_EDGES = [ - { id: 'e1-2', source: '1', target: '2', type: 'smoothstep', animated: true }, - { id: 'e1-3', source: '1', target: '3', type: 'smoothstep', animated: true }, - { id: 'e1-4', source: '1', target: '4', type: 'smoothstep', animated: true }, -] - -export const ReplicationComingSoon = () => { - return ( - - - - ) -} - -const ReplicationStaticMockup = () => { - const { ref: projectRef = '_' } = useParams() - const nodes = useMemo(() => STATIC_NODES, []) - const edges = useMemo(() => STATIC_EDGES, []) - - const { resolvedTheme } = useTheme() - - const backgroundPatternColor = - resolvedTheme === 'dark' && projectRef !== '_' - ? 'rgba(255, 255, 255, 0.3)' - : 'rgba(0, 0, 0, 0.4)' - - const nodeTypes = useMemo( - () => ({ - primary: PrimaryNode, - replica: ReplicaNode, - blank: BlankNode, - cta: () => CTANode({ projectRef }), - }), - [projectRef] - ) - - return ( -
- - - -
- ) -} - -const PrimaryNode = ({ - data, -}: { - data: { label: string; region: string; provider: string; regionIcon: string } -}) => { - return ( -
-
-
-
- -
-
-

{data.label}

-

- {data.region} -

-

- {data.provider} -

-
-
-
- region icon - -
-
- -
- ) -} -const ReplicaNode = ({ - data, -}: { - data: { label: string; details: string; regionIcon: string } -}) => { - return ( -
-
-
-
-

{data.label}

-

- {data.details} -

-
-
-
- region icon - -
-
- - -
- ) -} - -const BlankNode = () => { - return ( -
-
-
-
- - Add new -
-
- -
-
- ) -} - -const CTANode = ({ projectRef }: { projectRef: string }) => { - return ( - - -

Stream database changes to external destinations

-

- Automatically replicate your data to external data warehouses and analytics platforms in - real-time. No manual exports, no lag. -

-

- We are currently in private alpha and slowly - onboarding new customers to ensure stable data pipelines. Request access below to join the - waitlist. Read replicas are available now. -

-
- - -
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx index 68e51584c9b..475513d75e5 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel/DestinationPanel.tsx @@ -1,4 +1,3 @@ -import { useFlag } from 'common' import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' import { ArrowUpRight } from 'lucide-react' import Link from 'next/link' @@ -34,7 +33,6 @@ interface DestinationPanelProps { export const DestinationPanel = ({ onSuccessCreateReadReplica }: DestinationPanelProps) => { const enablePgReplicate = useIsETLPrivateAlpha() - const unifiedReplication = useFlag('unifiedReplication') const { hasAccess: hasETLReplicationAccess } = useCheckEntitlements('replication.etl') const [urlDestinationType, setDestinationType] = useQueryState( @@ -97,11 +95,7 @@ export const DestinationPanel = ({ onSuccessCreateReadReplica }: DestinationPane return ( <> - +
{editMode ? 'Edit destination' : 'Create a new destination'} @@ -118,7 +112,7 @@ export const DestinationPanel = ({ onSuccessCreateReadReplica }: DestinationPane {destinationType === 'Read Replica' ? ( onSuccessCreateReadReplica?.()} /> - ) : unifiedReplication && !enablePgReplicate ? ( + ) : !enablePgReplicate ? (
@@ -147,7 +141,7 @@ export const DestinationPanel = ({ onSuccessCreateReadReplica }: DestinationPane
- ) : unifiedReplication && replicationNotEnabled ? ( + ) : replicationNotEnabled ? ( { - const unifiedReplication = useFlag('unifiedReplication') const etlEnableBigQuery = useIsETLBigQueryPrivateAlpha() const etlEnableIceberg = useIsETLIcebergPrivateAlpha() + const { infrastructureReadReplicas } = useIsFeatureEnabled(['infrastructure:read_replicas']) - const numberOfTypes = [unifiedReplication, etlEnableBigQuery, etlEnableIceberg].filter( + const numberOfTypes = [infrastructureReadReplicas, etlEnableBigQuery, etlEnableIceberg].filter( Boolean ).length @@ -57,7 +57,7 @@ export const DestinationTypeSelection = () => { '[&>button:first-of-type]:!rounded-l-lg [&>button:last-of-type]:!rounded-r-lg' )} > - {((!editMode && unifiedReplication) || + {((!editMode && infrastructureReadReplicas) || (editMode && destinationType === 'Read Replica')) && ( {

BigQuery

- {unifiedReplication && Alpha} + Alpha

Send data to Google Cloud's data warehouse for analytics and business intelligence @@ -108,7 +108,7 @@ export const DestinationTypeSelection = () => {

Analytics Bucket

- {unifiedReplication && Alpha} + Alpha

Send data to Apache Iceberg tables in your Supabase Storage for flexible analytics diff --git a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx index 76b467a77de..0c9c8343e1a 100644 --- a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx +++ b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx @@ -1,13 +1,11 @@ import { useQueryClient } from '@tanstack/react-query' -import { useFlag, useParams } from 'common' +import { useParams } from 'common' import { AlertError } from 'components/ui/AlertError' import { DocsButton } from 'components/ui/DocsButton' import { useReplicationDestinationsQuery } from 'data/replication/destinations-query' import { replicationKeys } from 'data/replication/keys' import { fetchReplicationPipelineVersion } from 'data/replication/pipeline-version-query' import { useReplicationPipelinesQuery } from 'data/replication/pipelines-query' -import { useReplicationSourcesQuery } from 'data/replication/sources-query' -import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' import { DOCS_URL } from 'lib/constants' import { Plus, Search, X } from 'lucide-react' import { parseAsStringEnum, useQueryState } from 'nuqs' @@ -16,13 +14,13 @@ import { Button, Card, CardContent, + cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, - cn, } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns' import { Input } from 'ui-patterns/DataInputs/Input' @@ -31,18 +29,27 @@ import { REPLICA_STATUS } from '../../Settings/Infrastructure/InfrastructureConf import { DestinationPanel } from './DestinationPanel/DestinationPanel' import { DestinationType } from './DestinationPanel/DestinationPanel.types' import { DestinationRow } from './DestinationRow' -import { EnableReplicationCallout } from './EnableReplicationCallout' import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' import { ReadReplicaRow } from './ReadReplicas/ReadReplicaRow' +import { useIsETLBigQueryPrivateAlpha, useIsETLIcebergPrivateAlpha } from './useIsETLPrivateAlpha' import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query' +import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' export const Destinations = () => { const queryClient = useQueryClient() const { ref: projectRef } = useParams() - const { hasAccess: hasETLReplicationAccess, isLoading: isLoadingEntitlement } = - useCheckEntitlements('replication.etl') - const unifiedReplication = useFlag('unifiedReplication') + const etlEnableBigQuery = useIsETLBigQueryPrivateAlpha() + const etlEnableIceberg = useIsETLIcebergPrivateAlpha() + const { infrastructureReadReplicas } = useIsFeatureEnabled(['infrastructure:read_replicas']) + + const newDestinationDefaultType = infrastructureReadReplicas + ? 'Read Replica' + : etlEnableBigQuery + ? 'BigQuery' + : etlEnableIceberg + ? 'Analytics Bucket' + : null const prefetchedRef = useRef(false) const [filterString, setFilterString] = useState('') @@ -74,18 +81,6 @@ export const Destinations = () => { ? readReplicas : readReplicas.filter((replica) => replica.identifier.includes(filterString.toLowerCase())) - const { - data: sourcesData, - error: sourcesError, - isPending: isSourcesLoading, - isError: isSourcesError, - isSuccess: isSourcesSuccess, - } = useReplicationSourcesQuery({ - projectRef, - }) - const sourceId = sourcesData?.sources.find((s) => s.name === projectRef)?.id - const replicationNotEnabled = isSourcesSuccess && !sourceId - const { data: destinationsData, error: destinationsError, @@ -104,19 +99,12 @@ export const Destinations = () => { destination.name.toLowerCase().includes(filterString.toLowerCase()) ) - const { - data: pipelinesData, - error: pipelinesError, - isPending: isPipelinesLoading, - isError: isPipelinesError, - isSuccess: isPipelinesSuccess, - } = useReplicationPipelinesQuery({ + const { data: pipelinesData, isSuccess: isPipelinesSuccess } = useReplicationPipelinesQuery({ projectRef, }) - const isLoading = - isSourcesLoading || isDestinationsLoading || isDatabasesLoading || isLoadingEntitlement - const hasErrorsFetchingData = isSourcesError || isDestinationsError || isDatabasesError + const isLoading = isDestinationsLoading || isDatabasesLoading + const hasErrorsFetchingData = isDestinationsError || isDatabasesError useEffect(() => { if ( @@ -184,15 +172,14 @@ export const Destinations = () => { />

- {(unifiedReplication || !!sourceId) && ( - - )} +
@@ -201,16 +188,14 @@ export const Destinations = () => {
{hasErrorsFetchingData && ( )} {isLoading ? ( - ) : !unifiedReplication && replicationNotEnabled ? ( - - ) : (unifiedReplication && hasReplicas) || hasDestinations ? ( + ) : hasReplicas || hasDestinations ? ( @@ -231,16 +216,15 @@ export const Destinations = () => { - {unifiedReplication && - filteredReplicas.map((replica) => { - return ( - setStatusRefetchInterval(5000)} - /> - ) - })} + {filteredReplicas.map((replica) => { + return ( + setStatusRefetchInterval(5000)} + /> + ) + })} {filteredDestinations.map((destination) => ( @@ -249,7 +233,7 @@ export const Destinations = () => { {!isLoading && filteredDestinations.length === 0 && filteredReplicas.length === 0 && - ((unifiedReplication && hasReplicas) || hasDestinations) && ( + (hasReplicas || hasDestinations) && (

No results found

@@ -273,15 +257,10 @@ export const Destinations = () => { 'flex flex-col px-16 rounded-lg justify-center items-center py-8 mt-4' )} > -

- {unifiedReplication - ? 'Replication keeps your data in sync across systems' - : 'Create your first destination'} -

+

Replication keeps your data in sync across systems

- {unifiedReplication - ? 'Deploy read replicas for lower latency and better resource management, or capture database changes to external platforms for real-time data pipelines.' - : 'Destinations are external platforms where your database changes are automatically sent. Connect to various data warehouses and analytics platforms to enable real-time data pipelines.'} + Deploy read replicas for lower latency and better resource management, or capture + database changes to external platforms for real-time data pipelines.

- - - - ) : !isAWSProvider ? ( - - - - Read replicas are only supported for projects provisioned via AWS - - - - Projects provisioned by other cloud providers currently will not be able to use read - replicas - - - - - ) : isAwsK8s ? ( - - - - Read replicas are not supported for AWS (Revamped) projects - - - - Projects provisioned by other cloud providers currently will not be able to use read - replicas - - - - ) : currentPgVersion < 15 ? ( - - - - Read replicas can only be deployed with projects on Postgres version 15 and above - - - If you'd like to use read replicas, please contact us via support - - - - - - ) : !isMinimallyOnSmallCompute ? ( - - - - Project required to at least be on a Small compute - - - - This is to ensure that read replicas can keep up with the primary databases' - activities. - -
- - -
-
-
- ) : !isWalgEnabled ? ( - - - - {refetchInterval !== false - ? 'Physical backups are currently being enabled' - : 'Physical backups are required to deploy replicas'} - - {refetchInterval === false && ( - - Physical backups are used under the hood to spin up read replicas for your project. - - )} - - {refetchInterval !== false - ? 'This warning will go away once physical backups have been enabled - check back in a few minutes!' - : 'Enabling physical backups will take a few minutes, after which you will be able to deploy read replicas.'} - - {refetchInterval !== false ? ( - - You may start deploying read replicas thereafter once this is completed. - - ) : ( - - - - - )} - - ) : isProWithSpendCapEnabled ? ( - - - - Spend cap needs to be disabled to deploy replicas - - - - Launching a replica incurs additional disk size that will exceed the plan's quota. - Disable the spend cap first to allow overages before launching a replica. - -
- -
-
-
- ) : reachedMaxReplicas ? ( - - - - You can only deploy up to {maxNumberOfReplicas} read replicas at once - - - If you'd like to spin up another read replica, please drop an existing replica first. - - {maxNumberOfReplicas === MAX_REPLICAS_BELOW_XL && ( - <> - - - Alternatively, you may deploy up to{' '} - {MAX_REPLICAS_ABOVE_XL} replicas if - your project is on an XL compute or higher. - -
- -
-
- - )} -
- ) : null} - -
- - {availableRegions.map((region) => ( - ( - region icon - )} - > -

- {region.name} - {region.region} -

-
- ))} -
- -
- {showNewDiskManagementUI ? ( - <> - - -

- New replica will cost an additional{' '} - - {formatCurrency( - estComputeMonthlyCost + - additionalCostDiskSize + - Number(additionalCostIOPS) + - Number(additionalCostThroughput) - )} - /month - -

- -
- -

- Read replicas will match the compute size of your primary database and will - include 25% more disk size than the primary database to accommodate WAL files. -

-

- The additional cost for the replica breaks down to: -

-
- - - Item - Description - Cost (/month) - - - - - Compute size - {selectedComputeMeta?.name} - - {formatCurrency(estComputeMonthlyCost)} - - - - Disk size - - {((size_gb ?? 0) * 1.25).toLocaleString()} GB ({type}) - - - {formatCurrency(additionalCostDiskSize)} - - - - IOPS - {iops?.toLocaleString()} IOPS - - {formatCurrency(+additionalCostIOPS)} - - - {type === 'gp3' && ( - - Throughput - {throughput_mbps?.toLocaleString()} MB/s - - {formatCurrency(+additionalCostThroughput)} - - - )} - -
- - - - ) : ( -

- Read replicas will be on the same compute size as your primary database. Deploying a - read replica on the{' '} - {selectedComputeMeta?.name} size incurs - additional{' '} - - {selectedComputeMeta?.price_description} - - . -

- )} - -

- Read more about{' '} - - billing - {' '} - for read replicas. -

-
-
- - - ) -} - -export default DeployNewReplicaPanel diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx index a66053296b7..e5376107aef 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.tsx @@ -5,6 +5,7 @@ import { useTheme } from 'next-themes' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' import ReactFlow, { Background, Edge, ReactFlowProvider, useReactFlow } from 'reactflow' + import 'reactflow/dist/style.css' import { useParams } from 'common' @@ -24,17 +25,17 @@ import { useSelectedProjectQuery, } from 'hooks/misc/useSelectedProject' import { timeout } from 'lib/helpers' -import { type AWS_REGIONS_KEYS } from 'shared-data' +import { useRouter } from 'next/router' import { Button, + cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, - cn, } from 'ui' -import DeployNewReplicaPanel from './DeployNewReplicaPanel' + import DropAllReplicasConfirmationModal from './DropAllReplicasConfirmationModal' import { DropReplicaConfirmationModal } from './DropReplicaConfirmationModal' import { SmoothstepEdge } from './Edge' @@ -43,13 +44,13 @@ import { addRegionNodes, generateNodes, getDagreGraphLayout } from './InstanceCo import { LoadBalancerNode, PrimaryNode, RegionNode, ReplicaNode } from './InstanceNode' import MapView from './MapView' import { RestartReplicaConfirmationModal } from './RestartReplicaConfirmationModal' -import { useShowNewReplicaPanel } from './use-show-new-replica' interface InstanceConfigurationUIProps { diagramOnly?: boolean } const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationUIProps) => { + const router = useRouter() const reactFlow = useReactFlow() const isOrioleDb = useIsOrioleDb() const { resolvedTheme } = useTheme() @@ -58,12 +59,11 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU const isAws = useIsAwsCloudProvider() const { infrastructureReadReplicas } = useIsFeatureEnabled(['infrastructure:read_replicas']) + const newReplicaURL = `/project/${projectRef}/database/replication?type=Read+Replica` const [view, setView] = useState<'flow' | 'map'>('flow') const [showDeleteAllModal, setShowDeleteAllModal] = useState(false) - const { showNewReplicaPanel, setShowNewReplicaPanel } = useShowNewReplicaPanel() const [refetchInterval, setRefetchInterval] = useState(10000) - const [newReplicaRegion, setNewReplicaRegion] = useState() const [selectedReplicaToDrop, setSelectedReplicaToDrop] = useState() const [selectedReplicaToRestart, setSelectedReplicaToRestart] = useState() @@ -86,6 +86,7 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU () => partition(data ?? [], (db) => db.identifier === projectRef), [data, projectRef] ) + const numReplicas = useMemo(() => data?.length ?? 0, [data]) const { data: replicasStatuses, isSuccess: isSuccessReplicasStatuses } = useReadReplicasStatusesQuery( @@ -96,7 +97,6 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU } ) - const numReplicas = useMemo(() => data?.length ?? 0, [data]) useEffect(() => { if (!isSuccessReplicasStatuses) return const refetch = async () => { @@ -241,10 +241,10 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU
0 ? 'rounded-r-none' : '')} - onClick={() => setShowNewReplicaPanel(true)} tooltip={{ content: { side: 'bottom', @@ -256,7 +256,7 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU }, }} > - Deploy a new replica + Deploy a new replica {replicas.length > 0 && ( @@ -325,10 +325,7 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU ) : ( { - setNewReplicaRegion(region) - setShowNewReplicaPanel(true) - }} + onSelectDeployNewReplica={() => router.push(newReplicaURL)} onSelectRestartReplica={setSelectedReplicaToRestart} onSelectDropReplica={setSelectedReplicaToDrop} /> @@ -339,16 +336,6 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU {!diagramOnly && ( <> - setRefetchInterval(5000)} - onClose={() => { - setNewReplicaRegion(undefined) - setShowNewReplicaPanel(false) - }} - /> - setRefetchInterval(5000)} diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx index 30c9b1ab014..a4621775408 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx @@ -1,6 +1,4 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' -import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip' import SparkBar from 'components/ui/SparkBar' import { DatabaseInitEstimations, @@ -9,15 +7,12 @@ import { } from 'data/read-replicas/replicas-status-query' import { formatDatabaseID } from 'data/read-replicas/replicas.utils' import dayjs from 'dayjs' -import { useCustomContent } from 'hooks/custom-content/useCustomContent' -import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { BASE_PATH } from 'lib/constants' import { Database, DatabaseBackup, HelpCircle, Loader2, MoreVertical } from 'lucide-react' import Link from 'next/link' -import { parseAsBoolean, useQueryState } from 'nuqs' +import { parseAsBoolean, parseAsString, useQueryStates } from 'nuqs' import { Handle, NodeProps, Position } from 'reactflow' -import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { Badge, Button, @@ -41,6 +36,7 @@ import { REPLICA_STATUS, } from './InstanceConfiguration.constants' import { formatSeconds } from './InstanceConfiguration.utils' +import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector' interface NodeData { id: string @@ -111,13 +107,11 @@ export const LoadBalancerNode = ({ data }: NodeProps) => { export const PrimaryNode = ({ data }: NodeProps) => { // [Joshen] Just FYI Handles cannot be conditionally rendered - const { provider, region, computeSize, numReplicas, numRegions, hasLoadBalancer } = data + const { region, computeSize, numReplicas, numRegions, hasLoadBalancer } = data const { projectHomepageShowInstanceSize } = useIsFeatureEnabled([ 'project_homepage:show_instance_size', ]) - const { infraAwsNimbusLabel } = useCustomContent(['infra:aws_nimbus_label']) - const providerLabel = provider === 'AWS_NIMBUS' ? infraAwsNimbusLabel : provider return ( <> @@ -183,23 +177,17 @@ export const PrimaryNode = ({ data }: NodeProps) => { } export const ReplicaNode = ({ data }: NodeProps) => { - const { - id, - region, - computeSize, - status, - inserted_at, - onSelectRestartReplica, - onSelectDropReplica, - } = data const { ref } = useParams() - const dbSelectorState = useDatabaseSelectorStateSnapshot() - const { can: canManageReplicas } = useAsyncCheckPermissions(PermissionAction.CREATE, 'projects') + const { id, region, computeSize, status, inserted_at } = data const { projectHomepageShowInstanceSize } = useIsFeatureEnabled([ 'project_homepage:show_instance_size', ]) - const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false)) + const state = useDatabaseSelectorStateSnapshot() + const [, setConnect] = useQueryStates({ + showConnect: parseAsBoolean.withDefault(false), + source: parseAsString, + }) const { data: databaseStatuses } = useReadReplicasStatusesQuery({ projectRef: ref }) const { replicaInitializationStatus } = @@ -358,43 +346,20 @@ export const ReplicaNode = ({ data }: NodeProps) => { { - setShowConnect(true) - dbSelectorState.setSelectedDatabaseId(id) + setConnect({ showConnect: true, source: id }) + state.setSelectedDatabaseId(id) }} > View connection string - - - View replication lag + + + + Manage replica - - onSelectRestartReplica()} - disabled={status !== REPLICA_STATUS.ACTIVE_HEALTHY} - > - Restart replica - - { - if (canManageReplicas) onSelectDropReplica() - }} - tooltip={{ - content: { side: 'left', text: 'You need additional permissions to drop replicas' }, - }} - > - Drop replica -
diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/use-show-new-replica.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/use-show-new-replica.tsx deleted file mode 100644 index 5b89cceade3..00000000000 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/use-show-new-replica.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { parseAsBoolean, useQueryState } from 'nuqs' - -/** - * @deprecated Can remove after read replicas is fully moved into database replication page - */ -export function useShowNewReplicaPanel() { - const [showNewReplicaPanel, setShowNewReplicaPanel] = useQueryState( - 'createReplica', - parseAsBoolean.withDefault(false) - ) - - return { showNewReplicaPanel, setShowNewReplicaPanel } -} diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx index ab0e6de4b79..2fdfcf89b68 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx @@ -1,4 +1,4 @@ -import { useFlag, useParams } from 'common' +import { useParams } from 'common' import { NoticeBar } from 'components/interfaces/DiskManagement/ui/NoticeBar' import { ScaffoldContainer, @@ -19,15 +19,12 @@ import { Admonition } from 'ui-patterns/admonition' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { ProjectUpgradeAlert } from '../General/Infrastructure/ProjectUpgradeAlert' -import { InstanceConfiguration } from './InfrastructureConfiguration/InstanceConfiguration' import { ReadReplicasWarning, ValidationErrorsWarning } from './UpgradeWarnings' export const InfrastructureInfo = () => { const { ref } = useParams() const { data: project } = useSelectedProjectQuery() - const unifiedReplication = useFlag('unifiedReplication') - const { projectAuthAll: authEnabled, projectSettingsDatabaseUpgrades: showDatabaseUpgrades } = useIsFeatureEnabled(['project_auth:all', 'project_settings:database_upgrades']) @@ -73,31 +70,26 @@ export const InfrastructureInfo = () => { return ( <> - {project?.cloud_provider !== 'FLY' && - (unifiedReplication ? ( - - - - - Go to Replication - - - } - /> - - - ) : ( - <> - - - - ))} + + {project?.cloud_provider !== 'FLY' && ( + + + + + Go to Replication + + + } + /> + + + )} diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/UpgradeWarnings.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/UpgradeWarnings.tsx index 660a5fc060c..7086d365765 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/UpgradeWarnings.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/UpgradeWarnings.tsx @@ -1,15 +1,14 @@ -import Link from 'next/link' - -import { ProjectUpgradeEligibilityValidationError } from '@/data/config/project-upgrade-eligibility-query' -import { useFlag, useParams } from 'common' +import { useParams } from 'common' import { InlineLink } from 'components/ui/InlineLink' import { DOCS_URL } from 'lib/constants' +import Link from 'next/link' import { Button } from 'ui' import { Admonition } from 'ui-patterns/admonition' +import { ProjectUpgradeEligibilityValidationError } from '@/data/config/project-upgrade-eligibility-query' + export const ReadReplicasWarning = ({ latestPgVersion }: { latestPgVersion: string }) => { const { ref } = useParams() - const unifiedReplication = useFlag('unifiedReplication') return ( - Manage read replicas - - ) : undefined + } /> ) diff --git a/apps/studio/components/ui/DatabaseSelector.tsx b/apps/studio/components/ui/DatabaseSelector.tsx index cc0166f9d85..23143762b62 100644 --- a/apps/studio/components/ui/DatabaseSelector.tsx +++ b/apps/studio/components/ui/DatabaseSelector.tsx @@ -1,12 +1,10 @@ -import { useFlag, useParams } from 'common' +import { useParams } from 'common' import { Markdown } from 'components/interfaces/Markdown' import { REPLICA_STATUS } from 'components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' -import { useShowNewReplicaPanel } from 'components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/use-show-new-replica' import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { formatDatabaseID, formatDatabaseRegion } from 'data/read-replicas/replicas.utils' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { IS_PLATFORM } from 'lib/constants' -import { timeout } from 'lib/helpers' import { noop } from 'lodash' import { Check, ChevronDown, Loader2, Plus } from 'lucide-react' import Link from 'next/link' @@ -17,18 +15,18 @@ import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { Button, ButtonProps, + cn, + Command_Shadcn_, CommandGroup_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, - Command_Shadcn_, + Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, - Popover_Shadcn_, ScrollArea, Tooltip, TooltipContent, TooltipTrigger, - cn, } from 'ui' interface DatabaseSelectorProps { @@ -54,9 +52,7 @@ export const DatabaseSelector = ({ const { ref: projectRef } = useParams() const [open, setOpen] = useState(false) const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false)) - const { setShowNewReplicaPanel } = useShowNewReplicaPanel() - const unifiedReplication = useFlag('unifiedReplication') const { infrastructureReadReplicas } = useIsFeatureEnabled(['infrastructure:read_replicas']) const state = useDatabaseSelectorStateSnapshot() @@ -74,9 +70,7 @@ export const DatabaseSelector = ({ const selectedAdditionalOption = additionalOptions.find((x) => x.id === selectedDatabaseId) - const newReplicaURL = unifiedReplication - ? `/project/${projectRef}/database/replication?type=Read+Replica` - : `/project/${projectRef}/settings/infrastructure` + const newReplicaURL = `/project/${projectRef}/database/replication?type=Read+Replica` useEffect(() => { if (_selectedDatabaseId) state.setSelectedDatabaseId(_selectedDatabaseId) @@ -229,12 +223,6 @@ export const DatabaseSelector = ({ setOpen(false) // [Joshen] This is used in the Connect UI which is available across all pages setShowConnect(false) - - // [Joshen] Adding a short timeout to compensate for the shift in focus - // the replica panel from a "portal" based component (e.g dialog, sheet, dropdown, etc) - // Although I'd prefer if there's a better way to resolve this - await timeout(50) - if (!unifiedReplication) setShowNewReplicaPanel(true) }} className="w-full flex items-center gap-2" > diff --git a/apps/studio/pages/project/[ref]/database/replication/index.tsx b/apps/studio/pages/project/[ref]/database/replication/index.tsx index 5d5c8699fd3..46c71518f67 100644 --- a/apps/studio/pages/project/[ref]/database/replication/index.tsx +++ b/apps/studio/pages/project/[ref]/database/replication/index.tsx @@ -1,11 +1,8 @@ -import { useFlag, useParams } from 'common' -import { ReplicationComingSoon } from 'components/interfaces/Database/Replication/ComingSoon' +import { useParams } from 'common' import { Destinations } from 'components/interfaces/Database/Replication/Destinations' -import { useIsETLPrivateAlpha } from 'components/interfaces/Database/Replication/useIsETLPrivateAlpha' import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout' import { DefaultLayout } from 'components/layouts/DefaultLayout' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' -import { FormHeader } from 'components/ui/Forms/FormHeader' import { UnknownInterface } from 'components/ui/UnknownInterface' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { PipelineRequestStatusProvider } from 'state/replication-pipeline-request-status' @@ -15,56 +12,36 @@ import { ReplicationDiagram } from '@/components/interfaces/Database/Replication const DatabaseReplicationPage: NextPageWithLayout = () => { const { ref } = useParams() - const enablePgReplicate = useIsETLPrivateAlpha() const showPgReplicate = useIsFeatureEnabled('database:replication') - const unifiedReplication = useFlag('unifiedReplication') if (!showPgReplicate) { return } return ( - <> - {unifiedReplication || enablePgReplicate ? ( - - - -
-
-

Replication

-
-

- {unifiedReplication - ? 'Deploy read replicas across multiple regions, or replicate database changes to external data warehouses and analytics platforms' - : 'Automatically replicate your database changes to external data warehouses and analytics platforms in real-time'} -

-
-
-
+ + + +
+
+

Replication

+
+

+ Deploy read replicas across multiple regions, or replicate database changes to + external data warehouses and analytics platforms +

+
+
+
- + - - - - - -
- ) : ( - <> - - - - - - - - )} - + + + + + +
) }