Files
towercontrol/services/daily.ts
Julien Froidefond 936e0306fc feat: update Daily management features and enhance date handling
- Marked the calendar/history view of dailies as completed in the TODO list.
- Improved date formatting in `formatDateForAPI` to avoid timezone issues.
- Added `getDailyDates` method in `DailyClient` and `DailyService` to retrieve all dates with dailies.
- Enhanced `POST` route to robustly parse date input for better error handling.
- Integrated daily dates loading in `DailyPageClient` for calendar display.
2025-09-15 18:21:48 +02:00

266 lines
7.2 KiB
TypeScript

import { prisma } from './database';
import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData, BusinessError } from '@/lib/types';
/**
* 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 = new Date(date);
today.setHours(0, 0, 0, 0);
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
// 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 = new Date(date);
normalizedDate.setHours(0, 0, 0, 0);
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 = new Date(data.date);
normalizedDate.setHours(0, 0, 0, 0);
// 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(),
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: any = {};
if (data.text !== undefined) updateData.text = data.text.trim();
if (data.isChecked !== undefined) updateData.isChecked = data.isChecked;
if (data.taskId !== undefined) updateData.taskId = data.taskId;
if (data.order !== undefined) updateData.order = data.order;
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> {
// Normaliser la date
const normalizedDate = new Date(date);
normalizedDate.setHours(0, 0, 0, 0);
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(new Date());
}
/**
* Ajoute une checkbox pour aujourd'hui
*/
async addTodayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
return this.addCheckbox({
date: new Date(),
text,
taskId
});
}
/**
* Ajoute une checkbox pour hier
*/
async addYesterdayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
return this.addCheckbox({
date: yesterday,
text,
taskId
});
}
/**
* Mappe une checkbox Prisma vers notre interface
*/
private mapPrismaCheckbox(checkbox: any): DailyCheckbox {
return {
id: checkbox.id,
date: checkbox.date,
text: checkbox.text,
isChecked: checkbox.isChecked,
order: checkbox.order,
taskId: checkbox.taskId,
task: checkbox.task ? {
id: checkbox.task.id,
title: checkbox.task.title,
description: checkbox.task.description,
status: checkbox.task.status,
priority: checkbox.task.priority,
source: checkbox.task.source,
sourceId: checkbox.task.sourceId,
tags: [], // Les tags seront chargés séparément si nécessaire
dueDate: checkbox.task.dueDate,
completedAt: checkbox.task.completedAt,
createdAt: checkbox.task.createdAt,
updatedAt: checkbox.task.updatedAt,
jiraProject: checkbox.task.jiraProject,
jiraKey: checkbox.task.jiraKey,
assignee: checkbox.task.assignee
} : 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 => {
const date = checkbox.date;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
});
}
}
// Instance singleton du service
export const dailyService = new DailyService();