feat: refactor Daily components and enhance UI integration
- Replaced `DailyCalendar` with a new `Calendar` component for improved functionality and consistency. - Introduced `AlertBanner` to replace `DeadlineReminder`, providing a more flexible way to display urgent tasks. - Updated `DailyAddForm` to use new options for task types, enhancing user experience when adding tasks. - Removed unused state and components, streamlining the DailyPageClient for better performance and maintainability. - Enhanced `DailySection` to utilize new `CheckboxItem` format for better integration with the UI. - Cleaned up imports and improved overall structure for better readability.
This commit is contained in:
@@ -4,12 +4,15 @@ 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 { TagDisplay } from '@/components/ui/TagDisplay';
|
||||
import { getPriorityConfig } from '@/lib/status-config';
|
||||
import { MetricCard } from '@/components/ui/MetricCard';
|
||||
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';
|
||||
|
||||
interface ManagerWeeklySummaryProps {
|
||||
initialSummary: ManagerSummary;
|
||||
@@ -20,6 +23,10 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
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();
|
||||
@@ -30,23 +37,13 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
return `7 derniers jours (${format(summary.period.start, 'dd MMM', { locale: fr })} - ${format(summary.period.end, 'dd MMM yyyy', { locale: fr })})`;
|
||||
};
|
||||
|
||||
const getPriorityBadgeStyle = (priority: 'low' | 'medium' | 'high') => {
|
||||
const config = getPriorityConfig(priority);
|
||||
const baseClasses = 'text-xs px-2 py-0.5 rounded font-medium border';
|
||||
|
||||
switch (config.color) {
|
||||
case 'blue':
|
||||
return `${baseClasses} text-[var(--primary)] bg-[var(--primary)]/10 border-[var(--primary)]/20`;
|
||||
case 'yellow':
|
||||
return `${baseClasses} text-[var(--accent)] bg-[var(--accent)]/10 border-[var(--accent)]/20`;
|
||||
case 'purple':
|
||||
return `${baseClasses} text-[#8b5cf6] bg-[#8b5cf6]/10 border-[#8b5cf6]/20`;
|
||||
case 'red':
|
||||
return `${baseClasses} text-[var(--destructive)] bg-[var(--destructive)]/10 border-[var(--destructive)]/20`;
|
||||
default:
|
||||
return `${baseClasses} text-[var(--muted-foreground)] bg-[var(--muted)]/10 border-[var(--muted)]/20`;
|
||||
}
|
||||
};
|
||||
// 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 (
|
||||
@@ -67,50 +64,11 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
</div>
|
||||
|
||||
{/* Navigation des vues */}
|
||||
<div className="border-b border-[var(--border)]">
|
||||
<nav className="flex space-x-8">
|
||||
<button
|
||||
onClick={() => setActiveView('narrative')}
|
||||
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeView === 'narrative'
|
||||
? 'border-[var(--primary)] text-[var(--primary)]'
|
||||
: 'border-transparent text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
|
||||
}`}
|
||||
>
|
||||
📝 Vue Executive
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('accomplishments')}
|
||||
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeView === 'accomplishments'
|
||||
? 'border-[var(--primary)] text-[var(--primary)]'
|
||||
: 'border-transparent text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
|
||||
}`}
|
||||
>
|
||||
✅ Accomplissements ({summary.keyAccomplishments.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('challenges')}
|
||||
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeView === 'challenges'
|
||||
? 'border-[var(--primary)] text-[var(--primary)]'
|
||||
: 'border-transparent text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
|
||||
}`}
|
||||
>
|
||||
🎯 Enjeux à venir ({summary.upcomingChallenges.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveView('metrics')}
|
||||
className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors ${
|
||||
activeView === 'metrics'
|
||||
? 'border-[var(--primary)] text-[var(--primary)]'
|
||||
: 'border-transparent text-[var(--muted-foreground)] hover:text-[var(--foreground)]'
|
||||
}`}
|
||||
>
|
||||
📊 Métriques
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<Tabs
|
||||
items={tabItems}
|
||||
activeTab={activeView}
|
||||
onTabChange={handleTabChange}
|
||||
/>
|
||||
|
||||
{/* Vue Executive / Narrative */}
|
||||
{activeView === 'narrative' && (
|
||||
@@ -147,45 +105,33 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="outline-metric-blue">
|
||||
<div className="text-2xl font-bold">
|
||||
{summary.metrics.totalTasksCompleted}
|
||||
</div>
|
||||
<div className="text-sm">Tâches complétées</div>
|
||||
<div className="text-xs opacity-75">
|
||||
dont {summary.metrics.highPriorityTasksCompleted} priorité haute
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
title="Tâches complétées"
|
||||
value={summary.metrics.totalTasksCompleted}
|
||||
subtitle={`dont ${summary.metrics.highPriorityTasksCompleted} priorité haute`}
|
||||
color="primary"
|
||||
/>
|
||||
|
||||
<div className="outline-metric-green">
|
||||
<div className="text-2xl font-bold">
|
||||
{summary.metrics.totalCheckboxesCompleted}
|
||||
</div>
|
||||
<div className="text-sm">Todos complétés</div>
|
||||
<div className="text-xs opacity-75">
|
||||
dont {summary.metrics.meetingCheckboxesCompleted} meetings
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
title="Todos complétés"
|
||||
value={summary.metrics.totalCheckboxesCompleted}
|
||||
subtitle={`dont ${summary.metrics.meetingCheckboxesCompleted} meetings`}
|
||||
color="success"
|
||||
/>
|
||||
|
||||
<div className="outline-metric-purple">
|
||||
<div className="text-2xl font-bold">
|
||||
{summary.keyAccomplishments.filter(a => a.impact === 'high').length}
|
||||
</div>
|
||||
<div className="text-sm">Items à fort impact</div>
|
||||
<div className="text-xs opacity-75">
|
||||
/ {summary.keyAccomplishments.length} accomplissements
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
title="Items à fort impact"
|
||||
value={summary.keyAccomplishments.filter(a => a.impact === 'high').length}
|
||||
subtitle={`/ ${summary.keyAccomplishments.length} accomplissements`}
|
||||
color="warning"
|
||||
/>
|
||||
|
||||
<div className="outline-metric-orange">
|
||||
<div className="text-2xl font-bold">
|
||||
{summary.upcomingChallenges.filter(c => c.priority === 'high').length}
|
||||
</div>
|
||||
<div className="text-sm">Priorités critiques</div>
|
||||
<div className="text-xs opacity-75">
|
||||
/ {summary.upcomingChallenges.length} enjeux
|
||||
</div>
|
||||
</div>
|
||||
<MetricCard
|
||||
title="Priorités critiques"
|
||||
value={summary.upcomingChallenges.filter(c => c.priority === 'high').length}
|
||||
subtitle={`/ ${summary.upcomingChallenges.length} enjeux`}
|
||||
color="destructive"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -202,64 +148,18 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
<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) => (
|
||||
<div
|
||||
key={accomplishment.id}
|
||||
className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 transition-all duration-200 group"
|
||||
>
|
||||
{/* Barre colorée gauche */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-green-500 rounded-l-lg"></div>
|
||||
|
||||
{/* Header compact */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-5 h-5 rounded-full text-xs font-bold flex items-center justify-center text-[var(--success)] bg-[var(--success)]/15 border border-[var(--success)]/25">
|
||||
#{index + 1}
|
||||
</span>
|
||||
<span className={getPriorityBadgeStyle(accomplishment.impact)}>
|
||||
{getPriorityConfig(accomplishment.impact).label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-[var(--muted-foreground)]">
|
||||
{format(accomplishment.completedAt, 'dd/MM', { locale: fr })}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Titre */}
|
||||
<h4 className="font-semibold text-sm text-[var(--foreground)] mb-2 line-clamp-2">
|
||||
{accomplishment.title}
|
||||
</h4>
|
||||
|
||||
{/* Tags */}
|
||||
{accomplishment.tags && accomplishment.tags.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<TagDisplay
|
||||
tags={accomplishment.tags}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={2}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description si disponible */}
|
||||
{accomplishment.description && (
|
||||
<p className="text-xs text-[var(--muted-foreground)] line-clamp-2 leading-relaxed mb-2">
|
||||
{accomplishment.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Count de todos */}
|
||||
{accomplishment.todosCount > 0 && (
|
||||
<div className="flex items-center gap-1 text-xs text-[var(--muted-foreground)]">
|
||||
<span>📋</span>
|
||||
<span>{accomplishment.todosCount} todo{accomplishment.todosCount > 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
</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>
|
||||
@@ -276,64 +176,16 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
<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) => (
|
||||
<div
|
||||
key={challenge.id}
|
||||
className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 transition-all duration-200 group"
|
||||
>
|
||||
{/* Barre colorée gauche */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-orange-500 rounded-l-lg"></div>
|
||||
|
||||
{/* Header compact */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-5 h-5 rounded-full text-xs font-bold flex items-center justify-center text-[var(--accent)] bg-[var(--accent)]/15 border border-[var(--accent)]/25">
|
||||
#{index + 1}
|
||||
</span>
|
||||
<span className={getPriorityBadgeStyle(challenge.priority)}>
|
||||
{getPriorityConfig(challenge.priority).label}
|
||||
</span>
|
||||
</div>
|
||||
{challenge.deadline && (
|
||||
<span className="text-xs text-[var(--muted-foreground)]">
|
||||
{format(challenge.deadline, 'dd/MM', { locale: fr })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Titre */}
|
||||
<h4 className="font-semibold text-sm text-[var(--foreground)] mb-2 line-clamp-2">
|
||||
{challenge.title}
|
||||
</h4>
|
||||
|
||||
{/* Tags */}
|
||||
{challenge.tags && challenge.tags.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<TagDisplay
|
||||
tags={challenge.tags}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={2}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description si disponible */}
|
||||
{challenge.description && (
|
||||
<p className="text-xs text-[var(--muted-foreground)] line-clamp-2 leading-relaxed mb-2">
|
||||
{challenge.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Count de todos */}
|
||||
{challenge.todosCount > 0 && (
|
||||
<div className="flex items-center gap-1 text-xs text-[var(--muted-foreground)]">
|
||||
<span>📋</span>
|
||||
<span>{challenge.todosCount} todo{challenge.todosCount > 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
</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>
|
||||
@@ -354,60 +206,14 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{summary.keyAccomplishments.map((accomplishment, index) => (
|
||||
<div
|
||||
key={accomplishment.id}
|
||||
className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 transition-all duration-200 group"
|
||||
>
|
||||
{/* Barre colorée gauche */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-green-500 rounded-l-lg"></div>
|
||||
|
||||
{/* Header compact */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-5 h-5 rounded-full text-xs font-bold flex items-center justify-center text-[var(--success)] bg-[var(--success)]/15 border border-[var(--success)]/25">
|
||||
#{index + 1}
|
||||
</span>
|
||||
<span className={getPriorityBadgeStyle(accomplishment.impact)}>
|
||||
{getPriorityConfig(accomplishment.impact).label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-[var(--muted-foreground)]">
|
||||
{format(accomplishment.completedAt, 'dd/MM', { locale: fr })}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Titre */}
|
||||
<h4 className="font-semibold text-sm text-[var(--foreground)] mb-2 line-clamp-2">
|
||||
{accomplishment.title}
|
||||
</h4>
|
||||
|
||||
{/* Tags */}
|
||||
{accomplishment.tags && accomplishment.tags.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<TagDisplay
|
||||
tags={accomplishment.tags}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description si disponible */}
|
||||
{accomplishment.description && (
|
||||
<p className="text-xs text-[var(--muted-foreground)] line-clamp-3 leading-relaxed mb-2">
|
||||
{accomplishment.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Count de todos */}
|
||||
{accomplishment.todosCount > 0 && (
|
||||
<div className="flex items-center gap-1 text-xs text-[var(--muted-foreground)]">
|
||||
<span>📋</span>
|
||||
<span>{accomplishment.todosCount} todo{accomplishment.todosCount > 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<AchievementCard
|
||||
key={accomplishment.id}
|
||||
achievement={accomplishment}
|
||||
availableTags={availableTags as (Tag & { usage: number })[]}
|
||||
index={index}
|
||||
showDescription={true}
|
||||
maxTags={3}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -426,62 +232,14 @@ export default function ManagerWeeklySummary({ initialSummary }: ManagerWeeklySu
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{summary.upcomingChallenges.map((challenge, index) => (
|
||||
<div
|
||||
key={challenge.id}
|
||||
className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-3 transition-all duration-200 group"
|
||||
>
|
||||
{/* Barre colorée gauche */}
|
||||
<div className="absolute left-0 top-0 bottom-0 w-1 bg-orange-500 rounded-l-lg"></div>
|
||||
|
||||
{/* Header compact */}
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-5 h-5 rounded-full text-xs font-bold flex items-center justify-center text-[var(--accent)] bg-[var(--accent)]/15 border border-[var(--accent)]/25">
|
||||
#{index + 1}
|
||||
</span>
|
||||
<span className={getPriorityBadgeStyle(challenge.priority)}>
|
||||
{getPriorityConfig(challenge.priority).label}
|
||||
</span>
|
||||
</div>
|
||||
{challenge.deadline && (
|
||||
<span className="text-xs text-[var(--muted-foreground)]">
|
||||
{format(challenge.deadline, 'dd/MM', { locale: fr })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Titre */}
|
||||
<h4 className="font-semibold text-sm text-[var(--foreground)] mb-2 line-clamp-2">
|
||||
{challenge.title}
|
||||
</h4>
|
||||
|
||||
{/* Tags */}
|
||||
{challenge.tags && challenge.tags.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<TagDisplay
|
||||
tags={challenge.tags}
|
||||
availableTags={availableTags}
|
||||
size="sm"
|
||||
maxTags={3}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description si disponible */}
|
||||
{challenge.description && (
|
||||
<p className="text-xs text-[var(--muted-foreground)] line-clamp-3 leading-relaxed mb-2">
|
||||
{challenge.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Count de todos */}
|
||||
{challenge.todosCount > 0 && (
|
||||
<div className="flex items-center gap-1 text-xs text-[var(--muted-foreground)]">
|
||||
<span>📋</span>
|
||||
<span>{challenge.todosCount} todo{challenge.todosCount > 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ChallengeCard
|
||||
key={challenge.id}
|
||||
challenge={challenge}
|
||||
availableTags={availableTags as (Tag & { usage: number })[]}
|
||||
index={index}
|
||||
showDescription={true}
|
||||
maxTags={3}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
Reference in New Issue
Block a user