- 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.
156 lines
6.7 KiB
TypeScript
156 lines
6.7 KiB
TypeScript
'use client';
|
|
|
|
import { PERIOD_OPTIONS, PeriodOption, PeriodComparison } from '@/services/weekly-summary';
|
|
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
|
|
import { Button } from '@/components/ui/Button';
|
|
|
|
interface PeriodSelectorProps {
|
|
currentPeriod: PeriodOption;
|
|
onPeriodChange: (period: PeriodOption) => void;
|
|
comparison: PeriodComparison | null;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export function PeriodSelector({
|
|
currentPeriod,
|
|
onPeriodChange,
|
|
comparison,
|
|
isLoading = false
|
|
}: PeriodSelectorProps) {
|
|
|
|
const formatChange = (change: number): string => {
|
|
const sign = change > 0 ? '+' : '';
|
|
return `${sign}${change.toFixed(1)}%`;
|
|
};
|
|
|
|
const getChangeColor = (change: number): string => {
|
|
if (change > 10) return 'text-[var(--success)]';
|
|
if (change < -10) return 'text-[var(--destructive)]';
|
|
return 'text-[var(--muted-foreground)]';
|
|
};
|
|
|
|
const getChangeIcon = (change: number): string => {
|
|
if (change > 10) return '📈';
|
|
if (change < -10) return '📉';
|
|
return '➡️';
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="text-lg font-semibold">📊 Sélection de période</h3>
|
|
<div className="flex gap-2">
|
|
{PERIOD_OPTIONS.map((option) => (
|
|
<Button
|
|
key={option.key}
|
|
onClick={() => onPeriodChange(option)}
|
|
variant={currentPeriod.key === option.key ? "primary" : "secondary"}
|
|
size="sm"
|
|
disabled={isLoading}
|
|
>
|
|
{option.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
{comparison && (
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{/* Titre de comparaison */}
|
|
<h4 className="font-medium">
|
|
Comparaison avec la période précédente
|
|
</h4>
|
|
|
|
{/* Métriques de comparaison */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
{/* Tâches */}
|
|
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--primary)]/50 transition-colors">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm font-medium text-[var(--primary)]">Tâches complétées</span>
|
|
<span className={`text-sm font-medium ${getChangeColor(comparison.changes.tasks)}`}>
|
|
{getChangeIcon(comparison.changes.tasks)} {formatChange(comparison.changes.tasks)}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="text-[var(--foreground)] font-medium">
|
|
Actuelle: {comparison.currentPeriod.tasks}
|
|
</span>
|
|
<span className="text-[var(--muted-foreground)]">
|
|
Précédente: {comparison.previousPeriod.tasks}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Daily items */}
|
|
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--success)]/50 transition-colors">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm font-medium text-[var(--success)]">Daily items</span>
|
|
<span className={`text-sm font-medium ${getChangeColor(comparison.changes.checkboxes)}`}>
|
|
{getChangeIcon(comparison.changes.checkboxes)} {formatChange(comparison.changes.checkboxes)}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="text-[var(--foreground)] font-medium">
|
|
Actuelle: {comparison.currentPeriod.checkboxes}
|
|
</span>
|
|
<span className="text-[var(--muted-foreground)]">
|
|
Précédente: {comparison.previousPeriod.checkboxes}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Total */}
|
|
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)] hover:border-[var(--accent)]/50 transition-colors">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm font-medium text-[var(--accent)]">Total activités</span>
|
|
<span className={`text-sm font-medium ${getChangeColor(comparison.changes.total)}`}>
|
|
{getChangeIcon(comparison.changes.total)} {formatChange(comparison.changes.total)}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="text-[var(--foreground)] font-medium">
|
|
Actuelle: {comparison.currentPeriod.total}
|
|
</span>
|
|
<span className="text-[var(--muted-foreground)]">
|
|
Précédente: {comparison.previousPeriod.total}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Insights sur la comparaison */}
|
|
<div className="bg-[var(--card)] p-4 rounded-lg border border-[var(--border)]">
|
|
<h5 className="font-medium mb-2">💡 Insights comparatifs</h5>
|
|
<div className="text-sm text-[var(--muted-foreground)] space-y-1">
|
|
{comparison.changes.total > 15 && (
|
|
<p>🚀 Excellente progression ! Productivité en hausse de {formatChange(comparison.changes.total)}.</p>
|
|
)}
|
|
{comparison.changes.total < -15 && (
|
|
<p>📉 Baisse d'activité de {formatChange(Math.abs(comparison.changes.total))}. Période moins chargée ?</p>
|
|
)}
|
|
{Math.abs(comparison.changes.total) <= 15 && (
|
|
<p>✅ Rythme stable maintenu entre les deux périodes.</p>
|
|
)}
|
|
|
|
{comparison.changes.tasks > comparison.changes.checkboxes + 10 && (
|
|
<p>🎯 Focus accru sur les tâches importantes cette période.</p>
|
|
)}
|
|
{comparison.changes.checkboxes > comparison.changes.tasks + 10 && (
|
|
<p>📝 Activité quotidienne plus intense cette période.</p>
|
|
)}
|
|
|
|
<p>
|
|
📊 Évolution globale: {comparison.currentPeriod.total} activités vs {comparison.previousPeriod.total} la période précédente.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|