feat: complete Phase 4 of service refactoring
- Marked tasks in `TODO.md` as completed for moving task-related files to the `task-management` directory and correcting imports across the codebase. - Updated imports in `seed-data.ts`, `seed-tags.ts`, API routes, and various components to reflect the new structure. - Removed obsolete `daily.ts`, `tags.ts`, and `tasks.ts` files to streamline the codebase. - Added new tasks in `TODO.md` for future cleaning and organization of service imports.
This commit is contained in:
378
src/services/task-management/daily.ts
Normal file
378
src/services/task-management/daily.ts
Normal file
@@ -0,0 +1,378 @@
|
||||
import { prisma } from '../core/database';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData, BusinessError, DailyCheckboxType, TaskStatus, TaskPriority, TaskSource } from '@/lib/types';
|
||||
import { getPreviousWorkday, normalizeDate, formatDateForAPI, getToday, getYesterday } from '@/lib/date-utils';
|
||||
|
||||
/**
|
||||
* Service pour la gestion des checkboxes daily
|
||||
*/
|
||||
export class DailyService {
|
||||
|
||||
/**
|
||||
* Récupère la vue daily pour une date donnée (checkboxes d'hier et d'aujourd'hui)
|
||||
*/
|
||||
async getDailyView(date: Date): Promise<DailyView> {
|
||||
// Normaliser la date (début de journée)
|
||||
const today = normalizeDate(date);
|
||||
|
||||
// Utiliser la logique de jour de travail précédent au lieu de jour-1
|
||||
const yesterday = getPreviousWorkday(today);
|
||||
|
||||
// Récupérer les checkboxes des deux jours
|
||||
const [yesterdayCheckboxes, todayCheckboxes] = await Promise.all([
|
||||
this.getCheckboxesByDate(yesterday),
|
||||
this.getCheckboxesByDate(today)
|
||||
]);
|
||||
|
||||
return {
|
||||
date: today,
|
||||
yesterday: yesterdayCheckboxes,
|
||||
today: todayCheckboxes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les checkboxes d'une date donnée
|
||||
*/
|
||||
async getCheckboxesByDate(date: Date): Promise<DailyCheckbox[]> {
|
||||
// Normaliser la date (début de journée)
|
||||
const normalizedDate = normalizeDate(date);
|
||||
|
||||
const checkboxes = await prisma.dailyCheckbox.findMany({
|
||||
where: { date: normalizedDate },
|
||||
include: { task: true },
|
||||
orderBy: { order: 'asc' }
|
||||
});
|
||||
|
||||
return checkboxes.map(this.mapPrismaCheckbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une checkbox à une date donnée
|
||||
*/
|
||||
async addCheckbox(data: CreateDailyCheckboxData): Promise<DailyCheckbox> {
|
||||
// Normaliser la date
|
||||
const normalizedDate = normalizeDate(data.date);
|
||||
|
||||
// Calculer l'ordre suivant pour cette date
|
||||
const maxOrder = await prisma.dailyCheckbox.aggregate({
|
||||
where: { date: normalizedDate },
|
||||
_max: { order: true }
|
||||
});
|
||||
|
||||
const order = data.order ?? ((maxOrder._max.order ?? -1) + 1);
|
||||
|
||||
const checkbox = await prisma.dailyCheckbox.create({
|
||||
data: {
|
||||
date: normalizedDate,
|
||||
text: data.text.trim(),
|
||||
type: data.type ?? 'task',
|
||||
taskId: data.taskId,
|
||||
order,
|
||||
isChecked: data.isChecked ?? false
|
||||
},
|
||||
include: { task: true }
|
||||
});
|
||||
|
||||
return this.mapPrismaCheckbox(checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour une checkbox
|
||||
*/
|
||||
async updateCheckbox(checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox> {
|
||||
const updateData: Prisma.DailyCheckboxUpdateInput = {};
|
||||
|
||||
if (data.text !== undefined) updateData.text = data.text.trim();
|
||||
if (data.isChecked !== undefined) updateData.isChecked = data.isChecked;
|
||||
if (data.type !== undefined) updateData.type = data.type;
|
||||
if (data.taskId !== undefined) {
|
||||
if (data.taskId === null) {
|
||||
updateData.task = { disconnect: true };
|
||||
} else {
|
||||
updateData.task = { connect: { id: data.taskId } };
|
||||
}
|
||||
}
|
||||
if (data.order !== undefined) updateData.order = data.order;
|
||||
if (data.date !== undefined) updateData.date = normalizeDate(data.date);
|
||||
|
||||
const checkbox = await prisma.dailyCheckbox.update({
|
||||
where: { id: checkboxId },
|
||||
data: updateData,
|
||||
include: { task: true }
|
||||
});
|
||||
|
||||
return this.mapPrismaCheckbox(checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une checkbox
|
||||
*/
|
||||
async deleteCheckbox(checkboxId: string): Promise<void> {
|
||||
const checkbox = await prisma.dailyCheckbox.findUnique({
|
||||
where: { id: checkboxId }
|
||||
});
|
||||
|
||||
if (!checkbox) {
|
||||
throw new BusinessError('Checkbox non trouvée');
|
||||
}
|
||||
|
||||
await prisma.dailyCheckbox.delete({
|
||||
where: { id: checkboxId }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Réordonne les checkboxes d'une date donnée
|
||||
*/
|
||||
async reorderCheckboxes(date: Date, checkboxIds: string[]): Promise<void> {
|
||||
await prisma.$transaction(async (prisma) => {
|
||||
for (let i = 0; i < checkboxIds.length; i++) {
|
||||
await prisma.dailyCheckbox.update({
|
||||
where: { id: checkboxIds[i] },
|
||||
data: { order: i }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche dans les checkboxes
|
||||
*/
|
||||
async searchCheckboxes(query: string, limit: number = 20): Promise<DailyCheckbox[]> {
|
||||
const checkboxes = await prisma.dailyCheckbox.findMany({
|
||||
where: {
|
||||
text: {
|
||||
contains: query
|
||||
}
|
||||
},
|
||||
include: { task: true },
|
||||
orderBy: { date: 'desc' },
|
||||
take: limit
|
||||
});
|
||||
|
||||
return checkboxes.map(this.mapPrismaCheckbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'historique des checkboxes (groupées par date)
|
||||
*/
|
||||
async getCheckboxHistory(limit: number = 30): Promise<{ date: Date; checkboxes: DailyCheckbox[] }[]> {
|
||||
// Récupérer les dates distinctes des dernières checkboxes
|
||||
const distinctDates = await prisma.dailyCheckbox.findMany({
|
||||
select: { date: true },
|
||||
distinct: ['date'],
|
||||
orderBy: { date: 'desc' },
|
||||
take: limit
|
||||
});
|
||||
|
||||
const history = [];
|
||||
for (const { date } of distinctDates) {
|
||||
const checkboxes = await this.getCheckboxesByDate(date);
|
||||
if (checkboxes.length > 0) {
|
||||
history.push({ date, checkboxes });
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère la vue daily d'aujourd'hui
|
||||
*/
|
||||
async getTodaysDailyView(): Promise<DailyView> {
|
||||
return this.getDailyView(getToday());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une checkbox pour aujourd'hui
|
||||
*/
|
||||
async addTodayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
|
||||
return this.addCheckbox({
|
||||
date: getToday(),
|
||||
text,
|
||||
taskId
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une checkbox pour hier
|
||||
*/
|
||||
async addYesterdayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
|
||||
return this.addCheckbox({
|
||||
date: getYesterday(),
|
||||
text,
|
||||
taskId
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mappe une checkbox Prisma vers notre interface
|
||||
*/
|
||||
private mapPrismaCheckbox(checkbox: Prisma.DailyCheckboxGetPayload<{ include: { task: true } }>): DailyCheckbox {
|
||||
return {
|
||||
id: checkbox.id,
|
||||
date: checkbox.date,
|
||||
text: checkbox.text,
|
||||
isChecked: checkbox.isChecked,
|
||||
type: checkbox.type as DailyCheckboxType,
|
||||
order: checkbox.order,
|
||||
taskId: checkbox.taskId || undefined,
|
||||
task: checkbox.task ? {
|
||||
id: checkbox.task.id,
|
||||
title: checkbox.task.title,
|
||||
description: checkbox.task.description || undefined,
|
||||
status: checkbox.task.status as TaskStatus,
|
||||
priority: checkbox.task.priority as TaskPriority,
|
||||
source: checkbox.task.source as TaskSource,
|
||||
sourceId: checkbox.task.sourceId || undefined,
|
||||
tags: [], // Les tags seront chargés séparément si nécessaire
|
||||
dueDate: checkbox.task.dueDate || undefined,
|
||||
completedAt: checkbox.task.completedAt || undefined,
|
||||
createdAt: checkbox.task.createdAt,
|
||||
updatedAt: checkbox.task.updatedAt,
|
||||
jiraProject: checkbox.task.jiraProject || undefined,
|
||||
jiraKey: checkbox.task.jiraKey || undefined,
|
||||
assignee: checkbox.task.assignee || undefined
|
||||
} : undefined,
|
||||
createdAt: checkbox.createdAt,
|
||||
updatedAt: checkbox.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les dates qui ont des checkboxes (pour le calendrier)
|
||||
*/
|
||||
async getDailyDates(): Promise<string[]> {
|
||||
const checkboxes = await prisma.dailyCheckbox.findMany({
|
||||
select: {
|
||||
date: true
|
||||
},
|
||||
distinct: ['date'],
|
||||
orderBy: {
|
||||
date: 'desc'
|
||||
}
|
||||
});
|
||||
|
||||
return checkboxes.map(checkbox => {
|
||||
return formatDateForAPI(checkbox.date);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les checkboxes non cochées (tâches en attente)
|
||||
*/
|
||||
async getPendingCheckboxes(options?: {
|
||||
maxDays?: number;
|
||||
excludeToday?: boolean;
|
||||
type?: DailyCheckboxType;
|
||||
limit?: number;
|
||||
}): Promise<DailyCheckbox[]> {
|
||||
const today = normalizeDate(getToday());
|
||||
const maxDays = options?.maxDays ?? 30;
|
||||
const excludeToday = options?.excludeToday ?? true;
|
||||
|
||||
// Calculer la date limite (maxDays jours en arrière)
|
||||
const limitDate = new Date(today);
|
||||
limitDate.setDate(limitDate.getDate() - maxDays);
|
||||
|
||||
// Construire les conditions de filtrage
|
||||
const whereConditions: {
|
||||
isChecked: boolean;
|
||||
date: {
|
||||
gte: Date;
|
||||
lt?: Date;
|
||||
lte?: Date;
|
||||
};
|
||||
type?: DailyCheckboxType;
|
||||
} = {
|
||||
isChecked: false,
|
||||
date: {
|
||||
gte: limitDate,
|
||||
...(excludeToday ? { lt: today } : { lte: today })
|
||||
}
|
||||
};
|
||||
|
||||
// Filtrer par type si spécifié
|
||||
if (options?.type) {
|
||||
whereConditions.type = options.type;
|
||||
}
|
||||
|
||||
const checkboxes = await prisma.dailyCheckbox.findMany({
|
||||
where: whereConditions,
|
||||
include: { task: true },
|
||||
orderBy: [
|
||||
{ date: 'desc' },
|
||||
{ order: 'asc' }
|
||||
],
|
||||
...(options?.limit ? { take: options.limit } : {})
|
||||
});
|
||||
|
||||
return checkboxes.map(this.mapPrismaCheckbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive une checkbox (marque comme archivée sans la cocher)
|
||||
*/
|
||||
async archiveCheckbox(checkboxId: string): Promise<DailyCheckbox> {
|
||||
// Pour l'instant, on utilise un champ text pour marquer comme archivé
|
||||
// Plus tard on pourra ajouter un champ dédié dans la DB
|
||||
const checkbox = await prisma.dailyCheckbox.update({
|
||||
where: { id: checkboxId },
|
||||
data: {
|
||||
text: (await prisma.dailyCheckbox.findUnique({ where: { id: checkboxId } }))?.text + ' [ARCHIVÉ]',
|
||||
updatedAt: new Date()
|
||||
},
|
||||
include: { task: true }
|
||||
});
|
||||
|
||||
return this.mapPrismaCheckbox(checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Déplace une checkbox non cochée à aujourd'hui
|
||||
*/
|
||||
async moveCheckboxToToday(checkboxId: string): Promise<DailyCheckbox> {
|
||||
const checkbox = await prisma.dailyCheckbox.findUnique({
|
||||
where: { id: checkboxId }
|
||||
});
|
||||
|
||||
if (!checkbox) {
|
||||
throw new BusinessError('Checkbox non trouvée');
|
||||
}
|
||||
|
||||
if (checkbox.isChecked) {
|
||||
throw new BusinessError('Impossible de déplacer une tâche déjà cochée');
|
||||
}
|
||||
|
||||
const today = normalizeDate(getToday());
|
||||
|
||||
// Vérifier si la checkbox est déjà pour aujourd'hui
|
||||
if (normalizeDate(checkbox.date).getTime() === today.getTime()) {
|
||||
throw new BusinessError('La tâche est déjà programmée pour aujourd\'hui');
|
||||
}
|
||||
|
||||
// Calculer l'ordre suivant pour aujourd'hui
|
||||
const maxOrder = await prisma.dailyCheckbox.aggregate({
|
||||
where: { date: today },
|
||||
_max: { order: true }
|
||||
});
|
||||
|
||||
const newOrder = (maxOrder._max.order ?? -1) + 1;
|
||||
|
||||
const updatedCheckbox = await prisma.dailyCheckbox.update({
|
||||
where: { id: checkboxId },
|
||||
data: {
|
||||
date: today,
|
||||
order: newOrder,
|
||||
updatedAt: new Date()
|
||||
},
|
||||
include: { task: true }
|
||||
});
|
||||
|
||||
return this.mapPrismaCheckbox(updatedCheckbox);
|
||||
}
|
||||
}
|
||||
|
||||
// Instance singleton du service
|
||||
export const dailyService = new DailyService();
|
||||
Reference in New Issue
Block a user