mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 21:05: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>
168 lines
6.3 KiB
TypeScript
168 lines
6.3 KiB
TypeScript
import { useParams } from 'common'
|
|
import { AnalyticsBucket, BigQuery, Database } from 'icons'
|
|
import { PropsWithChildren, useMemo } from 'react'
|
|
import { Handle, Position } from 'reactflow'
|
|
import { AWS_REGIONS } from 'shared-data'
|
|
import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
|
|
|
import { getStatusName } from '../Pipeline.utils'
|
|
import { getStatusLabel } from '../ReadReplicas/ReadReplicas.utils'
|
|
import { STATUS_REFRESH_FREQUENCY_MS } from '../Replication.constants'
|
|
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
|
|
import { formatDatabaseID } from '@/data/read-replicas/replicas.utils'
|
|
import { useReplicationDestinationsQuery } from '@/data/replication/destinations-query'
|
|
import { useReplicationPipelineStatusQuery } from '@/data/replication/pipeline-status-query'
|
|
import { useReplicationPipelinesQuery } from '@/data/replication/pipelines-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { BASE_PATH } from '@/lib/constants'
|
|
|
|
export const NODE_WIDTH = 480
|
|
|
|
const NodeContainer = ({ className, children }: PropsWithChildren<{ className?: string }>) => {
|
|
return (
|
|
<div
|
|
style={{ width: NODE_WIDTH / 2 + 55 }}
|
|
className={cn(
|
|
'flex items-start justify-between p-3 rounded bg-surface-100 border border-default',
|
|
className
|
|
)}
|
|
>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export const PrimaryDatabaseNode = () => {
|
|
const { ref: projectRef } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const { data: databases = [] } = useReadReplicasQuery({ projectRef })
|
|
const hasReadReplicas = databases.some((x) => x.identifier !== projectRef)
|
|
|
|
const { data: destinationsData } = useReplicationDestinationsQuery({ projectRef })
|
|
const hasDestinations = (destinationsData?.destinations ?? []).length > 0
|
|
|
|
const region = Object.values(AWS_REGIONS).find((x) => x.code === project?.region)
|
|
const hasReplication = hasReadReplicas || hasDestinations
|
|
|
|
return (
|
|
<NodeContainer>
|
|
<div className="text-sm flex flex-col gap-y-0.5">
|
|
<p>Primary Database</p>
|
|
<p className="text-foreground-light">{region?.displayName}</p>
|
|
<p className="text-foreground-light">{region?.code}</p>
|
|
</div>
|
|
{!!project && (
|
|
<img
|
|
alt="region icon"
|
|
className="w-8 rounded-sm mt-0.5"
|
|
src={`${BASE_PATH}/img/regions/${project?.region}.svg`}
|
|
/>
|
|
)}
|
|
{hasReplication && <Handle type="source" position={Position.Right} className="opacity-25" />}
|
|
</NodeContainer>
|
|
)
|
|
}
|
|
|
|
export const ReplicationNode = ({ id }: { id: string }) => {
|
|
const { ref: projectRef } = useParams()
|
|
|
|
const { data: destinationsData } = useReplicationDestinationsQuery({ projectRef })
|
|
const destination = (destinationsData?.destinations ?? []).find((x) => x.id.toString() === id)
|
|
|
|
const { data: pipelinesData } = useReplicationPipelinesQuery({
|
|
projectRef,
|
|
})
|
|
const pipeline = (pipelinesData?.pipelines ?? []).find((x) => x.destination_id.toString() === id)
|
|
const { data: pipelineStatusData } = useReplicationPipelineStatusQuery(
|
|
{ projectRef, pipelineId: pipeline?.id },
|
|
{ refetchInterval: STATUS_REFRESH_FREQUENCY_MS }
|
|
)
|
|
const statusName = getStatusName(pipelineStatusData?.status)
|
|
|
|
const config = destination?.config ?? {}
|
|
const type =
|
|
'big_query' in config ? 'BigQuery' : 'iceberg' in config ? 'Analytics Bucket' : undefined
|
|
|
|
return (
|
|
<NodeContainer className="justify-start gap-x-3">
|
|
{type === 'BigQuery' ? (
|
|
<BigQuery size={20} className="text-foreground-light" />
|
|
) : type === 'Analytics Bucket' ? (
|
|
<AnalyticsBucket size={20} className="text-foreground-light" />
|
|
) : null}
|
|
<div className="text-sm flex flex-col gap-y-0.5">
|
|
<div className="flex items-center">
|
|
<p>{type}</p>
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<div className="w-6 h-full flex items-center justify-center">
|
|
<div
|
|
className={cn(
|
|
'w-2 h-2 rounded-full',
|
|
statusName === 'started'
|
|
? 'bg-brand'
|
|
: statusName === 'failed'
|
|
? 'bg-destructive'
|
|
: 'bg-selection'
|
|
)}
|
|
/>
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom" className="capitalize">
|
|
{statusName}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
<p className="text-foreground-light">{destination?.name}</p>
|
|
<p className="text-foreground-light">ID: {destination?.id}</p>
|
|
</div>
|
|
<Handle type="target" position={Position.Left} className="opacity-25" />
|
|
</NodeContainer>
|
|
)
|
|
}
|
|
|
|
export const ReadReplicaNode = ({ id }: { id: string }) => {
|
|
const { ref: projectRef } = useParams()
|
|
const { data: databases = [] } = useReadReplicasQuery({ projectRef })
|
|
const database = databases.find((x) => x.identifier === id)
|
|
|
|
const region = Object.values(AWS_REGIONS).find((x) => x.code === database?.region)
|
|
const formattedId = formatDatabaseID(database?.identifier ?? '')
|
|
const statusLabel = useMemo(
|
|
() => getStatusLabel({ status: database?.status }),
|
|
[database?.status]
|
|
)
|
|
|
|
return (
|
|
<NodeContainer className="justify-start gap-x-3">
|
|
<Database size={20} className="text-foreground-light" />
|
|
<div className="flex flex-col gap-y-0.5">
|
|
<div className="flex items-center">
|
|
<p className="text-sm">Read Replica</p>
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<div className="w-6 h-full flex items-center justify-center">
|
|
<div
|
|
className={cn(
|
|
'w-2 h-2 rounded-full',
|
|
database?.status === 'ACTIVE_HEALTHY' ? 'bg-brand' : 'bg-selection'
|
|
)}
|
|
/>
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom">{statusLabel}</TooltipContent>
|
|
</Tooltip>
|
|
</div>
|
|
<p className="text-sm text-foreground-light">{region?.displayName}</p>
|
|
<div className="flex gap-x-2 items-center text-sm text-foreground-light">
|
|
<span>ID: {formattedId}</span>
|
|
<span>•</span>
|
|
<span>{region?.code}</span>
|
|
</div>
|
|
</div>
|
|
<Handle type="target" position={Position.Left} className="opacity-25" />
|
|
</NodeContainer>
|
|
)
|
|
}
|