Files
towercontrol/services/daily.ts
Julien Froidefond dfa8d34855 feat: add workday utility functions
- Introduced utility functions for workday calculations in `workday-utils.ts`, including `getPreviousWorkday`, `getNextWorkday`, `isWorkday`, and `getDayName`.
- Updated `DailyService` and `DailyPageClient` to utilize `getPreviousWorkday` for accurate date handling instead of simple date subtraction.
2025-09-20 15:43:38 +02:00

277 lines
7.9 KiB
TypeScript

import { prisma } from './database';
import { Prisma } from '@prisma/client';
import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData, BusinessError, DailyCheckboxType, TaskStatus, TaskPriority, TaskSource } from '@/lib/types';
import { getPreviousWorkday } from '@/lib/workday-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 = new Date(date);
today.setHours(0, 0, 0, 0);
// 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 = 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(),
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;
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: 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 => {
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();