feat: add "Move to Today" functionality for pending tasks

- Implemented a new button in the `PendingTasksSection` to move unchecked tasks to today's date.
- Created `moveCheckboxToToday` action in `daily.ts` to handle the logic for moving tasks.
- Updated `DailyPageClient` and `PendingTasksSection` to integrate the new functionality and refresh the daily view after moving tasks.
- Marked the feature as completed in `TODO.md`.
This commit is contained in:
Julien Froidefond
2025-09-22 08:51:59 +02:00
parent f9d0641d77
commit b5d53ef0f1
6 changed files with 114 additions and 11 deletions

View File

@@ -256,3 +256,25 @@ export async function reorderCheckboxes(dailyId: string, checkboxIds: string[]):
};
}
}
/**
* Déplace une checkbox non cochée à aujourd'hui
*/
export async function moveCheckboxToToday(checkboxId: string): Promise<{
success: boolean;
data?: DailyCheckbox;
error?: string;
}> {
try {
const updatedCheckbox = await dailyService.moveCheckboxToToday(checkboxId);
revalidatePath('/daily');
return { success: true, data: updatedCheckbox };
} catch (error) {
console.error('Erreur moveCheckboxToToday:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Erreur inconnue'
};
}
}

View File

@@ -42,7 +42,8 @@ export function DailyPageClient({
goToPreviousDay,
goToNextDay,
goToToday,
setDate
setDate,
refreshDailySilent
} = useDaily(initialDate, initialDailyView);
const [dailyDates, setDailyDates] = useState<string[]>(initialDailyDates);
@@ -294,6 +295,7 @@ export function DailyPageClient({
<PendingTasksSection
onToggleCheckbox={handleToggleCheckbox}
onDeleteCheckbox={handleDeleteCheckbox}
onRefreshDaily={refreshDailySilent}
refreshTrigger={refreshTrigger}
/>

View File

@@ -1,26 +1,30 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useTransition } 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';
import { moveCheckboxToToday } from '@/actions/daily';
interface PendingTasksSectionProps {
onToggleCheckbox: (checkboxId: string) => Promise<void>;
onDeleteCheckbox: (checkboxId: string) => Promise<void>;
onRefreshDaily?: () => Promise<void>; // Pour rafraîchir la vue daily principale
refreshTrigger?: number; // Pour forcer le refresh depuis le parent
}
export function PendingTasksSection({
onToggleCheckbox,
onDeleteCheckbox,
onRefreshDaily,
refreshTrigger
}: PendingTasksSectionProps) {
const [isCollapsed, setIsCollapsed] = useState(true);
const [pendingTasks, setPendingTasks] = useState<DailyCheckbox[]>([]);
const [loading, setLoading] = useState(false);
const [isPending, startTransition] = useTransition();
const [filters, setFilters] = useState({
maxDays: 7,
type: 'all' as 'all' | DailyCheckboxType,
@@ -74,6 +78,22 @@ export function PendingTasksSection({
await loadPendingTasks(); // Recharger la liste
};
// Gérer le déplacement d'une tâche à aujourd'hui
const handleMoveToToday = (checkboxId: string) => {
startTransition(async () => {
const result = await moveCheckboxToToday(checkboxId);
if (result.success) {
await loadPendingTasks(); // Recharger la liste des tâches en attente
if (onRefreshDaily) {
await onRefreshDaily(); // Rafraîchir la vue daily principale
}
} else {
console.error('Erreur lors du déplacement vers aujourd\'hui:', result.error);
}
});
};
// Obtenir la couleur selon l'ancienneté
const getAgeColor = (date: Date) => {
const days = getDaysAgo(date);
@@ -207,15 +227,27 @@ export function PendingTasksSection({
{/* 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={() => handleMoveToToday(task.id)}
disabled={isPending}
title="Déplacer à aujourd'hui"
className="text-xs px-2 py-1 text-[var(--primary)] hover:text-[var(--primary)] disabled:opacity-50"
>
📅
</Button>
<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"

View File

@@ -360,6 +360,7 @@ export interface UpdateDailyCheckboxData {
type?: DailyCheckboxType;
taskId?: string;
order?: number;
date?: Date;
}
// Interface pour récupérer les checkboxes d'une journée

View File

@@ -94,6 +94,7 @@ export class DailyService {
}
}
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 },
@@ -327,6 +328,50 @@ export class DailyService {
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