- Introduced `CategoryBreakdown`, `JiraWeeklyMetrics`, `PeriodSelector`, and `VelocityMetrics` components to enhance the weekly summary dashboard. - Updated `WeeklySummaryClient` to manage period selection and PDF export functionality. - Enhanced `WeeklySummaryService` to support period comparisons and activity categorization. - Added new API route for fetching weekly summary data based on selected period. - Updated `package.json` and `package-lock.json` to include `jspdf` and related types for PDF generation. - Marked several tasks as complete in `TODO.md` to reflect progress on summary features.
169 lines
7.1 KiB
TypeScript
169 lines
7.1 KiB
TypeScript
'use client';
|
||
|
||
import type { VelocityMetrics } from '@/services/weekly-summary';
|
||
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
|
||
import { Badge } from '@/components/ui/Badge';
|
||
|
||
interface VelocityMetricsProps {
|
||
velocity: VelocityMetrics;
|
||
}
|
||
|
||
export function VelocityMetrics({ velocity }: VelocityMetricsProps) {
|
||
const getTrendIcon = (trend: number) => {
|
||
if (trend > 10) return '📈';
|
||
if (trend < -10) return '📉';
|
||
return '➡️';
|
||
};
|
||
|
||
const getTrendColor = (trend: number) => {
|
||
if (trend > 10) return 'text-[var(--success)]';
|
||
if (trend < -10) return 'text-[var(--destructive)]';
|
||
return 'text-[var(--muted-foreground)]';
|
||
};
|
||
|
||
const formatTrend = (trend: number) => {
|
||
const sign = trend > 0 ? '+' : '';
|
||
return `${sign}${trend.toFixed(1)}%`;
|
||
};
|
||
|
||
const maxActivities = Math.max(...velocity.weeklyData.map(w => w.totalActivities));
|
||
|
||
return (
|
||
<Card>
|
||
<CardHeader>
|
||
<h3 className="text-lg font-semibold">⚡ Métriques de vélocité</h3>
|
||
<p className="text-sm text-[var(--muted-foreground)]">
|
||
Performance sur les 4 dernières semaines
|
||
</p>
|
||
</CardHeader>
|
||
|
||
<CardContent className="space-y-6">
|
||
{/* Métriques principales */}
|
||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--primary)]/50 transition-colors text-center">
|
||
<div className="text-2xl font-bold text-[var(--primary)]">
|
||
{velocity.currentWeekTasks}
|
||
</div>
|
||
<div className="text-sm text-[var(--muted-foreground)]">Tâches cette semaine</div>
|
||
</div>
|
||
|
||
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--muted-foreground)]/50 transition-colors text-center">
|
||
<div className="text-2xl font-bold text-[var(--muted-foreground)]">
|
||
{velocity.previousWeekTasks}
|
||
</div>
|
||
<div className="text-sm text-[var(--muted-foreground)]">Semaine précédente</div>
|
||
</div>
|
||
|
||
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--accent)]/50 transition-colors text-center">
|
||
<div className="text-2xl font-bold text-[var(--accent)]">
|
||
{velocity.fourWeekAverage.toFixed(1)}
|
||
</div>
|
||
<div className="text-sm text-[var(--muted-foreground)]">Moyenne 4 semaines</div>
|
||
</div>
|
||
|
||
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--success)]/50 transition-colors text-center">
|
||
<div className={`text-2xl font-bold ${getTrendColor(velocity.weeklyTrend)}`}>
|
||
{getTrendIcon(velocity.weeklyTrend)} {formatTrend(velocity.weeklyTrend)}
|
||
</div>
|
||
<div className="text-sm text-[var(--muted-foreground)]">Tendance</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Graphique de tendance simple */}
|
||
<div>
|
||
<h4 className="font-medium mb-3">📊 Tendance sur 4 semaines</h4>
|
||
<div className="grid grid-cols-4 gap-2">
|
||
{velocity.weeklyData.map((week, index) => {
|
||
const height = maxActivities > 0 ? (week.totalActivities / maxActivities) * 100 : 0;
|
||
const weekLabel = `S${index + 1}`;
|
||
|
||
return (
|
||
<div key={index} className="text-center">
|
||
<div className="h-24 flex items-end justify-center mb-2">
|
||
<div
|
||
className="bg-[var(--primary)] w-8 rounded-t transition-all hover:bg-[var(--primary)]/80"
|
||
style={{ height: `${Math.max(height, 5)}%` }}
|
||
title={`${week.totalActivities} activités complétées`}
|
||
/>
|
||
</div>
|
||
<div className="text-xs font-medium">{weekLabel}</div>
|
||
<div className="text-xs text-[var(--muted-foreground)]">
|
||
{week.totalActivities}
|
||
</div>
|
||
<div className="text-xs text-[var(--muted-foreground)]">
|
||
{week.weekStart.toLocaleDateString('fr-FR', {
|
||
day: 'numeric',
|
||
month: 'short'
|
||
})}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Détails par semaine */}
|
||
<div>
|
||
<h4 className="font-medium mb-3">📈 Détails par semaine</h4>
|
||
<div className="space-y-2">
|
||
{velocity.weeklyData.map((week, index) => {
|
||
const isCurrentWeek = index === velocity.weeklyData.length - 1;
|
||
return (
|
||
<div
|
||
key={index}
|
||
className={`flex items-center justify-between p-3 rounded-lg border transition-colors ${
|
||
isCurrentWeek ? 'bg-[var(--primary)]/10 border-[var(--primary)]/30' : 'bg-[var(--card)] border-[var(--border)] hover:border-[var(--border)]/80'
|
||
}`}
|
||
>
|
||
<div className="flex items-center gap-3">
|
||
<span className="text-sm font-medium">
|
||
Semaine du {week.weekStart.toLocaleDateString('fr-FR', {
|
||
day: 'numeric',
|
||
month: 'short'
|
||
})}
|
||
</span>
|
||
{isCurrentWeek && (
|
||
<Badge className="bg-[var(--primary)]/20 text-[var(--primary)]">Actuelle</Badge>
|
||
)}
|
||
</div>
|
||
<div className="flex items-center gap-4 text-sm">
|
||
<span className="text-[var(--success)]">
|
||
{week.completedTasks} tâches
|
||
</span>
|
||
<span className="text-[var(--primary)]">
|
||
{week.completedCheckboxes} daily
|
||
</span>
|
||
<span className="font-medium text-[var(--foreground)]">
|
||
Total: {week.totalActivities}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Insights */}
|
||
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)]">
|
||
<h4 className="font-medium mb-2">💡 Insights</h4>
|
||
<div className="text-sm text-[var(--muted-foreground)] space-y-1">
|
||
{velocity.weeklyTrend > 10 && (
|
||
<p>🚀 Excellente progression ! Vous êtes {velocity.weeklyTrend.toFixed(1)}% plus productif cette semaine.</p>
|
||
)}
|
||
{velocity.weeklyTrend < -10 && (
|
||
<p>⚠️ Baisse d'activité de {Math.abs(velocity.weeklyTrend).toFixed(1)}%. Peut-être temps de revoir votre planning ?</p>
|
||
)}
|
||
{Math.abs(velocity.weeklyTrend) <= 10 && (
|
||
<p>✅ Rythme stable. Vous maintenez une productivité constante.</p>
|
||
)}
|
||
<p>
|
||
📊 Votre moyenne sur 4 semaines est de {velocity.fourWeekAverage.toFixed(1)} activités par semaine.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
}
|
||
|