'use client' import React, { useEffect, useCallback, useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import Sidebar from '../../../components/ui/Sidebar' import useNavigationStore from '../../store/navigationStore' import Monitoring from '../../../components/navigation/Monitoring' import FloorNavigation from '../../../components/navigation/FloorNavigation' import DetectorMenu from '../../../components/navigation/DetectorMenu' import ListOfDetectors from '../../../components/navigation/ListOfDetectors' import Sensors from '../../../components/navigation/Sensors' import AlertMenu from '../../../components/navigation/AlertMenu' import Notifications from '../../../components/notifications/Notifications' import NotificationDetectorInfo from '../../../components/notifications/NotificationDetectorInfo' import dynamic from 'next/dynamic' import type { ModelViewerProps } from '../../../components/model/ModelViewer' const ModelViewer = dynamic(() => import('../../../components/model/ModelViewer'), { ssr: false, loading: () => (
Загрузка 3D-модуля…
), }) interface DetectorType { detector_id: number name: string serial_number: string object: string status: string checked: boolean type: string detector_type: string location: string floor: number notifications: Array<{ id: number type: string message: string timestamp: string acknowledged: boolean priority: string }> } interface NotificationType { id: number detector_id: number detector_name: string type: string status: string message: string timestamp: string location: string object: string acknowledged: boolean priority: string } interface AlertType { id: number detector_id: number detector_name: string type: string status: string message: string timestamp: string location: string object: string acknowledged: boolean priority: string } const NavigationPage: React.FC = () => { const router = useRouter() const searchParams = useSearchParams() const { currentObject, setCurrentObject, showMonitoring, showFloorNavigation, showNotifications, showListOfDetectors, showSensors, selectedDetector, showDetectorMenu, selectedNotification, showNotificationDetectorInfo, selectedAlert, showAlertMenu, closeMonitoring, closeFloorNavigation, closeNotifications, closeListOfDetectors, closeSensors, setSelectedDetector, setShowDetectorMenu, setSelectedNotification, setShowNotificationDetectorInfo, setSelectedAlert, setShowAlertMenu } = useNavigationStore() const [detectorsData, setDetectorsData] = useState<{ detectors: Record }>({ detectors: {} }) const [detectorsError, setDetectorsError] = useState(null) const [modelError, setModelError] = useState(null) const [isModelReady, setIsModelReady] = useState(false) const urlObjectId = searchParams.get('objectId') const urlObjectTitle = searchParams.get('objectTitle') const objectId = currentObject.id || urlObjectId const objectTitle = currentObject.title || urlObjectTitle const [selectedModelPath, setSelectedModelPath] = useState('') const handleModelLoaded = useCallback(() => { setIsModelReady(true) setModelError(null) }, []) const handleModelError = useCallback((error: string) => { console.error('[NavigationPage] Model loading error:', error) setModelError(error) setIsModelReady(false) }, []) useEffect(() => { if (selectedModelPath) { setIsModelReady(false); setModelError(null); } }, [selectedModelPath]); useEffect(() => { if (urlObjectId && urlObjectTitle && (!currentObject.id || currentObject.id !== urlObjectId)) { setCurrentObject(urlObjectId, urlObjectTitle) } }, [urlObjectId, urlObjectTitle, currentObject.id, setCurrentObject]) useEffect(() => { const loadDetectors = async () => { try { setDetectorsError(null) const res = await fetch('/api/get-detectors', { cache: 'no-store' }) const text = await res.text() let payload: any try { payload = JSON.parse(text) } catch { payload = text } console.log('[NavigationPage] GET /api/get-detectors', { status: res.status, payload }) if (!res.ok) throw new Error(typeof payload === 'string' ? payload : (payload?.error || 'Не удалось получить детекторов')) const data = payload?.data ?? payload const detectors = (data?.detectors ?? {}) as Record setDetectorsData({ detectors }) } catch (e: any) { console.error('Ошибка загрузки детекторов:', e) setDetectorsError(e?.message || 'Ошибка при загрузке детекторов') } } loadDetectors() }, []) const handleBackClick = () => { router.push('/dashboard') } const handleDetectorMenuClick = (detector: DetectorType) => { // Для тестов. Выбор детектора. console.log('[NavigationPage] Selected detector click:', { detector_id: detector.detector_id, name: detector.name, serial_number: detector.serial_number, }) // Проверяем, что детектор имеет необходимые данные if (!detector || !detector.detector_id || !detector.serial_number) { console.warn('[NavigationPage] Invalid detector data, skipping menu display:', detector) return } if (selectedDetector?.detector_id === detector.detector_id && showDetectorMenu) { setShowDetectorMenu(false) setSelectedDetector(null) } else { setSelectedDetector(detector) setShowDetectorMenu(true) } } const closeDetectorMenu = () => { setShowDetectorMenu(false) setSelectedDetector(null) } const handleNotificationClick = (notification: NotificationType) => { if (selectedNotification?.id === notification.id && showNotificationDetectorInfo) { setShowNotificationDetectorInfo(false) setSelectedNotification(null) } else { setSelectedNotification(notification) setShowNotificationDetectorInfo(true) } } const closeNotificationDetectorInfo = () => { setShowNotificationDetectorInfo(false) setSelectedNotification(null) } const closeAlertMenu = () => { setShowAlertMenu(false) setSelectedAlert(null) } const handleAlertClick = (alert: AlertType) => { console.log('[NavigationPage] Alert clicked, focusing on detector in 3D scene:', alert) const detector = Object.values(detectorsData.detectors).find( d => d.detector_id === alert.detector_id ) if (detector) { console.log('[NavigationPage] Found detector for alert:', detector) setSelectedAlert(alert) setShowAlertMenu(true) setSelectedDetector(detector) console.log('[NavigationPage] Showing AlertMenu for alert:', alert.detector_name) } else { console.warn('[NavigationPage] Could not find detector for alert:', alert.detector_id) } } const getStatusText = (status: string) => { const s = (status || '').toLowerCase() switch (s) { case '#b3261e': case 'critical': return 'Критический' case '#fd7c22': case 'warning': return 'Предупреждение' case '#00ff00': case 'normal': return 'Норма' default: return 'Неизвестно' } } return (
{showMonitoring && (
{ console.log('[NavigationPage] Model selected:', path); setSelectedModelPath(path) setModelError(null) setIsModelReady(false) }} />
)} {showFloorNavigation && (
)} {showNotifications && (
{detectorsError && (
{detectorsError}
)}
)} {showListOfDetectors && (
{detectorsError && (
{detectorsError}
)}
)} {showSensors && (
{detectorsError && (
{detectorsError}
)}
)} {showNotifications && showNotificationDetectorInfo && selectedNotification && (() => { const detectorData = Object.values(detectorsData.detectors).find( detector => detector.detector_id === selectedNotification.detector_id ); return detectorData ? (
) : null; })()} {showFloorNavigation && showDetectorMenu && selectedDetector && ( null )}
{modelError ? ( <> {console.log('[NavigationPage] Rendering error message, modelError:', modelError)}
Ошибка загрузки 3D модели
{modelError}
Используйте навигацию по этажам для просмотра детекторов
) : !selectedModelPath ? (
3D модель не загружена
Модель не готова к отображению
Выберите модель из навигации по этажам
) : ( ( <> {selectedAlert && showAlertMenu && anchor ? ( ) : selectedDetector && showDetectorMenu && anchor ? ( ) : null} )} /> )}
) } export default NavigationPage