diff --git a/components/jira/BurndownChart.tsx b/components/jira/BurndownChart.tsx index a7fbc65..cb33ff0 100644 --- a/components/jira/BurndownChart.tsx +++ b/components/jira/BurndownChart.tsx @@ -131,12 +131,12 @@ export function BurndownChart({ sprintHistory, className }: BurndownChartProps) {/* Légende visuelle */}
-
- Idéal +
+ Idéal
-
- Réel +
+ Réel
diff --git a/components/jira/CollaborationMatrix.tsx b/components/jira/CollaborationMatrix.tsx index fe674b7..26c961d 100644 --- a/components/jira/CollaborationMatrix.tsx +++ b/components/jira/CollaborationMatrix.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { JiraAnalytics } from '@/lib/types'; -import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import { Card } from '@/components/ui/Card'; interface CollaborationMatrixProps { analytics: JiraAnalytics; @@ -78,17 +78,9 @@ export function CollaborationMatrix({ analytics, className }: CollaborationMatri // Couleur d'intensité const getIntensityColor = (intensity: 'low' | 'medium' | 'high') => { switch (intensity) { - case 'high': return 'bg-green-500'; - case 'medium': return 'bg-yellow-500'; - case 'low': return 'bg-gray-400'; - } - }; - - const getIntensityLabel = (intensity: 'low' | 'medium' | 'high') => { - switch (intensity) { - case 'high': return 'Forte'; - case 'medium': return 'Modérée'; - case 'low': return 'Faible'; + case 'high': return 'bg-green-600 dark:bg-green-500'; + case 'medium': return 'bg-yellow-600 dark:bg-yellow-500'; + case 'low': return 'bg-gray-500 dark:bg-gray-400'; } }; @@ -108,8 +100,8 @@ export function CollaborationMatrix({ analytics, className }: CollaborationMatri Score: {person.collaborationScore}
@@ -149,8 +141,7 @@ export function CollaborationMatrix({ analytics, className }: CollaborationMatri const ranges = [[0, 30], [30, 50], [50, 70], [70, 100]]; const [min, max] = ranges[index]; const count = collaborationData.filter(d => d.isolation >= min && d.isolation < max).length; - const percentage = (count / collaborationData.length) * 100; - const colors = ['bg-green-500', 'bg-blue-500', 'bg-yellow-500', 'bg-red-500']; + const colors = ['bg-green-600 dark:bg-green-500', 'bg-blue-600 dark:bg-blue-500', 'bg-yellow-600 dark:bg-yellow-500', 'bg-red-600 dark:bg-red-500']; return (
diff --git a/components/jira/JiraSync.tsx b/components/jira/JiraSync.tsx index e4327e5..e7c2e90 100644 --- a/components/jira/JiraSync.tsx +++ b/components/jira/JiraSync.tsx @@ -149,7 +149,7 @@ export function JiraSync({ onSyncComplete, className = "" }: JiraSyncProps) {
-
+

JIRA SYNC

diff --git a/components/jira/SprintComparison.tsx b/components/jira/SprintComparison.tsx index 77b5f2d..03d6d5a 100644 --- a/components/jira/SprintComparison.tsx +++ b/components/jira/SprintComparison.tsx @@ -1,8 +1,8 @@ 'use client'; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar, Cell } from 'recharts'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, BarChart, Bar } from 'recharts'; import { SprintVelocity } from '@/lib/types'; -import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import { Card } from '@/components/ui/Card'; interface SprintComparisonProps { sprintHistory: SprintVelocity[]; diff --git a/components/jira/TeamActivityHeatmap.tsx b/components/jira/TeamActivityHeatmap.tsx index f73b776..681e6e5 100644 --- a/components/jira/TeamActivityHeatmap.tsx +++ b/components/jira/TeamActivityHeatmap.tsx @@ -21,21 +21,21 @@ export function TeamActivityHeatmap({ workloadByAssignee, statusDistribution, cl // 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 'bg-[var(--muted)]/20'; + if (total === 0) return null; // Géré séparément - // Dominante par type de travail + // Dominante par type de travail avec couleurs CSS directes (versions plus douces) if (review > inProgress && review > todo) { - return 'bg-purple-500'; // Review dominant + return '#a855f7'; // purple-500 - Review dominant (plus doux) } else if (inProgress > todo) { - return 'bg-orange-500'; // In Progress dominant + return '#f59e0b'; // amber-500 - In Progress dominant (plus doux) } else { - return 'bg-blue-500'; // Todo dominant + return '#3b82f6'; // blue-500 - Todo dominant (plus doux) } }; const getOpacity = (total: number) => { const intensity = getIntensity(total); - return Math.max(0.2, intensity / 100); + return Math.max(0.6, Math.min(0.9, intensity / 100)); // Opacité plus élevée et moins de variation }; return ( @@ -45,28 +45,37 @@ export function TeamActivityHeatmap({ workloadByAssignee, statusDistribution, cl

Intensité de travail par membre

- {workloadByAssignee.map(assignee => ( -
-
+ {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`}
- ))} + ); + })}
@@ -77,7 +86,7 @@ export function TeamActivityHeatmap({ workloadByAssignee, statusDistribution, cl À faire dominant
-
+
En cours dominant
diff --git a/components/jira/ThroughputChart.tsx b/components/jira/ThroughputChart.tsx index 1a785c8..420e18a 100644 --- a/components/jira/ThroughputChart.tsx +++ b/components/jira/ThroughputChart.tsx @@ -138,16 +138,16 @@ export function ThroughputChart({ sprintHistory, className }: ThroughputChartPro {/* Légende visuelle */}
-
- Points complétés +
+ Points complétés
-
- Throughput +
+ Throughput
-
- Tendance +
+ Tendance
diff --git a/lib/jira-period-filter.ts b/lib/jira-period-filter.ts index 5e15594..a6fa8ac 100644 --- a/lib/jira-period-filter.ts +++ b/lib/jira-period-filter.ts @@ -1,4 +1,4 @@ -import { JiraAnalytics, JiraTask, SprintVelocity } from './types'; +import { JiraAnalytics } from './types'; export type PeriodFilter = '7d' | '30d' | '3m' | 'current'; diff --git a/src/actions/jira-export.ts b/src/actions/jira-export.ts index b62d0cd..5221e7c 100644 --- a/src/actions/jira-export.ts +++ b/src/actions/jira-export.ts @@ -11,6 +11,82 @@ export type ExportResult = { error?: string; }; +export interface JiraProjectMetrics { + key: string; + name: string; + totalIssues: number; +} + +export interface AssigneeMetrics { + assignee: string; + displayName: string; + totalIssues: number; + completedIssues: number; + inProgressIssues: number; + percentage: number; +} + +export interface TeamMetrics { + issuesDistribution: AssigneeMetrics[]; + totalAssignees: number; + activeAssignees: number; +} + +export interface SprintHistory { + sprintName: string; + startDate: string; + endDate: string; + plannedPoints: number; + completedPoints: number; + completionRate: number; +} + +export interface VelocityMetrics { + sprintHistory: SprintHistory[]; + currentSprintPoints: number; + averageVelocity: number; +} + +export interface CycleTimeByType { + issueType: string; + averageDays: number; + medianDays: number; + samples: number; +} + +export interface CycleTimeMetrics { + cycleTimeByType: CycleTimeByType[]; + averageCycleTime: number; +} + +export interface WorkInProgressStatus { + status: string; + count: number; + percentage: number; +} + +export interface WorkInProgressAssignee { + assignee: string; + displayName: string; + todoCount: number; + inProgressCount: number; + reviewCount: number; + totalActive: number; +} + +export interface WorkInProgress { + byStatus: WorkInProgressStatus[]; + byAssignee: WorkInProgressAssignee[]; +} + +export interface JiraAnalytics { + project: JiraProjectMetrics; + teamMetrics: TeamMetrics; + velocityMetrics: VelocityMetrics; + cycleTimeMetrics: CycleTimeMetrics; + workInProgress: WorkInProgress; +} + /** * Server Action pour exporter les analytics Jira au format CSV ou JSON */ @@ -60,7 +136,7 @@ export async function exportJiraAnalytics(format: ExportFormat = 'csv'): Promise /** * Génère un CSV à partir des analytics Jira */ -function generateCSV(analytics: any): string { +function generateCSV(analytics: JiraAnalytics): string { const lines: string[] = []; // Header du rapport @@ -73,7 +149,7 @@ function generateCSV(analytics: any): string { // Section 1: Métriques d'équipe lines.push('## Répartition de l\'équipe'); lines.push('Assignee,Nom,Total Tickets,Tickets Complétés,Tickets En Cours,Pourcentage'); - analytics.teamMetrics.issuesDistribution.forEach((assignee: any) => { + analytics.teamMetrics.issuesDistribution.forEach((assignee: AssigneeMetrics) => { lines.push([ escapeCsv(assignee.assignee), escapeCsv(assignee.displayName), @@ -88,7 +164,7 @@ function generateCSV(analytics: any): string { // Section 2: Historique des sprints lines.push('## Historique des sprints'); lines.push('Sprint,Date Début,Date Fin,Points Planifiés,Points Complétés,Taux de Complétion'); - analytics.velocityMetrics.sprintHistory.forEach((sprint: any) => { + analytics.velocityMetrics.sprintHistory.forEach((sprint: SprintHistory) => { lines.push([ escapeCsv(sprint.sprintName), sprint.startDate.slice(0, 10), @@ -103,7 +179,7 @@ function generateCSV(analytics: any): string { // Section 3: Cycle time par type lines.push('## Cycle Time par type de ticket'); lines.push('Type de Ticket,Temps Moyen (jours),Temps Médian (jours),Échantillons'); - analytics.cycleTimeMetrics.cycleTimeByType.forEach((type: any) => { + analytics.cycleTimeMetrics.cycleTimeByType.forEach((type: CycleTimeByType) => { lines.push([ escapeCsv(type.issueType), type.averageDays, @@ -116,7 +192,7 @@ function generateCSV(analytics: any): string { // Section 4: Work in Progress lines.push('## Work in Progress par statut'); lines.push('Statut,Nombre,Pourcentage'); - analytics.workInProgress.byStatus.forEach((status: any) => { + analytics.workInProgress.byStatus.forEach((status: WorkInProgressStatus) => { lines.push([ escapeCsv(status.status), status.count, @@ -128,7 +204,7 @@ function generateCSV(analytics: any): string { // Section 5: Charge de travail par assignee lines.push('## Charge de travail par assignee'); lines.push('Assignee,Nom,À Faire,En Cours,En Revue,Total Actif'); - analytics.workInProgress.byAssignee.forEach((assignee: any) => { + analytics.workInProgress.byAssignee.forEach((assignee: WorkInProgressAssignee) => { lines.push([ escapeCsv(assignee.assignee), escapeCsv(assignee.displayName),