refactor: remove unused analytics actions and integrate metrics directly

- Deleted `analytics.ts` and `deadline-analytics.ts` as they were no longer needed.
- Integrated `AnalyticsService` and `DeadlineAnalyticsService` directly into `HomePage` and `DailyPage`, streamlining data fetching.
- Updated components to utilize the new metrics structure, ensuring proper data flow and rendering.
This commit is contained in:
Julien Froidefond
2025-09-23 22:07:52 +02:00
parent 21e1f68921
commit 11ebe5cd00
9 changed files with 107 additions and 252 deletions

View File

@@ -8,15 +8,22 @@ import { DashboardStats } from '@/components/dashboard/DashboardStats';
import { QuickActions } from '@/components/dashboard/QuickActions';
import { RecentTasks } from '@/components/dashboard/RecentTasks';
import { ProductivityAnalytics } from '@/components/dashboard/ProductivityAnalytics';
import { ProductivityMetrics } from '@/services/analytics/analytics';
import { DeadlineMetrics } from '@/services/analytics/deadline-analytics';
interface HomePageClientProps {
initialTasks: Task[];
initialTags: (Tag & { usage: number })[];
initialStats: TaskStats;
productivityMetrics: ProductivityMetrics;
deadlineMetrics: DeadlineMetrics;
}
function HomePageContent() {
function HomePageContent({ productivityMetrics, deadlineMetrics }: {
productivityMetrics: ProductivityMetrics;
deadlineMetrics: DeadlineMetrics;
}) {
const { stats, syncing, createTask, tasks } = useTasksContext();
// Handler pour la création de tâche
@@ -40,7 +47,10 @@ function HomePageContent() {
<QuickActions onCreateTask={handleCreateTask} />
{/* Analytics et métriques */}
<ProductivityAnalytics />
<ProductivityAnalytics
metrics={productivityMetrics}
deadlineMetrics={deadlineMetrics}
/>
{/* Tâches récentes */}
<RecentTasks tasks={tasks} />
@@ -49,14 +59,23 @@ function HomePageContent() {
);
}
export function HomePageClient({ initialTasks, initialTags, initialStats }: HomePageClientProps) {
export function HomePageClient({
initialTasks,
initialTags,
initialStats,
productivityMetrics,
deadlineMetrics
}: HomePageClientProps) {
return (
<TasksProvider
initialTasks={initialTasks}
initialTags={initialTags}
initialStats={initialStats}
>
<HomePageContent />
<HomePageContent
productivityMetrics={productivityMetrics}
deadlineMetrics={deadlineMetrics}
/>
</TasksProvider>
);
}

View File

@@ -1,80 +1,38 @@
'use client';
import { useState, useEffect, useTransition } from 'react';
import { DeadlineTask } from '@/services/analytics/deadline-analytics';
import { getDeadlineMetrics } from '@/actions/deadline-analytics';
import { DeadlineTask, DeadlineMetrics } from '@/services/analytics/deadline-analytics';
import { Card } from '@/components/ui/Card';
export function DeadlineReminder() {
const [urgentTasks, setUrgentTasks] = useState<DeadlineTask[]>([]);
const [error, setError] = useState<string | null>(null);
const [isPending, startTransition] = useTransition();
useEffect(() => {
const loadUrgentTasks = () => {
startTransition(async () => {
try {
setError(null);
const response = await getDeadlineMetrics();
if (response.success && response.data) {
// Combiner toutes les tâches urgentes et trier par urgence
const combinedTasks = [
...response.data.overdue,
...response.data.critical,
...response.data.warning
].sort((a, b) => {
// En retard d'abord, puis critique, puis attention
const urgencyOrder: Record<string, number> = { 'overdue': 0, 'critical': 1, 'warning': 2 };
if (urgencyOrder[a.urgencyLevel] !== urgencyOrder[b.urgencyLevel]) {
return urgencyOrder[a.urgencyLevel] - urgencyOrder[b.urgencyLevel];
}
// Si même urgence, trier par jours restants
return a.daysRemaining - b.daysRemaining;
});
setUrgentTasks(combinedTasks);
} else {
setError(response.error || 'Erreur lors du chargement des échéances');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur lors du chargement des échéances');
console.error('Erreur échéances:', err);
}
});
};
loadUrgentTasks();
}, []);
const getUrgencyIcon = (task: DeadlineTask) => {
if (task.urgencyLevel === 'overdue') return '🔴';
if (task.urgencyLevel === 'critical') return '🟠';
return '🟡';
};
const getUrgencyText = (task: DeadlineTask) => {
if (task.urgencyLevel === 'overdue') {
return task.daysRemaining === -1 ? 'En retard de 1 jour' : `En retard de ${Math.abs(task.daysRemaining)} jours`;
} else if (task.urgencyLevel === 'critical') {
return task.daysRemaining === 0 ? 'Échéance aujourd\'hui' :
task.daysRemaining === 1 ? 'Échéance demain' :
`Dans ${task.daysRemaining} jours`;
} else {
return `Dans ${task.daysRemaining} jours`;
// Fonction utilitaire pour combiner et trier les tâches urgentes
function combineAndSortUrgentTasks(metrics: DeadlineMetrics): DeadlineTask[] {
return [
...metrics.overdue,
...metrics.critical,
...metrics.warning
].sort((a, b) => {
// En retard d'abord, puis critique, puis attention
const urgencyOrder: Record<string, number> = { 'overdue': 0, 'critical': 1, 'warning': 2 };
if (urgencyOrder[a.urgencyLevel] !== urgencyOrder[b.urgencyLevel]) {
return urgencyOrder[a.urgencyLevel] - urgencyOrder[b.urgencyLevel];
}
};
// Si même urgence, trier par jours restants
return a.daysRemaining - b.daysRemaining;
});
}
const getSourceIcon = (source: string) => {
switch (source) {
case 'jira': return '🔗';
case 'reminder': return '📱';
default: return '📋';
}
};
interface DeadlineReminderProps {
deadlineMetrics?: DeadlineMetrics | null;
}
// Ne rien afficher si pas de tâches urgentes ou si en cours de chargement
if (isPending || error || urgentTasks.length === 0) {
export function DeadlineReminder({ deadlineMetrics }: DeadlineReminderProps) {
// Ne rien afficher si pas de données ou pas de tâches urgentes
if (!deadlineMetrics) {
return null;
}
const urgentTasks = combineAndSortUrgentTasks(deadlineMetrics);
if (urgentTasks.length === 0) {
return null;
}
@@ -118,3 +76,30 @@ export function DeadlineReminder() {
</Card>
);
}
// Fonctions utilitaires déplacées en dehors du composant
function getUrgencyIcon(task: DeadlineTask): string {
if (task.urgencyLevel === 'overdue') return '🔴';
if (task.urgencyLevel === 'critical') return '🟠';
return '🟡';
}
function getUrgencyText(task: DeadlineTask): string {
if (task.urgencyLevel === 'overdue') {
return task.daysRemaining === -1 ? 'En retard de 1 jour' : `En retard de ${Math.abs(task.daysRemaining)} jours`;
} else if (task.urgencyLevel === 'critical') {
return task.daysRemaining === 0 ? 'Échéance aujourd\'hui' :
task.daysRemaining === 1 ? 'Échéance demain' :
`Dans ${task.daysRemaining} jours`;
} else {
return `Dans ${task.daysRemaining} jours`;
}
}
function getSourceIcon(source: string): string {
switch (source) {
case 'jira': return '🔗';
case 'reminder': return '📱';
default: return '📋';
}
}

View File

@@ -1,8 +1,5 @@
'use client';
import { useState, useEffect, useTransition } from 'react';
import { ProductivityMetrics } from '@/services/analytics/analytics';
import { getProductivityMetrics } from '@/actions/analytics';
import { DeadlineMetrics } from '@/services/analytics/deadline-analytics';
import { CompletionTrendChart } from '@/components/charts/CompletionTrendChart';
import { VelocityChart } from '@/components/charts/VelocityChart';
import { PriorityDistributionChart } from '@/components/charts/PriorityDistributionChart';
@@ -10,66 +7,17 @@ import { WeeklyStatsCard } from '@/components/charts/WeeklyStatsCard';
import { Card } from '@/components/ui/Card';
import { DeadlineOverview } from '@/components/deadline/DeadlineOverview';
export function ProductivityAnalytics() {
const [metrics, setMetrics] = useState<ProductivityMetrics | null>(null);
const [error, setError] = useState<string | null>(null);
const [isPending, startTransition] = useTransition();
interface ProductivityAnalyticsProps {
metrics: ProductivityMetrics;
deadlineMetrics: DeadlineMetrics;
}
useEffect(() => {
const loadMetrics = () => {
startTransition(async () => {
try {
setError(null);
const response = await getProductivityMetrics();
if (response.success && response.data) {
setMetrics(response.data);
} else {
setError(response.error || 'Erreur lors du chargement des métriques');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur lors du chargement des métriques');
console.error('Erreur analytics:', err);
}
});
};
loadMetrics();
}, []);
if (isPending) {
return (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
{Array.from({ length: 4 }).map((_, i) => (
<Card key={i} className="p-6 animate-pulse">
<div className="h-4 bg-[var(--border)] rounded mb-4 w-1/3"></div>
<div className="h-64 bg-[var(--border)] rounded"></div>
</Card>
))}
</div>
);
}
if (error) {
return (
<Card className="p-6 mb-8 mt-8">
<div className="text-center">
<div className="text-red-500 text-4xl mb-2"></div>
<h3 className="text-lg font-semibold mb-2">Erreur de chargement</h3>
<p className="text-[var(--muted-foreground)] text-sm">{error}</p>
</div>
</Card>
);
}
if (!metrics) {
return null;
}
export function ProductivityAnalytics({ metrics, deadlineMetrics }: ProductivityAnalyticsProps) {
return (
<div className="space-y-8">
{/* Section Échéances Critiques */}
<DeadlineOverview />
<DeadlineOverview metrics={deadlineMetrics} />
{/* Titre de section Analytics */}
<div className="flex items-center justify-between">

View File

@@ -1,68 +1,13 @@
'use client';
import { useState, useEffect, useTransition } from 'react';
import { DeadlineMetrics } from '@/services/analytics/deadline-analytics';
import { getDeadlineMetrics } from '@/actions/deadline-analytics';
import { Card } from '@/components/ui/Card';
import { DeadlineRiskCard } from './DeadlineRiskCard';
import { CriticalDeadlinesCard } from './CriticalDeadlinesCard';
import { DeadlineSummaryCard } from './DeadlineSummaryCard';
export function DeadlineOverview() {
const [metrics, setMetrics] = useState<DeadlineMetrics | null>(null);
const [error, setError] = useState<string | null>(null);
const [isPending, startTransition] = useTransition();
interface DeadlineOverviewProps {
metrics: DeadlineMetrics;
}
useEffect(() => {
const loadMetrics = () => {
startTransition(async () => {
try {
setError(null);
const response = await getDeadlineMetrics();
if (response.success && response.data) {
setMetrics(response.data);
} else {
setError(response.error || 'Erreur lors du chargement des échéances');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur lors du chargement des échéances');
console.error('Erreur échéances:', err);
}
});
};
loadMetrics();
}, []);
if (isPending) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
{Array.from({ length: 3 }).map((_, i) => (
<Card key={i} className="p-6 animate-pulse">
<div className="h-4 bg-[var(--border)] rounded mb-4 w-1/2"></div>
<div className="h-20 bg-[var(--border)] rounded"></div>
</Card>
))}
</div>
);
}
if (error) {
return (
<Card className="p-6 mb-8">
<div className="text-center">
<div className="text-red-500 text-4xl mb-2"></div>
<h3 className="text-lg font-semibold mb-2">Erreur de chargement des échéances</h3>
<p className="text-[var(--muted-foreground)] text-sm">{error}</p>
</div>
</Card>
);
}
if (!metrics) {
return null;
}
export function DeadlineOverview({ metrics }: DeadlineOverviewProps) {
return (
<div className="space-y-6">