feat: refactor daily task management with new pending tasks section

- Added `PendingTasksSection` to `DailyPageClient` for displaying uncompleted tasks.
- Implemented `getPendingCheckboxes` method in `DailyClient` and `DailyService` to fetch pending tasks.
- Introduced `getDaysAgo` utility function for calculating elapsed days since a date.
- Updated `TODO.md` to reflect the new task management features and adjustments.
- Cleaned up and organized folder structure to align with Next.js 13+ best practices.
This commit is contained in:
Julien Froidefond
2025-09-21 19:55:04 +02:00
parent 0a03e40469
commit 3cfed60f43
9 changed files with 482 additions and 64 deletions

67
TODO.md
View File

@@ -1,13 +1,7 @@
# TowerControl v2.0 - Gestionnaire de tâches moderne # TowerControl v2.0 - Gestionnaire de tâches moderne
## Autre Todos #2 ## Autre Todos
- [x] Synchro Jira auto en background timé comme pour la synchro de sauvegarde - [ ] Désactiver le hover sur les taskCard
- [ ] refacto des getallpreferences en frontend : ca devrait eter un contexte dans le layout qui balance serverside dans le hook
- [x] backups : ne backuper que si il y a eu un changement entre le dernier backup et la base actuelle
- [x] refacto des dates avec le utils qui pour l'instant n'est pas utilisé
- [ ] split de certains gros composants.
- [x] Page jira-dashboard : onglets analytics avancés et Qualité et collaboration : les charts sortent des cards; il faut reprendre la UI pour que ce soit consistant.
- [x] Page Daily : les mots aujourd'hui et hier ne fonctionnent dans les titres que si c'est vraiment aujourd'hui :)
## 🔧 Phase 6: Fonctionnalités avancées (Priorité 6) ## 🔧 Phase 6: Fonctionnalités avancées (Priorité 6)
@@ -87,61 +81,6 @@
- [ ] Configuration unifiée des filtres et synchronisations - [ ] Configuration unifiée des filtres et synchronisations
- [ ] Dashboard multi-intégrations - [ ] Dashboard multi-intégrations
### 📁 Refactoring structure des dossiers (PRIORITÉ HAUTE)
#### **Problème actuel**
- Structure mixte : `src/app/`, `src/actions/`, `src/contexts/` mais `components/`, `lib/`, `services/`, etc. à la racine
- Alias TypeScript incohérents dans `tsconfig.json`
- Non-conformité avec les bonnes pratiques Next.js 13+ App Router
#### **Plan de migration**
- [x] **Phase 1: Migration des dossiers**
- [x] `mv components/ src/components/`
- [x] `mv lib/ src/lib/`
- [x] `mv hooks/ src/hooks/`
- [x] `mv clients/ src/clients/`
- [x] `mv services/ src/services/`
- [x] **Phase 2: Mise à jour tsconfig.json**
```json
"paths": {
"@/*": ["./src/*"]
// Supprimer les alias spécifiques devenus inutiles
}
```
- [x] **Phase 3: Correction des imports**
- [x] Tous les imports `@/services/*` → `@/services/*` (déjà OK)
- [x] Tous les imports `@/lib/*` → `@/lib/*` (déjà OK)
- [x] Tous les imports `@/components/*` → `@/components/*` (déjà OK)
- [x] Tous les imports `@/clients/*` → `@/clients/*` (déjà OK)
- [x] Tous les imports `@/hooks/*` → `@/hooks/*` (déjà OK)
- [x] Vérifier les imports relatifs dans les scripts/
- [x] **Phase 4: Mise à jour des règles Cursor**
- [x] Règle "services" : Mettre à jour les exemples avec `src/services/`
- [x] Règle "components" : Mettre à jour avec `src/components/`
- [x] Règle "clients" : Mettre à jour avec `src/clients/`
- [x] Vérifier tous les liens MDC dans les règles
- [x] **Phase 5: Tests et validation**
- [x] `npm run build` - Vérifier que le build passe
- [x] `npm run dev` - Vérifier que le dev fonctionne
- [x] `npm run lint` - Vérifier ESLint
- [x] `npx tsc --noEmit` - Vérifier TypeScript
- [x] Tester les fonctionnalités principales
#### **Structure finale attendue**
```
src/
├── app/ # Pages Next.js (déjà OK)
├── actions/ # Server Actions (déjà OK)
├── contexts/ # React Contexts (déjà OK)
├── components/ # Composants React (à déplacer)
├── lib/ # Utilitaires et types (à déplacer)
├── hooks/ # Hooks React (à déplacer)
├── clients/ # Clients HTTP (à déplacer)
└── services/ # Services backend (à déplacer)
``` ```
### 👥 Gestion multi-utilisateurs (PROJET MAJEUR) ### 👥 Gestion multi-utilisateurs (PROJET MAJEUR)
@@ -195,7 +134,7 @@ src/
- [ ] Breakpoints adaptés pour tablettes - [ ] Breakpoints adaptés pour tablettes
- [ ] **Phase 2: Interface mobile pour les tâches** - [ ] **Phase 2: Interface mobile pour les tâches**
- [ ] **Vue liste simple** : Remplacement du Kanban - [ ] **Vue liste simple** : Kanban simple OK, mais swimlane KO. Ajouter une autre interface plus simple pour mobile en plus du Kanban Simple
- [ ] Liste verticale avec statuts en badges - [ ] Liste verticale avec statuts en badges
- [ ] Actions par swipe (marquer terminé, changer statut) - [ ] Actions par swipe (marquer terminé, changer statut)
- [ ] Filtres simplifiés (dropdown au lieu de sidebar) - [ ] Filtres simplifiés (dropdown au lieu de sidebar)

View File

@@ -304,3 +304,68 @@ Endpoints complexes → API Routes conservées
- [x] Filtrage par composant, version, type de ticket - [x] Filtrage par composant, version, type de ticket
- [x] Vue détaillée par sprint avec drill-down - [x] Vue détaillée par sprint avec drill-down
- [x] ~~Intégration avec les daily notes (mentions des blockers)~~ (supprimé) - [x] ~~Intégration avec les daily notes (mentions des blockers)~~ (supprimé)
### 📁 Refactoring structure des dossiers (PRIORITÉ HAUTE)
#### **Problème actuel**
- Structure mixte : `src/app/`, `src/actions/`, `src/contexts/` mais `components/`, `lib/`, `services/`, etc. à la racine
- Alias TypeScript incohérents dans `tsconfig.json`
- Non-conformité avec les bonnes pratiques Next.js 13+ App Router
#### **Plan de migration**
- [x] **Phase 1: Migration des dossiers**
- [x] `mv components/ src/components/`
- [x] `mv lib/ src/lib/`
- [x] `mv hooks/ src/hooks/`
- [x] `mv clients/ src/clients/`
- [x] `mv services/ src/services/`
- [x] **Phase 2: Mise à jour tsconfig.json**
```json
"paths": {
"@/*": ["./src/*"]
// Supprimer les alias spécifiques devenus inutiles
}
```
- [x] **Phase 3: Correction des imports**
- [x] Tous les imports `@/services/*` → `@/services/*` (déjà OK)
- [x] Tous les imports `@/lib/*` → `@/lib/*` (déjà OK)
- [x] Tous les imports `@/components/*` → `@/components/*` (déjà OK)
- [x] Tous les imports `@/clients/*` → `@/clients/*` (déjà OK)
- [x] Tous les imports `@/hooks/*` → `@/hooks/*` (déjà OK)
- [x] Vérifier les imports relatifs dans les scripts/
- [x] **Phase 4: Mise à jour des règles Cursor**
- [x] Règle "services" : Mettre à jour les exemples avec `src/services/`
- [x] Règle "components" : Mettre à jour avec `src/components/`
- [x] Règle "clients" : Mettre à jour avec `src/clients/`
- [x] Vérifier tous les liens MDC dans les règles
- [x] **Phase 5: Tests et validation**
- [x] `npm run build` - Vérifier que le build passe
- [x] `npm run dev` - Vérifier que le dev fonctionne
- [x] `npm run lint` - Vérifier ESLint
- [x] `npx tsc --noEmit` - Vérifier TypeScript
- [x] Tester les fonctionnalités principales
#### **Structure finale attendue**
```
src/
├── app/ # Pages Next.js (déjà OK)
├── actions/ # Server Actions (déjà OK)
├── contexts/ # React Contexts (déjà OK)
├── components/ # Composants React (à déplacer)
├── lib/ # Utilitaires et types (à déplacer)
├── hooks/ # Hooks React (à déplacer)
├── clients/ # Clients HTTP (à déplacer)
└── services/ # Services backend (à déplacer)
## Autre Todos
- [x] Synchro Jira auto en background timé comme pour la synchro de sauvegarde
- [x] refacto des getallpreferences en frontend : ca devrait eter un contexte dans le layout qui balance serverside dans le hook
- [x] backups : ne backuper que si il y a eu un changement entre le dernier backup et la base actuelle
- [x] refacto des dates avec le utils qui pour l'instant n'est pas utilisé
- [x] split de certains gros composants.
- [x] Page jira-dashboard : onglets analytics avancés et Qualité et collaboration : les charts sortent des cards; il faut reprendre la UI pour que ce soit consistant.
- [x] Page Daily : les mots aujourd'hui et hier ne fonctionnent dans les titres que si c'est vraiment aujourd'hui :)

View File

@@ -0,0 +1,28 @@
import { NextRequest, NextResponse } from 'next/server';
import { dailyService } from '@/services/daily';
export async function PATCH(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id: checkboxId } = await params;
if (!checkboxId) {
return NextResponse.json(
{ error: 'Checkbox ID is required' },
{ status: 400 }
);
}
const archivedCheckbox = await dailyService.archiveCheckbox(checkboxId);
return NextResponse.json(archivedCheckbox);
} catch (error) {
console.error('Error archiving checkbox:', error);
return NextResponse.json(
{ error: 'Failed to archive checkbox' },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,29 @@
import { NextRequest, NextResponse } from 'next/server';
import { dailyService } from '@/services/daily';
import { DailyCheckboxType } from '@/lib/types';
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const maxDays = searchParams.get('maxDays') ? parseInt(searchParams.get('maxDays')!) : undefined;
const excludeToday = searchParams.get('excludeToday') === 'true';
const type = searchParams.get('type') as DailyCheckboxType | undefined;
const limit = searchParams.get('limit') ? parseInt(searchParams.get('limit')!) : undefined;
const pendingCheckboxes = await dailyService.getPendingCheckboxes({
maxDays,
excludeToday,
type,
limit
});
return NextResponse.json(pendingCheckboxes);
} catch (error) {
console.error('Error fetching pending checkboxes:', error);
return NextResponse.json(
{ error: 'Failed to fetch pending checkboxes' },
{ status: 500 }
);
}
}

View File

@@ -8,6 +8,7 @@ import { Button } from '@/components/ui/Button';
import { Card } from '@/components/ui/Card'; import { Card } from '@/components/ui/Card';
import { DailyCalendar } from '@/components/daily/DailyCalendar'; import { DailyCalendar } from '@/components/daily/DailyCalendar';
import { DailySection } from '@/components/daily/DailySection'; import { DailySection } from '@/components/daily/DailySection';
import { PendingTasksSection } from '@/components/daily/PendingTasksSection';
import { dailyClient } from '@/clients/daily-client'; import { dailyClient } from '@/clients/daily-client';
import { Header } from '@/components/ui/Header'; import { Header } from '@/components/ui/Header';
import { getPreviousWorkday, formatDateLong, isToday, generateDateTitle, formatDateShort, isYesterday } from '@/lib/date-utils'; import { getPreviousWorkday, formatDateLong, isToday, generateDateTitle, formatDateShort, isYesterday } from '@/lib/date-utils';
@@ -45,6 +46,7 @@ export function DailyPageClient({
} = useDaily(initialDate, initialDailyView); } = useDaily(initialDate, initialDailyView);
const [dailyDates, setDailyDates] = useState<string[]>(initialDailyDates); const [dailyDates, setDailyDates] = useState<string[]>(initialDailyDates);
const [refreshTrigger, setRefreshTrigger] = useState(0);
// Fonction pour rafraîchir la liste des dates avec des dailies // Fonction pour rafraîchir la liste des dates avec des dailies
const refreshDailyDates = async () => { const refreshDailyDates = async () => {
@@ -79,12 +81,14 @@ export function DailyPageClient({
const handleToggleCheckbox = async (checkboxId: string) => { const handleToggleCheckbox = async (checkboxId: string) => {
await toggleCheckbox(checkboxId); await toggleCheckbox(checkboxId);
setRefreshTrigger(prev => prev + 1); // Trigger refresh pour les tâches en attente
}; };
const handleDeleteCheckbox = async (checkboxId: string) => { const handleDeleteCheckbox = async (checkboxId: string) => {
await deleteCheckbox(checkboxId); await deleteCheckbox(checkboxId);
// Refresh dates après suppression pour mettre à jour le calendrier // Refresh dates après suppression pour mettre à jour le calendrier
await refreshDailyDates(); await refreshDailyDates();
setRefreshTrigger(prev => prev + 1); // Trigger refresh pour les tâches en attente
}; };
const handleUpdateCheckbox = async (checkboxId: string, text: string, type: DailyCheckboxType, taskId?: string) => { const handleUpdateCheckbox = async (checkboxId: string, text: string, type: DailyCheckboxType, taskId?: string) => {
@@ -254,6 +258,13 @@ export function DailyPageClient({
)} )}
</div> </div>
{/* Section des tâches en attente */}
<PendingTasksSection
onToggleCheckbox={handleToggleCheckbox}
onDeleteCheckbox={handleDeleteCheckbox}
refreshTrigger={refreshTrigger}
/>
{/* Footer avec stats - dans le flux normal */} {/* Footer avec stats - dans le flux normal */}
{dailyView && ( {dailyView && (
<Card className="mt-8 p-4"> <Card className="mt-8 p-4">

View File

@@ -153,6 +153,34 @@ export class DailyClient {
const response = await httpClient.get<{ dates: string[] }>('/daily/dates'); const response = await httpClient.get<{ dates: string[] }>('/daily/dates');
return response.dates; return response.dates;
} }
/**
* Récupère les checkboxes en attente (non cochées)
*/
async getPendingCheckboxes(options?: {
maxDays?: number;
excludeToday?: boolean;
type?: 'task' | 'meeting';
limit?: number;
}): Promise<DailyCheckbox[]> {
const params = new URLSearchParams();
if (options?.maxDays) params.append('maxDays', options.maxDays.toString());
if (options?.excludeToday !== undefined) params.append('excludeToday', options.excludeToday.toString());
if (options?.type) params.append('type', options.type);
if (options?.limit) params.append('limit', options.limit.toString());
const queryString = params.toString();
const result = await httpClient.get<ApiCheckbox[]>(`/daily/pending${queryString ? `?${queryString}` : ''}`);
return result.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb));
}
/**
* Archive une checkbox
*/
async archiveCheckbox(checkboxId: string): Promise<DailyCheckbox> {
const result = await httpClient.patch<ApiCheckbox>(`/daily/checkboxes/${checkboxId}/archive`);
return this.transformCheckboxDates(result);
}
} }
// Instance singleton du client // Instance singleton du client

