'use client'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar, Cell } from 'recharts'; import { SprintVelocity } from '@/lib/types'; interface PredictabilityMetricsProps { sprintHistory: SprintVelocity[]; className?: string; } interface PredictabilityDataPoint { sprint: string; planned: number; actual: number; variance: number; // Pourcentage de variance (positif = dépassement, négatif = sous-performance) accuracy: number; // Pourcentage d'exactitude (100% = parfait) } export function PredictabilityMetrics({ sprintHistory, className }: PredictabilityMetricsProps) { // Calculer les métriques de predictabilité const predictabilityData: PredictabilityDataPoint[] = sprintHistory.map(sprint => { const variance = sprint.plannedPoints > 0 ? ((sprint.completedPoints - sprint.plannedPoints) / sprint.plannedPoints) * 100 : 0; const accuracy = sprint.plannedPoints > 0 ? Math.max(0, 100 - Math.abs(variance)) : 0; return { sprint: sprint.sprintName.replace('Sprint ', ''), planned: sprint.plannedPoints, actual: sprint.completedPoints, variance: Math.round(variance * 10) / 10, accuracy: Math.round(accuracy * 10) / 10 }; }); // Calculer les statistiques globales const averageVariance = predictabilityData.length > 0 ? predictabilityData.reduce((sum, d) => sum + Math.abs(d.variance), 0) / predictabilityData.length : 0; const averageAccuracy = predictabilityData.length > 0 ? predictabilityData.reduce((sum, d) => sum + d.accuracy, 0) / predictabilityData.length : 0; const consistencyScore = averageVariance < 10 ? 'Excellent' : averageVariance < 20 ? 'Bon' : averageVariance < 30 ? 'Moyen' : 'À améliorer'; // Tendance de l'exactitude (en amélioration ou dégradation) const recentAccuracy = predictabilityData.slice(-2); const trend = recentAccuracy.length >= 2 ? recentAccuracy[1].accuracy - recentAccuracy[0].accuracy : 0; const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ payload: PredictabilityDataPoint; value: number; name: string; color: string }>; label?: string }) => { if (active && payload && payload.length) { const data = payload[0].payload; return (

Sprint {label}

Planifié: {data.planned} pts
Réalisé: {data.actual} pts
Variance: 0 ? 'text-green-500' : data.variance < 0 ? 'text-red-500' : 'text-gray-500'}`}> {data.variance > 0 ? '+' : ''}{data.variance}%
Exactitude: {data.accuracy}%
); } return null; }; return (
{/* Graphique de variance */}

Variance planifié vs réalisé

} /> {predictabilityData.map((entry, index) => ( 0 ? 'hsl(142, 76%, 36%)' : entry.variance < 0 ? 'hsl(0, 84%, 60%)' : 'hsl(240, 5%, 64%)'} /> ))}
{/* Graphique d'exactitude */}

Évolution de l'exactitude

} />
{/* Métriques de predictabilité */}
80 ? 'text-green-500' : averageAccuracy > 60 ? 'text-orange-500' : 'text-red-500'}`}> {Math.round(averageAccuracy)}%
Exactitude moyenne
{Math.round(averageVariance * 10) / 10}%
Variance moyenne
{consistencyScore}
Consistance
5 ? 'text-green-500' : trend < -5 ? 'text-red-500' : 'text-blue-500'}`}> {trend > 0 ? '↗️' : trend < 0 ? '↘️' : '→'} {Math.abs(Math.round(trend))}%
Tendance récente
{/* Analyse et recommandations */}

Analyse de predictabilité

{averageAccuracy > 80 && (
Excellente predictabilité - L'équipe estime bien sa capacité
)} {averageAccuracy < 60 && (
⚠️ Predictabilité faible - Revoir les méthodes d'estimation
)} {averageVariance > 25 && (
📊 Variance élevée - Considérer des sprints plus courts ou un meilleur découpage
)} {trend > 10 && (
📈 Tendance positive - L'équipe s'améliore dans ses estimations
)} {trend < -10 && (
📉 Tendance négative - Attention aux changements récents (équipe, processus)
)}
); }