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

@@ -1,25 +0,0 @@
'use server';
import { AnalyticsService, ProductivityMetrics, TimeRange } from '@/services/analytics/analytics';
export async function getProductivityMetrics(timeRange?: TimeRange): Promise<{
success: boolean;
data?: ProductivityMetrics;
error?: string;
}> {
try {
const metrics = await AnalyticsService.getProductivityMetrics(timeRange);
return {
success: true,
data: metrics
};
} catch (error) {
console.error('Erreur lors de la récupération des métriques:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Erreur inconnue'
};
}
}

View File

@@ -1,29 +0,0 @@
'use server';
import { DeadlineAnalyticsService } from '@/services/analytics/deadline-analytics';
export async function getDeadlineMetrics() {
try {
const metrics = await DeadlineAnalyticsService.getDeadlineMetrics();
return { success: true, data: metrics };
} catch (error) {
console.error('Erreur lors de la récupération des métriques d\'échéance:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Erreur lors de la récupération des métriques d\'échéance'
};
}
}
export async function getCriticalDeadlines() {
try {
const tasks = await DeadlineAnalyticsService.getCriticalDeadlines();
return { success: true, data: tasks };
} catch (error) {
console.error('Erreur lors de la récupération des échéances critiques:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Erreur lors de la récupération des échéances critiques'
};
}
}

View File

@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import React from 'react';
import { useDaily } from '@/hooks/useDaily';
import { DailyView, DailyCheckboxType } from '@/lib/types';
import { DeadlineMetrics } from '@/services/analytics/deadline-analytics';
import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card';
import { DailyCalendar } from '@/components/daily/DailyCalendar';
@@ -18,12 +19,14 @@ interface DailyPageClientProps {
initialDailyView?: DailyView;
initialDailyDates?: string[];
initialDate?: Date;
initialDeadlineMetrics?: DeadlineMetrics | null;
}
export function DailyPageClient({
initialDailyView,
initialDailyDates = [],
initialDate
initialDate,
initialDeadlineMetrics
}: DailyPageClientProps = {}) {
const {
dailyView,
@@ -214,7 +217,7 @@ export function DailyPageClient({
{/* Rappel des échéances urgentes - Desktop uniquement */}
<div className="hidden sm:block container mx-auto px-4 pt-4 pb-2">
<DeadlineReminder />
<DeadlineReminder deadlineMetrics={initialDeadlineMetrics} />
</div>
{/* Contenu principal */}

View File

@@ -1,6 +1,7 @@
import { Metadata } from 'next';
import { DailyPageClient } from './DailyPageClient';
import { dailyService } from '@/services/task-management/daily';
import { DeadlineAnalyticsService } from '@/services/analytics/deadline-analytics';
import { getToday } from '@/lib/date-utils';
// Force dynamic rendering (no static generation)
@@ -16,9 +17,10 @@ export default async function DailyPage() {
const today = getToday();
try {
const [dailyView, dailyDates] = await Promise.all([
const [dailyView, dailyDates, deadlineMetrics] = await Promise.all([
dailyService.getDailyView(today),
dailyService.getDailyDates()
dailyService.getDailyDates(),
DeadlineAnalyticsService.getDeadlineMetrics().catch(() => null) // Graceful fallback
]);
return (
@@ -26,6 +28,7 @@ export default async function DailyPage() {
initialDailyView={dailyView}
initialDailyDates={dailyDates}
initialDate={today}
initialDeadlineMetrics={deadlineMetrics}
/>
);
} catch (error) {

View File

@@ -1,5 +1,7 @@
import { tasksService } from '@/services/task-management/tasks';
import { tagsService } from '@/services/task-management/tags';
import { AnalyticsService } from '@/services/analytics/analytics';
import { DeadlineAnalyticsService } from '@/services/analytics/deadline-analytics';
import { HomePageClient } from '@/components/HomePageClient';
// Force dynamic rendering (no static generation)
@@ -7,10 +9,12 @@ export const dynamic = 'force-dynamic';
export default async function HomePage() {
// SSR - Récupération des données côté serveur
const [initialTasks, initialTags, initialStats] = await Promise.all([
const [initialTasks, initialTags, initialStats, productivityMetrics, deadlineMetrics] = await Promise.all([
tasksService.getTasks(),
tagsService.getTags(),
tasksService.getTaskStats()
tasksService.getTaskStats(),
AnalyticsService.getProductivityMetrics(),
DeadlineAnalyticsService.getDeadlineMetrics()
]);
return (
@@ -18,6 +22,8 @@ export default async function HomePage() {
initialTasks={initialTasks}
initialTags={initialTags}
initialStats={initialStats}
productivityMetrics={productivityMetrics}
deadlineMetrics={deadlineMetrics}
/>
);
}

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,28 +1,14 @@
'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
// 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 };
@@ -32,49 +18,21 @@ export function DeadlineReminder() {
// 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');
interface DeadlineReminderProps {
deadlineMetrics?: DeadlineMetrics | null;
}
export function DeadlineReminder({ deadlineMetrics }: DeadlineReminderProps) {
// Ne rien afficher si pas de données ou pas de tâches urgentes
if (!deadlineMetrics) {
return null;
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur lors du chargement des échéances');
console.error('Erreur échéances:', err);
}
});
};
loadUrgentTasks();
}, []);
const urgentTasks = combineAndSortUrgentTasks(deadlineMetrics);
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`;
}
};
const getSourceIcon = (source: string) => {
switch (source) {
case 'jira': return '🔗';
case 'reminder': return '📱';
default: return '📋';
}
};
// Ne rien afficher si pas de tâches urgentes ou si en cours de chargement
if (isPending || error || urgentTasks.length === 0) {
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">