refactor: unify date handling with utility functions
- Replaced direct date manipulations with utility functions like `getToday`, `parseDate`, and `createDateFromParts` across various components and services for consistency. - Updated date initialization in `JiraAnalyticsService`, `BackupService`, and `DailyClient` to improve clarity and maintainability. - Enhanced date parsing in forms and API routes to ensure proper handling of date strings.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Task, TaskStatus, TaskPriority, TaskSource } from '@/lib/types';
|
||||
import { prisma } from './database';
|
||||
import { getToday, parseDate, subtractDays, addDays } from '@/lib/date-utils';
|
||||
|
||||
export interface ProductivityMetrics {
|
||||
completionTrend: Array<{
|
||||
@@ -42,8 +43,8 @@ export class AnalyticsService {
|
||||
*/
|
||||
static async getProductivityMetrics(timeRange?: TimeRange): Promise<ProductivityMetrics> {
|
||||
try {
|
||||
const now = new Date();
|
||||
const defaultStart = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); // 30 jours
|
||||
const now = getToday();
|
||||
const defaultStart = subtractDays(now, 30); // 30 jours
|
||||
|
||||
const start = timeRange?.start || defaultStart;
|
||||
const end = timeRange?.end || now;
|
||||
@@ -99,7 +100,7 @@ export class AnalyticsService {
|
||||
const trend: Array<{ date: string; completed: number; created: number; total: number }> = [];
|
||||
|
||||
// Générer les dates pour la période
|
||||
const currentDate = new Date(start);
|
||||
const currentDate = new Date(start.getTime());
|
||||
while (currentDate <= end) {
|
||||
const dateStr = currentDate.toISOString().split('T')[0];
|
||||
|
||||
@@ -116,7 +117,7 @@ export class AnalyticsService {
|
||||
|
||||
// Total cumulé jusqu'à ce jour
|
||||
const totalUntilThisDay = tasks.filter(task =>
|
||||
new Date(task.createdAt) <= currentDate
|
||||
task.createdAt <= currentDate
|
||||
).length;
|
||||
|
||||
trend.push({
|
||||
@@ -156,7 +157,7 @@ export class AnalyticsService {
|
||||
|
||||
// Convertir en format pour le graphique
|
||||
weekGroups.forEach((count, weekKey) => {
|
||||
const weekDate = new Date(weekKey);
|
||||
const weekDate = parseDate(weekKey);
|
||||
weeklyData.push({
|
||||
week: `Sem. ${this.getWeekNumber(weekDate)}`,
|
||||
completed: count,
|
||||
@@ -209,10 +210,10 @@ export class AnalyticsService {
|
||||
* Calcule les statistiques hebdomadaires
|
||||
*/
|
||||
private static calculateWeeklyStats(tasks: Task[]) {
|
||||
const now = new Date();
|
||||
const now = getToday();
|
||||
const thisWeekStart = this.getWeekStart(now);
|
||||
const lastWeekStart = new Date(thisWeekStart.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
const lastWeekEnd = new Date(thisWeekStart.getTime() - 1);
|
||||
const lastWeekStart = subtractDays(thisWeekStart, 7);
|
||||
const lastWeekEnd = subtractDays(thisWeekStart, 1);
|
||||
|
||||
const thisWeekCompleted = tasks.filter(task =>
|
||||
task.completedAt &&
|
||||
@@ -243,7 +244,7 @@ export class AnalyticsService {
|
||||
* Obtient le début de la semaine pour une date
|
||||
*/
|
||||
private static getWeekStart(date: Date): Date {
|
||||
const d = new Date(date);
|
||||
const d = new Date(date.getTime());
|
||||
const day = d.getDay();
|
||||
const diff = d.getDate() - day + (day === 0 ? -6 : 1); // Lundi = début de semaine
|
||||
return new Date(d.setDate(diff));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { backupService, BackupConfig } from './backup';
|
||||
import { addMinutes, getToday } from '@/lib/date-utils';
|
||||
|
||||
export class BackupScheduler {
|
||||
private timer: NodeJS.Timeout | null = null;
|
||||
@@ -106,7 +107,7 @@ export class BackupScheduler {
|
||||
const config = backupService.getConfig();
|
||||
const intervalMs = this.getIntervalMs(config.interval);
|
||||
|
||||
return new Date(Date.now() + intervalMs);
|
||||
return addMinutes(getToday(), Math.floor(intervalMs / (1000 * 60)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'path';
|
||||
import { prisma } from './database';
|
||||
import { userPreferencesService } from './user-preferences';
|
||||
import { BackupUtils } from '../lib/backup-utils';
|
||||
import { getToday } from '@/lib/date-utils';
|
||||
|
||||
export interface BackupConfig {
|
||||
enabled: boolean;
|
||||
@@ -280,7 +281,7 @@ export class BackupService {
|
||||
id: backupId,
|
||||
filename: path.basename(finalPath),
|
||||
size: stats.size,
|
||||
createdAt: new Date(),
|
||||
createdAt: getToday(),
|
||||
type,
|
||||
status: 'success',
|
||||
databaseHash,
|
||||
@@ -289,7 +290,7 @@ export class BackupService {
|
||||
// Sauvegarder les métadonnées du backup
|
||||
await this.saveBackupMetadata(path.basename(finalPath), {
|
||||
databaseHash,
|
||||
createdAt: new Date(),
|
||||
createdAt: getToday(),
|
||||
type,
|
||||
});
|
||||
|
||||
@@ -313,7 +314,7 @@ export class BackupService {
|
||||
id: backupId,
|
||||
filename,
|
||||
size: 0,
|
||||
createdAt: new Date(),
|
||||
createdAt: getToday(),
|
||||
type,
|
||||
status: 'failed',
|
||||
error: errorMessage,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { JiraTask, JiraAnalytics, JiraAnalyticsFilters, AvailableFilters, FilterOption } from '@/lib/types';
|
||||
import { parseDate } from '@/lib/date-utils';
|
||||
|
||||
export class JiraAdvancedFiltersService {
|
||||
|
||||
@@ -137,7 +138,7 @@ export class JiraAdvancedFiltersService {
|
||||
|
||||
// Filtrage par date
|
||||
if (filters.dateRange) {
|
||||
const issueDate = new Date(issue.created);
|
||||
const issueDate = parseDate(issue.created);
|
||||
if (issueDate < filters.dateRange.from || issueDate > filters.dateRange.to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { JiraService } from './jira';
|
||||
import { jiraAnalyticsCache } from './jira-analytics-cache';
|
||||
import { getToday, parseDate, addDays, subtractDays } from '@/lib/date-utils';
|
||||
import {
|
||||
JiraAnalytics,
|
||||
JiraTask,
|
||||
@@ -248,23 +249,23 @@ export class JiraAnalyticsService {
|
||||
* Génère un historique de sprints basé sur les dates de création/résolution des tickets
|
||||
*/
|
||||
private generateSprintHistoryFromIssues(allIssues: JiraTask[], completedIssues: JiraTask[]): SprintVelocity[] {
|
||||
const now = new Date();
|
||||
const now = getToday();
|
||||
const sprintHistory: SprintVelocity[] = [];
|
||||
|
||||
// Créer 4 périodes de 2 semaines (8 semaines au total)
|
||||
for (let i = 3; i >= 0; i--) {
|
||||
const endDate = new Date(now.getTime() - (i * 14 * 24 * 60 * 60 * 1000));
|
||||
const startDate = new Date(endDate.getTime() - (14 * 24 * 60 * 60 * 1000));
|
||||
const endDate = subtractDays(now, i * 14);
|
||||
const startDate = subtractDays(endDate, 14);
|
||||
|
||||
// Compter les tickets complétés dans cette période
|
||||
const completedInPeriod = completedIssues.filter(issue => {
|
||||
const updatedDate = new Date(issue.updated);
|
||||
const updatedDate = parseDate(issue.updated);
|
||||
return updatedDate >= startDate && updatedDate <= endDate;
|
||||
});
|
||||
|
||||
// Compter les tickets créés dans cette période (approximation du planifié)
|
||||
const createdInPeriod = allIssues.filter(issue => {
|
||||
const createdDate = new Date(issue.created);
|
||||
const createdDate = parseDate(issue.created);
|
||||
return createdDate >= startDate && createdDate <= endDate;
|
||||
});
|
||||
|
||||
@@ -315,8 +316,8 @@ export class JiraAnalyticsService {
|
||||
const cycleTimes = completedIssues
|
||||
.filter(issue => issue.created && issue.updated) // S'assurer qu'on a les dates
|
||||
.map(issue => {
|
||||
const created = new Date(issue.created);
|
||||
const resolved = new Date(issue.updated);
|
||||
const created = parseDate(issue.created);
|
||||
const resolved = parseDate(issue.updated);
|
||||
const days = Math.max(0.1, (resolved.getTime() - created.getTime()) / (1000 * 60 * 60 * 24)); // Minimum 0.1 jour
|
||||
return Math.round(days * 10) / 10; // Arrondir à 1 décimale
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { JiraAnalytics, SprintVelocity, CycleTimeByType, AssigneeWorkload } from '@/lib/types';
|
||||
import { getToday } from '@/lib/date-utils';
|
||||
|
||||
export interface JiraAnomaly {
|
||||
id: string;
|
||||
@@ -44,7 +45,7 @@ export class JiraAnomalyDetectionService {
|
||||
*/
|
||||
async detectAnomalies(analytics: JiraAnalytics): Promise<JiraAnomaly[]> {
|
||||
const anomalies: JiraAnomaly[] = [];
|
||||
const timestamp = new Date().toISOString();
|
||||
const timestamp = getToday().toISOString();
|
||||
|
||||
// 1. Détection d'anomalies de vélocité
|
||||
const velocityAnomalies = this.detectVelocityAnomalies(analytics.velocityMetrics, timestamp);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { userPreferencesService } from './user-preferences';
|
||||
import { JiraService } from './jira';
|
||||
import { addMinutes, getToday } from '@/lib/date-utils';
|
||||
|
||||
export interface JiraSchedulerConfig {
|
||||
enabled: boolean;
|
||||
@@ -143,7 +144,7 @@ export class JiraScheduler {
|
||||
const config = await this.getConfig();
|
||||
const intervalMs = this.getIntervalMs(config.interval);
|
||||
|
||||
return new Date(Date.now() + intervalMs);
|
||||
return addMinutes(getToday(), Math.floor(intervalMs / (1000 * 60)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from './database';
|
||||
import { startOfWeek, endOfWeek } from 'date-fns';
|
||||
import { getToday } from '@/lib/date-utils';
|
||||
|
||||
type TaskType = {
|
||||
id: string;
|
||||
@@ -84,7 +85,7 @@ export class ManagerSummaryService {
|
||||
/**
|
||||
* Génère un résumé orienté manager pour la semaine
|
||||
*/
|
||||
static async getManagerSummary(date: Date = new Date()): Promise<ManagerSummary> {
|
||||
static async getManagerSummary(date: Date = getToday()): Promise<ManagerSummary> {
|
||||
const weekStart = startOfWeek(date, { weekStartsOn: 1 }); // Lundi
|
||||
const weekEnd = endOfWeek(date, { weekStartsOn: 1 }); // Dimanche
|
||||
|
||||
@@ -249,7 +250,7 @@ export class ManagerSummaryService {
|
||||
description: task.description || undefined,
|
||||
tags: task.taskTags?.map(tt => tt.tag.name) || [],
|
||||
impact,
|
||||
completedAt: task.completedAt || new Date(),
|
||||
completedAt: task.completedAt || getToday(),
|
||||
relatedItems: [task.id, ...relatedTodos.map(t => t.id)],
|
||||
todosCount: relatedTodos.length // Nombre réel de todos associés
|
||||
});
|
||||
@@ -322,7 +323,7 @@ export class ManagerSummaryService {
|
||||
where: {
|
||||
isChecked: false,
|
||||
date: {
|
||||
gte: new Date()
|
||||
gte: getToday()
|
||||
},
|
||||
OR: [
|
||||
{ type: 'meeting' },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { prisma } from './database';
|
||||
import { startOfWeek, endOfWeek, eachDayOfInterval, format, startOfDay, endOfDay } from 'date-fns';
|
||||
import { fr } from 'date-fns/locale';
|
||||
import { formatDateForAPI, getDayName, getToday } from '@/lib/date-utils';
|
||||
import { formatDateForAPI, getDayName, getToday, subtractDays } from '@/lib/date-utils';
|
||||
|
||||
export interface DailyMetrics {
|
||||
date: string; // Format ISO
|
||||
@@ -326,7 +326,7 @@ export class MetricsService {
|
||||
const trends = [];
|
||||
|
||||
for (let i = weeksBack - 1; i >= 0; i--) {
|
||||
const weekStart = startOfWeek(new Date(Date.now() - i * 7 * 24 * 60 * 60 * 1000), { weekStartsOn: 1 });
|
||||
const weekStart = startOfWeek(subtractDays(getToday(), i * 7), { weekStartsOn: 1 });
|
||||
const weekEnd = endOfWeek(weekStart, { weekStartsOn: 1 });
|
||||
|
||||
const [completed, created] = await Promise.all([
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { prisma } from './database';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { getToday, parseDate } from '@/lib/date-utils';
|
||||
|
||||
export interface SystemInfo {
|
||||
version: string;
|
||||
@@ -168,8 +169,8 @@ export class SystemInfoService {
|
||||
const fs = require('fs'); // eslint-disable-line @typescript-eslint/no-require-imports
|
||||
const packagePath = join(process.cwd(), 'package.json');
|
||||
const stats = fs.statSync(packagePath);
|
||||
const now = new Date();
|
||||
const lastModified = new Date(stats.mtime);
|
||||
const now = getToday();
|
||||
const lastModified = parseDate(stats.mtime.toISOString());
|
||||
const diffTime = Math.abs(now.getTime() - lastModified.getTime());
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user