- Updated radar chart to use abbreviated category labels for better readability. - Enhanced chart visibility with improved grid and axis styling. - Added a gradient background to the radar area for a more polished look. - Simplified links in JSON files for backend, cloud, and devops skills. - Adjusted HomePage text for clarity and consistency.
140 lines
3.8 KiB
TypeScript
140 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Radar,
|
|
RadarChart,
|
|
PolarGrid,
|
|
PolarAngleAxis,
|
|
PolarRadiusAxis,
|
|
ResponsiveContainer,
|
|
} from "recharts";
|
|
import { RadarChartData } from "@/lib/types";
|
|
|
|
interface SkillsRadarChartProps {
|
|
data: RadarChartData[];
|
|
}
|
|
|
|
export function SkillsRadarChart({ data }: SkillsRadarChartProps) {
|
|
// Transform data for the chart with abbreviated labels
|
|
const chartData = data.map((item) => ({
|
|
category: item.category,
|
|
shortLabel:
|
|
item.category.length > 8
|
|
? item.category.substring(0, 8) + "..."
|
|
: item.category,
|
|
score: item.score,
|
|
maxScore: item.maxScore,
|
|
}));
|
|
|
|
if (data.length === 0) {
|
|
return (
|
|
<div className="flex items-center justify-center h-[400px] text-muted-foreground">
|
|
<p>Aucune donnée disponible</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-full h-[400px] relative">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<RadarChart
|
|
data={chartData}
|
|
margin={{ top: 40, right: 60, bottom: 40, left: 60 }}
|
|
>
|
|
{/* Grid with better visibility */}
|
|
<PolarGrid
|
|
gridType="polygon"
|
|
stroke="rgba(255, 255, 255, 0.2)"
|
|
strokeWidth={1}
|
|
/>
|
|
|
|
{/* Category labels with better positioning */}
|
|
<PolarAngleAxis
|
|
dataKey="shortLabel"
|
|
tick={{
|
|
fontSize: 13,
|
|
fill: "rgba(255, 255, 255, 0.9)",
|
|
fontWeight: 500,
|
|
}}
|
|
tickFormatter={(value, index) => {
|
|
return chartData[index]?.category || value;
|
|
}}
|
|
/>
|
|
|
|
{/* Radial axis with custom ticks */}
|
|
<PolarRadiusAxis
|
|
angle={90}
|
|
domain={[0, 3]}
|
|
tick={{
|
|
fontSize: 11,
|
|
fill: "rgba(255, 255, 255, 0.6)",
|
|
fontWeight: 400,
|
|
}}
|
|
tickCount={4}
|
|
tickFormatter={(value) => {
|
|
if (value === 0) return "";
|
|
if (value === 1) return "Débutant";
|
|
if (value === 2) return "Autonome";
|
|
if (value === 3) return "Expert";
|
|
return value.toString();
|
|
}}
|
|
/>
|
|
|
|
{/* Main radar area with gradient */}
|
|
<Radar
|
|
name="Niveau"
|
|
dataKey="score"
|
|
stroke="#3b82f6"
|
|
fill="url(#radarGradient)"
|
|
fillOpacity={0.3}
|
|
strokeWidth={3}
|
|
dot={{
|
|
fill: "#3b82f6",
|
|
strokeWidth: 2,
|
|
stroke: "#ffffff",
|
|
r: 6,
|
|
}}
|
|
/>
|
|
|
|
{/* Background reference radar for max score */}
|
|
<Radar
|
|
name="Maximum"
|
|
dataKey="maxScore"
|
|
stroke="rgba(255, 255, 255, 0.2)"
|
|
fill="transparent"
|
|
strokeWidth={1}
|
|
strokeDasharray="5 5"
|
|
dot={false}
|
|
/>
|
|
|
|
{/* Gradient definition */}
|
|
<defs>
|
|
<linearGradient
|
|
id="radarGradient"
|
|
x1="0%"
|
|
y1="0%"
|
|
x2="100%"
|
|
y2="100%"
|
|
>
|
|
<stop offset="0%" stopColor="#3b82f6" stopOpacity={0.4} />
|
|
<stop offset="100%" stopColor="#1d4ed8" stopOpacity={0.1} />
|
|
</linearGradient>
|
|
</defs>
|
|
</RadarChart>
|
|
</ResponsiveContainer>
|
|
|
|
{/* Legend */}
|
|
<div className="absolute bottom-2 right-2 text-xs text-slate-400 space-y-1">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-0.5 bg-blue-500"></div>
|
|
<span>Votre niveau</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-0.5 border-t border-dashed border-white/40"></div>
|
|
<span>Maximum</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|