From c3c1d24fa27289b9d4c02361d553cd167b86a740 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Sun, 21 Sep 2025 12:02:06 +0200 Subject: [PATCH] refactor: enhance date handling across components - Replaced direct date manipulations with utility functions for consistency and readability. - Updated date formatting in `DailyCalendar`, `RecentTasks`, `CompletionRateChart`, and other components to use `formatDateShort` and `formatDateForDisplay`. - Improved date parsing in `JiraLogs`, `JiraSchedulerConfig`, and `BackupSettingsPageClient` to ensure proper handling of date strings. - Streamlined date initialization in `useDaily` and `DailyService` to utilize `getToday` and `getYesterday` for better clarity. --- src/components/daily/DailyCalendar.tsx | 7 +++---- src/components/dashboard/RecentTasks.tsx | 8 +++----- .../dashboard/charts/CompletionRateChart.tsx | 3 ++- src/components/forms/RelatedTodos.tsx | 6 +++--- src/components/jira/JiraLogs.tsx | 3 ++- src/components/jira/JiraSchedulerConfig.tsx | 5 +++-- src/components/jira/JiraSync.tsx | 3 ++- src/components/jira/SprintDetailModal.tsx | 5 +++-- src/components/kanban/TaskCard.tsx | 2 +- .../settings/AdvancedSettingsPageClient.tsx | 15 ++++----------- .../settings/BackupSettingsPageClient.tsx | 6 +++--- .../settings/GeneralSettingsPageClient.tsx | 3 ++- src/hooks/useDaily.ts | 2 +- src/services/daily.ts | 11 ++++------- src/services/jira.ts | 6 +++--- 15 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/components/daily/DailyCalendar.tsx b/src/components/daily/DailyCalendar.tsx index ad7c274..148873f 100644 --- a/src/components/daily/DailyCalendar.tsx +++ b/src/components/daily/DailyCalendar.tsx @@ -4,6 +4,8 @@ import React, { useState } from 'react'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { formatDateForAPI, createDate, getToday } from '@/lib/date-utils'; +import { format } from 'date-fns'; +import { fr } from 'date-fns/locale'; interface DailyCalendarProps { currentDate: Date; @@ -97,10 +99,7 @@ export function DailyCalendar({ }; const formatMonthYear = () => { - return viewDate.toLocaleDateString('fr-FR', { - month: 'long', - year: 'numeric', - }); + return format(viewDate, 'MMMM yyyy', { locale: fr }); }; const weekDays = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']; diff --git a/src/components/dashboard/RecentTasks.tsx b/src/components/dashboard/RecentTasks.tsx index bb19b29..92517b9 100644 --- a/src/components/dashboard/RecentTasks.tsx +++ b/src/components/dashboard/RecentTasks.tsx @@ -3,6 +3,7 @@ import { Task } from '@/lib/types'; import { Card } from '@/components/ui/Card'; import { TagDisplay } from '@/components/ui/TagDisplay'; +import { formatDateShort } from '@/lib/date-utils'; import { Badge } from '@/components/ui/Badge'; import { useTasksContext } from '@/contexts/TasksContext'; import { getPriorityConfig, getPriorityColorHex, getStatusBadgeClasses, getStatusLabel } from '@/lib/status-config'; @@ -18,7 +19,7 @@ export function RecentTasks({ tasks }: RecentTasksProps) { // Prendre les 5 tâches les plus récentes (créées ou modifiées) const recentTasks = tasks - .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) + .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) .slice(0, 5); // Fonctions simplifiées utilisant la configuration centralisée @@ -116,10 +117,7 @@ export function RecentTasks({ tasks }: RecentTasksProps) {
- {new Date(task.updatedAt).toLocaleDateString('fr-FR', { - day: 'numeric', - month: 'short' - })} + {formatDateShort(task.updatedAt)}
diff --git a/src/components/dashboard/charts/CompletionRateChart.tsx b/src/components/dashboard/charts/CompletionRateChart.tsx index 6f9d3a6..ad45f95 100644 --- a/src/components/dashboard/charts/CompletionRateChart.tsx +++ b/src/components/dashboard/charts/CompletionRateChart.tsx @@ -2,6 +2,7 @@ import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import { DailyMetrics } from '@/services/metrics'; +import { parseDate, formatDateShort } from '@/lib/date-utils'; interface CompletionRateChartProps { data: DailyMetrics[]; @@ -12,7 +13,7 @@ export function CompletionRateChart({ data, className }: CompletionRateChartProp // Transformer les données pour le graphique const chartData = data.map(day => ({ day: day.dayName.substring(0, 3), // Lun, Mar, etc. - date: new Date(day.date).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit' }), + date: formatDateShort(parseDate(day.date)), completionRate: day.completionRate, completed: day.completed, total: day.totalTasks diff --git a/src/components/forms/RelatedTodos.tsx b/src/components/forms/RelatedTodos.tsx index 84a8d40..94e3568 100644 --- a/src/components/forms/RelatedTodos.tsx +++ b/src/components/forms/RelatedTodos.tsx @@ -4,7 +4,7 @@ import { useState, useEffect, useCallback, useTransition } from 'react'; import { DailyCheckbox } from '@/lib/types'; import { tasksClient } from '@/clients/tasks-client'; import { Button } from '@/components/ui/Button'; -import { formatDateSmart } from '@/lib/date-utils'; +import { formatDateSmart, parseDate } from '@/lib/date-utils'; import { Input } from '@/components/ui/Input'; import { addTodoToTask, toggleCheckbox } from '@/actions/daily'; @@ -42,7 +42,7 @@ export function RelatedTodos({ taskId }: RelatedTodosProps) { startTransition(async () => { try { // Si une date est spécifiée, l'utiliser, sinon undefined (aujourd'hui par défaut) - const targetDate = newTodoDate ? new Date(newTodoDate) : undefined; + const targetDate = newTodoDate ? parseDate(newTodoDate) : undefined; const result = await addTodoToTask(taskId, newTodoText, targetDate); @@ -79,7 +79,7 @@ export function RelatedTodos({ taskId }: RelatedTodosProps) { const formatDate = (date: Date | string) => { try { - const dateObj = typeof date === 'string' ? new Date(date) : date; + const dateObj = typeof date === 'string' ? parseDate(date) : date; if (isNaN(dateObj.getTime())) { return 'Date invalide'; } diff --git a/src/components/jira/JiraLogs.tsx b/src/components/jira/JiraLogs.tsx index 1755df7..5cef6d4 100644 --- a/src/components/jira/JiraLogs.tsx +++ b/src/components/jira/JiraLogs.tsx @@ -6,6 +6,7 @@ import { Badge } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; import { formatDistanceToNow } from 'date-fns'; import { fr } from 'date-fns/locale'; +import { parseDate } from '@/lib/date-utils'; interface SyncLog { id: string; @@ -111,7 +112,7 @@ export function JiraLogs({ className = "" }: JiraLogsProps) {
{getStatusBadge(log.status)} - {formatDistanceToNow(new Date(log.createdAt), { + {formatDistanceToNow(parseDate(log.createdAt), { addSuffix: true, locale: fr })} diff --git a/src/components/jira/JiraSchedulerConfig.tsx b/src/components/jira/JiraSchedulerConfig.tsx index 549d555..1f5b7ef 100644 --- a/src/components/jira/JiraSchedulerConfig.tsx +++ b/src/components/jira/JiraSchedulerConfig.tsx @@ -5,6 +5,7 @@ import { Button } from '@/components/ui/Button'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { jiraClient, JiraSchedulerStatus } from '@/clients/jira-client'; +import { parseDate, getToday } from '@/lib/date-utils'; interface JiraSchedulerConfigProps { className?: string; @@ -85,8 +86,8 @@ export function JiraSchedulerConfig({ className = "" }: JiraSchedulerConfigProps const getNextSyncText = () => { if (!schedulerStatus?.nextSync) return 'Aucune synchronisation planifiée'; - const nextSync = new Date(schedulerStatus.nextSync); - const now = new Date(); + const nextSync = parseDate(schedulerStatus.nextSync); + const now = getToday(); const diffMs = nextSync.getTime() - now.getTime(); if (diffMs <= 0) return 'Synchronisation en cours...'; diff --git a/src/components/jira/JiraSync.tsx b/src/components/jira/JiraSync.tsx index e7c2e90..77d53ff 100644 --- a/src/components/jira/JiraSync.tsx +++ b/src/components/jira/JiraSync.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { Button } from '@/components/ui/Button'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; +import { getToday } from '@/lib/date-utils'; import { Modal } from '@/components/ui/Modal'; import { jiraClient } from '@/clients/jira-client'; import { JiraSyncResult, JiraSyncAction } from '@/services/jira'; @@ -79,7 +80,7 @@ export function JiraSync({ onSyncComplete, className = "" }: JiraSyncProps) { {success ? "✓ Succès" : "⚠ Erreurs"} - {new Date().toLocaleTimeString()} + {getToday().toLocaleTimeString()}
diff --git a/src/components/jira/SprintDetailModal.tsx b/src/components/jira/SprintDetailModal.tsx index 8390d6c..311452d 100644 --- a/src/components/jira/SprintDetailModal.tsx +++ b/src/components/jira/SprintDetailModal.tsx @@ -4,6 +4,7 @@ import { useState, useEffect, useCallback } from 'react'; import { SprintVelocity, JiraTask, AssigneeDistribution, StatusDistribution } from '@/lib/types'; import { Modal } from '@/components/ui/Modal'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; +import { parseDate, formatDateForDisplay } from '@/lib/date-utils'; import { Badge } from '@/components/ui/Badge'; import { Button } from '@/components/ui/Button'; @@ -144,7 +145,7 @@ export default function SprintDetailModal({
Période
- {new Date(sprint.startDate).toLocaleDateString('fr-FR')} - {new Date(sprint.endDate).toLocaleDateString('fr-FR')} + {formatDateForDisplay(parseDate(sprint.startDate))} - {formatDateForDisplay(parseDate(sprint.endDate))}
@@ -318,7 +319,7 @@ export default function SprintDetailModal({
📋 {issue.issuetype.name} 👤 {issue.assignee?.displayName || 'Non assigné'} - 📅 {new Date(issue.created).toLocaleDateString('fr-FR')} + 📅 {formatDateForDisplay(parseDate(issue.created))}
diff --git a/src/components/kanban/TaskCard.tsx b/src/components/kanban/TaskCard.tsx index 578ce03..aae8783 100644 --- a/src/components/kanban/TaskCard.tsx +++ b/src/components/kanban/TaskCard.tsx @@ -427,7 +427,7 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) { {task.dueDate ? ( - {formatDistanceToNow(new Date(task.dueDate), { + {formatDistanceToNow(task.dueDate, { addSuffix: true, locale: fr })} diff --git a/src/components/settings/AdvancedSettingsPageClient.tsx b/src/components/settings/AdvancedSettingsPageClient.tsx index 2f66f24..525db4e 100644 --- a/src/components/settings/AdvancedSettingsPageClient.tsx +++ b/src/components/settings/AdvancedSettingsPageClient.tsx @@ -8,6 +8,7 @@ import { Button } from '@/components/ui/Button'; import { UserPreferencesProvider } from '@/contexts/UserPreferencesContext'; import { backupClient, BackupListResponse } from '@/clients/backup-client'; import Link from 'next/link'; +import { parseDate, getToday, formatDateForDisplay } from '@/lib/date-utils'; interface DatabaseStats { taskCount: number; @@ -86,22 +87,14 @@ export function AdvancedSettingsPageClient({ const formatTimeAgo = (date: Date): string => { // Format fixe pour éviter les erreurs d'hydratation - const d = new Date(date); - return d.toLocaleDateString('fr-FR', { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - hour12: false - }); + return formatDateForDisplay(date, 'DISPLAY_MEDIUM'); }; const getNextBackupTime = (): string => { if (!backupData.scheduler.nextBackup) return 'Non planifiée'; - const nextBackup = new Date(backupData.scheduler.nextBackup); - const now = new Date(); + const nextBackup = parseDate(backupData.scheduler.nextBackup); + const now = getToday(); const diffMs = nextBackup.getTime() - now.getTime(); const diffMins = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMins / 60); diff --git a/src/components/settings/BackupSettingsPageClient.tsx b/src/components/settings/BackupSettingsPageClient.tsx index 339c582..f98d920 100644 --- a/src/components/settings/BackupSettingsPageClient.tsx +++ b/src/components/settings/BackupSettingsPageClient.tsx @@ -8,7 +8,7 @@ import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Input } from '@/components/ui/Input'; import { Modal } from '@/components/ui/Modal'; import { Header } from '@/components/ui/Header'; -import { formatDateForDisplay } from '@/lib/date-utils'; +import { formatDateForDisplay, parseDate, getToday } from '@/lib/date-utils'; import Link from 'next/link'; interface BackupSettingsPageClientProps { @@ -209,8 +209,8 @@ export default function BackupSettingsPageClient({ initialData }: BackupSettings const getNextBackupTime = (): string => { if (!data?.scheduler.nextBackup) return 'Non planifiée'; - const nextBackup = new Date(data.scheduler.nextBackup); - const now = new Date(); + const nextBackup = parseDate(data.scheduler.nextBackup); + const now = getToday(); const diffMs = nextBackup.getTime() - now.getTime(); const diffMins = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMins / 60); diff --git a/src/components/settings/GeneralSettingsPageClient.tsx b/src/components/settings/GeneralSettingsPageClient.tsx index b8d55a9..875e382 100644 --- a/src/components/settings/GeneralSettingsPageClient.tsx +++ b/src/components/settings/GeneralSettingsPageClient.tsx @@ -10,6 +10,7 @@ import { Input } from '@/components/ui/Input'; import { TagForm } from '@/components/forms/TagForm'; import { UserPreferencesProvider } from '@/contexts/UserPreferencesContext'; import Link from 'next/link'; +import { formatDateForDisplay } from '@/lib/date-utils'; interface GeneralSettingsPageClientProps { initialPreferences: UserPreferences; @@ -294,7 +295,7 @@ export function GeneralSettingsPageClient({ initialPreferences, initialTags }: G {('createdAt' in tag && (tag as Tag & { createdAt: Date }).createdAt) && (
- Créé le {new Date((tag as Tag & { createdAt: Date }).createdAt).toLocaleDateString('fr-FR')} + Créé le {formatDateForDisplay((tag as Tag & { createdAt: Date }).createdAt)}
)} diff --git a/src/hooks/useDaily.ts b/src/hooks/useDaily.ts index 8af0ac5..55e831a 100644 --- a/src/hooks/useDaily.ts +++ b/src/hooks/useDaily.ts @@ -43,7 +43,7 @@ interface UseDailyActions { * Hook pour la gestion d'une vue daily spécifique */ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseDailyState & UseDailyActions & { currentDate: Date } { - const [currentDate, setCurrentDate] = useState(initialDate || new Date()); + const [currentDate, setCurrentDate] = useState(initialDate || getToday()); const [dailyView, setDailyView] = useState(initialDailyView || null); const [loading, setLoading] = useState(!initialDailyView); // Pas de loading si on a des données SSR const [refreshing, setRefreshing] = useState(false); // Pour les refresh silencieux diff --git a/src/services/daily.ts b/src/services/daily.ts index 020dcdb..ff89d1d 100644 --- a/src/services/daily.ts +++ b/src/services/daily.ts @@ -1,7 +1,7 @@ import { prisma } from './database'; import { Prisma } from '@prisma/client'; import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData, BusinessError, DailyCheckboxType, TaskStatus, TaskPriority, TaskSource } from '@/lib/types'; -import { getPreviousWorkday, normalizeDate, formatDateForAPI } from '@/lib/date-utils'; +import { getPreviousWorkday, normalizeDate, formatDateForAPI, getToday, getYesterday } from '@/lib/date-utils'; /** * Service pour la gestion des checkboxes daily @@ -180,7 +180,7 @@ export class DailyService { * Récupère la vue daily d'aujourd'hui */ async getTodaysDailyView(): Promise { - return this.getDailyView(new Date()); + return this.getDailyView(getToday()); } /** @@ -188,7 +188,7 @@ export class DailyService { */ async addTodayCheckbox(text: string, taskId?: string): Promise { return this.addCheckbox({ - date: new Date(), + date: getToday(), text, taskId }); @@ -198,11 +198,8 @@ export class DailyService { * Ajoute une checkbox pour hier */ async addYesterdayCheckbox(text: string, taskId?: string): Promise { - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - return this.addCheckbox({ - date: yesterday, + date: getYesterday(), text, taskId }); diff --git a/src/services/jira.ts b/src/services/jira.ts index 4c1f0d8..4eaf516 100644 --- a/src/services/jira.ts +++ b/src/services/jira.ts @@ -5,7 +5,7 @@ import { JiraTask } from '@/lib/types'; import { prisma } from './database'; -import { parseDate } from '@/lib/date-utils'; +import { parseDate, formatDateForDisplay } from '@/lib/date-utils'; export interface JiraConfig { baseUrl: string; @@ -389,8 +389,8 @@ export class JiraService { changes.push(`Priorité: préservée localement (${existingTask.priority})`); } if ((existingTask.dueDate?.getTime() || null) !== (taskData.dueDate?.getTime() || null)) { - const oldDate = existingTask.dueDate ? existingTask.dueDate.toLocaleDateString() : 'Aucune'; - const newDate = taskData.dueDate ? taskData.dueDate.toLocaleDateString() : 'Aucune'; + const oldDate = existingTask.dueDate ? formatDateForDisplay(existingTask.dueDate) : 'Aucune'; + const newDate = taskData.dueDate ? formatDateForDisplay(taskData.dueDate) : 'Aucune'; changes.push(`Échéance: ${oldDate} → ${newDate}`); } if (existingTask.jiraProject !== taskData.jiraProject) {