mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## Context Replaces the provider name with the region code in the infrastructure settings overview node diagram - The latter is more important, and we don't really specify the cloud provider anywhere in the dashboard <img width="315" height="137" alt="Screenshot 2026-02-05 at 18 43 11" src="https://github.com/user-attachments/assets/bc6f4dab-e9be-4663-ae1e-50e76b517c86" /> Also made the same changes in the upcoming database replication page too in the form of tooltips And swapped the title + description to be more consistent (Top: type of data, bottom: description), same for the table below Before: <img width="737" height="335" alt="image" src="https://github.com/user-attachments/assets/6674564b-e871-4cd9-83a3-e0bfde7a9f83" /> <img width="287" height="354" alt="image" src="https://github.com/user-attachments/assets/f234de68-107b-470d-804a-bd3b1d9ae9dc" /> After: <img width="637" height="350" alt="image" src="https://github.com/user-attachments/assets/03e08d55-43a1-4a16-8be1-11dd7d14fef3" /> <img width="364" height="357" alt="image" src="https://github.com/user-attachments/assets/0e49df5b-68e5-4652-8dca-4d44ebb8c3ab" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added region metadata with interactive tooltips across replication and database views. * Actions that modify replicas now refresh the list automatically. * Replication diagram layout updated with increased node spacing and a wider zoom-out range. * **Style** * Destination and replica name/ID display reorganized for clearer, two-line presentation. * BigQuery icon now inherits color from CSS for better visual consistency. * Provider/region labels refined for clearer wording. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <a@alaisteryoung.com>
149 lines
4.9 KiB
TypeScript
149 lines
4.9 KiB
TypeScript
import { useQueryClient } from '@tanstack/react-query'
|
|
import { useParams } from 'common'
|
|
import { useTheme } from 'next-themes'
|
|
import { useEffect, useMemo } from 'react'
|
|
import ReactFlow, { Background, ReactFlowProvider, useReactFlow } from 'reactflow'
|
|
|
|
import { getStatusName } from '../Pipeline.utils'
|
|
import { PrimaryDatabaseNode, ReadReplicaNode, ReplicationNode } from './Nodes'
|
|
import { getDagreGraphLayout } from './ReplicationDiagram.utils'
|
|
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
|
|
import { useReplicationDestinationsQuery } from '@/data/replication/destinations-query'
|
|
import { replicationKeys } from '@/data/replication/keys'
|
|
import { ReplicationPipelineStatusResponse } from '@/data/replication/pipeline-status-query'
|
|
import { useReplicationPipelinesQuery } from '@/data/replication/pipelines-query'
|
|
import { timeout } from '@/lib/helpers'
|
|
|
|
export const ReplicationDiagram = () => {
|
|
return (
|
|
<ReactFlowProvider>
|
|
<ReplicationDiagramContent />
|
|
</ReactFlowProvider>
|
|
)
|
|
}
|
|
|
|
const nodeTypes = {
|
|
primary: PrimaryDatabaseNode,
|
|
replication: ReplicationNode,
|
|
readReplica: ReadReplicaNode,
|
|
}
|
|
|
|
const ReplicationDiagramContent = () => {
|
|
const reactFlow = useReactFlow()
|
|
const { resolvedTheme } = useTheme()
|
|
const queryClient = useQueryClient()
|
|
const { ref: projectRef = 'default' } = useParams()
|
|
|
|
const { data: databases = [], isSuccess: isSuccessReplicas } = useReadReplicasQuery({
|
|
projectRef,
|
|
})
|
|
const readReplicas = databases.filter((x) => x.identifier !== projectRef)
|
|
|
|
const { data, isSuccess: isSuccessDestinations } = useReplicationDestinationsQuery({
|
|
projectRef,
|
|
})
|
|
const destinations = useMemo(() => data?.destinations ?? [], [data])
|
|
|
|
const { data: pipelinesData } = useReplicationPipelinesQuery({ projectRef })
|
|
|
|
const nodes = useMemo(() => {
|
|
return [
|
|
{ id: projectRef, type: 'primary', data: {}, position: { x: 0, y: 5 } },
|
|
...readReplicas.map((x) => ({
|
|
id: x.identifier,
|
|
type: 'readReplica',
|
|
data: {},
|
|
position: { x: 0, y: 0 },
|
|
})),
|
|
...destinations.map((x) => ({
|
|
id: x.id.toString(),
|
|
type: 'replication',
|
|
data: {},
|
|
position: { x: 0, y: 0 },
|
|
})),
|
|
]
|
|
}, [destinations, projectRef, readReplicas])
|
|
|
|
const edges = useMemo(() => {
|
|
return [
|
|
...readReplicas.map((x) => {
|
|
const isReplicating = x.status === 'ACTIVE_HEALTHY'
|
|
|
|
return {
|
|
id: `${projectRef}-${x.identifier}`,
|
|
source: projectRef,
|
|
target: x.identifier,
|
|
type: 'smoothstep',
|
|
className: '!cursor-default',
|
|
animated: isReplicating,
|
|
style: {
|
|
opacity: isReplicating ? 1 : 0.4,
|
|
strokeDasharray: isReplicating ? undefined : '5 5',
|
|
},
|
|
}
|
|
}),
|
|
...destinations.map((x) => {
|
|
const pipeline = (pipelinesData?.pipelines ?? []).find((p) => p.destination_id === x.id)
|
|
const pipelineStatus = queryClient.getQueryData(
|
|
replicationKeys.pipelinesStatus(projectRef, pipeline?.id)
|
|
) as ReplicationPipelineStatusResponse
|
|
const statusName = getStatusName(pipelineStatus?.status)
|
|
const isReplicating = statusName === 'started'
|
|
|
|
return {
|
|
id: `${projectRef}-${x.id}`,
|
|
source: projectRef,
|
|
target: x.id.toString(),
|
|
type: 'smoothstep',
|
|
className: '!cursor-default',
|
|
animated: isReplicating,
|
|
style: {
|
|
opacity: isReplicating ? 1 : 0.4,
|
|
strokeDasharray: isReplicating ? undefined : '5 5',
|
|
},
|
|
}
|
|
}),
|
|
]
|
|
}, [destinations, pipelinesData?.pipelines, projectRef, queryClient, readReplicas])
|
|
|
|
const backgroundPatternColor =
|
|
resolvedTheme === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.4)'
|
|
|
|
const setReactFlow = async () => {
|
|
const graph = getDagreGraphLayout(nodes, edges)
|
|
reactFlow.setNodes(graph.nodes)
|
|
reactFlow.setEdges(graph.edges)
|
|
|
|
// [Joshen] Odd fix to ensure that react flow snaps back to center when adding nodes
|
|
await timeout(1)
|
|
reactFlow.fitView({ minZoom: 0.8, maxZoom: 0.9 })
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (nodes.length > 0 && isSuccessDestinations && isSuccessReplicas) setReactFlow()
|
|
}, [nodes, isSuccessDestinations, isSuccessReplicas])
|
|
|
|
return (
|
|
<div className="nowheel relative min-h-[350px]">
|
|
<ReactFlow
|
|
fitView
|
|
fitViewOptions={{ minZoom: 0.8, maxZoom: 0.9 }}
|
|
className="bg"
|
|
zoomOnPinch={false}
|
|
zoomOnScroll={false}
|
|
nodesDraggable={false}
|
|
nodesConnectable={false}
|
|
zoomOnDoubleClick={false}
|
|
edgesFocusable={false}
|
|
edgesUpdatable={false}
|
|
defaultNodes={[]}
|
|
defaultEdges={[]}
|
|
nodeTypes={nodeTypes}
|
|
proOptions={{ hideAttribution: true }}
|
|
>
|
|
<Background color={backgroundPatternColor} />
|
|
</ReactFlow>
|
|
</div>
|
|
)
|
|
}
|