637 lines
24 KiB
TypeScript
637 lines
24 KiB
TypeScript
'use client'
|
||
|
||
import React, { useEffect, useCallback, useState } from 'react'
|
||
import { useRouter, useSearchParams } from 'next/navigation'
|
||
import Image from 'next/image'
|
||
import Sidebar from '../../../components/ui/Sidebar'
|
||
import AnimatedBackground from '../../../components/ui/AnimatedBackground'
|
||
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'
|
||
import * as statusColors from '../../../lib/statusColors'
|
||
|
||
const ModelViewer = dynamic<ModelViewerProps>(() => import('../../../components/model/ModelViewer'), {
|
||
ssr: false,
|
||
loading: () => (
|
||
<div className="w-full h-full flex items-center justify-center bg-[#0e111a]">
|
||
<div className="text-gray-300 animate-pulse">Загрузка 3D-модуля…</div>
|
||
</div>
|
||
),
|
||
})
|
||
|
||
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,
|
||
showSensorHighlights,
|
||
toggleSensorHighlights
|
||
} = useNavigationStore()
|
||
|
||
const [detectorsData, setDetectorsData] = useState<{ detectors: Record<string, DetectorType> }>({ detectors: {} })
|
||
const [detectorsError, setDetectorsError] = useState<string | null>(null)
|
||
const [modelError, setModelError] = useState<string | null>(null)
|
||
const [isModelReady, setIsModelReady] = useState(false)
|
||
const [focusedSensorId, setFocusedSensorId] = useState<string | null>(null)
|
||
const [highlightAllSensors, setHighlightAllSensors] = useState(false)
|
||
const sensorStatusMap = React.useMemo(() => {
|
||
const map: Record<string, string> = {}
|
||
Object.values(detectorsData.detectors).forEach(d => {
|
||
if (d.serial_number && d.status) {
|
||
map[String(d.serial_number).trim()] = d.status
|
||
}
|
||
})
|
||
console.log('[NavigationPage] sensorStatusMap created with', Object.keys(map).length, 'sensors')
|
||
console.log('[NavigationPage] Sample sensor IDs in map:', Object.keys(map).slice(0, 5))
|
||
return map
|
||
}, [detectorsData])
|
||
|
||
useEffect(() => {
|
||
if (selectedDetector === null && selectedAlert === null) {
|
||
setFocusedSensorId(null);
|
||
}
|
||
}, [selectedDetector, selectedAlert]);
|
||
|
||
// Управление выделением всех сенсоров при открытии/закрытии меню Sensors
|
||
// ИСПРАВЛЕНО: Подсветка датчиков остается включенной всегда, независимо от состояния панели Sensors
|
||
useEffect(() => {
|
||
console.log('[NavigationPage] showSensors changed:', showSensors, 'modelReady:', isModelReady)
|
||
if (isModelReady) {
|
||
// Всегда включаем подсветку всех сенсоров когда модель готова
|
||
console.log('[NavigationPage] Setting highlightAllSensors to TRUE (always enabled)')
|
||
setHighlightAllSensors(true)
|
||
// Сбрасываем фокус только если панель Sensors закрыта
|
||
if (!showSensors) {
|
||
setFocusedSensorId(null)
|
||
}
|
||
}
|
||
}, [showSensors, isModelReady])
|
||
|
||
// Дополнительный эффект для задержки выделения сенсоров при открытии меню
|
||
// ИСПРАВЛЕНО: Задержка применяется только при открытии панели Sensors
|
||
useEffect(() => {
|
||
if (showSensors && isModelReady) {
|
||
const timer = setTimeout(() => {
|
||
console.log('[NavigationPage] Delayed highlightAllSensors to TRUE')
|
||
setHighlightAllSensors(true)
|
||
}, 500) // Задержка 500мс для полной инициализации модели
|
||
|
||
return () => clearTimeout(timer)
|
||
}
|
||
}, [showSensors, isModelReady])
|
||
|
||
const urlObjectId = searchParams.get('objectId')
|
||
const urlObjectTitle = searchParams.get('objectTitle')
|
||
const urlModelPath = searchParams.get('modelPath')
|
||
const urlFocusSensorId = searchParams.get('focusSensorId')
|
||
const objectId = currentObject.id || urlObjectId
|
||
const objectTitle = currentObject.title || urlObjectTitle
|
||
const [selectedModelPath, setSelectedModelPath] = useState<string>(urlModelPath || '')
|
||
|
||
|
||
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);
|
||
// Сохраняем выбранную модель в URL для восстановления при возврате
|
||
const params = new URLSearchParams(searchParams.toString());
|
||
params.set('modelPath', selectedModelPath);
|
||
window.history.replaceState(null, '', `?${params.toString()}`);
|
||
}
|
||
}, [selectedModelPath, searchParams]);
|
||
|
||
useEffect(() => {
|
||
if (urlObjectId && (!currentObject.id || currentObject.id !== urlObjectId)) {
|
||
setCurrentObject(urlObjectId, urlObjectTitle ?? currentObject.title ?? undefined)
|
||
}
|
||
}, [urlObjectId, urlObjectTitle, currentObject.id, currentObject.title, setCurrentObject])
|
||
|
||
// Восстановление выбранной модели из URL при загрузке страницы
|
||
useEffect(() => {
|
||
if (urlModelPath && !selectedModelPath) {
|
||
setSelectedModelPath(urlModelPath);
|
||
}
|
||
}, [urlModelPath, selectedModelPath])
|
||
|
||
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<string, DetectorType>
|
||
console.log('[NavigationPage] Received detectors count:', Object.keys(detectors).length)
|
||
console.log('[NavigationPage] Sample detector keys:', Object.keys(detectors).slice(0, 5))
|
||
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?.serial_number === detector.serial_number && showDetectorMenu) {
|
||
closeDetectorMenu()
|
||
} else {
|
||
setSelectedDetector(detector)
|
||
setShowDetectorMenu(true)
|
||
setFocusedSensorId(detector.serial_number)
|
||
setShowAlertMenu(false)
|
||
setSelectedAlert(null)
|
||
// При открытии меню детектора - сбрасываем множественное выделение
|
||
setHighlightAllSensors(false)
|
||
}
|
||
}
|
||
|
||
const closeDetectorMenu = () => {
|
||
setShowDetectorMenu(false)
|
||
setSelectedDetector(null)
|
||
setFocusedSensorId(null)
|
||
setSelectedAlert(null)
|
||
// При закрытии меню детектора - выделяем все сенсоры снова
|
||
setHighlightAllSensors(true)
|
||
}
|
||
|
||
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)
|
||
setFocusedSensorId(null)
|
||
setSelectedDetector(null)
|
||
// При закрытии меню алерта - выделяем все сенсоры снова
|
||
setHighlightAllSensors(true)
|
||
}
|
||
|
||
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) {
|
||
if (selectedAlert?.id === alert.id && showAlertMenu) {
|
||
closeAlertMenu()
|
||
} else {
|
||
setSelectedAlert(alert)
|
||
setShowAlertMenu(true)
|
||
setFocusedSensorId(detector.serial_number)
|
||
setShowDetectorMenu(false)
|
||
setSelectedDetector(null)
|
||
// При открытии меню алерта - сбрасываем множественное выделение
|
||
setHighlightAllSensors(false)
|
||
console.log('[NavigationPage] Showing AlertMenu for alert:', alert.detector_name)
|
||
}
|
||
} else {
|
||
console.warn('[NavigationPage] Could not find detector for alert:', alert.detector_id)
|
||
}
|
||
}
|
||
|
||
const handleSensorSelection = (serialNumber: string | null) => {
|
||
if (serialNumber === null) {
|
||
setFocusedSensorId(null);
|
||
closeDetectorMenu();
|
||
closeAlertMenu();
|
||
// If we're in Sensors menu and no sensor is selected, highlight all sensors
|
||
if (showSensors) {
|
||
setHighlightAllSensors(true);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (focusedSensorId === serialNumber) {
|
||
setFocusedSensorId(null);
|
||
closeDetectorMenu();
|
||
closeAlertMenu();
|
||
// If we're in Sensors menu and deselected the current sensor, highlight all sensors
|
||
if (showSensors) {
|
||
setHighlightAllSensors(true);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// При выборе конкретного сенсора - сбрасываем множественное выделение
|
||
setHighlightAllSensors(false)
|
||
|
||
const detector = Object.values(detectorsData?.detectors || {}).find(
|
||
(d) => d.serial_number === serialNumber
|
||
);
|
||
|
||
if (detector) {
|
||
if (showFloorNavigation || showListOfDetectors) {
|
||
handleDetectorMenuClick(detector);
|
||
} else if (detector.notifications && detector.notifications.length > 0) {
|
||
const sortedNotifications = [...detector.notifications].sort((a, b) => {
|
||
const priorityOrder: { [key: string]: number } = { critical: 0, warning: 1, info: 2 };
|
||
return priorityOrder[a.priority.toLowerCase()] - priorityOrder[b.priority.toLowerCase()];
|
||
});
|
||
const notification = sortedNotifications[0];
|
||
const alert: AlertType = {
|
||
...notification,
|
||
detector_id: detector.detector_id,
|
||
detector_name: detector.name,
|
||
location: detector.location,
|
||
object: detector.object,
|
||
status: detector.status,
|
||
type: notification.type || 'info',
|
||
};
|
||
handleAlertClick(alert);
|
||
} else {
|
||
handleDetectorMenuClick(detector);
|
||
}
|
||
} else {
|
||
setFocusedSensorId(null);
|
||
closeDetectorMenu();
|
||
closeAlertMenu();
|
||
// If we're in Sensors menu and no valid detector found, highlight all sensors
|
||
if (showSensors) {
|
||
setHighlightAllSensors(true);
|
||
}
|
||
}
|
||
};
|
||
|
||
// Обработка focusSensorId из URL (при переходе из таблиц событий)
|
||
useEffect(() => {
|
||
if (urlFocusSensorId && isModelReady && detectorsData) {
|
||
console.log('[NavigationPage] Setting focusSensorId from URL:', urlFocusSensorId)
|
||
setFocusedSensorId(urlFocusSensorId)
|
||
setHighlightAllSensors(false)
|
||
|
||
// Автоматически открываем тултип датчика
|
||
setTimeout(() => {
|
||
handleSensorSelection(urlFocusSensorId)
|
||
}, 500) // Задержка для полной инициализации
|
||
|
||
// Очищаем URL от параметра после применения
|
||
const newUrl = new URL(window.location.href)
|
||
newUrl.searchParams.delete('focusSensorId')
|
||
window.history.replaceState({}, '', newUrl.toString())
|
||
}
|
||
}, [urlFocusSensorId, isModelReady, detectorsData])
|
||
|
||
const getStatusText = (status: string) => {
|
||
const s = (status || '').toLowerCase()
|
||
switch (s) {
|
||
case statusColors.STATUS_COLOR_CRITICAL:
|
||
case 'critical':
|
||
return 'Критический'
|
||
case statusColors.STATUS_COLOR_WARNING:
|
||
case 'warning':
|
||
return 'Предупреждение'
|
||
case statusColors.STATUS_COLOR_NORMAL:
|
||
case 'normal':
|
||
return 'Норма'
|
||
default:
|
||
return 'Неизвестно'
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="relative flex h-screen bg-[#0e111a] overflow-hidden">
|
||
<AnimatedBackground />
|
||
<div className="relative z-20">
|
||
<Sidebar
|
||
activeItem={2}
|
||
/>
|
||
</div>
|
||
|
||
<div className="relative z-10 flex-1 flex flex-col">
|
||
|
||
{showMonitoring && (
|
||
<div className="absolute left-0 top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<Monitoring
|
||
onClose={closeMonitoring}
|
||
onSelectModel={(path) => {
|
||
console.log('[NavigationPage] Model selected:', path);
|
||
setSelectedModelPath(path)
|
||
setModelError(null)
|
||
setIsModelReady(false)
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{showFloorNavigation && (
|
||
<div className="absolute left-0 top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<FloorNavigation
|
||
objectId={objectId || undefined}
|
||
detectorsData={detectorsData}
|
||
onDetectorMenuClick={handleDetectorMenuClick}
|
||
onClose={closeFloorNavigation}
|
||
is3DReady={isModelReady && !modelError}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{showNotifications && (
|
||
<div className="absolute left-0 top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<Notifications
|
||
objectId={objectId || undefined}
|
||
detectorsData={detectorsData}
|
||
onNotificationClick={handleNotificationClick}
|
||
onClose={closeNotifications}
|
||
/>
|
||
{detectorsError && (
|
||
<div className="mt-2 text-sm text-red-400">{detectorsError}</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{showListOfDetectors && (
|
||
<div className="absolute left-0 top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<ListOfDetectors
|
||
objectId={objectId || undefined}
|
||
detectorsData={detectorsData}
|
||
onDetectorMenuClick={handleDetectorMenuClick}
|
||
onClose={closeListOfDetectors}
|
||
is3DReady={selectedModelPath ? !modelError : false}
|
||
/>
|
||
{detectorsError && (
|
||
<div className="mt-2 text-sm text-red-400">{detectorsError}</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{showSensors && (
|
||
<div className="absolute left-0 top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<Sensors
|
||
objectId={objectId || undefined}
|
||
detectorsData={detectorsData}
|
||
onAlertClick={handleAlertClick}
|
||
onClose={closeSensors}
|
||
is3DReady={selectedModelPath ? !modelError : false}
|
||
/>
|
||
{detectorsError && (
|
||
<div className="mt-2 text-sm text-red-400">{detectorsError}</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{showNotifications && showNotificationDetectorInfo && selectedNotification && (() => {
|
||
const detectorData = Object.values(detectorsData.detectors).find(
|
||
detector => detector.detector_id === selectedNotification.detector_id
|
||
);
|
||
return detectorData ? (
|
||
<div className="absolute left-[500px] top-[73px] bottom-0 bg-[#161824] border-r border-gray-700 z-30 w-[454px]">
|
||
<div className="h-full overflow-auto p-4">
|
||
<NotificationDetectorInfo
|
||
detectorData={detectorData}
|
||
onClose={closeNotificationDetectorInfo}
|
||
/>
|
||
</div>
|
||
</div>
|
||
) : null;
|
||
})()}
|
||
|
||
{showFloorNavigation && showDetectorMenu && selectedDetector && (
|
||
null
|
||
)}
|
||
|
||
<header className="bg-[#161824] border-b border-gray-700 px-6 h-[73px] flex items-center">
|
||
<div className="flex items-center justify-between w-full">
|
||
<div className="flex items-center gap-4">
|
||
<button
|
||
onClick={handleBackClick}
|
||
className="text-gray-400 hover:text-white transition-colors"
|
||
aria-label="Назад к дашборду"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||
</svg>
|
||
</button>
|
||
<nav className="flex items-center gap-2 text-sm">
|
||
<span className="text-gray-400">Дашборд</span>
|
||
<span className="text-gray-600">{'/'}</span>
|
||
<span className="text-white">{objectTitle || 'Объект'}</span>
|
||
<span className="text-gray-600">{'/'}</span>
|
||
<span className="text-white">Навигация</span>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div className="flex-1 overflow-hidden">
|
||
<div className="h-full">
|
||
{modelError ? (
|
||
<>
|
||
{console.log('[NavigationPage] Rendering error message, modelError:', modelError)}
|
||
<div className="h-full flex items-center justify-center bg-[#0e111a]">
|
||
<div className="text-center p-8 bg-[#161824] rounded-lg border border-gray-700 max-w-md">
|
||
<div className="text-red-400 text-lg font-semibold mb-4">
|
||
Ошибка загрузки 3D модели
|
||
</div>
|
||
<div className="text-gray-300 mb-4">
|
||
{modelError}
|
||
</div>
|
||
<div className="text-sm text-gray-400">
|
||
Используйте навигацию по этажам для просмотра детекторов
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
) : !selectedModelPath ? (
|
||
<div className="h-full flex items-center justify-center bg-[#0e111a]">
|
||
<div className="text-center p-8 flex flex-col items-center">
|
||
<Image
|
||
src="/icons/logo.png"
|
||
alt="AerBIM HT Monitor"
|
||
width={300}
|
||
height={41}
|
||
className="mb-6"
|
||
/>
|
||
<div className="text-gray-300 text-lg">
|
||
Выберите модель для отображения
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<ModelViewer
|
||
key={selectedModelPath || 'no-model'}
|
||
modelPath={selectedModelPath}
|
||
onSelectModel={(path) => {
|
||
console.log('[NavigationPage] Model selected:', path);
|
||
setSelectedModelPath(path)
|
||
setModelError(null)
|
||
setIsModelReady(false)
|
||
}}
|
||
onModelLoaded={handleModelLoaded}
|
||
onError={handleModelError}
|
||
activeMenu={showSensors ? 'sensors' : showFloorNavigation ? 'floor' : showListOfDetectors ? 'detectors' : null}
|
||
focusSensorId={focusedSensorId}
|
||
highlightAllSensors={showSensorHighlights && highlightAllSensors}
|
||
sensorStatusMap={sensorStatusMap}
|
||
isSensorSelectionEnabled={showSensors || showFloorNavigation || showListOfDetectors}
|
||
onSensorPick={handleSensorSelection}
|
||
renderOverlay={({ anchor }) => (
|
||
<>
|
||
{selectedAlert && showAlertMenu && anchor ? (
|
||
<AlertMenu
|
||
alert={selectedAlert}
|
||
isOpen={true}
|
||
onClose={closeAlertMenu}
|
||
getStatusText={getStatusText}
|
||
compact={true}
|
||
anchor={anchor}
|
||
/>
|
||
) : selectedDetector && showDetectorMenu && anchor ? (
|
||
<DetectorMenu
|
||
detector={selectedDetector}
|
||
isOpen={true}
|
||
onClose={closeDetectorMenu}
|
||
getStatusText={getStatusText}
|
||
compact={true}
|
||
anchor={anchor}
|
||
/>
|
||
) : null}
|
||
</>
|
||
)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default NavigationPage
|