60 lines
2.5 KiB
JavaScript
60 lines
2.5 KiB
JavaScript
import React from 'react';
|
|
|
|
export default function TrendChart({ trendSeries = [] }) {
|
|
if (trendSeries.length < 2) {
|
|
return (
|
|
<div className="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 bg-white/70 dark:bg-gray-900/70 p-4 text-sm text-gray-600 dark:text-gray-300">
|
|
Für den Trend werden mindestens 2 Assessments benötigt.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const width = 760;
|
|
const height = 280;
|
|
const left = 50;
|
|
const right = 20;
|
|
const top = 16;
|
|
const bottom = 52;
|
|
|
|
const values = trendSeries.map((entry) => entry.totalPoints);
|
|
const minValue = Math.min(...values);
|
|
const maxValue = Math.max(...values);
|
|
const range = maxValue - minValue || 1;
|
|
|
|
const innerWidth = width - left - right;
|
|
const innerHeight = height - top - bottom;
|
|
|
|
const points = trendSeries.map((entry, index) => {
|
|
const x = left + (index / (trendSeries.length - 1)) * innerWidth;
|
|
const normalized = (entry.totalPoints - minValue) / range;
|
|
const y = top + innerHeight - normalized * innerHeight;
|
|
return { ...entry, x, y };
|
|
});
|
|
|
|
const path = points.map((point, index) => `${index === 0 ? 'M' : 'L'}${point.x},${point.y}`).join(' ');
|
|
|
|
return (
|
|
<div className="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-4 shadow-sm">
|
|
<h3 className="text-sm font-semibold text-gray-800 dark:text-gray-100 mb-3">Trend (Gesamtpunkte)</h3>
|
|
<svg viewBox={`0 0 ${width} ${height}`} className="w-full h-auto" role="img" aria-label="Trenddiagramm">
|
|
<line x1={left} y1={top} x2={left} y2={top + innerHeight} className="stroke-gray-300 dark:stroke-gray-700" strokeWidth="1" />
|
|
<line x1={left} y1={top + innerHeight} x2={left + innerWidth} y2={top + innerHeight} className="stroke-gray-300 dark:stroke-gray-700" strokeWidth="1" />
|
|
|
|
<path d={path} fill="none" className="stroke-indigo-500" strokeWidth="3" strokeLinecap="round" />
|
|
|
|
{points.map((point) => (
|
|
<g key={point.assessmentId}>
|
|
<circle cx={point.x} cy={point.y} r="4" className="fill-indigo-500" />
|
|
<text x={point.x} y={point.y - 10} textAnchor="middle" className="fill-gray-700 dark:fill-gray-200" style={{ fontSize: 11, fontWeight: 600 }}>
|
|
{point.totalPoints}
|
|
</text>
|
|
<text x={point.x} y={height - 18} textAnchor="middle" className="fill-gray-500 dark:fill-gray-400" style={{ fontSize: 10 }}>
|
|
{point.label}
|
|
</text>
|
|
</g>
|
|
))}
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|