Files
aerbim-ht-monitor/frontend/components/alerts/DetectorList.tsx
2026-01-21 03:16:52 +03:00

217 lines
8.8 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 * as statusColors from '../../lib/statusColors'
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
detector_type: string
location: string
floor: number
notifications: Array<{
id: number
type: string
message: string
timestamp: string
acknowledged: boolean
priority: string
}>
}
interface DetectorListProps {
objectId?: string
selectedDetectors: number[]
onDetectorSelect: (detectorId: number, selected: boolean) => void
initialSearchTerm?: string
}
const DetectorList: React.FC<DetectorListProps> = ({ objectId, selectedDetectors, onDetectorSelect, initialSearchTerm = '' }) => {
const [detectors, setDetectors] = useState<Detector[]>([])
const [searchTerm, setSearchTerm] = useState<string>(initialSearchTerm)
useEffect(() => {
const loadDetectors = async () => {
try {
const res = await fetch('/api/get-detectors', { cache: 'no-store' })
if (!res.ok) return
const payload = await res.json()
const detectorsData: Record<string, RawDetector> = payload?.data?.detectors ?? {}
const rawArray: RawDetector[] = Object.values(detectorsData).filter(
(detector) => (objectId ? detector.object === objectId : true)
)
const normalized: Detector[] = rawArray.map((d) => ({
detector_id: d.detector_id,
name: d.name,
location: d.location,
status: d.status,
object: d.object,
floor: d.floor,
checked: false,
}))
console.log('[DetectorList] Payload:', payload)
setDetectors(normalized)
} catch (e) {
console.error('Failed to load detectors:', e)
}
}
loadDetectors()
}, [objectId])
const filteredDetectors = detectors.filter(detector => {
const matchesSearch = searchTerm === '' || detector.detector_id.toString() === searchTerm
return matchesSearch
})
return (
<div className="space-y-6">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
</div>
<div className="flex items-center gap-3">
<div className="relative">
<input
type="text"
placeholder="Поиск по ID детектора..."
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 === statusColors.STATUS_COLOR_CRITICAL
? 'Критическое'
: detector.status === statusColors.STATUS_COLOR_WARNING
? 'Предупреждение'
: 'Норма'}
</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 === statusColors.STATUS_COLOR_NORMAL).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 === statusColors.STATUS_COLOR_WARNING).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 === statusColors.STATUS_COLOR_CRITICAL).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