Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
'use client'
|
|
|
|
import type { TrendData } from '../types'
|
|
|
|
export function TrendChart({ data }: { data: TrendData }) {
|
|
if (!data || data.dates.length === 0) {
|
|
return (
|
|
<div className="h-48 flex items-center justify-center text-slate-400">
|
|
Keine Trend-Daten verfuegbar
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const maxScore = Math.max(...data.scores, 5)
|
|
const minScore = Math.min(...data.scores, 0)
|
|
const range = maxScore - minScore || 1
|
|
|
|
return (
|
|
<div className="h-48 relative">
|
|
<div className="absolute left-0 top-0 bottom-4 w-8 flex flex-col justify-between text-xs text-slate-400">
|
|
<span>{maxScore.toFixed(1)}</span>
|
|
<span>{((maxScore + minScore) / 2).toFixed(1)}</span>
|
|
<span>{minScore.toFixed(1)}</span>
|
|
</div>
|
|
|
|
<div className="ml-10 h-full pr-4">
|
|
<svg className="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
<line x1="0" y1="0" x2="100" y2="0" stroke="#e2e8f0" strokeWidth="0.5" />
|
|
<line x1="0" y1="50" x2="100" y2="50" stroke="#e2e8f0" strokeWidth="0.5" />
|
|
<line x1="0" y1="100" x2="100" y2="100" stroke="#e2e8f0" strokeWidth="0.5" />
|
|
|
|
<polyline
|
|
fill="none"
|
|
stroke="#14b8a6"
|
|
strokeWidth="2"
|
|
points={data.scores
|
|
.map((score, i) => {
|
|
const x = (i / (data.scores.length - 1 || 1)) * 100
|
|
const y = 100 - ((score - minScore) / range) * 100
|
|
return `${x},${y}`
|
|
})
|
|
.join(' ')}
|
|
/>
|
|
|
|
{data.scores.map((score, i) => {
|
|
const x = (i / (data.scores.length - 1 || 1)) * 100
|
|
const y = 100 - ((score - minScore) / range) * 100
|
|
return <circle key={i} cx={x} cy={y} r="2" fill="#14b8a6" />
|
|
})}
|
|
</svg>
|
|
</div>
|
|
|
|
<div className="ml-10 flex justify-between text-xs text-slate-400 mt-1">
|
|
{data.dates.slice(0, 5).map((date, i) => (
|
|
<span key={i}>{new Date(date).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' })}</span>
|
|
))}
|
|
</div>
|
|
|
|
<div className="absolute top-2 right-2">
|
|
<span
|
|
className={`px-2 py-1 rounded text-xs font-medium ${
|
|
data.trend === 'improving'
|
|
? 'bg-emerald-100 text-emerald-700'
|
|
: data.trend === 'declining'
|
|
? 'bg-red-100 text-red-700'
|
|
: 'bg-slate-100 text-slate-700'
|
|
}`}
|
|
>
|
|
{data.trend === 'improving' ? 'Verbessernd' : data.trend === 'declining' ? 'Verschlechternd' : 'Stabil'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|