New api and zone management; highligh occlusion and highlighAll functionality; improved search in reports and alerts history + autofill; refactored alert panel
This commit is contained in:
@@ -5,13 +5,14 @@ import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import Sidebar from '../../../components/ui/Sidebar'
|
||||
import useNavigationStore from '../../store/navigationStore'
|
||||
import DetectorList from '../../../components/alerts/DetectorList'
|
||||
import AlertsList from '../../../components/alerts/AlertsList'
|
||||
import ExportMenu from '../../../components/ui/ExportMenu'
|
||||
import { useSession } from 'next-auth/react'
|
||||
|
||||
const AlertsPage: React.FC = () => {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { currentObject, setCurrentObject } = useNavigationStore()
|
||||
const { currentObject, setCurrentObject, selectedDetector } = useNavigationStore()
|
||||
const [selectedDetectors, setSelectedDetectors] = useState<number[]>([])
|
||||
const { data: session } = useSession()
|
||||
|
||||
@@ -22,10 +23,9 @@ const AlertsPage: React.FC = () => {
|
||||
timestamp: string
|
||||
acknowledged: boolean
|
||||
priority: string
|
||||
detector_id?: number
|
||||
detector_id?: string
|
||||
detector_name?: string
|
||||
location?: string
|
||||
object?: string
|
||||
}
|
||||
const [alerts, setAlerts] = useState<AlertItem[]>([])
|
||||
|
||||
@@ -40,6 +40,13 @@ const AlertsPage: React.FC = () => {
|
||||
}
|
||||
}, [urlObjectId, urlObjectTitle, currentObject.id, setCurrentObject])
|
||||
|
||||
// Auto-select detector when it comes from navigation store
|
||||
useEffect(() => {
|
||||
if (selectedDetector && !selectedDetectors.includes(selectedDetector.detector_id)) {
|
||||
setSelectedDetectors(prev => [...prev, selectedDetector.detector_id])
|
||||
}
|
||||
}, [selectedDetector, selectedDetectors])
|
||||
|
||||
useEffect(() => {
|
||||
const loadAlerts = async () => {
|
||||
try {
|
||||
@@ -55,7 +62,11 @@ const AlertsPage: React.FC = () => {
|
||||
})
|
||||
const data = Array.isArray(payload?.data) ? payload.data : (payload?.data?.alerts || [])
|
||||
console.log('[AlertsPage] parsed alerts:', data)
|
||||
setAlerts(data as AlertItem[])
|
||||
console.log('[AlertsPage] Sample alert structure:', data[0])
|
||||
console.log('[AlertsPage] Alerts with detector_id:', data.filter((alert: any) => alert.detector_id).length)
|
||||
|
||||
console.log('[AlertsPage] using transformed alerts:', data)
|
||||
setAlerts(data)
|
||||
} catch (e) {
|
||||
console.error('Failed to load alerts:', e)
|
||||
}
|
||||
@@ -177,87 +188,15 @@ const AlertsPage: React.FC = () => {
|
||||
objectId={objectId || undefined}
|
||||
selectedDetectors={selectedDetectors}
|
||||
onDetectorSelect={handleDetectorSelect}
|
||||
initialSearchTerm={selectedDetector?.detector_id.toString() || ''}
|
||||
/>
|
||||
|
||||
{/* История тревог */}
|
||||
<div className="mt-6 bg-[#161824] rounded-[20px] p-6">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold text-white">История тревог</h2>
|
||||
<span className="text-sm text-gray-400">Всего: {alerts.length}</span>
|
||||
</div>
|
||||
<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">Детектор</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>
|
||||
<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>
|
||||
{alerts.map((item) => (
|
||||
<tr key={item.id} className="border-b border-gray-700 hover:bg-gray-800/50 transition-colors">
|
||||
<td className="py-4">
|
||||
<div className="text-sm font-medium text-white">{item.detector_name || 'Детектор'}</div>
|
||||
{item.detector_id ? (
|
||||
<div className="text-sm text-gray-400">ID: {item.detector_id}</div>
|
||||
) : null}
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: item.type === 'critical' ? '#b3261e' : item.type === 'warning' ? '#fd7c22' : '#00ff00' }}
|
||||
></div>
|
||||
<span className="text-sm text-gray-300">
|
||||
{item.type === 'critical' ? 'Критический' : item.type === 'warning' ? 'Предупреждение' : 'Информация'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<div className="text-sm text-white">{item.message}</div>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<div className="text-sm text-white">{item.location || '-'}</div>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<span
|
||||
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium text-white"
|
||||
style={{ backgroundColor: item.priority === 'high' ? '#b3261e' : item.priority === 'medium' ? '#fd7c22' : '#00ff00' }}
|
||||
>
|
||||
{item.priority === 'high' ? 'Высокий' : item.priority === 'medium' ? 'Средний' : 'Низкий'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
item.acknowledged ? 'bg-green-600/20 text-green-300 ring-1 ring-green-600/40' : 'bg-red-600/20 text-red-300 ring-1 ring-red-600/40'
|
||||
}`}>
|
||||
{item.acknowledged ? 'Да' : 'Нет'}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => handleAcknowledgeToggle(item.id)}
|
||||
className="ml-2 inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-[#2a2e3e] text-white hover:bg-[#353a4d]"
|
||||
>
|
||||
{item.acknowledged ? 'Снять' : 'Подтвердить'}
|
||||
</button>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<div className="text-sm text-gray-300">{new Date(item.timestamp).toLocaleString('ru-RU')}</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{alerts.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={7} className="py-8 text-center text-gray-400">Записей не найдено</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<AlertsList
|
||||
alerts={alerts}
|
||||
onAcknowledgeToggle={handleAcknowledgeToggle}
|
||||
initialSearchTerm={selectedDetector?.detector_id.toString() || ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user