View File

@@ -0,0 +1,239 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { DailyCheckbox, DailyCheckboxType } from '@/lib/types';
import { dailyClient } from '@/clients/daily-client';
import { formatDateShort, getDaysAgo } from '@/lib/date-utils';
interface PendingTasksSectionProps {
onToggleCheckbox: (checkboxId: string) => Promise<void>;
onDeleteCheckbox: (checkboxId: string) => Promise<void>;
refreshTrigger?: number; // Pour forcer le refresh depuis le parent
}
export function PendingTasksSection({
onToggleCheckbox,
onDeleteCheckbox,
refreshTrigger
}: PendingTasksSectionProps) {
const [isCollapsed, setIsCollapsed] = useState(true);
const [pendingTasks, setPendingTasks] = useState<DailyCheckbox[]>([]);
const [loading, setLoading] = useState(false);
const [filters, setFilters] = useState({
maxDays: 7,
type: 'all' as 'all' | DailyCheckboxType,
limit: 50
});
// Charger les tâches en attente
const loadPendingTasks = useCallback(async () => {
setLoading(true);
try {
const tasks = await dailyClient.getPendingCheckboxes({
maxDays: filters.maxDays,
excludeToday: true,
type: filters.type === 'all' ? undefined : filters.type,
limit: filters.limit
});
setPendingTasks(tasks);
} catch (error) {
console.error('Erreur lors du chargement des tâches en attente:', error);
} finally {
setLoading(false);
}
}, [filters]);
// Charger au montage et quand les filtres changent
useEffect(() => {
if (!isCollapsed) {
loadPendingTasks();
}
}, [isCollapsed, filters, refreshTrigger, loadPendingTasks]);
// Gérer l'archivage d'une tâche
const handleArchiveTask = async (checkboxId: string) => {
try {
await dailyClient.archiveCheckbox(checkboxId);
await loadPendingTasks(); // Recharger la liste
} catch (error) {
console.error('Erreur lors de l\'archivage:', error);
}
};
// Gérer le cochage d'une tâche
const handleToggleTask = async (checkboxId: string) => {
await onToggleCheckbox(checkboxId);
await loadPendingTasks(); // Recharger la liste
};
// Gérer la suppression d'une tâche
const handleDeleteTask = async (checkboxId: string) => {
await onDeleteCheckbox(checkboxId);
await loadPendingTasks(); // Recharger la liste
};
// Obtenir la couleur selon l'ancienneté
const getAgeColor = (date: Date) => {
const days = getDaysAgo(date);
if (days <= 1) return 'text-green-600';
if (days <= 3) return 'text-yellow-600';
if (days <= 7) return 'text-orange-600';
return 'text-red-600';
};
// Obtenir l'icône selon le type
const getTypeIcon = (type: DailyCheckboxType) => {
return type === 'meeting' ? '🤝' : '📋';
};
const pendingCount = pendingTasks.length;
return (
<Card className="mt-6">
<CardHeader>
<div className="flex items-center justify-between">
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="flex items-center gap-2 text-lg font-semibold hover:text-[var(--primary)] transition-colors"
>
<span className={`transform transition-transform ${isCollapsed ? 'rotate-0' : 'rotate-90'}`}>
</span>
📋 Tâches en attente
{pendingCount > 0 && (
<span className="bg-[var(--warning)] text-[var(--warning-foreground)] px-2 py-1 rounded-full text-xs font-medium">
{pendingCount}
</span>
)}
</button>
{!isCollapsed && (
<div className="flex items-center gap-2">
{/* Filtres rapides */}
<select
value={filters.maxDays}
onChange={(e) => setFilters(prev => ({ ...prev, maxDays: parseInt(e.target.value) }))}
className="text-xs px-2 py-1 border border-[var(--border)] rounded bg-[var(--background)]"
>
<option value={7}>7 derniers jours</option>
<option value={14}>14 derniers jours</option>
<option value={30}>30 derniers jours</option>
</select>
<select
value={filters.type}
onChange={(e) => setFilters(prev => ({ ...prev, type: e.target.value as 'all' | DailyCheckboxType }))}
className="text-xs px-2 py-1 border border-[var(--border)] rounded bg-[var(--background)]"
>
<option value="all">Tous types</option>
<option value="task">Tâches</option>
<option value="meeting">Réunions</option>
</select>
<Button
variant="ghost"
size="sm"
onClick={loadPendingTasks}
disabled={loading}
>
{loading ? '🔄' : '↻'}
</Button>
</div>
)}
</div>
</CardHeader>
{!isCollapsed && (
<CardContent>
{loading ? (
<div className="text-center py-4 text-[var(--muted-foreground)]">
Chargement des tâches en attente...
</div>
) : pendingTasks.length === 0 ? (
<div className="text-center py-4 text-[var(--muted-foreground)]">
🎉 Aucune tâche en attente ! Excellent travail.
</div>
) : (
<div className="space-y-2">
{pendingTasks.map((task) => {
const daysAgo = getDaysAgo(task.date);
const isArchived = task.text.includes('[ARCHIVÉ]');
return (
<div
key={task.id}
className={`flex items-center gap-3 p-3 rounded-lg border border-[var(--border)] ${
isArchived ? 'opacity-60 bg-[var(--muted)]/20' : 'bg-[var(--card)]'
}`}
>
{/* Checkbox */}
<button
onClick={() => handleToggleTask(task.id)}
disabled={isArchived}
className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-colors ${
isArchived
? 'border-[var(--muted)] cursor-not-allowed'
: 'border-[var(--border)] hover:border-[var(--primary)]'
}`}
>
{task.isChecked && <span className="text-[var(--primary)]"></span>}
</button>
{/* Contenu */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span>{getTypeIcon(task.type)}</span>
<span className={`text-sm font-medium ${isArchived ? 'line-through' : ''}`}>
{task.text}
</span>
</div>
<div className="flex items-center gap-3 text-xs text-[var(--muted-foreground)]">
<span>{formatDateShort(task.date)}</span>
<span className={getAgeColor(task.date)}>
{daysAgo === 0 ? 'Aujourd\'hui' :
daysAgo === 1 ? 'Hier' :
`Il y a ${daysAgo} jours`}
</span>
{task.task && (
<span className="text-[var(--primary)]">
🔗 {task.task.title}
</span>
)}
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-1">
{!isArchived && (
<Button
variant="ghost"
size="sm"
onClick={() => handleArchiveTask(task.id)}
title="Archiver cette tâche"
className="text-xs px-2 py-1"
>
📦
</Button>
)}
<Button
variant="ghost"
size="sm"
onClick={() => handleDeleteTask(task.id)}
title="Supprimer cette tâche"
className="text-xs px-2 py-1 text-[var(--destructive)] hover:text-[var(--destructive)]"
>
🗑
</Button>
</div>
</div>
);
})}
</div>
)}
</CardContent>
)}
</Card>
);
}

View File

@@ -63,6 +63,15 @@ export function formatDateShort(date: Date): string {
return formatDateForDisplay(date, 'DISPLAY_SHORT'); return formatDateForDisplay(date, 'DISPLAY_SHORT');
} }
/**
* Calcule le nombre de jours écoulés depuis une date
*/
export function getDaysAgo(date: Date): number {
const today = getToday();
const diffTime = today.getTime() - normalizeDate(date).getTime();
return Math.floor(diffTime / (1000 * 60 * 60 * 24));
}
/** /**
* Formate une date longue pour l'affichage (lundi 1 décembre 2025) * Formate une date longue pour l'affichage (lundi 1 décembre 2025)
*/ */

View File

@@ -257,6 +257,76 @@ export class DailyService {
return formatDateForAPI(checkbox.date); 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);
}
} }
// Instance singleton du service // Instance singleton du service