feat: enhance metrics dashboard with new components and data handling

- Introduced `MetricsOverview`, `MetricsMainCharts`, `MetricsDistributionCharts`, `MetricsVelocitySection`, and `MetricsProductivitySection` for improved metrics visualization.
- Updated `MetricsTab` to integrate new components and streamline data presentation.
- Added compatibility fields in `JiraTask` and `AssigneeDistribution` for better data handling.
- Refactored `calculateAssigneeDistribution` to include a count for total issues.
- Enhanced `JiraAnalyticsService` and `JiraAdvancedFiltersService` to support new metrics calculations.
- Cleaned up unused imports and components for a more maintainable codebase.
This commit is contained in:
Julien Froidefond
2025-09-21 15:55:11 +02:00
parent c650c67627
commit 0a03e40469
43 changed files with 2781 additions and 1805 deletions

View File

@@ -3,15 +3,13 @@
import { useState } from 'react';
import { useWeeklyMetrics, useVelocityTrends } from '@/hooks/use-metrics';
import { getToday } from '@/lib/date-utils';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { Card, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { DailyStatusChart } from './charts/DailyStatusChart';
import { CompletionRateChart } from './charts/CompletionRateChart';
import { StatusDistributionChart } from './charts/StatusDistributionChart';
import { PriorityBreakdownChart } from './charts/PriorityBreakdownChart';
import { VelocityTrendChart } from './charts/VelocityTrendChart';
import { WeeklyActivityHeatmap } from './charts/WeeklyActivityHeatmap';
import { ProductivityInsights } from './charts/ProductivityInsights';
import { MetricsOverview } from './charts/MetricsOverview';
import { MetricsMainCharts } from './charts/MetricsMainCharts';
import { MetricsDistributionCharts } from './charts/MetricsDistributionCharts';
import { MetricsVelocitySection } from './charts/MetricsVelocitySection';
import { MetricsProductivitySection } from './charts/MetricsProductivitySection';
import { format } from 'date-fns';
import { fr } from 'date-fns/locale';
@@ -36,23 +34,6 @@ export function MetricsTab({ className }: MetricsTabProps) {
return `Semaine du ${format(metrics.period.start, 'dd MMM', { locale: fr })} au ${format(metrics.period.end, 'dd MMM yyyy', { locale: fr })}`;
};
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'improving': return '📈';
case 'declining': return '📉';
case 'stable': return '➡️';
default: return '📊';
}
};
const getPatternIcon = (pattern: string) => {
switch (pattern) {
case 'consistent': return '🎯';
case 'variable': return '📊';
case 'weekend-heavy': return '📅';
default: return '📋';
}
};
if (metricsError || trendsError) {
return (
@@ -107,150 +88,24 @@ export function MetricsTab({ className }: MetricsTabProps) {
) : metrics ? (
<div className="space-y-6">
{/* Vue d'ensemble rapide */}
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">🎯 Vue d&apos;ensemble</h3>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 lg:grid-cols-5 gap-4">
<div className="text-center p-4 bg-green-50 dark:bg-green-950/20 rounded-lg">
<div className="text-2xl font-bold text-green-600">
{metrics.summary.totalTasksCompleted}
</div>
<div className="text-sm text-green-600">Terminées</div>
</div>
<div className="text-center p-4 bg-blue-50 dark:bg-blue-950/20 rounded-lg">
<div className="text-2xl font-bold text-blue-600">
{metrics.summary.totalTasksCreated}
</div>
<div className="text-sm text-blue-600">Créées</div>
</div>
<div className="text-center p-4 bg-purple-50 dark:bg-purple-950/20 rounded-lg">
<div className="text-2xl font-bold text-purple-600">
{metrics.summary.averageCompletionRate.toFixed(1)}%
</div>
<div className="text-sm text-purple-600">Taux moyen</div>
</div>
<div className="text-center p-4 bg-orange-50 dark:bg-orange-950/20 rounded-lg">
<div className="text-2xl font-bold text-orange-600">
{getTrendIcon(metrics.summary.trendsAnalysis.completionTrend)}
</div>
<div className="text-sm text-orange-600 capitalize">
{metrics.summary.trendsAnalysis.completionTrend}
</div>
</div>
<div className="text-center p-4 bg-gray-50 dark:bg-gray-950/20 rounded-lg">
<div className="text-2xl font-bold text-gray-600">
{getPatternIcon(metrics.summary.trendsAnalysis.productivityPattern)}
</div>
<div className="text-sm text-gray-600">
{metrics.summary.trendsAnalysis.productivityPattern === 'consistent' ? 'Régulier' :
metrics.summary.trendsAnalysis.productivityPattern === 'variable' ? 'Variable' : 'Weekend+'}
</div>
</div>
</div>
</CardContent>
</Card>
<MetricsOverview metrics={metrics} />
{/* Graphiques principaux */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">📈 Évolution quotidienne des statuts</h3>
</CardHeader>
<CardContent>
<DailyStatusChart data={metrics.dailyBreakdown} />
</CardContent>
</Card>
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">🎯 Taux de completion quotidien</h3>
</CardHeader>
<CardContent>
<CompletionRateChart data={metrics.dailyBreakdown} />
</CardContent>
</Card>
</div>
<MetricsMainCharts metrics={metrics} />
{/* Distribution et priorités */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">🍰 Répartition des statuts</h3>
</CardHeader>
<CardContent>
<StatusDistributionChart data={metrics.statusDistribution} />
</CardContent>
</Card>
<Card>
<CardHeader>
<h3 className="text-lg font-semibold"> Performance par priorité</h3>
</CardHeader>
<CardContent>
<PriorityBreakdownChart data={metrics.priorityBreakdown} />
</CardContent>
</Card>
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">🔥 Heatmap d&apos;activité</h3>
</CardHeader>
<CardContent>
<WeeklyActivityHeatmap data={metrics.dailyBreakdown} />
</CardContent>
</Card>
</div>
<MetricsDistributionCharts metrics={metrics} />
{/* Tendances de vélocité */}
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">🚀 Tendances de vélocité</h3>
<select
value={weeksBack}
onChange={(e) => setWeeksBack(parseInt(e.target.value))}
className="text-sm border border-[var(--border)] rounded px-2 py-1 bg-[var(--background)]"
disabled={trendsLoading}
>
<option value={4}>4 semaines</option>
<option value={8}>8 semaines</option>
<option value={12}>12 semaines</option>
</select>
</div>
</CardHeader>
<CardContent>
{trendsLoading ? (
<div className="h-[300px] flex items-center justify-center">
<div className="animate-pulse text-center">
<div className="h-4 bg-[var(--border)] rounded w-32 mx-auto mb-2"></div>
<div className="h-48 bg-[var(--border)] rounded"></div>
</div>
</div>
) : trends.length > 0 ? (
<VelocityTrendChart data={trends} />
) : (
<div className="h-[300px] flex items-center justify-center text-[var(--muted-foreground)]">
Aucune donnée de vélocité disponible
</div>
)}
</CardContent>
</Card>
<MetricsVelocitySection
trends={trends}
trendsLoading={trendsLoading}
weeksBack={weeksBack}
onWeeksBackChange={setWeeksBack}
/>
{/* Analyses de productivité */}
<Card>
<CardHeader>
<h3 className="text-lg font-semibold">💡 Analyses de productivité</h3>
</CardHeader>
<CardContent>
<ProductivityInsights data={metrics.dailyBreakdown} />
</CardContent>
</Card>
<MetricsProductivitySection metrics={metrics} />
</div>
) : null}
</div>