+ {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
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),