Improved authentication; added fallbacks to 3D; cleaner dashboard charts
This commit is contained in:
@@ -215,28 +215,28 @@ const Dashboard: React.FC = () => {
|
||||
title="Тренды детекторов"
|
||||
subtitle="За последний месяц"
|
||||
>
|
||||
<DetectorChart type="line" />
|
||||
<DetectorChart type="line" data={chartData?.map((d: any) => ({ value: d.value }))} />
|
||||
</ChartCard>
|
||||
|
||||
<ChartCard
|
||||
title="Статистика по месяцам"
|
||||
subtitle="Активность детекторов"
|
||||
>
|
||||
<DetectorChart type="bar" />
|
||||
<DetectorChart type="bar" data={chartData?.map((d: any) => ({ value: d.value }))} />
|
||||
</ChartCard>
|
||||
|
||||
<ChartCard
|
||||
title="Анализ производительности"
|
||||
subtitle="Эффективность работы"
|
||||
>
|
||||
<DetectorChart type="line" />
|
||||
<DetectorChart type="line" data={chartData?.map((d: any) => ({ value: d.value }))} />
|
||||
</ChartCard>
|
||||
|
||||
<ChartCard
|
||||
title="Сводка по статусам"
|
||||
subtitle="Распределение состояний"
|
||||
>
|
||||
<DetectorChart type="bar" />
|
||||
<DetectorChart type="bar" data={chartData?.map((d: any) => ({ value: d.value }))} />
|
||||
</ChartCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,10 +17,11 @@ interface DetectorChartProps {
|
||||
|
||||
const DetectorChart: React.FC<DetectorChartProps> = ({
|
||||
className = '',
|
||||
data,
|
||||
type = 'line'
|
||||
}) => {
|
||||
if (type === 'bar') {
|
||||
const barData = [
|
||||
const defaultBarData = [
|
||||
{ value: 85, label: 'Янв' },
|
||||
{ value: 70, label: 'Фев' },
|
||||
{ value: 90, label: 'Мар' },
|
||||
@@ -29,6 +30,13 @@ const DetectorChart: React.FC<DetectorChartProps> = ({
|
||||
{ value: 95, label: 'Июн' }
|
||||
]
|
||||
|
||||
const barData = (Array.isArray(data) && data.length > 0)
|
||||
? data.slice(0, 6).map((d, i) => ({
|
||||
value: d.value || 0,
|
||||
label: d.label || defaultBarData[i]?.label || `${i + 1}`
|
||||
}))
|
||||
: defaultBarData
|
||||
|
||||
return (
|
||||
<div className={`w-full h-full ${className}`}>
|
||||
<svg className="w-full h-full" viewBox="0 0 400 200">
|
||||
@@ -69,9 +77,40 @@ const DetectorChart: React.FC<DetectorChartProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
// Line chart implementation
|
||||
const defaultLineData = [
|
||||
{ value: 150 },
|
||||
{ value: 120 },
|
||||
{ value: 100 },
|
||||
{ value: 80 },
|
||||
{ value: 90 },
|
||||
{ value: 70 },
|
||||
{ value: 60 }
|
||||
]
|
||||
|
||||
const lineData = (Array.isArray(data) && data.length > 0)
|
||||
? data.slice(0, 7)
|
||||
: defaultLineData
|
||||
|
||||
const maxVal = Math.max(...lineData.map(d => d.value || 0), 1)
|
||||
const width = 400
|
||||
const height = 200
|
||||
const padding = 20
|
||||
const plotHeight = height - 40
|
||||
const stepX = lineData.length > 1 ? (width - 2 * padding) / (lineData.length - 1) : 0
|
||||
|
||||
const points = lineData.map((d, i) => {
|
||||
const x = padding + i * stepX
|
||||
const y = height - padding - ((d.value || 0) / maxVal) * plotHeight
|
||||
return { x, y }
|
||||
})
|
||||
|
||||
const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' ')
|
||||
const areaPath = `${linePath} L${width - padding},${height - padding} L${padding},${height - padding} Z`
|
||||
|
||||
return (
|
||||
<div className={`w-full h-full ${className}`}>
|
||||
<svg className="w-full h-full" viewBox="0 0 400 200">
|
||||
<svg className="w-full h-full" viewBox={`0 0 ${width} ${height}`}>
|
||||
<defs>
|
||||
<linearGradient id="detectorGradient" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stopColor="rgb(231, 110, 80)" stopOpacity="0.3" />
|
||||
@@ -79,22 +118,18 @@ const DetectorChart: React.FC<DetectorChartProps> = ({
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
d="M20,150 L80,120 L140,100 L200,80 L260,90 L320,70 L380,60 L380,180 L20,180 Z"
|
||||
d={areaPath}
|
||||
fill="url(#detectorGradient)"
|
||||
/>
|
||||
<path
|
||||
d="M20,150 L80,120 L140,100 L200,80 L260,90 L320,70 L380,60"
|
||||
d={linePath}
|
||||
stroke="rgb(231, 110, 80)"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
/>
|
||||
<circle cx="20" cy="150" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="80" cy="120" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="140" cy="100" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="200" cy="80" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="260" cy="90" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="320" cy="70" r="3" fill="rgb(231, 110, 80)" />
|
||||
<circle cx="380" cy="60" r="3" fill="rgb(231, 110, 80)" />
|
||||
{points.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r="3" fill="rgb(231, 110, 80)" />
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user