- Implemented user authentication in the daily dates API route to ensure secure access. - Added functionality to retrieve task deadlines and associated tasks, improving task management capabilities. - Updated DailyPageClient to display tasks with deadlines in the calendar view, enhancing user experience. - Enhanced Calendar component to visually indicate deadline dates, providing clearer task management context.
757 lines
20 KiB
TypeScript
757 lines
20 KiB
TypeScript
import { prisma } from '@/services/core/database';
|
|
import { Prisma } from '@prisma/client';
|
|
import {
|
|
DailyCheckbox,
|
|
DailyView,
|
|
CreateDailyCheckboxData,
|
|
UpdateDailyCheckboxData,
|
|
BusinessError,
|
|
DailyCheckboxType,
|
|
TaskStatus,
|
|
TaskPriority,
|
|
TaskSource,
|
|
Task,
|
|
} 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, userId: string): 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, userId),
|
|
this.getCheckboxesByDate(today, userId),
|
|
]);
|
|
|
|
return {
|
|
date: today,
|
|
yesterday: yesterdayCheckboxes,
|
|
today: todayCheckboxes,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les checkboxes d'une date donnée pour un utilisateur
|
|
*/
|
|
async getCheckboxesByDate(
|
|
date: Date,
|
|
userId: string
|
|
): Promise<DailyCheckbox[]> {
|
|
// Normaliser la date (début de journée)
|
|
const normalizedDate = normalizeDate(date);
|
|
|
|
const checkboxes = await prisma.dailyCheckbox.findMany({
|
|
where: {
|
|
date: normalizedDate,
|
|
userId: userId,
|
|
},
|
|
include: {
|
|
task: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: 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,
|
|
userId: data.userId,
|
|
order,
|
|
isChecked: data.isChecked ?? false,
|
|
},
|
|
include: {
|
|
task: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: 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: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: true,
|
|
},
|
|
});
|
|
|
|
return this.mapPrismaCheckbox(checkbox);
|
|
}
|
|
|
|
/**
|
|
* Toggle l'état d'une checkbox par son ID (indépendant de la date)
|
|
*/
|
|
async toggleCheckbox(checkboxId: string): Promise<DailyCheckbox> {
|
|
const existing = await prisma.dailyCheckbox.findUnique({
|
|
where: { id: checkboxId },
|
|
});
|
|
|
|
if (!existing) {
|
|
throw new BusinessError('Checkbox non trouvée');
|
|
}
|
|
|
|
const updated = await prisma.dailyCheckbox.update({
|
|
where: { id: checkboxId },
|
|
data: { isChecked: !existing.isChecked, updatedAt: new Date() },
|
|
include: {
|
|
task: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: true,
|
|
},
|
|
});
|
|
|
|
return this.mapPrismaCheckbox(updated);
|
|
}
|
|
|
|
/**
|
|
* 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: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: true,
|
|
},
|
|
orderBy: { date: 'desc' },
|
|
take: limit,
|
|
});
|
|
|
|
return checkboxes.map(this.mapPrismaCheckbox);
|
|
}
|
|
|
|
/**
|
|
* Récupère l'historique des checkboxes (groupées par date)
|
|
*/
|
|
async getCheckboxHistory(
|
|
userId: string,
|
|
limit: number = 30
|
|
): Promise<{ date: Date; checkboxes: DailyCheckbox[] }[]> {
|
|
// Récupérer les dates distinctes des dernières checkboxes pour cet utilisateur
|
|
const distinctDates = await prisma.dailyCheckbox.findMany({
|
|
where: { userId },
|
|
select: { date: true },
|
|
distinct: ['date'],
|
|
orderBy: { date: 'desc' },
|
|
take: limit,
|
|
});
|
|
|
|
const history = [];
|
|
for (const { date } of distinctDates) {
|
|
const checkboxes = await this.getCheckboxesByDate(date, userId);
|
|
if (checkboxes.length > 0) {
|
|
history.push({ date, checkboxes });
|
|
}
|
|
}
|
|
|
|
return history;
|
|
}
|
|
|
|
/**
|
|
* Récupère la vue daily d'aujourd'hui
|
|
*/
|
|
async getTodaysDailyView(userId: string): Promise<DailyView> {
|
|
return this.getDailyView(getToday(), userId);
|
|
}
|
|
|
|
/**
|
|
* Ajoute une checkbox pour aujourd'hui
|
|
*/
|
|
async addTodayCheckbox(
|
|
userId: string,
|
|
text: string,
|
|
taskId?: string
|
|
): Promise<DailyCheckbox> {
|
|
return this.addCheckbox({
|
|
date: getToday(),
|
|
userId,
|
|
text,
|
|
taskId,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ajoute une checkbox pour hier
|
|
*/
|
|
async addYesterdayCheckbox(
|
|
userId: string,
|
|
text: string,
|
|
taskId?: string
|
|
): Promise<DailyCheckbox> {
|
|
return this.addCheckbox({
|
|
date: getYesterday(),
|
|
userId,
|
|
text,
|
|
taskId,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Mappe une checkbox Prisma vers notre interface
|
|
* Accepte les checkboxes avec ou sans les relations taskTags et primaryTag
|
|
*/
|
|
private mapPrismaCheckbox(
|
|
checkbox: Prisma.DailyCheckboxGetPayload<{
|
|
include: {
|
|
task:
|
|
| true
|
|
| {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true;
|
|
};
|
|
};
|
|
primaryTag: true;
|
|
};
|
|
};
|
|
user: true;
|
|
};
|
|
}>
|
|
): DailyCheckbox {
|
|
// Extraire les tags de la tâche si elle existe
|
|
let taskTags: string[] = [];
|
|
let taskTagDetails:
|
|
| Array<{ id: string; name: string; color: string; isPinned?: boolean }>
|
|
| undefined = undefined;
|
|
let taskPrimaryTag:
|
|
| { id: string; name: string; color: string; isPinned?: boolean }
|
|
| undefined = undefined;
|
|
|
|
if (checkbox.task) {
|
|
// Vérifier si taskTags est disponible (peut être true ou un objet avec include)
|
|
const taskWithTags = checkbox.task as unknown as {
|
|
taskTags?: Array<{
|
|
tag: { id: string; name: string; color: string; isPinned: boolean };
|
|
}>;
|
|
primaryTag?: {
|
|
id: string;
|
|
name: string;
|
|
color: string;
|
|
isPinned: boolean;
|
|
} | null;
|
|
};
|
|
|
|
if (
|
|
'taskTags' in taskWithTags &&
|
|
taskWithTags.taskTags &&
|
|
Array.isArray(taskWithTags.taskTags)
|
|
) {
|
|
// Utiliser les relations Prisma pour récupérer les noms et détails des tags
|
|
taskTags = taskWithTags.taskTags.map((tt) => tt.tag.name);
|
|
taskTagDetails = taskWithTags.taskTags.map((tt) => ({
|
|
id: tt.tag.id,
|
|
name: tt.tag.name,
|
|
color: tt.tag.color,
|
|
isPinned: tt.tag.isPinned,
|
|
}));
|
|
}
|
|
|
|
// Extraire le primaryTag si disponible
|
|
if ('primaryTag' in taskWithTags && taskWithTags.primaryTag) {
|
|
taskPrimaryTag = {
|
|
id: taskWithTags.primaryTag.id,
|
|
name: taskWithTags.primaryTag.name,
|
|
color: taskWithTags.primaryTag.color,
|
|
isPinned: taskWithTags.primaryTag.isPinned,
|
|
};
|
|
}
|
|
}
|
|
|
|
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,
|
|
userId: checkbox.userId,
|
|
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: taskTags,
|
|
tagDetails: taskTagDetails,
|
|
primaryTagId: checkbox.task.primaryTagId || undefined,
|
|
primaryTag: taskPrimaryTag,
|
|
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,
|
|
ownerId: (checkbox.task as unknown as { ownerId: string }).ownerId, // Cast temporaire jusqu'à ce que Prisma soit mis à jour
|
|
}
|
|
: undefined,
|
|
isArchived: checkbox.text.includes('[ARCHIVÉ]'),
|
|
createdAt: checkbox.createdAt,
|
|
updatedAt: checkbox.updatedAt,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les dates qui ont des checkboxes (pour le calendrier)
|
|
*/
|
|
async getDailyDates(userId?: string): Promise<string[]> {
|
|
const whereConditions: Prisma.DailyCheckboxWhereInput = {};
|
|
|
|
// Filtrer par utilisateur si spécifié
|
|
if (userId) {
|
|
whereConditions.userId = userId;
|
|
}
|
|
|
|
const checkboxes = await prisma.dailyCheckbox.findMany({
|
|
where: whereConditions,
|
|
select: {
|
|
date: true,
|
|
},
|
|
distinct: ['date'],
|
|
orderBy: {
|
|
date: 'desc',
|
|
},
|
|
});
|
|
|
|
return checkboxes.map((checkbox) => {
|
|
return formatDateForAPI(checkbox.date);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les dates de fin (dueDate) des tâches non terminées (pour le calendrier)
|
|
* Retourne un objet avec les dates comme clés et les tâches associées
|
|
*/
|
|
async getTaskDeadlineDates(
|
|
userId: string
|
|
): Promise<Record<string, string[]>> {
|
|
const tasks = await prisma.task.findMany({
|
|
where: {
|
|
ownerId: userId,
|
|
dueDate: {
|
|
not: null,
|
|
},
|
|
status: {
|
|
notIn: ['done', 'cancelled', 'archived'],
|
|
},
|
|
},
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
dueDate: true,
|
|
},
|
|
orderBy: {
|
|
dueDate: 'asc',
|
|
},
|
|
});
|
|
|
|
const datesMap: Record<string, string[]> = {};
|
|
|
|
tasks.forEach((task) => {
|
|
if (!task.dueDate) return;
|
|
// Normaliser la date pour éviter les problèmes de timezone
|
|
const normalizedDate = normalizeDate(task.dueDate);
|
|
const dateKey = formatDateForAPI(normalizedDate);
|
|
|
|
if (!datesMap[dateKey]) {
|
|
datesMap[dateKey] = [];
|
|
}
|
|
datesMap[dateKey].push(task.title);
|
|
});
|
|
|
|
return datesMap;
|
|
}
|
|
|
|
/**
|
|
* Récupère les tâches avec deadline pour une date donnée
|
|
* Retourne un tableau de tâches complètes
|
|
*/
|
|
async getTasksByDeadlineDate(userId: string, date: Date): Promise<Task[]> {
|
|
const normalizedDate = normalizeDate(date);
|
|
const dateKey = formatDateForAPI(normalizedDate);
|
|
|
|
const tasks = await prisma.task.findMany({
|
|
where: {
|
|
ownerId: userId,
|
|
dueDate: {
|
|
not: null,
|
|
},
|
|
status: {
|
|
notIn: ['done', 'cancelled', 'archived'],
|
|
},
|
|
},
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
_count: {
|
|
select: {
|
|
dailyCheckboxes: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: {
|
|
dueDate: 'asc',
|
|
},
|
|
});
|
|
|
|
// Filtrer les tâches dont la date de fin correspond à la date demandée
|
|
const filteredTasks = tasks
|
|
.filter((task) => {
|
|
if (!task.dueDate) return false;
|
|
const taskDateKey = formatDateForAPI(normalizeDate(task.dueDate));
|
|
return taskDateKey === dateKey;
|
|
})
|
|
.map((task) => ({
|
|
id: task.id,
|
|
title: task.title,
|
|
description: task.description ?? undefined,
|
|
status: task.status as TaskStatus,
|
|
priority: task.priority as TaskPriority,
|
|
source: task.source as TaskSource,
|
|
sourceId: task.sourceId ?? undefined,
|
|
tags: task.taskTags.map((tt) => tt.tag.name),
|
|
tagDetails: task.taskTags.map((tt) => ({
|
|
id: tt.tag.id,
|
|
name: tt.tag.name,
|
|
color: tt.tag.color,
|
|
isPinned: tt.tag.isPinned,
|
|
})),
|
|
primaryTagId: task.primaryTagId ?? undefined,
|
|
primaryTag: task.primaryTag
|
|
? {
|
|
id: task.primaryTag.id,
|
|
name: task.primaryTag.name,
|
|
color: task.primaryTag.color,
|
|
isPinned: task.primaryTag.isPinned,
|
|
}
|
|
: undefined,
|
|
dueDate: task.dueDate ?? undefined,
|
|
completedAt: task.completedAt ?? undefined,
|
|
createdAt: task.createdAt,
|
|
updatedAt: task.updatedAt,
|
|
jiraProject: task.jiraProject ?? undefined,
|
|
jiraKey: task.jiraKey ?? undefined,
|
|
jiraType: task.jiraType ?? undefined,
|
|
tfsProject: task.tfsProject ?? undefined,
|
|
tfsPullRequestId: task.tfsPullRequestId ?? undefined,
|
|
tfsRepository: task.tfsRepository ?? undefined,
|
|
tfsSourceBranch: task.tfsSourceBranch ?? undefined,
|
|
tfsTargetBranch: task.tfsTargetBranch ?? undefined,
|
|
assignee: task.assignee ?? undefined,
|
|
ownerId: (task as unknown as { ownerId: string }).ownerId,
|
|
todosCount: task._count.dailyCheckboxes,
|
|
}));
|
|
|
|
return filteredTasks;
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les checkboxes non cochées (tâches en attente)
|
|
*/
|
|
async getPendingCheckboxes(options?: {
|
|
maxDays?: number;
|
|
excludeToday?: boolean;
|
|
type?: DailyCheckboxType;
|
|
limit?: number;
|
|
userId?: string; // Filtrer par utilisateur
|
|
}): 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: Prisma.DailyCheckboxWhereInput = {
|
|
isChecked: false,
|
|
date: {
|
|
gte: limitDate,
|
|
...(excludeToday ? { lt: today } : { lte: today }),
|
|
},
|
|
};
|
|
|
|
// Filtrer par type si spécifié
|
|
if (options?.type) {
|
|
whereConditions.type = options.type;
|
|
}
|
|
|
|
// Filtrer par utilisateur si spécifié
|
|
if (options?.userId) {
|
|
whereConditions.userId = options.userId;
|
|
|
|
// S'assurer que si le todo est lié à une tâche, cette tâche appartient bien à l'utilisateur
|
|
whereConditions.OR = [
|
|
{ task: null }, // Todos standalone (sans tâche associée)
|
|
{ task: { ownerId: options.userId } }, // Todos liés à une tâche de l'utilisateur
|
|
];
|
|
}
|
|
|
|
const checkboxes = await prisma.dailyCheckbox.findMany({
|
|
where: whereConditions,
|
|
include: {
|
|
task: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: 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: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: 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: {
|
|
include: {
|
|
taskTags: {
|
|
include: {
|
|
tag: true,
|
|
},
|
|
},
|
|
primaryTag: true,
|
|
},
|
|
},
|
|
user: true,
|
|
},
|
|
});
|
|
|
|
return this.mapPrismaCheckbox(updatedCheckbox);
|
|
}
|
|
}
|
|
|
|
// Instance singleton du service
|
|
export const dailyService = new DailyService();
|