Initial commit
This commit is contained in:
59
src/components/Analysis/TrendChart.jsx
Normal file
59
src/components/Analysis/TrendChart.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user