Files
aerbim-ht-monitor/frontend/components/navigation/FloorNavigation.tsx

206 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React, { useState } from 'react'
interface DetectorsDataType {
detectors: Record<string, DetectorType>
}
interface FloorNavigationProps {
objectId?: string
detectorsData: DetectorsDataType
onDetectorMenuClick: (detector: DetectorType) => void
onClose?: () => void
}
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
}>
}
const FloorNavigation: React.FC<FloorNavigationProps> = ({ objectId, detectorsData, onDetectorMenuClick, onClose }) => {
const [expandedFloors, setExpandedFloors] = useState<Set<number>>(new Set())
const [searchTerm, setSearchTerm] = useState('')
// конвертация детекторов в array и фильтруем по objectId и тексту запроса
const detectorsArray = Object.values(detectorsData.detectors) as DetectorType[]
let filteredDetectors = objectId
? detectorsArray.filter(detector => detector.object === objectId)
: detectorsArray
// Фильтр-поиск
if (searchTerm) {
filteredDetectors = filteredDetectors.filter(detector =>
detector.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
detector.location.toLowerCase().includes(searchTerm.toLowerCase())
)
}
// Группиовка детекторов по этажам
const detectorsByFloor = filteredDetectors.reduce((acc, detector) => {
const floor = detector.floor
if (!acc[floor]) {
acc[floor] = []
}
acc[floor].push(detector)
return acc
}, {} as Record<number, DetectorType[]>)
// Сортировка этажей
const sortedFloors = Object.keys(detectorsByFloor)
.map(Number)
.sort((a, b) => a - b)
const toggleFloor = (floor: number) => {
const newExpanded = new Set(expandedFloors)
if (newExpanded.has(floor)) {
newExpanded.delete(floor)
} else {
newExpanded.add(floor)
}
setExpandedFloors(newExpanded)
}
const getStatusColor = (status: string) => {
switch (status) {
case '#b3261e': return 'bg-red-500'
case '#fd7c22': return 'bg-orange-500'
case '#00ff00': return 'bg-green-500'
default: return 'bg-gray-500'
}
}
const getStatusText = (status: string) => {
switch (status) {
case '#b3261e': return 'Критический'
case '#fd7c22': return 'Предупреждение'
case '#00ff00': return 'Норма'
default: return 'Неизвестно'
}
}
const handleDetectorMenuClick = (detector: DetectorType) => {
onDetectorMenuClick(detector)
}
return (
<div className="w-full max-w-2xl">
<div className="bg-[rgb(22,24,36)] rounded-[12px] p-4 space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-white text-2xl font-semibold">Навигация по этажам</h2>
{onClose && (
<button
onClick={onClose}
className="text-white hover:text-gray-300 transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
)}
</div>
<div className="flex items-center gap-3">
<div className="flex-1 relative">
<input
type="text"
placeholder="Поиск детекторов..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full bg-[rgb(27,30,40)] text-white placeholder-gray-400 px-4 py-2 rounded-lg border border-gray-600 focus:border-blue-500 focus:outline-none"
/>
<svg className="absolute right-3 top-2.5 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<button className="bg-[rgb(27,29,41)] hover:bg-[rgb(37,39,51)] text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" />
</svg>
Фильтр
</button>
</div>
<div className="space-y-2">
{sortedFloors.map(floor => {
const floorDetectors = detectorsByFloor[floor]
const isExpanded = expandedFloors.has(floor)
return (
<div key={floor} className="bg-[rgb(27,30,40)] rounded-lg overflow-hidden">
<button
onClick={() => toggleFloor(floor)}
className="w-full px-4 py-3 flex items-center justify-between hover:bg-[rgb(53,58,70)] transition-colors"
>
<div className="flex items-center gap-3">
<span className="text-white font-medium">{floor} этаж</span>
<span className="text-gray-400 text-sm">({floorDetectors.length} детекторов)</span>
</div>
<svg
className={`w-5 h-5 text-gray-400 transition-transform ${
isExpanded ? 'rotate-180' : ''
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* суб-меню с детекторами */}
{isExpanded && (
<div className="px-4 pb-3 space-y-2">
{floorDetectors.map(detector => (
<div
key={detector.detector_id}
className="bg-[rgb(53,58,70)] rounded-md p-3 flex items-center justify-between"
>
<div className="flex-1">
<div className="text-white text-sm font-medium">{detector.name}</div>
<div className="text-gray-400 text-xs">{detector.location}</div>
</div>
<div className="flex items-center gap-2">
<div className={`w-3 h-3 rounded-full ${getStatusColor(detector.status)}`}></div>
<span className="text-xs text-gray-300">{getStatusText(detector.status)}</span>
{detector.checked && (
<svg className="w-4 h-4 text-green-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
<button
onClick={() => handleDetectorMenuClick(detector)}
className="w-6 h-6 bg-[rgb(27,29,41)] hover:bg-[rgb(37,39,51)] rounded-full flex items-center justify-center transition-colors relative"
>
<div className="w-2 h-2 bg-white rounded-full"></div>
</button>
</div>
</div>
))}
</div>
)}
</div>
)
})}
</div>
</div>
</div>
)
}
export default FloorNavigation