import type { JiraConfig } from './jira'; import { Task } from '@/lib/types'; export interface JiraWeeklyMetrics { totalJiraTasks: number; completedJiraTasks: number; totalStoryPoints: number; // Estimation basée sur le type de ticket projectsContributed: string[]; ticketTypes: { [type: string]: number }; jiraLinks: Array<{ key: string; title: string; status: string; type: string; url: string; estimatedPoints: number; }>; } export class JiraSummaryService { /** * Enrichit les tâches hebdomadaires avec des métriques Jira */ static async getJiraWeeklyMetrics( weeklyTasks: Task[], jiraConfig?: JiraConfig ): Promise { if (!jiraConfig?.baseUrl || !jiraConfig?.email || !jiraConfig?.apiToken) { return null; } const jiraTasks = weeklyTasks.filter(task => task.source === 'jira' && task.jiraKey && task.jiraProject ); if (jiraTasks.length === 0) { return { totalJiraTasks: 0, completedJiraTasks: 0, totalStoryPoints: 0, projectsContributed: [], ticketTypes: {}, jiraLinks: [] }; } // Calculer les métriques basiques const completedJiraTasks = jiraTasks.filter(task => task.status === 'done'); const projectsContributed = [...new Set(jiraTasks.map(task => task.jiraProject).filter((project): project is string => Boolean(project)))]; // Analyser les types de tickets const ticketTypes: { [type: string]: number } = {}; jiraTasks.forEach(task => { const type = task.jiraType || 'Unknown'; ticketTypes[type] = (ticketTypes[type] || 0) + 1; }); // Estimer les story points basés sur le type de ticket const estimateStoryPoints = (type: string): number => { const typeMapping: { [key: string]: number } = { 'Story': 3, 'Task': 2, 'Bug': 1, 'Epic': 8, 'Sub-task': 1, 'Improvement': 2, 'New Feature': 5, 'défaut': 1, // French 'amélioration': 2, // French 'nouvelle fonctionnalité': 5, // French }; return typeMapping[type] || typeMapping[type?.toLowerCase()] || 2; // Défaut: 2 points }; const totalStoryPoints = jiraTasks.reduce((sum, task) => { return sum + estimateStoryPoints(task.jiraType || ''); }, 0); // Créer les liens Jira const jiraLinks = jiraTasks.map(task => ({ key: task.jiraKey || '', title: task.title, status: task.status, type: task.jiraType || 'Unknown', url: `${jiraConfig.baseUrl.replace('/rest/api/3', '')}/browse/${task.jiraKey}`, estimatedPoints: estimateStoryPoints(task.jiraType || '') })); return { totalJiraTasks: jiraTasks.length, completedJiraTasks: completedJiraTasks.length, totalStoryPoints, projectsContributed, ticketTypes, jiraLinks }; } /** * Récupère la configuration Jira depuis les préférences utilisateur */ static async getJiraConfig(): Promise { try { // Import dynamique pour éviter les cycles de dépendance const { userPreferencesService } = await import('./user-preferences'); const preferences = await userPreferencesService.getAllPreferences(); if (!preferences.jiraConfig?.baseUrl || !preferences.jiraConfig?.email || !preferences.jiraConfig?.apiToken) { return null; } return { baseUrl: preferences.jiraConfig.baseUrl, email: preferences.jiraConfig.email, apiToken: preferences.jiraConfig.apiToken, projectKey: preferences.jiraConfig.projectKey, ignoredProjects: preferences.jiraConfig.ignoredProjects }; } catch (error) { console.error('Erreur lors de la récupération de la config Jira:', error); return null; } } /** * Génère des insights business basés sur les métriques Jira */ static generateBusinessInsights(jiraMetrics: JiraWeeklyMetrics): string[] { const insights: string[] = []; if (jiraMetrics.totalJiraTasks === 0) { insights.push("Aucune tâche Jira cette semaine. Concentré sur des tâches internes ?"); return insights; } // Insights sur la completion const completionRate = (jiraMetrics.completedJiraTasks / jiraMetrics.totalJiraTasks) * 100; if (completionRate >= 80) { insights.push(`🎯 Excellent taux de completion Jira: ${completionRate.toFixed(0)}%`); } else if (completionRate < 50) { insights.push(`⚠️ Taux de completion Jira faible: ${completionRate.toFixed(0)}%. Revoir les estimations ?`); } // Insights sur les story points if (jiraMetrics.totalStoryPoints > 0) { insights.push(`📊 Estimation: ${jiraMetrics.totalStoryPoints} story points traités cette semaine`); const avgPointsPerTask = jiraMetrics.totalStoryPoints / jiraMetrics.totalJiraTasks; if (avgPointsPerTask > 4) { insights.push(`🏋️ Travail sur des tâches complexes (${avgPointsPerTask.toFixed(1)} pts/tâche en moyenne)`); } } // Insights sur les projets if (jiraMetrics.projectsContributed.length > 1) { insights.push(`🤝 Contribution multi-projets: ${jiraMetrics.projectsContributed.join(', ')}`); } else if (jiraMetrics.projectsContributed.length === 1) { insights.push(`🎯 Focus sur le projet ${jiraMetrics.projectsContributed[0]}`); } // Insights sur les types de tickets const bugCount = jiraMetrics.ticketTypes['Bug'] || jiraMetrics.ticketTypes['défaut'] || 0; const totalTickets = Object.values(jiraMetrics.ticketTypes).reduce((sum, count) => sum + count, 0); if (bugCount > 0) { const bugRatio = (bugCount / totalTickets) * 100; if (bugRatio > 50) { insights.push(`🐛 Semaine focalisée sur la correction de bugs (${bugRatio.toFixed(0)}%)`); } else if (bugRatio < 20) { insights.push(`✨ Semaine productive avec peu de bugs (${bugRatio.toFixed(0)}%)`); } } return insights; } }