Notifications corrections

This commit is contained in:
alma 2026-01-16 10:16:33 +01:00
parent 28fe97edd9
commit c5295179f9

View File

@ -81,79 +81,66 @@ export function useTaskNotifications() {
notifiedCount: notifiedTaskIdsRef.current.size,
});
const dueTasks = tasksRef.current.filter((task) => {
// Helper function to parse date correctly (handles Leantime dates with Z)
const parseTaskDate = (dateStr: string | null): Date | null => {
if (!dateStr) return null;
// Leantime dates with 'Z' are actually local time stored as UTC
// We need to extract the time components and create a local date
if (dateStr.endsWith('Z')) {
// Parse as UTC first to get the components, then create local date
// Example: "2026-01-16T01:13:00.000Z" -> treat 01:13 as local time, not UTC
const utcDate = new Date(dateStr);
// Extract components from UTC date and create local date
// This assumes the UTC date actually represents local time
return new Date(
utcDate.getUTCFullYear(),
utcDate.getUTCMonth(),
utcDate.getUTCDate(),
utcDate.getUTCHours(),
utcDate.getUTCMinutes(),
utcDate.getUTCSeconds(),
utcDate.getUTCMilliseconds()
);
} else if (dateStr.includes('T')) {
// ISO format without Z - treat as local time
return new Date(dateStr);
} else if (dateStr.includes(' ')) {
// MySQL format: "YYYY-MM-DD HH:MM:SS" - parse as local time
const isoLike = dateStr.replace(' ', 'T');
return new Date(isoLike);
} else {
// Try direct parsing
return new Date(dateStr);
}
};
interface TaskWithParsedDate extends Task {
parsedNotificationDate: Date;
}
const dueTasksWithDates: TaskWithParsedDate[] = [];
for (const task of tasksRef.current) {
// Skip if already notified
if (notifiedTaskIdsRef.current.has(task.id)) {
console.log('[useTaskNotifications] ⏭️ Task already notified', {
id: task.id,
title: task.headline,
});
return false;
continue;
}
// For both Leantime and Twenty CRM tasks: notification at due date time (dateToFinish)
// Note: Leantime dates might be in MySQL format (YYYY-MM-DD HH:MM:SS) without timezone
// We need to parse them correctly to avoid timezone issues
let notificationDate: Date | null = null;
// Parse the notification date
const notificationDate = parseTaskDate(task.dateToFinish);
if (task.dateToFinish) {
const dateStr = task.dateToFinish;
// Leantime dates with 'Z' are actually local time stored as UTC
// We need to extract the time components and create a local date
if (dateStr.endsWith('Z')) {
// Parse as UTC first to get the components, then create local date
// Example: "2026-01-16T01:13:00.000Z" -> treat 01:13 as local time, not UTC
const utcDate = new Date(dateStr);
// Extract components from UTC date and create local date
// This assumes the UTC date actually represents local time
notificationDate = new Date(
utcDate.getUTCFullYear(),
utcDate.getUTCMonth(),
utcDate.getUTCDate(),
utcDate.getUTCHours(),
utcDate.getUTCMinutes(),
utcDate.getUTCSeconds(),
utcDate.getUTCMilliseconds()
);
console.log('[useTaskNotifications] 📅 Parsing Leantime date (Z -> local)', {
id: task.id,
title: task.headline,
rawDate: dateStr,
utcComponents: {
year: utcDate.getUTCFullYear(),
month: utcDate.getUTCMonth(),
day: utcDate.getUTCDate(),
hour: utcDate.getUTCHours(),
minute: utcDate.getUTCMinutes(),
},
localDate: notificationDate.toLocaleString('fr-FR'),
localISO: notificationDate.toISOString(),
});
} else if (dateStr.includes('T')) {
// ISO format without Z - treat as local time
notificationDate = new Date(dateStr);
} else if (dateStr.includes(' ')) {
// MySQL format: "YYYY-MM-DD HH:MM:SS" - parse as local time
const isoLike = dateStr.replace(' ', 'T');
notificationDate = new Date(isoLike);
} else {
// Try direct parsing
notificationDate = new Date(dateStr);
}
if (!notificationDate || isNaN(notificationDate.getTime())) {
console.error('[useTaskNotifications] ❌ Invalid date', {
id: task.id,
rawDate: dateStr,
});
return false;
}
}
if (!notificationDate || isNaN(notificationDate.getTime())) {
return false;
console.log('[useTaskNotifications] ⏭️ Task has no valid date', {
id: task.id,
title: task.headline,
dateToFinish: task.dateToFinish,
});
continue;
}
const notificationTime = notificationDate.getTime();
@ -182,23 +169,25 @@ export function useTaskNotifications() {
inWindow: inNotificationWindow,
});
return inNotificationWindow;
});
if (inNotificationWindow) {
dueTasksWithDates.push({
...task,
parsedNotificationDate: notificationDate,
});
}
}
const dueTasks = dueTasksWithDates;
if (dueTasks.length > 0) {
// Sort by notification time (earliest first)
// Sort by notification time (earliest first) using parsed dates
dueTasks.sort((a, b) => {
const dateA = a.dateToFinish ? new Date(a.dateToFinish).getTime() : Infinity;
const dateB = b.dateToFinish ? new Date(b.dateToFinish).getTime() : Infinity;
return dateA - dateB;
return a.parsedNotificationDate.getTime() - b.parsedNotificationDate.getTime();
});
// Show notification for the first task
const task = dueTasks[0];
const notificationDate = task.dateToFinish
? new Date(task.dateToFinish)
: new Date();
const notificationDate = task.parsedNotificationDate;
const timeStr = format(notificationDate, 'HH:mm', { locale: fr });
const sourceLabel = task.source === 'twenty-crm' ? 'Médiation' : 'Agilité';
@ -207,8 +196,11 @@ export function useTaskNotifications() {
id: task.id,
title: task.headline,
source: task.source,
notificationDate: notificationDate.toISOString(),
now: now.toISOString(),
rawDate: task.dateToFinish,
parsedNotificationDateLocal: notificationDate.toLocaleString('fr-FR'),
parsedNotificationDateISO: notificationDate.toISOString(),
nowLocal: now.toLocaleString('fr-FR'),
nowISO: now.toISOString(),
});
const notification: OutlookNotificationData = {