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:
@@ -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'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 '📋';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user