191 lines
8.2 KiB
TypeScript
191 lines
8.2 KiB
TypeScript
'use client';
|
|
|
|
import { DailyMetrics } from '@/services/metrics';
|
|
|
|
interface ProductivityInsightsProps {
|
|
data: DailyMetrics[];
|
|
className?: string;
|
|
}
|
|
|
|
export function ProductivityInsights({ data, className }: ProductivityInsightsProps) {
|
|
// Calculer les insights
|
|
const totalCompleted = data.reduce((sum, day) => sum + day.completed, 0);
|
|
const totalCreated = data.reduce((sum, day) => sum + day.newTasks, 0);
|
|
// const averageCompletion = data.reduce((sum, day) => sum + day.completionRate, 0) / data.length;
|
|
|
|
// Trouver le jour le plus productif
|
|
const mostProductiveDay = data.reduce((best, day) =>
|
|
day.completed > best.completed ? day : best
|
|
);
|
|
|
|
// Trouver le jour avec le plus de nouvelles tâches
|
|
const mostCreativeDay = data.reduce((best, day) =>
|
|
day.newTasks > best.newTasks ? day : best
|
|
);
|
|
|
|
// Analyser la tendance
|
|
const firstHalf = data.slice(0, Math.ceil(data.length / 2));
|
|
const secondHalf = data.slice(Math.ceil(data.length / 2));
|
|
|
|
const firstHalfAvg = firstHalf.reduce((sum, day) => sum + day.completed, 0) / firstHalf.length;
|
|
const secondHalfAvg = secondHalf.reduce((sum, day) => sum + day.completed, 0) / secondHalf.length;
|
|
|
|
const trend = secondHalfAvg > firstHalfAvg ? 'up' : secondHalfAvg < firstHalfAvg ? 'down' : 'stable';
|
|
|
|
// Calculer la consistance (écart-type faible = plus consistant)
|
|
const avgCompleted = totalCompleted / data.length;
|
|
const variance = data.reduce((sum, day) => {
|
|
const diff = day.completed - avgCompleted;
|
|
return sum + diff * diff;
|
|
}, 0) / data.length;
|
|
const standardDeviation = Math.sqrt(variance);
|
|
const consistencyScore = Math.max(0, 100 - (standardDeviation * 10)); // Score sur 100
|
|
|
|
// Ratio création/completion
|
|
const creationRatio = totalCreated > 0 ? (totalCompleted / totalCreated) * 100 : 0;
|
|
|
|
const getTrendIcon = () => {
|
|
switch (trend) {
|
|
case 'up': return { icon: '📈', color: 'text-green-600', label: 'En amélioration' };
|
|
case 'down': return { icon: '📉', color: 'text-red-600', label: 'En baisse' };
|
|
default: return { icon: '➡️', color: 'text-blue-600', label: 'Stable' };
|
|
}
|
|
};
|
|
|
|
const getConsistencyLevel = () => {
|
|
if (consistencyScore >= 80) return { label: 'Très régulier', color: 'text-green-600', icon: '🎯' };
|
|
if (consistencyScore >= 60) return { label: 'Assez régulier', color: 'text-blue-600', icon: '📊' };
|
|
if (consistencyScore >= 40) return { label: 'Variable', color: 'text-yellow-600', icon: '📊' };
|
|
return { label: 'Très variable', color: 'text-red-600', icon: '📊' };
|
|
};
|
|
|
|
const getRatioStatus = () => {
|
|
if (creationRatio >= 100) return { label: 'Équilibré+', color: 'text-green-600', icon: '⚖️' };
|
|
if (creationRatio >= 80) return { label: 'Bien équilibré', color: 'text-blue-600', icon: '⚖️' };
|
|
if (creationRatio >= 60) return { label: 'Légèrement en retard', color: 'text-yellow-600', icon: '⚖️' };
|
|
return { label: 'Accumulation', color: 'text-red-600', icon: '⚖️' };
|
|
};
|
|
|
|
const trendInfo = getTrendIcon();
|
|
const consistencyInfo = getConsistencyLevel();
|
|
const ratioInfo = getRatioStatus();
|
|
|
|
return (
|
|
<div className={className}>
|
|
<div className="space-y-4">
|
|
{/* Insights principaux */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{/* Jour le plus productif */}
|
|
<div className="p-4 bg-green-50 dark:bg-green-950/20 rounded-lg">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h4 className="font-medium text-green-900 dark:text-green-100">
|
|
🏆 Jour champion
|
|
</h4>
|
|
<span className="text-2xl font-bold text-green-600">
|
|
{mostProductiveDay.completed}
|
|
</span>
|
|
</div>
|
|
<p className="text-sm text-green-800 dark:text-green-200">
|
|
{mostProductiveDay.dayName} - {mostProductiveDay.completed} tâches terminées
|
|
</p>
|
|
<p className="text-xs text-green-600 mt-1">
|
|
Taux: {mostProductiveDay.completionRate.toFixed(1)}%
|
|
</p>
|
|
</div>
|
|
|
|
{/* Jour le plus créatif */}
|
|
<div className="p-4 bg-blue-50 dark:bg-blue-950/20 rounded-lg">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h4 className="font-medium text-blue-900 dark:text-blue-100">
|
|
💡 Jour créatif
|
|
</h4>
|
|
<span className="text-2xl font-bold text-blue-600">
|
|
{mostCreativeDay.newTasks}
|
|
</span>
|
|
</div>
|
|
<p className="text-sm text-blue-800 dark:text-blue-200">
|
|
{mostCreativeDay.dayName} - {mostCreativeDay.newTasks} nouvelles tâches
|
|
</p>
|
|
<p className="text-xs text-blue-600 mt-1">
|
|
{mostCreativeDay.dayName === mostProductiveDay.dayName ?
|
|
'Également jour le plus productif!' :
|
|
'Journée de planification'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Analyses comportementales */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
{/* Tendance */}
|
|
<div className="p-4 bg-[var(--card)] border border-[var(--border)] rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="text-2xl">{trendInfo.icon}</span>
|
|
<h4 className="font-medium text-[var(--foreground)]">Tendance</h4>
|
|
</div>
|
|
<p className={`text-sm font-medium ${trendInfo.color}`}>
|
|
{trendInfo.label}
|
|
</p>
|
|
<p className="text-xs text-[var(--muted-foreground)] mt-1">
|
|
{secondHalfAvg > firstHalfAvg ?
|
|
`+${(((secondHalfAvg - firstHalfAvg) / firstHalfAvg) * 100).toFixed(1)}%` :
|
|
`${(((secondHalfAvg - firstHalfAvg) / firstHalfAvg) * 100).toFixed(1)}%`}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Consistance */}
|
|
<div className="p-4 bg-[var(--card)] border border-[var(--border)] rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="text-2xl">{consistencyInfo.icon}</span>
|
|
<h4 className="font-medium text-[var(--foreground)]">Régularité</h4>
|
|
</div>
|
|
<p className={`text-sm font-medium ${consistencyInfo.color}`}>
|
|
{consistencyInfo.label}
|
|
</p>
|
|
<p className="text-xs text-[var(--muted-foreground)] mt-1">
|
|
Score: {consistencyScore.toFixed(0)}/100
|
|
</p>
|
|
</div>
|
|
|
|
{/* Ratio Création/Completion */}
|
|
<div className="p-4 bg-[var(--card)] border border-[var(--border)] rounded-lg">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className="text-2xl">{ratioInfo.icon}</span>
|
|
<h4 className="font-medium text-[var(--foreground)]">Équilibre</h4>
|
|
</div>
|
|
<p className={`text-sm font-medium ${ratioInfo.color}`}>
|
|
{ratioInfo.label}
|
|
</p>
|
|
<p className="text-xs text-[var(--muted-foreground)] mt-1">
|
|
{creationRatio.toFixed(0)}% de completion
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Recommandations */}
|
|
<div className="p-4 bg-yellow-50 dark:bg-yellow-950/20 rounded-lg">
|
|
<h4 className="font-medium text-yellow-900 dark:text-yellow-100 mb-2 flex items-center gap-2">
|
|
💡 Recommandations
|
|
</h4>
|
|
<div className="space-y-1 text-sm text-yellow-800 dark:text-yellow-200">
|
|
{trend === 'down' && (
|
|
<p>• Essayez de retrouver votre rythme du début de semaine</p>
|
|
)}
|
|
{consistencyScore < 60 && (
|
|
<p>• Essayez de maintenir un rythme plus régulier</p>
|
|
)}
|
|
{creationRatio < 80 && (
|
|
<p>• Concentrez-vous plus sur terminer les tâches existantes</p>
|
|
)}
|
|
{creationRatio > 120 && (
|
|
<p>• Excellent rythme! Peut-être ralentir la création de nouvelles tâches</p>
|
|
)}
|
|
{mostProductiveDay.dayName === mostCreativeDay.dayName && (
|
|
<p>• Excellente synergie création/exécution le {mostProductiveDay.dayName}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|