import { memo, useState } from 'react'; import { Marker, Popup } from 'react-map-gl/maplibre'; import { useTranslation } from 'react-i18next'; import type { OilFacility, OilFacilityType } from '../types'; interface Props { facilities: OilFacility[]; currentTime: number; } const TYPE_COLORS: Record = { refinery: '#f59e0b', oilfield: '#10b981', gasfield: '#6366f1', terminal: '#ec4899', petrochemical: '#8b5cf6', desalination: '#06b6d4', }; function formatNumber(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`; return String(n); } function getTooltipLabel(f: OilFacility): string { if (f.capacityMgd) return `${formatNumber(f.capacityMgd)} MGD`; if (f.capacityBpd) return `${formatNumber(f.capacityBpd)} bpd`; if (f.reservesBbl) return `${f.reservesBbl}B bbl`; if (f.reservesTcf) return `${f.reservesTcf} Tcf`; if (f.capacityMcfd) return `${formatNumber(f.capacityMcfd)} Mcf/d`; return ''; } function getIconSize(f: OilFacility): number { if (f.type === 'desalination') { const m = f.capacityMgd ?? 0; return m >= 200 ? 14 : m >= 80 ? 12 : 10; } if (f.type === 'terminal') return (f.capacityBpd ?? 0) >= 1_000_000 ? 20 : 16; if (f.type === 'oilfield') { const b = f.reservesBbl ?? 0; return b >= 20 ? 20 : b >= 10 ? 18 : 14; } if (f.type === 'gasfield') { const t = f.reservesTcf ?? 0; return t >= 100 ? 20 : t >= 50 ? 18 : 14; } if (f.type === 'refinery') { const b = f.capacityBpd ?? 0; return b >= 300_000 ? 20 : b >= 100_000 ? 18 : 14; } return 16; } // Shared damage overlay (X mark + circle) function DamageOverlay() { return ( <> ); } // SVG icon renderers (JSX versions) function RefineryIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { const sc = damaged ? '#ff0000' : color; return ( {damaged && } ); } function OilFieldIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { const sc = damaged ? '#ff0000' : color; return ( {damaged && } ); } function GasFieldIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { return ( {damaged && } ); } function TerminalIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { return ( {damaged && } ); } function PetrochemIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { return ( {damaged && } ); } function DesalIcon({ size, color, damaged }: { size: number; color: string; damaged: boolean }) { const sc = damaged ? '#ff0000' : color; return ( {damaged && } ); } function FacilityIconSvg({ facility, damaged }: { facility: OilFacility; damaged: boolean }) { const color = TYPE_COLORS[facility.type]; const size = getIconSize(facility); switch (facility.type) { case 'refinery': return ; case 'oilfield': return ; case 'gasfield': return ; case 'terminal': return ; case 'petrochemical': return ; case 'desalination': return ; } } export const OilFacilityLayer = memo(function OilFacilityLayer({ facilities, currentTime }: Props) { return ( <> {facilities.map(f => ( ))} ); }); function FacilityMarker({ facility, currentTime }: { facility: OilFacility; currentTime: number }) { const { t } = useTranslation('ships'); const [showPopup, setShowPopup] = useState(false); const color = TYPE_COLORS[facility.type]; const isDamaged = !!(facility.damaged && facility.damagedAt && currentTime >= facility.damagedAt); const isPlanned = !!facility.planned && !isDamaged; const stat = getTooltipLabel(facility); return ( <>
{/* Planned strike targeting ring */} {isPlanned && (
{/* Crosshair lines */}
)}
{ e.stopPropagation(); setShowPopup(true); }}>
{isDamaged ? '\u{1F4A5} ' : isPlanned ? '\u{1F3AF} ' : ''}{facility.nameKo} {stat && {stat}}
{showPopup && ( setShowPopup(false)} closeOnClick={false} anchor="bottom" maxWidth="280px" className="gl-popup">
{t(`facility.type.${facility.type}`)} {isDamaged && ( {t('facility.damaged')} )} {isPlanned && ( {t('facility.plannedStrike')} )}
{facility.nameKo}
{facility.name}
{facility.capacityBpd != null && ( <>{t('facility.production')} {formatNumber(facility.capacityBpd)} bpd )} {facility.capacityMgd != null && ( <>{t('facility.desalProduction')} {formatNumber(facility.capacityMgd)} MGD )} {facility.capacityMcfd != null && ( <>{t('facility.gasProduction')} {formatNumber(facility.capacityMcfd)} Mcf/d )} {facility.reservesBbl != null && ( <>{t('facility.reserveOil')} {facility.reservesBbl}B {t('facility.barrels')} )} {facility.reservesTcf != null && ( <>{t('facility.reserveGas')} {facility.reservesTcf} Tcf )} {facility.operator && ( <>{t('facility.operator')} {facility.operator} )}
{facility.description && (

{facility.description}

)} {isPlanned && facility.plannedLabel && (
{facility.plannedLabel}
)}
{facility.lat.toFixed(4)}°N, {facility.lng.toFixed(4)}°E
)} ); }