Files
aerbim-ht-monitor/frontend/components/alerts/DetectorList.tsx

241 lines
10 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, useEffect } from 'react'
import detectorsData from '../../data/detectors.json'
interface Detector {
detector_id: number
name: string
location: string
status: string
object: string
floor: number
checked: boolean
}
interface RawDetector {
detector_id: number
name: string
object: string
status: string
type: string
location: string
floor: number
notifications: Array<{
id: number
type: string
message: string
timestamp: string
acknowledged: boolean
priority: string
}>
}
type FilterType = 'all' | 'critical' | 'warning' | 'normal'
interface DetectorListProps {
objectId?: string
selectedDetectors: number[]
onDetectorSelect: (detectorId: number, selected: boolean) => void
}
const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors, onDetectorSelect }) => {
const [detectors, setDetectors] = useState<Detector[]>([])
const [selectedFilter, setSelectedFilter] = useState<FilterType>('all')
const [searchTerm, setSearchTerm] = useState<string>('')
useEffect(() => {
const detectorsArray = Object.values(detectorsData.detectors).filter(
(detector: RawDetector) => objectId ? detector.object === objectId : true
)
setDetectors(detectorsArray as Detector[])
}, [objectId])
const filteredDetectors = detectors.filter(detector => {
const matchesSearch = detector.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
detector.location.toLowerCase().includes(searchTerm.toLowerCase())
if (selectedFilter === 'all') return matchesSearch
if (selectedFilter === 'critical') return matchesSearch && detector.status === '#b3261e'
if (selectedFilter === 'warning') return matchesSearch && detector.status === '#fd7c22'
if (selectedFilter === 'normal') return matchesSearch && detector.status === '#00ff00'
return matchesSearch
})
return (
<div className="space-y-6">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<button
onClick={() => setSelectedFilter('all')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedFilter === 'all'
? 'bg-blue-600 text-white'
: 'bg-[#161824] text-gray-300 hover:bg-[#1f2937]'
}`}
>
Все ({detectors.length})
</button>
<button
onClick={() => setSelectedFilter('critical')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedFilter === 'critical'
? 'bg-red-600 text-white'
: 'bg-[#161824] text-gray-300 hover:bg-[#1f2937]'
}`}
>
Критические ({detectors.filter(d => d.status === '#b3261e').length})
</button>
<button
onClick={() => setSelectedFilter('warning')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedFilter === 'warning'
? 'bg-orange-600 text-white'
: 'bg-[#161824] text-gray-300 hover:bg-[#1f2937]'
}`}
>
Предупреждения ({detectors.filter(d => d.status === '#fd7c22').length})
</button>
<button
onClick={() => setSelectedFilter('normal')}
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedFilter === 'normal'
? 'bg-green-600 text-white'
: 'bg-[#161824] text-gray-300 hover:bg-[#1f2937]'
}`}
>
Норма ({detectors.filter(d => d.status === '#00ff00').length})
</button>
</div>
<div className="flex items-center gap-3">
<div className="relative">
<input
type="text"
placeholder="Поиск детекторов..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="bg-[#161824] text-white placeholder-gray-400 px-4 py-2 rounded-lg border border-gray-600 focus:border-blue-500 focus:outline-none w-64"
/>
<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>
</div>
</div>
{/* Таблица детекторов */}
<div className="bg-[#161824] rounded-[20px] p-6">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-gray-700">
<th className="text-left text-white font-medium py-3 w-12">
<input
type="checkbox"
checked={selectedDetectors.length === filteredDetectors.length && filteredDetectors.length > 0}
onChange={(e) => {
if (e.target.checked) {
filteredDetectors.forEach(detector => {
if (!selectedDetectors.includes(detector.detector_id)) {
onDetectorSelect(detector.detector_id, true)
}
})
} else {
filteredDetectors.forEach(detector => {
if (selectedDetectors.includes(detector.detector_id)) {
onDetectorSelect(detector.detector_id, false)
}
})
}
}}
className="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2"
/>
</th>
<th className="text-left text-white font-medium py-3">Детектор</th>
<th className="text-left text-white font-medium py-3">Статус</th>
<th className="text-left text-white font-medium py-3">Местоположение</th>
<th className="text-left text-white font-medium py-3">Проверен</th>
</tr>
</thead>
<tbody>
{filteredDetectors.map((detector) => {
const isSelected = selectedDetectors.includes(detector.detector_id)
return (
<tr key={detector.detector_id} className="border-b border-gray-800">
<td className="py-3">
<input
type="checkbox"
checked={isSelected}
onChange={(e) => onDetectorSelect(detector.detector_id, e.target.checked)}
className="w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2"
/>
</td>
<td className="py-3 text-white text-sm">{detector.name}</td>
<td className="py-3">
<div className="flex items-center gap-2">
<div
className={`w-3 h-3 rounded-full`}
style={{ backgroundColor: detector.status }}
></div>
<span className="text-sm text-gray-300">
{detector.status === '#b3261e' ? 'Критическое' :
detector.status === '#fd7c22' ? 'Предупреждение' : 'Норма'}
</span>
</div>
</td>
<td className="py-3 text-gray-400 text-sm">{detector.location}</td>
<td className="py-3">
{detector.checked ? (
<div className="flex items-center gap-1">
<svg className="w-4 h-4 text-green-500" 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>
<span className="text-sm text-green-500">Да</span>
</div>
) : (
<span className="text-sm text-gray-500">Нет</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
{/* Статы детекторров*/}
<div className="mt-6 grid grid-cols-4 gap-4">
<div className="bg-[#161824] p-4 rounded-lg">
<div className="text-2xl font-bold text-white">{filteredDetectors.length}</div>
<div className="text-sm text-gray-400">Всего</div>
</div>
<div className="bg-[#161824] p-4 rounded-lg">
<div className="text-2xl font-bold text-green-500">{filteredDetectors.filter(d => d.status === '#00ff00').length}</div>
<div className="text-sm text-gray-400">Норма</div>
</div>
<div className="bg-[#161824] p-4 rounded-lg">
<div className="text-2xl font-bold text-orange-500">{filteredDetectors.filter(d => d.status === '#fd7c22').length}</div>
<div className="text-sm text-gray-400">Предупреждения</div>
</div>
<div className="bg-[#161824] p-4 rounded-lg">
<div className="text-2xl font-bold text-red-500">{filteredDetectors.filter(d => d.status === '#b3261e').length}</div>
<div className="text-sm text-gray-400">Критические</div>
</div>
</div>
{filteredDetectors.length === 0 && (
<div className="text-center py-8">
<p className="text-gray-400">Детекторы не найдены</p>
</div>
)}
</div>
)
}
export default DetectorList