Files
aerbim-ht-monitor/frontend/components/dashboard/Dashboard.tsx
2025-10-20 11:02:34 +03:00

250 lines
12 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, { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import Sidebar from '../ui/Sidebar'
import useNavigationStore from '../../app/store/navigationStore'
import ChartCard from './ChartCard'
import AreaChart from './AreaChart'
import BarChart from './BarChart'
import DetectorChart from './DetectorChart'
const Dashboard: React.FC = () => {
const router = useRouter()
const { currentObject, setCurrentSubmenu, closeMonitoring, closeFloorNavigation, closeNotifications } = useNavigationStore()
const objectId = currentObject?.id
const objectTitle = currentObject?.title
const [dashboardAlerts, setDashboardAlerts] = useState<any[]>([])
const [chartData, setChartData] = useState<{ timestamp: string; value: number }[]>([])
useEffect(() => {
const loadDashboard = async () => {
try {
const res = await fetch('/api/get-dashboard', { cache: 'no-store' })
if (!res.ok) return
const payload = await res.json()
console.log('[Dashboard] GET /api/get-dashboard', { status: res.status, payload })
const tableData = payload?.data?.table_data ?? []
const arr = (Array.isArray(tableData) ? tableData : [])
.filter((a: any) => (objectTitle ? a.object === objectTitle : true))
setDashboardAlerts(arr as any[])
const cd = Array.isArray(payload?.data?.chart_data) ? payload.data.chart_data : []
setChartData(cd as any[])
} catch (e) {
console.error('Failed to load dashboard:', e)
}
}
loadDashboard()
}, [objectTitle])
const handleBackClick = () => {
router.push('/objects')
}
// Статусы
const statusCounts = dashboardAlerts.reduce((acc: { critical: number; warning: number; normal: number }, a: any) => {
if (a.severity === 'critical') acc.critical++
else if (a.severity === 'warning') acc.warning++
else acc.normal++
return acc
}, { critical: 0, warning: 0, normal: 0 })
const handleNavigationClick = () => {
closeMonitoring()
closeFloorNavigation()
closeNotifications()
setCurrentSubmenu(null)
router.push('/navigation')
}
return (
<div className="flex h-screen bg-[#0e111a]">
<Sidebar
activeItem={1} // Dashboard
/>
<div className="flex-1 flex flex-col">
<header className="bg-[#161824] border-b border-gray-700 px-6 py-4">
<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>
</nav>
</div>
</header>
<div className="flex-1 p-6 overflow-auto">
<div className="mb-6">
<h1 className="text-white text-2xl font-semibold mb-6">Объект {objectId?.replace('object_', '')}</h1>
<div className="flex items-center gap-3 mb-6">
<button
className="flex items-center gap-6 rounded-[10px] px-4 py-[18px] bg-[rgb(22,24,36)] text-white"
>
<span className="text-sm font-medium">Датчики : все</span>
<svg className="w-4 h-4" 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>
<div className="flex items-center gap-3 ml-auto">
<button
onClick={handleNavigationClick}
className="rounded-[10px] px-4 py-[18px] bg-gray-600 text-gray-300 hover:bg-[rgb(22,24,36)] hover:text-white transition-colors"
>
<span className="text-sm font-medium">Навигация</span>
</button>
<div className="flex items-center gap-2 bg-[rgb(22,24,36)] rounded-lg px-3 py-2">
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span className="text-white text-sm font-medium">Период</span>
<div className="w-2 h-2 bg-white rounded-full"></div>
</div>
</div>
</div>
{/* Карты-графики */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-[18px]">
<ChartCard
title="Показатель"
subtitle="За последние 6 месяцев"
>
<AreaChart data={chartData} />
</ChartCard>
<ChartCard
title="Статистика"
subtitle="Данные за период"
>
<BarChart data={chartData?.map((d: any) => ({ value: d.value }))} />
</ChartCard>
</div>
</div>
{/* Список детекторов */}
<div>
<div>
<div className="flex items-center justify-between mb-6">
<h2 className="text-white text-2xl font-semibold">Тренды</h2>
<div className="bg-[#161824] rounded-lg px-3 py-2 flex items-center gap-2">
<svg className="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z" clipRule="evenodd" />
</svg>
<span className="text-white text-sm font-medium">Месяц</span>
<svg className="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
</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">Детектор</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>
{dashboardAlerts.map((alert: any) => (
<tr key={alert.id} className="border-b border-gray-800">
<td className="py-3 text-white text-sm">{alert.name}</td>
<td className="py-3 text-gray-300 text-sm">{alert.message}</td>
<td className="py-3">
<span className={`text-sm ${alert.severity === 'critical' ? 'text-red-500' : alert.severity === 'warning' ? 'text-orange-500' : 'text-green-500'}`}>
{alert.severity === 'critical' ? 'Критическое' : alert.severity === 'warning' ? 'Предупреждение' : 'Норма'}
</span>
</td>
<td className="py-3 text-gray-400 text-sm">{new Date(alert.created_at).toLocaleString()}</td>
<td className="py-3">
{alert.resolved ? (
<span className="text-sm text-green-500">Да</span>
) : (
<span className="text-sm text-gray-500">Нет</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Статы */}
<div className="mt-6 grid grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-white">{dashboardAlerts.length}</div>
<div className="text-sm text-gray-400">Всего</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-500">{statusCounts.normal}</div>
<div className="text-sm text-gray-400">Норма</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-orange-500">{statusCounts.warning}</div>
<div className="text-sm text-gray-400">Предупреждения</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-red-500">{statusCounts.critical}</div>
<div className="text-sm text-gray-400">Критические</div>
</div>
</div>
</div>
{/* Графики с аналитикой */}
<div className="mt-6 grid grid-cols-1 lg:grid-cols-4 gap-[18px]">
<ChartCard
title="Тренды детекторов"
subtitle="За последний месяц"
>
<DetectorChart type="line" />
</ChartCard>
<ChartCard
title="Статистика по месяцам"
subtitle="Активность детекторов"
>
<DetectorChart type="bar" />
</ChartCard>
<ChartCard
title="Анализ производительности"
subtitle="Эффективность работы"
>
<DetectorChart type="line" />
</ChartCard>
<ChartCard
title="Сводка по статусам"
subtitle="Распределение состояний"
>
<DetectorChart type="bar" />
</ChartCard>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default Dashboard