diff --git a/src/app/jira-dashboard/JiraDashboardPageClient.tsx b/src/app/jira-dashboard/JiraDashboardPageClient.tsx index 6627893..61ef078 100644 --- a/src/app/jira-dashboard/JiraDashboardPageClient.tsx +++ b/src/app/jira-dashboard/JiraDashboardPageClient.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState, useEffect, useMemo } from 'react'; -import { JiraConfig } from '@/lib/types'; +import { JiraConfig, JiraAnalytics } from '@/lib/types'; import { useJiraAnalytics } from '@/hooks/useJiraAnalytics'; import { useJiraExport } from '@/hooks/useJiraExport'; import { filterAnalyticsByPeriod, getPeriodInfo, type PeriodFilter } from '@/lib/jira-period-filter'; @@ -28,10 +28,11 @@ import Link from 'next/link'; interface JiraDashboardPageClientProps { initialJiraConfig: JiraConfig; + initialAnalytics?: JiraAnalytics | null; } -export function JiraDashboardPageClient({ initialJiraConfig }: JiraDashboardPageClientProps) { - const { analytics: rawAnalytics, isLoading, error, loadAnalytics, refreshAnalytics } = useJiraAnalytics(); +export function JiraDashboardPageClient({ initialJiraConfig, initialAnalytics }: JiraDashboardPageClientProps) { + const { analytics: rawAnalytics, isLoading, error, loadAnalytics, refreshAnalytics } = useJiraAnalytics(initialAnalytics); const { isExporting, error: exportError, exportCSV, exportJSON } = useJiraExport(); const { availableFilters, @@ -56,11 +57,11 @@ export function JiraDashboardPageClient({ initialJiraConfig }: JiraDashboardPage const periodInfo = getPeriodInfo(selectedPeriod); useEffect(() => { - // Charger les analytics au montage si Jira est configuré avec un projet - if (initialJiraConfig.enabled && initialJiraConfig.projectKey) { + // Charger les analytics au montage seulement si Jira est configuré ET qu'on n'a pas déjà des données + if (initialJiraConfig.enabled && initialJiraConfig.projectKey && !initialAnalytics) { loadAnalytics(); } - }, [initialJiraConfig.enabled, initialJiraConfig.projectKey, loadAnalytics]); + }, [initialJiraConfig.enabled, initialJiraConfig.projectKey, loadAnalytics, initialAnalytics]); // Gestion du clic sur un sprint const handleSprintClick = (sprint: SprintVelocity) => { diff --git a/src/app/jira-dashboard/page.tsx b/src/app/jira-dashboard/page.tsx index d4e661e..64d73ce 100644 --- a/src/app/jira-dashboard/page.tsx +++ b/src/app/jira-dashboard/page.tsx @@ -1,4 +1,5 @@ import { userPreferencesService } from '@/services/core/user-preferences'; +import { getJiraAnalytics } from '@/actions/jira-analytics'; import { JiraDashboardPageClient } from './JiraDashboardPageClient'; // Force dynamic rendering @@ -7,8 +8,20 @@ export const dynamic = 'force-dynamic'; export default async function JiraDashboardPage() { // Récupérer la config Jira côté serveur const jiraConfig = await userPreferencesService.getJiraConfig(); + + // Récupérer les analytics côté serveur (utilise le cache du service) + let initialAnalytics = null; + if (jiraConfig.enabled && jiraConfig.projectKey) { + const analyticsResult = await getJiraAnalytics(false); // Utilise le cache + if (analyticsResult.success) { + initialAnalytics = analyticsResult.data; + } + } return ( - + ); } diff --git a/src/components/jira/CollaborationMatrix.tsx b/src/components/jira/CollaborationMatrix.tsx index 26c961d..1b27e1a 100644 --- a/src/components/jira/CollaborationMatrix.tsx +++ b/src/components/jira/CollaborationMatrix.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { JiraAnalytics } from '@/lib/types'; import { Card } from '@/components/ui/Card'; @@ -23,10 +23,16 @@ interface CollaborationData { } export function CollaborationMatrix({ analytics, className }: CollaborationMatrixProps) { - // Analyser les patterns de collaboration basés sur les données existantes - const collaborationData: CollaborationData[] = analytics.teamMetrics.issuesDistribution.map(assignee => { - // Simuler des collaborations basées sur les données réelles - const totalTickets = assignee.totalIssues; + const [collaborationData, setCollaborationData] = useState([]); + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + + // Analyser les patterns de collaboration basés sur les données existantes + const data: CollaborationData[] = analytics.teamMetrics.issuesDistribution.map(assignee => { + // Simuler des collaborations basées sur les données réelles + const totalTickets = assignee.totalIssues; // Générer des partenaires de collaboration réalistes const otherAssignees = analytics.teamMetrics.issuesDistribution.filter(a => a.assignee !== assignee.assignee); @@ -67,13 +73,25 @@ export function CollaborationMatrix({ analytics, className }: CollaborationMatri }; }); - // Statistiques globales - const avgCollaboration = collaborationData.reduce((sum, d) => sum + d.collaborationScore, 0) / collaborationData.length; - const avgIsolation = collaborationData.reduce((sum, d) => sum + d.isolation, 0) / collaborationData.length; - const mostCollaborative = collaborationData.reduce((max, current) => - current.collaborationScore > max.collaborationScore ? current : max, collaborationData[0]); - const mostIsolated = collaborationData.reduce((max, current) => - current.isolation > max.isolation ? current : max, collaborationData[0]); + setCollaborationData(data); +}, [analytics]); + +// Ne pas rendre côté serveur pour éviter l'erreur d'hydratation +if (!isClient) { + return ( +
+
+
+ ); +} + +// Statistiques globales +const avgCollaboration = collaborationData.reduce((sum, d) => sum + d.collaborationScore, 0) / collaborationData.length; +const avgIsolation = collaborationData.reduce((sum, d) => sum + d.isolation, 0) / collaborationData.length; +const mostCollaborative = collaborationData.reduce((max, current) => + current.collaborationScore > max.collaborationScore ? current : max, collaborationData[0]); +const mostIsolated = collaborationData.reduce((max, current) => + current.isolation > max.isolation ? current : max, collaborationData[0]); // Couleur d'intensité const getIntensityColor = (intensity: 'low' | 'medium' | 'high') => { diff --git a/src/hooks/useJiraAnalytics.ts b/src/hooks/useJiraAnalytics.ts index 1f0376b..4c8aecb 100644 --- a/src/hooks/useJiraAnalytics.ts +++ b/src/hooks/useJiraAnalytics.ts @@ -4,8 +4,8 @@ import { useState, useTransition, useCallback } from 'react'; import { getJiraAnalytics } from '@/actions/jira-analytics'; import { JiraAnalytics } from '@/lib/types'; -export function useJiraAnalytics() { - const [analytics, setAnalytics] = useState(null); +export function useJiraAnalytics(initialAnalytics?: JiraAnalytics | null) { + const [analytics, setAnalytics] = useState(initialAnalytics || null); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition();