Files
towercontrol/components/dashboard/PeriodSelector.tsx
Julien Froidefond fded7d0078 feat: add weekly summary features and components
- 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.
2025-09-19 12:28:11 +02:00

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&apos;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>
);
}