Разработка интерфейса фронт

This commit is contained in:
iv_vuytsik
2025-09-05 03:16:17 +03:00
parent 6c2ea027a4
commit 4d6b7b48d7
35 changed files with 3806 additions and 276 deletions

View File

@@ -0,0 +1,241 @@
'use client'
import React, { useEffect, useCallback } 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 Notifications from '../../../components/notifications/Notifications'
import NotificationDetectorInfo from '../../../components/notifications/NotificationDetectorInfo'
import ModelViewer from '../../../components/model/ModelViewer'
import detectorsData from '../../../data/detectors.json'
interface DetectorType {
detector_id: number
name: string
object: string
status: string
checked: boolean
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
}
const NavigationPage: React.FC = () => {
const router = useRouter()
const searchParams = useSearchParams()
const {
currentObject,
setCurrentObject,
showMonitoring,
showFloorNavigation,
showNotifications,
selectedDetector,
showDetectorMenu,
selectedNotification,
showNotificationDetectorInfo,
closeMonitoring,
closeFloorNavigation,
closeNotifications,
setSelectedDetector,
setShowDetectorMenu,
setSelectedNotification,
setShowNotificationDetectorInfo
} = useNavigationStore()
const urlObjectId = searchParams.get('objectId')
const urlObjectTitle = searchParams.get('objectTitle')
const objectId = currentObject.id || urlObjectId
const objectTitle = currentObject.title || urlObjectTitle
const handleModelLoaded = useCallback(() => {
}, [])
const handleModelError = useCallback((error: string) => {
console.error('Model loading error:', error)
}, [])
useEffect(() => {
if (urlObjectId && urlObjectTitle && (!currentObject.id || currentObject.id !== urlObjectId)) {
setCurrentObject(urlObjectId, urlObjectTitle)
}
}, [urlObjectId, urlObjectTitle, currentObject.id, setCurrentObject])
const handleBackClick = () => {
router.push('/dashboard')
}
const handleDetectorMenuClick = (detector: DetectorType) => {
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 getStatusText = (status: string) => {
switch (status) {
case 'active': return 'Активен'
case 'inactive': return 'Неактивен'
case 'error': return 'Ошибка'
case 'maintenance': return 'Обслуживание'
default: return 'Неизвестно'
}
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar
activeItem={2}
/>
<div className="flex-1 flex flex-col relative">
{showMonitoring && (
<div className="absolute left-0 top-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]" style={{height: 'calc(100% - 73px)', top: '73px'}}>
<div className="h-full overflow-auto p-4">
<Monitoring
objectId={objectId || undefined}
onClose={closeMonitoring}
/>
</div>
</div>
)}
{showFloorNavigation && (
<div className="absolute left-0 top-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]" style={{height: 'calc(100% - 73px)', top: '73px'}}>
<div className="h-full overflow-auto p-4">
<FloorNavigation
objectId={objectId || undefined}
detectorsData={detectorsData}
onDetectorMenuClick={handleDetectorMenuClick}
onClose={closeFloorNavigation}
/>
</div>
</div>
)}
{showNotifications && (
<div className="absolute left-0 top-0 bg-[#161824] border-r border-gray-700 z-20 w-[500px]" style={{height: 'calc(100% - 73px)', top: '73px'}}>
<div className="h-full overflow-auto p-4">
<Notifications
objectId={objectId || undefined}
detectorsData={detectorsData}
onNotificationClick={handleNotificationClick}
onClose={closeNotifications}
/>
</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-0 bg-[#161824] border-r border-gray-700 z-30 w-[454px]" style={{height: 'calc(100% - 73px)', top: '73px'}}>
<div className="h-full overflow-auto p-4">
<NotificationDetectorInfo
detectorData={detectorData}
onClose={closeNotificationDetectorInfo}
/>
</div>
</div>
) : null;
})()}
{showFloorNavigation && showDetectorMenu && selectedDetector && (
<DetectorMenu
detector={selectedDetector}
isOpen={showDetectorMenu}
onClose={closeDetectorMenu}
getStatusText={getStatusText}
/>
)}
<header className="bg-[#161824] border-b border-gray-700 px-6 py-4">
<div className="flex items-center justify-between">
<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">
<ModelViewer
modelPath='/models/EXPO_АР_PostRecon_level.gltf'
onModelLoaded={handleModelLoaded}
onError={handleModelError}
/>
</div>
</div>
</div>
</div>
)
}
export default NavigationPage