This commit is contained in:
2026-02-02 11:00:40 +03:00
parent 87a1a628d3
commit 2d0f236fa4
22 changed files with 1119 additions and 461 deletions

View File

@@ -3,6 +3,7 @@
import React, { useState, useEffect } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import Sidebar from '../../../components/ui/Sidebar'
import AnimatedBackground from '../../../components/ui/AnimatedBackground'
import useNavigationStore from '../../store/navigationStore'
import DetectorList from '../../../components/alerts/DetectorList'
import AlertsList from '../../../components/alerts/AlertsList'
@@ -141,12 +142,15 @@ const AlertsPage: React.FC = () => {
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar
activeItem={8} // История тревог
/>
<div className="relative flex h-screen bg-[#0e111a] overflow-hidden">
<AnimatedBackground />
<div className="relative z-20">
<Sidebar
activeItem={8} // История тревог
/>
</div>
<div className="flex-1 flex flex-col">
<div className="relative z-10 flex-1 flex flex-col">
<header className="bg-[#161824] border-b border-gray-700 px-6 py-4">
<div className="flex items-center gap-4">
<button
@@ -171,7 +175,7 @@ const AlertsPage: React.FC = () => {
<div className="flex-1 p-6 overflow-auto">
<div className="mb-6">
<div className="flex items-center justify-between mb-6">
<h1 className="text-white text-2xl font-semibold">Уведомления и тревоги</h1>
<h1 style={{ fontFamily: 'Inter, sans-serif', fontWeight: 600 }} className="text-white text-2xl">Уведомления и тревоги</h1>
<div className="flex items-center gap-4">
{selectedDetectors.length > 0 && (

View File

@@ -3,6 +3,7 @@
import React, { useEffect, useCallback, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
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'
@@ -117,6 +118,8 @@ const NavigationPage: React.FC = () => {
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])
@@ -127,21 +130,22 @@ const NavigationPage: React.FC = () => {
}, [selectedDetector, selectedAlert]);
// Управление выделением всех сенсоров при открытии/закрытии меню Sensors
// ИСПРАВЛЕНО: Подсветка датчиков остается включенной всегда, независимо от состояния панели Sensors
useEffect(() => {
console.log('[NavigationPage] showSensors changed:', showSensors, 'modelReady:', isModelReady)
if (showSensors && isModelReady) {
// При открытии меню Sensors - выделяем все сенсоры (только если модель готова)
console.log('[NavigationPage] Setting highlightAllSensors to TRUE')
if (isModelReady) {
// Всегда включаем подсветку всех сенсоров когда модель готова
console.log('[NavigationPage] Setting highlightAllSensors to TRUE (always enabled)')
setHighlightAllSensors(true)
setFocusedSensorId(null)
} else if (!showSensors) {
// При закрытии меню Sensors - сбрасываем выделение
console.log('[NavigationPage] Setting highlightAllSensors to FALSE')
setHighlightAllSensors(false)
// Сбрасываем фокус только если панель Sensors закрыта
if (!showSensors) {
setFocusedSensorId(null)
}
}
}, [showSensors, isModelReady])
// Дополнительный эффект для задержки выделения сенсоров при открытии меню
// ИСПРАВЛЕНО: Задержка применяется только при открытии панели Sensors
useEffect(() => {
if (showSensors && isModelReady) {
const timer = setTimeout(() => {
@@ -155,9 +159,10 @@ const NavigationPage: React.FC = () => {
const urlObjectId = searchParams.get('objectId')
const urlObjectTitle = searchParams.get('objectTitle')
const urlModelPath = searchParams.get('modelPath')
const objectId = currentObject.id || urlObjectId
const objectTitle = currentObject.title || urlObjectTitle
const [selectedModelPath, setSelectedModelPath] = useState<string>('')
const [selectedModelPath, setSelectedModelPath] = useState<string>(urlModelPath || '')
const handleModelLoaded = useCallback(() => {
setIsModelReady(true)
@@ -174,8 +179,12 @@ const NavigationPage: React.FC = () => {
if (selectedModelPath) {
setIsModelReady(false);
setModelError(null);
// Сохраняем выбранную модель в URL для восстановления при возврате
const params = new URLSearchParams(searchParams.toString());
params.set('modelPath', selectedModelPath);
window.history.replaceState(null, '', `?${params.toString()}`);
}
}, [selectedModelPath]);
}, [selectedModelPath, searchParams]);
useEffect(() => {
if (urlObjectId && (!currentObject.id || currentObject.id !== urlObjectId)) {
@@ -183,6 +192,13 @@ const NavigationPage: React.FC = () => {
}
}, [urlObjectId, urlObjectTitle, currentObject.id, currentObject.title, setCurrentObject])
// Восстановление выбранной модели из URL при загрузке страницы
useEffect(() => {
if (urlModelPath && !selectedModelPath) {
setSelectedModelPath(urlModelPath);
}
}, [urlModelPath, selectedModelPath])
useEffect(() => {
const loadDetectors = async () => {
try {
@@ -195,6 +211,8 @@ const NavigationPage: React.FC = () => {
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)
@@ -240,10 +258,8 @@ const NavigationPage: React.FC = () => {
setSelectedDetector(null)
setFocusedSensorId(null)
setSelectedAlert(null)
// При закрытии меню детектора из Sensors - выделяем все сенсоры снова
if (showSensors) {
setHighlightAllSensors(true)
}
// При закрытии меню детектора - выделяем все сенсоры снова
setHighlightAllSensors(true)
}
const handleNotificationClick = (notification: NotificationType) => {
@@ -266,10 +282,8 @@ const NavigationPage: React.FC = () => {
setSelectedAlert(null)
setFocusedSensorId(null)
setSelectedDetector(null)
// При закрытии меню алерта из Sensors - выделяем все сенсоры снова
if (showSensors) {
setHighlightAllSensors(true)
}
// При закрытии меню алерта - выделяем все сенсоры снова
setHighlightAllSensors(true)
}
const handleAlertClick = (alert: AlertType) => {
@@ -378,12 +392,15 @@ const NavigationPage: React.FC = () => {
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar
activeItem={2}
/>
<div className="relative flex h-screen bg-[#0e111a] overflow-hidden">
<AnimatedBackground />
<div className="relative z-20">
<Sidebar
activeItem={2}
/>
</div>
<div className="flex-1 flex flex-col relative">
<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]">
@@ -439,7 +456,7 @@ const NavigationPage: React.FC = () => {
detectorsData={detectorsData}
onDetectorMenuClick={handleDetectorMenuClick}
onClose={closeListOfDetectors}
is3DReady={isModelReady && !modelError}
is3DReady={selectedModelPath ? !modelError : false}
/>
{detectorsError && (
<div className="mt-2 text-sm text-red-400">{detectorsError}</div>
@@ -456,7 +473,7 @@ const NavigationPage: React.FC = () => {
detectorsData={detectorsData}
onAlertClick={handleAlertClick}
onClose={closeSensors}
is3DReady={isModelReady && !modelError}
is3DReady={selectedModelPath ? !modelError : false}
/>
{detectorsError && (
<div className="mt-2 text-sm text-red-400">{detectorsError}</div>

View File

@@ -4,7 +4,9 @@ import React, { useState, useEffect } from 'react'
import ObjectGallery from '../../../components/objects/ObjectGallery'
import { ObjectData } from '../../../components/objects/ObjectCard'
import Sidebar from '../../../components/ui/Sidebar'
import AnimatedBackground from '../../../components/ui/AnimatedBackground'
import { useRouter } from 'next/navigation'
import Image from 'next/image'
// Универсальная функция для преобразования объекта из бэкенда в ObjectData
const transformRawToObjectData = (raw: any): ObjectData => {
@@ -126,19 +128,83 @@ const ObjectsPage: React.FC = () => {
</div>
)
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar activeItem={null} />
<div className="flex-1 overflow-hidden">
<ObjectGallery
objects={objects}
title="Объекты"
onObjectSelect={handleObjectSelect}
selectedObjectId={selectedObjectId}
/>
<div className="relative flex h-screen bg-[#0e111a] overflow-hidden">
<AnimatedBackground />
<div className="relative z-20">
<Sidebar activeItem={null} />
</div>
<div className="relative z-10 flex-1 overflow-y-auto">
{/* Приветствие и информация */}
<div className="min-h-screen flex flex-col items-center justify-start pt-20 px-8">
{/* Логотип */}
<div className="mb-8 flex justify-center">
<div className="relative w-64 h-20">
<Image
src="/icons/logo.png"
alt="AerBIM Logo"
width={438}
height={60}
className="object-contain"
/>
</div>
</div>
{/* Приветствие */}
<h1 className="text-5xl font-bold text-white mb-4 text-center animate-fade-in" style={{ fontFamily: 'Inter, sans-serif' }}>
Добро пожаловать!
</h1>
<p className="text-xl text-gray-300 mb-8 text-center animate-fade-in" style={{ animationDelay: '0.2s', fontFamily: 'Inter, sans-serif' }}>
Система мониторинга AerBIM Monitor
</p>
{/* Версия системы */}
<div className="mb-16 p-4 rounded-lg bg-gradient-to-r from-blue-500/10 to-cyan-500/10 border border-blue-500/20 inline-block animate-fade-in" style={{ animationDelay: '0.4s' }}>
<p className="text-sm text-gray-400" style={{ fontFamily: 'Inter, sans-serif' }}>
Версия системы: <span className="text-cyan-400 font-semibold">3.0.0</span>
</p>
</div>
{/* Блок с галереей объектов */}
<div className="w-full max-w-6xl p-8 rounded-xl bg-gradient-to-r from-blue-500/10 to-cyan-500/10 border border-blue-500/20 backdrop-blur-sm">
{/* Заголовок галереи */}
<h2 className="text-3xl font-bold text-white mb-8 text-center" style={{ fontFamily: 'Inter, sans-serif' }}>
Выберите объект для работы
</h2>
{/* Галерея объектов */}
<ObjectGallery
objects={objects}
title=""
onObjectSelect={handleObjectSelect}
selectedObjectId={selectedObjectId}
/>
</div>
</div>
</div>
<style jsx>{`
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 0.8s ease-out forwards;
}
`}</style>
</div>
)
}
export default ObjectsPage
export default ObjectsPage

View File

@@ -3,6 +3,7 @@
import React, { useEffect, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import Sidebar from '../../../components/ui/Sidebar'
import AnimatedBackground from '../../../components/ui/AnimatedBackground'
import useNavigationStore from '../../store/navigationStore'
import ReportsList from '../../../components/reports/ReportsList'
import ExportMenu from '../../../components/ui/ExportMenu'
@@ -107,12 +108,15 @@ const ReportsPage: React.FC = () => {
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar
activeItem={9} // Отчёты
/>
<div className="relative flex h-screen bg-[#0e111a] overflow-hidden">
<AnimatedBackground />
<div className="relative z-20">
<Sidebar
activeItem={9} // Отчёты
/>
</div>
<div className="flex-1 flex flex-col">
<div className="relative z-10 flex-1 flex flex-col">
<header className="border-b border-gray-700 bg-[#161824] px-6 py-4">
<div className="flex items-center gap-4">
<button
@@ -142,7 +146,7 @@ const ReportsPage: React.FC = () => {
<div className="flex-1 overflow-auto p-6">
<div className="mb-6">
<div className="mb-6 flex items-center justify-between">
<h1 className="text-2xl font-semibold text-white">Отчеты по датчикам</h1>
<h1 style={{ fontFamily: 'Inter, sans-serif', fontWeight: 600 }} className="text-2xl text-white">Отчеты по датчикам</h1>
<ExportMenu onExport={handleExport} />
</div>