feat(DailyPage, DailyService, Calendar): enhance task deadline management and UI integration
- 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.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { dailyService } from '@/services/task-management/daily';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
import { authOptions } from '@/lib/auth';
|
||||
|
||||
/**
|
||||
* API route pour récupérer toutes les dates avec des dailies
|
||||
@@ -7,7 +9,12 @@ import { dailyService } from '@/services/task-management/daily';
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const dates = await dailyService.getDailyDates();
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
||||
}
|
||||
|
||||
const dates = await dailyService.getDailyDates(session.user.id);
|
||||
return NextResponse.json({ dates });
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des dates:', error);
|
||||
|
||||
42
src/app/api/daily/deadline-tasks/route.ts
Normal file
42
src/app/api/daily/deadline-tasks/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { dailyService } from '@/services/task-management/daily';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
import { authOptions } from '@/lib/auth';
|
||||
import { parseDate, isValidAPIDate } from '@/lib/date-utils';
|
||||
|
||||
/**
|
||||
* API route pour récupérer les tâches avec deadline pour une date donnée
|
||||
* GET /api/daily/deadline-tasks?date=YYYY-MM-DD
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const dateStr = searchParams.get('date');
|
||||
|
||||
if (!dateStr || !isValidAPIDate(dateStr)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Date invalide. Format attendu: YYYY-MM-DD' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const date = parseDate(dateStr);
|
||||
const tasks = await dailyService.getTasksByDeadlineDate(
|
||||
session.user.id,
|
||||
date
|
||||
);
|
||||
|
||||
return NextResponse.json({ tasks });
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des tâches:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur interne du serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
30
src/app/api/daily/deadlines/route.ts
Normal file
30
src/app/api/daily/deadlines/route.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { dailyService } from '@/services/task-management/daily';
|
||||
import { getServerSession } from 'next-auth/next';
|
||||
import { authOptions } from '@/lib/auth';
|
||||
|
||||
/**
|
||||
* API route pour récupérer toutes les dates de fin des tâches avec leurs noms
|
||||
* GET /api/daily/deadlines
|
||||
* Retourne un objet { dates: Record<string, string[]> } où chaque clé est une date (YYYY-MM-DD)
|
||||
* et la valeur est un tableau de noms de tâches
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
||||
}
|
||||
|
||||
const deadlineDates = await dailyService.getTaskDeadlineDates(
|
||||
session.user.id
|
||||
);
|
||||
return NextResponse.json({ dates: deadlineDates });
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de la récupération des dates de fin:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur interne du serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { useDaily } from '@/hooks/useDaily';
|
||||
import { DailyView, DailyCheckboxType, DailyCheckbox } from '@/lib/types';
|
||||
import { DailyView, DailyCheckboxType, DailyCheckbox, Task } from '@/lib/types';
|
||||
import { DeadlineMetrics } from '@/services/analytics/deadline-analytics';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
|
||||
import { TaskCard } from '@/components/ui/TaskCard';
|
||||
import { useTags } from '@/hooks/useTags';
|
||||
import { Calendar } from '@/components/ui/Calendar';
|
||||
import { AlertBanner, AlertItem } from '@/components/ui/AlertBanner';
|
||||
import { DailySection } from '@/components/daily/DailySection';
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
formatDateLong,
|
||||
isToday,
|
||||
generateDateTitle,
|
||||
formatDateForAPI,
|
||||
} from '@/lib/date-utils';
|
||||
import { useGlobalKeyboardShortcuts } from '@/hooks/useGlobalKeyboardShortcuts';
|
||||
import { Emoji } from '@/components/ui/Emoji';
|
||||
@@ -25,6 +28,7 @@ import { Emoji } from '@/components/ui/Emoji';
|
||||
interface DailyPageClientProps {
|
||||
initialDailyView?: DailyView;
|
||||
initialDailyDates?: string[];
|
||||
initialDeadlineDates?: Record<string, string[]>; // Date -> Array de noms de tâches
|
||||
initialDate?: Date;
|
||||
initialDeadlineMetrics?: DeadlineMetrics | null;
|
||||
initialPendingTasks?: DailyCheckbox[];
|
||||
@@ -33,10 +37,12 @@ interface DailyPageClientProps {
|
||||
export function DailyPageClient({
|
||||
initialDailyView,
|
||||
initialDailyDates = [],
|
||||
initialDeadlineDates = {},
|
||||
initialDate,
|
||||
initialDeadlineMetrics,
|
||||
initialPendingTasks = [],
|
||||
}: DailyPageClientProps = {}) {
|
||||
const { tags: availableTags } = useTags();
|
||||
const {
|
||||
dailyView,
|
||||
loading,
|
||||
@@ -60,6 +66,10 @@ export function DailyPageClient({
|
||||
} = useDaily(initialDate, initialDailyView);
|
||||
|
||||
const [dailyDates, setDailyDates] = useState<string[]>(initialDailyDates);
|
||||
const [deadlineDates, setDeadlineDates] =
|
||||
useState<Record<string, string[]>>(initialDeadlineDates);
|
||||
const [deadlineTasks, setDeadlineTasks] = useState<Task[]>([]);
|
||||
const [loadingDeadlineTasks, setLoadingDeadlineTasks] = useState(false);
|
||||
const [pendingRefreshTrigger, setPendingRefreshTrigger] = useState(0);
|
||||
|
||||
// Fonction pour rafraîchir la liste des dates avec des dailies
|
||||
@@ -84,6 +94,18 @@ export function DailyPageClient({
|
||||
}
|
||||
}, [initialDailyDates.length]);
|
||||
|
||||
// Charger les dates de fin pour le calendrier (seulement si pas de données SSR)
|
||||
useEffect(() => {
|
||||
if (Object.keys(initialDeadlineDates).length === 0) {
|
||||
import('@/clients/daily-client')
|
||||
.then(({ dailyClient }) => {
|
||||
return dailyClient.getDeadlineDates();
|
||||
})
|
||||
.then(setDeadlineDates)
|
||||
.catch(console.error);
|
||||
}
|
||||
}, [initialDeadlineDates]);
|
||||
|
||||
const handleAddTodayCheckbox = async (
|
||||
text: string,
|
||||
type: DailyCheckboxType
|
||||
@@ -163,6 +185,29 @@ export function DailyPageClient({
|
||||
return formatDateLong(currentDate);
|
||||
};
|
||||
|
||||
// Charger les tâches complètes pour la date sélectionnée
|
||||
useEffect(() => {
|
||||
const loadDeadlineTasks = async () => {
|
||||
const dateKey = formatDateForAPI(currentDate);
|
||||
if (deadlineDates[dateKey] && deadlineDates[dateKey].length > 0) {
|
||||
setLoadingDeadlineTasks(true);
|
||||
try {
|
||||
const tasks = await dailyClient.getDeadlineTasksForDate(currentDate);
|
||||
setDeadlineTasks(tasks);
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement des tâches:', error);
|
||||
setDeadlineTasks([]);
|
||||
} finally {
|
||||
setLoadingDeadlineTasks(false);
|
||||
}
|
||||
} else {
|
||||
setDeadlineTasks([]);
|
||||
}
|
||||
};
|
||||
|
||||
loadDeadlineTasks();
|
||||
}, [currentDate, deadlineDates]);
|
||||
|
||||
const isTodayDate = () => {
|
||||
return isToday(currentDate);
|
||||
};
|
||||
@@ -351,9 +396,55 @@ export function DailyPageClient({
|
||||
currentDate={currentDate}
|
||||
onDateSelect={handleDateSelect}
|
||||
markedDates={dailyDates}
|
||||
deadlineDates={deadlineDates}
|
||||
showTodayButton={true}
|
||||
showLegend={true}
|
||||
/>
|
||||
|
||||
{/* Section des tâches avec deadline pour la date sélectionnée - Mobile */}
|
||||
{deadlineTasks.length > 0 && (
|
||||
<Card variant="glass">
|
||||
<CardHeader padding="sm" separator={false}>
|
||||
<CardTitle size="sm" className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-[var(--destructive)]"></div>
|
||||
Tâches à terminer
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent padding="sm">
|
||||
{loadingDeadlineTasks ? (
|
||||
<div className="text-sm text-[var(--muted-foreground)] text-center py-4">
|
||||
Chargement...
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{deadlineTasks.map((task) => (
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
variant="compact"
|
||||
title={task.title}
|
||||
description={task.description}
|
||||
tags={task.tags}
|
||||
primaryTagId={task.primaryTagId}
|
||||
priority={task.priority}
|
||||
status={task.status}
|
||||
dueDate={task.dueDate}
|
||||
source={task.source}
|
||||
jiraKey={task.jiraKey}
|
||||
jiraProject={task.jiraProject}
|
||||
jiraType={task.jiraType}
|
||||
todosCount={task.todosCount}
|
||||
availableTags={availableTags}
|
||||
fontSize="small"
|
||||
onClick={() => {
|
||||
window.location.href = `/kanban?taskId=${task.id}`;
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -362,14 +453,60 @@ export function DailyPageClient({
|
||||
<div className="hidden sm:block">
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
|
||||
{/* Calendrier - Desktop */}
|
||||
<div className="xl:col-span-1">
|
||||
<div className="xl:col-span-1 space-y-6">
|
||||
<Calendar
|
||||
currentDate={currentDate}
|
||||
onDateSelect={handleDateSelect}
|
||||
markedDates={dailyDates}
|
||||
deadlineDates={deadlineDates}
|
||||
showTodayButton={true}
|
||||
showLegend={true}
|
||||
/>
|
||||
|
||||
{/* Section des tâches avec deadline pour la date sélectionnée */}
|
||||
{deadlineTasks.length > 0 && (
|
||||
<Card variant="glass">
|
||||
<CardHeader padding="sm" separator={false}>
|
||||
<CardTitle size="sm" className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 rounded-full bg-[var(--destructive)]"></div>
|
||||
Tâches à terminer
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent padding="sm">
|
||||
{loadingDeadlineTasks ? (
|
||||
<div className="text-sm text-[var(--muted-foreground)] text-center py-4">
|
||||
Chargement...
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{deadlineTasks.map((task) => (
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
variant="compact"
|
||||
title={task.title}
|
||||
description={task.description}
|
||||
tags={task.tags}
|
||||
primaryTagId={task.primaryTagId}
|
||||
priority={task.priority}
|
||||
status={task.status}
|
||||
dueDate={task.dueDate}
|
||||
source={task.source}
|
||||
jiraKey={task.jiraKey}
|
||||
jiraProject={task.jiraProject}
|
||||
jiraType={task.jiraType}
|
||||
todosCount={task.todosCount}
|
||||
availableTags={availableTags}
|
||||
fontSize="small"
|
||||
onClick={() => {
|
||||
window.location.href = `/kanban?taskId=${task.id}`;
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sections daily - Desktop */}
|
||||
|
||||
@@ -37,27 +37,34 @@ export default async function DailyPage() {
|
||||
const today = getToday();
|
||||
|
||||
try {
|
||||
const [dailyView, dailyDates, deadlineMetrics, pendingTasks] =
|
||||
await Promise.all([
|
||||
dailyService.getDailyView(today, session.user.id),
|
||||
dailyService.getDailyDates(session.user.id),
|
||||
DeadlineAnalyticsService.getDeadlineMetrics(session.user.id).catch(
|
||||
() => null
|
||||
), // Graceful fallback
|
||||
dailyService
|
||||
.getPendingCheckboxes({
|
||||
maxDays: 7,
|
||||
excludeToday: true,
|
||||
limit: 50,
|
||||
userId: session.user.id,
|
||||
})
|
||||
.catch(() => []), // Graceful fallback
|
||||
]);
|
||||
const [
|
||||
dailyView,
|
||||
dailyDates,
|
||||
deadlineDatesMap,
|
||||
deadlineMetrics,
|
||||
pendingTasks,
|
||||
] = await Promise.all([
|
||||
dailyService.getDailyView(today, session.user.id),
|
||||
dailyService.getDailyDates(session.user.id),
|
||||
dailyService.getTaskDeadlineDates(session.user.id).catch(() => ({})), // Graceful fallback
|
||||
DeadlineAnalyticsService.getDeadlineMetrics(session.user.id).catch(
|
||||
() => null
|
||||
), // Graceful fallback
|
||||
dailyService
|
||||
.getPendingCheckboxes({
|
||||
maxDays: 7,
|
||||
excludeToday: true,
|
||||
limit: 50,
|
||||
userId: session.user.id,
|
||||
})
|
||||
.catch(() => []), // Graceful fallback
|
||||
]);
|
||||
|
||||
return (
|
||||
<DailyPageClient
|
||||
initialDailyView={dailyView}
|
||||
initialDailyDates={dailyDates}
|
||||
initialDeadlineDates={deadlineDatesMap}
|
||||
initialDate={today}
|
||||
initialDeadlineMetrics={deadlineMetrics}
|
||||
initialPendingTasks={pendingTasks}
|
||||
|
||||
@@ -174,6 +174,29 @@ export class DailyClient {
|
||||
return response.dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les dates de fin des tâches avec leurs noms
|
||||
* Retourne un objet Record<string, string[]> où chaque clé est une date (YYYY-MM-DD)
|
||||
* et la valeur est un tableau de noms de tâches
|
||||
*/
|
||||
async getDeadlineDates(): Promise<Record<string, string[]>> {
|
||||
const response = await httpClient.get<{ dates: Record<string, string[]> }>(
|
||||
'/daily/deadlines'
|
||||
);
|
||||
return response.dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les tâches avec deadline pour une date donnée
|
||||
*/
|
||||
async getDeadlineTasksForDate(date: Date): Promise<Task[]> {
|
||||
const dateStr = this.formatDateForAPI(date);
|
||||
const response = await httpClient.get<{
|
||||
tasks: Task[];
|
||||
}>(`/daily/deadline-tasks?date=${dateStr}`);
|
||||
return response.tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les checkboxes en attente (non cochées)
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ interface CalendarProps {
|
||||
currentDate: Date;
|
||||
onDateSelect: (date: Date) => void;
|
||||
markedDates?: string[]; // Liste des dates marquées (format YYYY-MM-DD)
|
||||
deadlineDates?: Record<string, string[]>; // Date -> Array de noms de tâches
|
||||
showTodayButton?: boolean;
|
||||
showLegend?: boolean;
|
||||
className?: string;
|
||||
@@ -20,6 +21,7 @@ export function Calendar({
|
||||
currentDate,
|
||||
onDateSelect,
|
||||
markedDates = [],
|
||||
deadlineDates = {},
|
||||
showTodayButton = true,
|
||||
showLegend = true,
|
||||
className = '',
|
||||
@@ -100,6 +102,11 @@ export function Calendar({
|
||||
return markedDates.includes(formatDateKey(date));
|
||||
};
|
||||
|
||||
const hasDeadlineDate = (date: Date) => {
|
||||
const dateKey = formatDateKey(date);
|
||||
return deadlineDates[dateKey] && deadlineDates[dateKey].length > 0;
|
||||
};
|
||||
|
||||
const isSelected = (date: Date) => {
|
||||
return formatDateKey(date) === currentDateKey;
|
||||
};
|
||||
@@ -164,6 +171,7 @@ export function Calendar({
|
||||
const isCurrentMonthDay = isCurrentMonth(date);
|
||||
const isTodayDay = isTodayDate(date);
|
||||
const hasMarked = hasMarkedDate(date);
|
||||
const hasDeadline = hasDeadlineDate(date);
|
||||
const isSelectedDay = isSelected(date);
|
||||
|
||||
return (
|
||||
@@ -188,15 +196,27 @@ export function Calendar({
|
||||
>
|
||||
{date.getDate()}
|
||||
|
||||
{/* Indicateur de date marquée */}
|
||||
{hasMarked && (
|
||||
<div
|
||||
className={`
|
||||
absolute bottom-1 right-1 w-2 h-2 rounded-full
|
||||
${isSelectedDay ? 'bg-white' : 'bg-[var(--primary)]'}
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
{/* Indicateurs de dates */}
|
||||
<div className="absolute bottom-1 right-1 flex gap-0.5">
|
||||
{/* Indicateur de date marquée (point bleu) */}
|
||||
{hasMarked && (
|
||||
<div
|
||||
className={`
|
||||
w-2 h-2 rounded-full
|
||||
${isSelectedDay ? 'bg-white' : 'bg-[var(--primary)]'}
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
{/* Indicateur de date de fin (point rouge) */}
|
||||
{hasDeadline && (
|
||||
<div
|
||||
className={`
|
||||
w-2 h-2 rounded-full
|
||||
${isSelectedDay ? 'bg-white' : 'bg-[var(--destructive)]'}
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
@@ -213,6 +233,14 @@ export function Calendar({
|
||||
Jour avec des éléments
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 flex justify-center">
|
||||
<div className="w-2 h-2 rounded-full bg-[var(--destructive)]"></div>
|
||||
</div>
|
||||
<span className="text-xs text-left flex-1">
|
||||
Date de fin de tâche
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 flex justify-center">
|
||||
<div className="w-3 h-3 rounded border border-[var(--primary)] bg-[var(--primary)]/20"></div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
TaskStatus,
|
||||
TaskPriority,
|
||||
TaskSource,
|
||||
Task,
|
||||
} from '@/lib/types';
|
||||
import {
|
||||
getPreviousWorkday,
|
||||
@@ -466,6 +467,137 @@ export class DailyService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user