Files
towercontrol/src/components/dashboard/ManagerWeeklySummary.tsx
2025-10-09 13:40:03 +02:00

415 lines
15 KiB
TypeScript

'use client';
import { useState } from 'react';
import { ManagerSummary } from '@/services/analytics/manager-summary';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { Tabs, TabItem } from '@/components/ui/Tabs';
import { AchievementCard } from '@/components/ui/AchievementCard';
import { ChallengeCard } from '@/components/ui/ChallengeCard';
import { useTasksContext } from '@/contexts/TasksContext';
import { MetricsTab } from './MetricsTab';
import { format } from 'date-fns';
import { fr } from 'date-fns/locale';
import { Tag } from '@/lib/types';
import { Emoji } from '@/components/ui/Emoji';
interface ManagerWeeklySummaryProps {
initialSummary: ManagerSummary;
}
export default function ManagerWeeklySummary({
initialSummary,
}: ManagerWeeklySummaryProps) {
const [summary] = useState<ManagerSummary>(initialSummary);
const [activeView, setActiveView] = useState<
'narrative' | 'accomplishments' | 'challenges' | 'metrics'
>('narrative');
const { tags: availableTags } = useTasksContext();
const handleTabChange = (tabId: string) => {
setActiveView(
tabId as 'narrative' | 'accomplishments' | 'challenges' | 'metrics'
);
};
const handleRefresh = () => {
// SSR - refresh via page reload
window.location.reload();
};
const formatPeriod = () => {
return `7 derniers jours (${format(summary.period.start, 'dd MMM', { locale: fr })} - ${format(summary.period.end, 'dd MMM yyyy', { locale: fr })})`;
};
// Configuration des onglets
const tabItems: TabItem[] = [
{ id: 'narrative', label: 'Vue Executive', icon: '📝' },
{
id: 'accomplishments',
label: 'Accomplissements',
icon: '✅',
count: summary.keyAccomplishments.length,
},
{
id: 'challenges',
label: 'Enjeux à venir',
icon: '🎯',
count: summary.upcomingChallenges.length,
},
{ id: 'metrics', label: 'Métriques', icon: '📊' },
];
return (
<div className="space-y-6">
{/* Header avec navigation */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-[var(--foreground)]">
<Emoji emoji="👔" /> Weekly
</h1>
<p className="text-[var(--muted-foreground)]">{formatPeriod()}</p>
</div>
{activeView !== 'metrics' && (
<Button onClick={handleRefresh} variant="secondary" size="sm">
<Emoji emoji="🔄" />
&nbsp;Actualiser
</Button>
)}
</div>
{/* Navigation des vues */}
<Tabs
items={tabItems}
activeTab={activeView}
onTabChange={handleTabChange}
/>
{/* Vue Executive / Narrative */}
{activeView === 'narrative' && (
<div className="space-y-6">
{/* Section principale : Résumé + Métriques côte à côte */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Résumé narratif */}
<Card variant="elevated">
<CardHeader>
<h2 className="text-lg font-semibold flex items-center gap-2">
<Emoji emoji="📊" /> Résumé de la semaine
</h2>
</CardHeader>
<CardContent className="space-y-4">
<div className="outline-card-blue p-4">
<h3 className="font-medium mb-2">
<Emoji emoji="🎯" /> Points clés accomplis
</h3>
<p className="text-sm text-[var(--muted-foreground)]">
{summary.narrative.weekHighlight}
</p>
</div>
<div className="outline-card-orange p-4">
<h3 className="font-medium mb-2">
<Emoji emoji="⚡" /> Défis traités
</h3>
<p className="text-sm text-[var(--muted-foreground)]">
{summary.narrative.mainChallenges}
</p>
</div>
<div className="outline-card-green p-4">
<h3 className="font-medium mb-2">
<Emoji emoji="🔮" /> Focus 7 prochains jours
</h3>
<p className="text-sm text-[var(--muted-foreground)]">
{summary.narrative.nextWeekFocus}
</p>
</div>
</CardContent>
</Card>
{/* Métriques rapides */}
<Card variant="elevated">
<CardHeader>
<h2 className="text-lg font-semibold">
<Emoji emoji="📈" /> Métriques en bref
</h2>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 gap-4">
<div className="outline-metric-blue">
<div className="text-2xl font-bold mb-1">
{summary.metrics.totalTasksCompleted}
</div>
<div className="text-xs font-medium mb-1">
Tâches complétées
</div>
<div className="text-xs text-[var(--muted-foreground)]">
dont {summary.metrics.highPriorityTasksCompleted} priorité
haute
</div>
</div>
<div className="outline-metric-green">
<div className="text-2xl font-bold mb-1">
{summary.metrics.totalCheckboxesCompleted}
</div>
<div className="text-xs font-medium mb-1">
Todos complétés
</div>
<div className="text-xs text-[var(--muted-foreground)]">
dont {summary.metrics.meetingCheckboxesCompleted} meetings
</div>
</div>
<div className="outline-metric-orange">
<div className="text-2xl font-bold mb-1">
{
summary.keyAccomplishments.filter(
(a) => a.impact === 'high'
).length
}
</div>
<div className="text-xs font-medium mb-1">
Items à fort impact
</div>
<div className="text-xs text-[var(--muted-foreground)]">
/ {summary.keyAccomplishments.length} accomplissements
</div>
</div>
<div className="outline-metric-gray">
<div className="text-2xl font-bold mb-1">
{
summary.upcomingChallenges.filter(
(c) => c.priority === 'high'
).length
}
</div>
<div className="text-xs font-medium mb-1">
Priorités critiques
</div>
<div className="text-xs text-[var(--muted-foreground)]">
/ {summary.upcomingChallenges.length} enjeux
</div>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Top accomplissements */}
<Card variant="elevated">
<CardHeader
className="pb-4"
style={{
borderBottom: '1px solid',
borderBottomColor:
'color-mix(in srgb, var(--success) 10%, var(--border))',
}}
>
<h2
className="text-lg font-semibold flex items-center gap-2"
style={{ color: 'var(--success)' }}
>
<Emoji emoji="🏆" /> Top accomplissements
</h2>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{summary.keyAccomplishments.length === 0 ? (
<div className="col-span-3 text-center py-8 text-[var(--muted-foreground)]">
<p>
Aucun accomplissement significatif trouvé cette semaine.
</p>
<p className="text-sm mt-2">
Ajoutez des tâches avec priorité haute/medium ou des
meetings.
</p>
</div>
) : (
summary.keyAccomplishments
.slice(0, 6)
.map((accomplishment, index) => (
<AchievementCard
key={accomplishment.id}
achievement={accomplishment}
availableTags={
availableTags as (Tag & { usage: number })[]
}
index={index}
showDescription={true}
maxTags={2}
/>
))
)}
</div>
</CardContent>
</Card>
{/* Top challenges */}
<Card variant="elevated">
<CardHeader
className="pb-4"
style={{
borderBottom: '1px solid',
borderBottomColor:
'color-mix(in srgb, var(--destructive) 10%, var(--border))',
}}
>
<h2
className="text-lg font-semibold flex items-center gap-2"
style={{ color: 'var(--destructive)' }}
>
<Emoji emoji="🎯" /> Top enjeux à venir
</h2>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{summary.upcomingChallenges.length === 0 ? (
<div className="col-span-3 text-center py-8 text-[var(--muted-foreground)]">
<p>Aucun enjeu prioritaire trouvé.</p>
<p className="text-sm mt-2">
Ajoutez des tâches non complétées avec priorité
haute/medium.
</p>
</div>
) : (
summary.upcomingChallenges
.slice(0, 6)
.map((challenge, index) => (
<ChallengeCard
key={challenge.id}
challenge={challenge}
availableTags={
availableTags as (Tag & { usage: number })[]
}
index={index}
showDescription={true}
maxTags={2}
/>
))
)}
</div>
</CardContent>
</Card>
</div>
)}
{/* Vue détaillée des accomplissements */}
{activeView === 'accomplishments' && (
<Card variant="elevated">
<CardHeader>
<h2 className="text-lg font-semibold">
<Emoji emoji="✅" /> Accomplissements des 7 derniers jours
</h2>
<p className="text-sm text-[var(--muted-foreground)]">
{summary.keyAccomplishments.length} accomplissements significatifs
{summary.metrics.totalTasksCompleted} tâches {' '}
{summary.metrics.totalCheckboxesCompleted} todos complétés
</p>
</CardHeader>
<CardContent className="space-y-6">
{summary.keyAccomplishments.length === 0 ? (
<div
className="p-8 text-center rounded-xl border-2"
style={{
backgroundColor:
'color-mix(in srgb, var(--muted) 15%, transparent)',
borderColor:
'color-mix(in srgb, var(--muted) 40%, var(--border))',
color: 'var(--muted-foreground)',
}}
>
<div className="text-4xl mb-4">
<Emoji emoji="📭" />
</div>
<p className="text-lg mb-2">
Aucun accomplissement significatif trouvé cette semaine.
</p>
<p className="text-sm">
Ajoutez des tâches avec priorité haute/medium ou des meetings.
</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{summary.keyAccomplishments.map((accomplishment, index) => (
<AchievementCard
key={accomplishment.id}
achievement={accomplishment}
availableTags={availableTags as (Tag & { usage: number })[]}
index={index}
showDescription={true}
maxTags={3}
/>
))}
</div>
)}
</CardContent>
</Card>
)}
{/* Vue détaillée des challenges */}
{activeView === 'challenges' && (
<Card variant="elevated">
<CardHeader>
<h2 className="text-lg font-semibold">
<Emoji emoji="🎯" /> Enjeux et défis à venir
</h2>
<p className="text-sm text-[var(--muted-foreground)]">
{summary.upcomingChallenges.length} défis identifiés {' '}
{
summary.upcomingChallenges.filter((c) => c.priority === 'high')
.length
}{' '}
priorité haute {' '}
{
summary.upcomingChallenges.filter((c) => c.blockers.length > 0)
.length
}{' '}
avec blockers
</p>
</CardHeader>
<CardContent className="space-y-6">
{summary.upcomingChallenges.length === 0 ? (
<div
className="p-8 text-center rounded-xl border-2"
style={{
backgroundColor:
'color-mix(in srgb, var(--muted) 15%, transparent)',
borderColor:
'color-mix(in srgb, var(--muted) 40%, var(--border))',
color: 'var(--muted-foreground)',
}}
>
<div className="text-4xl mb-4">
<Emoji emoji="🎯" />
</div>
<p className="text-lg mb-2">Aucun enjeu prioritaire trouvé.</p>
<p className="text-sm">
Ajoutez des tâches non complétées avec priorité haute/medium.
</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{summary.upcomingChallenges.map((challenge, index) => (
<ChallengeCard
key={challenge.id}
challenge={challenge}
availableTags={availableTags as (Tag & { usage: number })[]}
index={index}
showDescription={true}
maxTags={3}
/>
))}
</div>
)}
</CardContent>
</Card>
)}
{/* Vue Métriques */}
{activeView === 'metrics' && <MetricsTab />}
</div>
);
}