import { prisma } from './database'; import { Task, TaskStatus, TaskPriority, TaskSource } from '@/lib/types'; export interface DailyItem { id: string; text: string; isChecked: boolean; createdAt: Date; updatedAt: Date; date: Date; } export interface WeeklyStats { totalCheckboxes: number; completedCheckboxes: number; totalTasks: number; completedTasks: number; checkboxCompletionRate: number; taskCompletionRate: number; mostProductiveDay: string; dailyBreakdown: Array<{ date: string; dayName: string; checkboxes: number; completedCheckboxes: number; tasks: number; completedTasks: number; }>; } export interface WeeklyActivity { id: string; type: 'checkbox' | 'task'; title: string; completed: boolean; completedAt?: Date; createdAt: Date; date: string; dayName: string; } export interface WeeklySummary { stats: WeeklyStats; activities: WeeklyActivity[]; period: { start: Date; end: Date; }; } export class WeeklySummaryService { /** * Récupère le résumé complet de la semaine écoulée */ static async getWeeklySummary(): Promise { const now = new Date(); const startOfWeek = new Date(now); startOfWeek.setDate(now.getDate() - 7); startOfWeek.setHours(0, 0, 0, 0); const endOfWeek = new Date(now); endOfWeek.setHours(23, 59, 59, 999); console.log(`📊 Génération du résumé hebdomadaire du ${startOfWeek.toLocaleDateString()} au ${endOfWeek.toLocaleDateString()}`); const [checkboxes, tasks] = await Promise.all([ this.getWeeklyCheckboxes(startOfWeek, endOfWeek), this.getWeeklyTasks(startOfWeek, endOfWeek) ]); const stats = this.calculateStats(checkboxes, tasks, startOfWeek, endOfWeek); const activities = this.mergeActivities(checkboxes, tasks); return { stats, activities, period: { start: startOfWeek, end: endOfWeek } }; } /** * Récupère les checkboxes des 7 derniers jours */ private static async getWeeklyCheckboxes(startDate: Date, endDate: Date): Promise { const items = await prisma.dailyCheckbox.findMany({ where: { date: { gte: startDate, lte: endDate } }, orderBy: [ { date: 'desc' }, { createdAt: 'desc' } ] }); return items.map(item => ({ id: item.id, text: item.text, isChecked: item.isChecked, createdAt: item.createdAt, updatedAt: item.updatedAt, date: item.date })); } /** * Récupère les tâches des 7 derniers jours (créées ou modifiées) */ private static async getWeeklyTasks(startDate: Date, endDate: Date): Promise { const tasks = await prisma.task.findMany({ where: { OR: [ { createdAt: { gte: startDate, lte: endDate } }, { updatedAt: { gte: startDate, lte: endDate } } ] }, orderBy: { updatedAt: 'desc' } }); return tasks.map(task => ({ id: task.id, title: task.title, description: task.description || '', status: task.status as TaskStatus, priority: task.priority as TaskPriority, source: task.source as TaskSource, sourceId: task.sourceId || undefined, createdAt: task.createdAt, updatedAt: task.updatedAt, dueDate: task.dueDate || undefined, completedAt: task.completedAt || undefined, jiraProject: task.jiraProject || undefined, jiraKey: task.jiraKey || undefined, jiraType: task.jiraType || undefined, assignee: task.assignee || undefined, tags: [] // Les tags sont dans une relation séparée, on les laisse vides pour l'instant })); } /** * Calcule les statistiques de la semaine */ private static calculateStats( checkboxes: DailyItem[], tasks: Task[], startDate: Date, endDate: Date ): WeeklyStats { const completedCheckboxes = checkboxes.filter(c => c.isChecked); const completedTasks = tasks.filter(t => t.status === 'done'); // Créer un breakdown par jour const dailyBreakdown = []; const current = new Date(startDate); while (current <= endDate) { const dayCheckboxes = checkboxes.filter(c => c.date.toISOString().split('T')[0] === current.toISOString().split('T')[0] ); const dayCompletedCheckboxes = dayCheckboxes.filter(c => c.isChecked); // Pour les tâches, on compte celles modifiées ce jour-là const dayTasks = tasks.filter(t => t.updatedAt.toISOString().split('T')[0] === current.toISOString().split('T')[0] || t.createdAt.toISOString().split('T')[0] === current.toISOString().split('T')[0] ); const dayCompletedTasks = dayTasks.filter(t => t.status === 'done'); dailyBreakdown.push({ date: current.toISOString().split('T')[0], dayName: current.toLocaleDateString('fr-FR', { weekday: 'long' }), checkboxes: dayCheckboxes.length, completedCheckboxes: dayCompletedCheckboxes.length, tasks: dayTasks.length, completedTasks: dayCompletedTasks.length }); current.setDate(current.getDate() + 1); } // Trouver le jour le plus productif const mostProductiveDay = dailyBreakdown.reduce((max, day) => { const dayScore = day.completedCheckboxes + day.completedTasks; const maxScore = max.completedCheckboxes + max.completedTasks; return dayScore > maxScore ? day : max; }, dailyBreakdown[0]); return { totalCheckboxes: checkboxes.length, completedCheckboxes: completedCheckboxes.length, totalTasks: tasks.length, completedTasks: completedTasks.length, checkboxCompletionRate: checkboxes.length > 0 ? (completedCheckboxes.length / checkboxes.length) * 100 : 0, taskCompletionRate: tasks.length > 0 ? (completedTasks.length / tasks.length) * 100 : 0, mostProductiveDay: mostProductiveDay.dayName, dailyBreakdown }; } /** * Fusionne les activités (checkboxes + tâches) en une timeline */ private static mergeActivities(checkboxes: DailyItem[], tasks: Task[]): WeeklyActivity[] { const activities: WeeklyActivity[] = []; // Ajouter les checkboxes checkboxes.forEach(checkbox => { activities.push({ id: `checkbox-${checkbox.id}`, type: 'checkbox', title: checkbox.text, completed: checkbox.isChecked, completedAt: checkbox.isChecked ? checkbox.updatedAt : undefined, createdAt: checkbox.createdAt, date: checkbox.date.toISOString().split('T')[0], dayName: checkbox.date.toLocaleDateString('fr-FR', { weekday: 'long' }) }); }); // Ajouter les tâches tasks.forEach(task => { const date = task.updatedAt.toISOString().split('T')[0]; const dateObj = new Date(date + 'T00:00:00'); activities.push({ id: `task-${task.id}`, type: 'task', title: task.title, completed: task.status === 'done', completedAt: task.status === 'done' ? task.updatedAt : undefined, createdAt: task.createdAt, date: date, dayName: dateObj.toLocaleDateString('fr-FR', { weekday: 'long' }) }); }); // Trier par date (plus récent en premier) return activities.sort((a, b) => { const dateA = a.completedAt || a.createdAt; const dateB = b.completedAt || b.createdAt; return dateB.getTime() - dateA.getTime(); }); } }