import { create } from 'zustand'; import { subscribeWithSelector } from 'zustand/middleware'; import type { Ship, VesselAnalysisDto } from '../types'; // ── Store interface ─────────────────────────────────────────────── interface ShipDeckState { // Ship data (from 5-second polling) ships: Ship[]; shipMap: Map; // mmsi → Ship lookup (for popup, hover) // Filter state militaryOnly: boolean; hiddenShipCategories: Set; // mtCategory strings like 'cargo', 'tanker' hiddenNationalities: Set; // natGroup strings like 'KR', 'JP' layerVisible: boolean; // layers.ships toggle from LayerPanel // Interaction state hoveredMmsi: string | null; hoverScreenPos: { x: number; y: number } | null; // screen coords for tooltip selectedMmsi: string | null; // popup target focusMmsi: string | null; // external focus request (e.g., from analysis panel) // Display state highlightKorean: boolean; // korean ships ring + label toggle zoomLevel: number; // integer floor of map zoom // Analysis state (for analysis ship markers overlay) analysisMap: Map | null; analysisActiveFilter: string | null; // 'illegalFishing' | 'darkVessel' | 'cnFishing' | null // Actions setShips: (ships: Ship[]) => void; setFilters: (patch: { militaryOnly?: boolean; hiddenShipCategories?: Set; hiddenNationalities?: Set; layerVisible?: boolean; }) => void; setHoveredMmsi: (mmsi: string | null, screenPos?: { x: number; y: number }) => void; setSelectedMmsi: (mmsi: string | null) => void; setFocusMmsi: (mmsi: string | null) => void; setHighlightKorean: (hl: boolean) => void; setZoomLevel: (zoom: number) => void; setAnalysis: (map: Map | null, filter: string | null) => void; } // ── Store ───────────────────────────────────────────────────────── export const useShipDeckStore = create()( subscribeWithSelector((set) => ({ // Ship data ships: [], shipMap: new Map(), // Filter state militaryOnly: false, hiddenShipCategories: new Set(), hiddenNationalities: new Set(), layerVisible: true, // Interaction state hoveredMmsi: null, hoverScreenPos: null, selectedMmsi: null, focusMmsi: null, // Display state highlightKorean: false, zoomLevel: 5, // Analysis state analysisMap: null, analysisActiveFilter: null, // ── Actions ──────────────────────────────────────────────── setShips: (ships) => { const shipMap = new Map(); for (const ship of ships) { shipMap.set(ship.mmsi, ship); } set({ ships, shipMap }); }, setFilters: (patch) => set((state) => ({ militaryOnly: patch.militaryOnly ?? state.militaryOnly, hiddenShipCategories: patch.hiddenShipCategories ?? state.hiddenShipCategories, hiddenNationalities: patch.hiddenNationalities ?? state.hiddenNationalities, layerVisible: patch.layerVisible ?? state.layerVisible, })), setHoveredMmsi: (mmsi, screenPos) => set({ hoveredMmsi: mmsi, hoverScreenPos: mmsi ? (screenPos ?? null) : null, }), setSelectedMmsi: (mmsi) => set({ selectedMmsi: mmsi }), setFocusMmsi: (mmsi) => set({ focusMmsi: mmsi }), setHighlightKorean: (hl) => set({ highlightKorean: hl }), setZoomLevel: (zoom) => set({ zoomLevel: zoom }), setAnalysis: (map, filter) => set({ analysisMap: map, analysisActiveFilter: filter }), })), );