'use client'; import { AssigneeWorkload, StatusDistribution } from '@/lib/types'; interface TeamActivityHeatmapProps { workloadByAssignee: AssigneeWorkload[]; statusDistribution: StatusDistribution[]; className?: string; } export function TeamActivityHeatmap({ workloadByAssignee, statusDistribution, className }: TeamActivityHeatmapProps) { // Calculer l'intensité maximale pour la normalisation const maxWorkload = Math.max(...workloadByAssignee.map(a => a.totalActive)); // Fonction pour calculer l'intensité de couleur const getIntensity = (value: number) => { if (maxWorkload === 0) return 0; return (value / maxWorkload) * 100; }; // Couleurs pour les différents types de travail const getWorkloadColor = (todo: number, inProgress: number, review: number) => { const total = todo + inProgress + review; if (total === 0) return null; // Géré séparément // Dominante par type de travail avec couleurs CSS directes (versions plus douces) if (review > inProgress && review > todo) { return '#a855f7'; // purple-500 - Review dominant (plus doux) } else if (inProgress > todo) { return '#f59e0b'; // amber-500 - In Progress dominant (plus doux) } else { return '#3b82f6'; // blue-500 - Todo dominant (plus doux) } }; const getOpacity = (total: number) => { const intensity = getIntensity(total); return Math.max(0.6, Math.min(0.9, intensity / 100)); // Opacité plus élevée et moins de variation }; return (
{/* Heatmap des assignees */}

Intensité de travail par membre

{workloadByAssignee.map(assignee => { const bgColor = getWorkloadColor(assignee.todoCount, assignee.inProgressCount, assignee.reviewCount); const isEmpty = assignee.totalActive === 0; return (
{assignee.displayName}
{assignee.totalActive}
{assignee.todoCount > 0 && `${assignee.todoCount} à faire`} {assignee.inProgressCount > 0 && ` ${assignee.inProgressCount} en cours`} {assignee.reviewCount > 0 && ` ${assignee.reviewCount} review`}
); })}
{/* Légende */}
À faire dominant
En cours dominant
Review dominant
(Opacité = charge de travail)
{/* Matrice de statuts */}

Répartition globale par statut

{statusDistribution.slice(0, 8).map(status => { const intensity = (status.count / Math.max(...statusDistribution.map(s => s.count))) * 100; return (
{status.status}
{status.count}
{status.percentage}%
); })}
{/* Métriques rapides */}
{workloadByAssignee.filter(a => a.totalActive > 0).length}
Membres actifs
{workloadByAssignee.reduce((sum, a) => sum + a.totalActive, 0)}
Total WIP
{Math.round(workloadByAssignee.reduce((sum, a) => sum + a.totalActive, 0) / Math.max(1, workloadByAssignee.filter(a => a.totalActive > 0).length) * 10) / 10}
WIP moyen/membre
); }