import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import dayjs from 'dayjs' import { partition, uniqBy } from 'lodash' import { MoreVertical } from 'lucide-react' import Link from 'next/link' import { parseAsBoolean, useQueryState } from 'nuqs' import { useEffect, useState } from 'react' import { ComposableMap, Geographies, Geography, Line, Marker, ZoomableGroup, } from 'react-simple-maps' import type { AWS_REGIONS_KEYS } from 'shared-data' import { Badge, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, ScrollArea, } from 'ui' import { AVAILABLE_REPLICA_REGIONS, REPLICA_STATUS } from './InstanceConfiguration.constants' import GeographyData from './MapData.json' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip' import { Database, useReadReplicasQuery } from '@/data/read-replicas/replicas-query' import { formatDatabaseID } from '@/data/read-replicas/replicas.utils' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { BASE_PATH } from '@/lib/constants' import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector' // [Joshen] Foresee that we'll skip this view for initial launch interface MapViewProps { onSelectDeployNewReplica: (region: AWS_REGIONS_KEYS) => void onSelectRestartReplica: (database: Database) => void onSelectDropReplica: (database: Database) => void } const MapView = ({ onSelectDeployNewReplica, onSelectRestartReplica, onSelectDropReplica, }: MapViewProps) => { const { ref } = useParams() const dbSelectorState = useDatabaseSelectorStateSnapshot() const { projectHomepageShowInstanceSize } = useIsFeatureEnabled([ 'project_homepage:show_instance_size', ]) const [mount, setMount] = useState(false) const [zoom, setZoom] = useState(1.5) const [center, setCenter] = useState<[number, number]>([14, 7]) const [tooltip, setTooltip] = useState<{ x: number y: number region: { key: string; country?: string; name?: string; region?: string } }>() const { can: canManageReplicas } = useAsyncCheckPermissions(PermissionAction.CREATE, 'projects') const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false)) const { data } = useReadReplicasQuery({ projectRef: ref }) const databases = data ?? [] const [[primary], replicas] = partition(databases, (db) => db.identifier === ref) const primaryCoordinates = AVAILABLE_REPLICA_REGIONS.find((region) => primary.region.includes(region.region) )?.coordinates ?? [0, 0] const uniqueRegionsByReplicas = uniqBy(replicas, (r) => { return AVAILABLE_REPLICA_REGIONS.find((region) => r.region.includes(region.region))?.key }) const selectedRegionKey = AVAILABLE_REPLICA_REGIONS.find((region) => region.coordinates === center)?.region ?? '' const showRegionDetails = zoom === 2.0 && selectedRegionKey !== undefined const selectedRegion = AVAILABLE_REPLICA_REGIONS.find( (region) => region.region === selectedRegionKey ) const databasesInSelectedRegion = databases .filter((database) => database.region.includes(selectedRegionKey)) .sort((a, b) => (a.inserted_at > b.inserted_at ? 1 : 0)) .sort((database) => (database.identifier === ref ? -1 : 0)) useEffect(() => { setTimeout(() => setMount(true), 100) }, []) return (
!['MouseEvent', 'WheelEvent'].includes(name) } > {({ geographies }) => geographies.map((geo) => ( )) } {uniqueRegionsByReplicas.map((database) => { const coordinates = AVAILABLE_REPLICA_REGIONS.find((region) => database.region.includes(region.region) )?.coordinates if (coordinates !== primaryCoordinates) { return ( ) } else { return null } })} {AVAILABLE_REPLICA_REGIONS.map((region) => { const dbs = databases.filter((database) => database.region.includes(region.region)) ?? [] const coordinates = AVAILABLE_REPLICA_REGIONS.find( (r) => r.region === region.region )?.coordinates const hasNoDatabases = dbs.length === 0 const hasPrimary = dbs.some((database) => database.identifier === ref) const replicas = dbs.filter((database) => database.identifier !== ref) ?? [] return ( { setTooltip({ x: coordinates![0], y: coordinates![1], region: { key: region.key, country: region.name, region: region.region, name: hasNoDatabases ? undefined : hasPrimary ? `Primary Database${ replicas.length > 0 ? ` + ${replicas.length} replica${replicas.length > 1 ? 's' : ''} ` : '' }` : `${replicas.length} Read Replica${ replicas.length > 1 ? 's' : '' } deployed`, }, }) }} onMouseLeave={() => setTooltip(undefined)} onClick={() => { if (coordinates) { setCenter(coordinates) setZoom(2.0) } }} > {selectedRegionKey === region.region && ( )} ) })} {tooltip !== undefined && zoom === 1.5 && (
region icon

{tooltip.region.country}

{tooltip.region.name ?? 'No databases deployed'}

)}
{showRegionDetails && selectedRegion && (

{databasesInSelectedRegion.length} database {databasesInSelectedRegion.length > 1 ? 's' : ''} deployed in

{selectedRegion.name}

region icon
{databasesInSelectedRegion.length > 0 && ( 2 ? '180px' : 'auto' }}>
    {databasesInSelectedRegion.map((database) => { const created = dayjs(database.inserted_at).format('DD MMM YYYY, HH:mm:ss (ZZ)') return (
  • {database.identifier === ref ? 'Primary Database' : `Read Replica ${ database.identifier.length > 0 && `(ID: ${formatDatabaseID(database.identifier)})` }`} {database.status === REPLICA_STATUS.ACTIVE_HEALTHY ? ( Healthy ) : database.status === REPLICA_STATUS.COMING_UP ? ( Coming up ) : database.status === REPLICA_STATUS.RESTARTING ? ( Restarting ) : database.status === REPLICA_STATUS.RESIZING ? ( Resizing ) : ( Unhealthy )}

    AWS{projectHomepageShowInstanceSize ? ` • ${database.size}` : ''}

    {database.identifier !== ref && (

    Created on: {created}

    )}
    {database.identifier !== ref && (
  • ) })}
)}
0 ? 'border-t' : '' }`} > onSelectDeployNewReplica(selectedRegion.key)} tooltip={{ content: { side: 'bottom', text: !canManageReplicas ? 'You need additional permissions to deploy replicas' : undefined, }, }} > Deploy new replica here
)}
) } export default MapView