diff --git a/src/actions/analytics.ts b/src/actions/analytics.ts
deleted file mode 100644
index fd19127..0000000
--- a/src/actions/analytics.ts
+++ /dev/null
@@ -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'
- };
- }
-}
diff --git a/src/actions/deadline-analytics.ts b/src/actions/deadline-analytics.ts
deleted file mode 100644
index 8856c95..0000000
--- a/src/actions/deadline-analytics.ts
+++ /dev/null
@@ -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'
- };
- }
-}
diff --git a/src/app/daily/DailyPageClient.tsx b/src/app/daily/DailyPageClient.tsx
index d8d359a..8d520eb 100644
--- a/src/app/daily/DailyPageClient.tsx
+++ b/src/app/daily/DailyPageClient.tsx
@@ -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 */}
-
+
{/* Contenu principal */}
diff --git a/src/app/daily/page.tsx b/src/app/daily/page.tsx
index 1d2df8c..4e397ef 100644
--- a/src/app/daily/page.tsx
+++ b/src/app/daily/page.tsx
@@ -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) {
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 9df933f..66801d0 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -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}
/>
);
}
diff --git a/src/components/HomePageClient.tsx b/src/components/HomePageClient.tsx
index 5ee849d..1eef6d9 100644
--- a/src/components/HomePageClient.tsx
+++ b/src/components/HomePageClient.tsx
@@ -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() {
{/* Analytics et métriques */}
-
+
{/* Tâches récentes */}
@@ -49,14 +59,23 @@ function HomePageContent() {
);
}
-export function HomePageClient({ initialTasks, initialTags, initialStats }: HomePageClientProps) {
+export function HomePageClient({
+ initialTasks,
+ initialTags,
+ initialStats,
+ productivityMetrics,
+ deadlineMetrics
+}: HomePageClientProps) {
return (
-
+
);
}
diff --git a/src/components/daily/DeadlineReminder.tsx b/src/components/daily/DeadlineReminder.tsx
index d85e1f5..1aae135 100644
--- a/src/components/daily/DeadlineReminder.tsx
+++ b/src/components/daily/DeadlineReminder.tsx
@@ -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([]);
- const [error, setError] = useState(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 = { '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 = { '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() {
);
}
+
+// 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 '📋';
+ }
+}
diff --git a/src/components/dashboard/ProductivityAnalytics.tsx b/src/components/dashboard/ProductivityAnalytics.tsx
index 8560826..2fb5822 100644
--- a/src/components/dashboard/ProductivityAnalytics.tsx
+++ b/src/components/dashboard/ProductivityAnalytics.tsx
@@ -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(null);
- const [error, setError] = useState(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 (
-
- {Array.from({ length: 4 }).map((_, i) => (
-
-
-
-
- ))}
-
- );
- }
-
- if (error) {
- return (
-
-
-
⚠️
-
Erreur de chargement
-
{error}
-
-
- );
- }
-
- if (!metrics) {
- return null;
- }
+export function ProductivityAnalytics({ metrics, deadlineMetrics }: ProductivityAnalyticsProps) {
return (
{/* Section Échéances Critiques */}
-
+
{/* Titre de section Analytics */}
diff --git a/src/components/deadline/DeadlineOverview.tsx b/src/components/deadline/DeadlineOverview.tsx
index bb406cd..963f414 100644
--- a/src/components/deadline/DeadlineOverview.tsx
+++ b/src/components/deadline/DeadlineOverview.tsx
@@ -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
(null);
- const [error, setError] = useState(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 (
-
- {Array.from({ length: 3 }).map((_, i) => (
-
-
-
-
- ))}
-
- );
- }
-
- if (error) {
- return (
-
-
-
⚠️
-
Erreur de chargement des échéances
-
{error}
-
-
- );
- }
-
- if (!metrics) {
- return null;
- }
+export function DeadlineOverview({ metrics }: DeadlineOverviewProps) {
return (