107 lines
3.1 KiB
JavaScript
107 lines
3.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Daily script to send due date reminders for projects
|
|
* Runs nightly to check for projects due in 3 days and 1 day
|
|
*/
|
|
|
|
import db from "./src/lib/db.js";
|
|
import { createNotification, NOTIFICATION_TYPES } from "./src/lib/notifications.js";
|
|
import { addDays, isBefore, parseISO, startOfDay } from "date-fns";
|
|
|
|
async function sendDueDateReminders() {
|
|
try {
|
|
console.log("🔍 Checking for projects with upcoming due dates...");
|
|
|
|
const today = startOfDay(new Date());
|
|
const threeDaysFromNow = addDays(today, 3);
|
|
const oneDayFromNow = addDays(today, 1);
|
|
|
|
// Get projects that are not fulfilled and have finish dates
|
|
const projects = db.prepare(`
|
|
SELECT
|
|
p.project_id,
|
|
p.project_name,
|
|
p.finish_date,
|
|
p.address,
|
|
p.project_status,
|
|
c.customer
|
|
FROM projects p
|
|
LEFT JOIN contracts c ON p.contract_id = c.contract_id
|
|
WHERE
|
|
p.finish_date IS NOT NULL
|
|
AND p.project_status != 'fulfilled'
|
|
AND p.project_status != 'cancelled'
|
|
`).all();
|
|
|
|
console.log(`📋 Found ${projects.length} active projects with due dates`);
|
|
|
|
let remindersSent = 0;
|
|
|
|
for (const project of projects) {
|
|
try {
|
|
const finishDate = parseISO(project.finish_date);
|
|
const finishDateStart = startOfDay(finishDate);
|
|
|
|
// Check if due in 3 days
|
|
if (finishDateStart.getTime() === threeDaysFromNow.getTime()) {
|
|
await sendReminder(project, 3);
|
|
remindersSent++;
|
|
}
|
|
// Check if due in 1 day
|
|
else if (finishDateStart.getTime() === oneDayFromNow.getTime()) {
|
|
await sendReminder(project, 1);
|
|
remindersSent++;
|
|
}
|
|
} catch (error) {
|
|
console.error(`❌ Error processing project ${project.project_id}:`, error);
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Sent ${remindersSent} due date reminders`);
|
|
|
|
} catch (error) {
|
|
console.error("❌ Error in due date reminder script:", error);
|
|
}
|
|
}
|
|
|
|
async function sendReminder(project, daysUntilDue) {
|
|
try {
|
|
// Get users who should receive notifications (admins and team leads)
|
|
const recipients = db.prepare(`
|
|
SELECT id, name, role
|
|
FROM users
|
|
WHERE role IN ('admin', 'team_lead') AND is_active = 1
|
|
`).all();
|
|
|
|
if (recipients.length === 0) {
|
|
console.log("⚠️ No active admin or team lead users found to notify");
|
|
return;
|
|
}
|
|
|
|
const dayText = daysUntilDue === 1 ? "dzień" : "dni";
|
|
const title = `Projekt kończy się za ${daysUntilDue} ${dayText}`;
|
|
const message = `Projekt "${project.project_name}" (${project.customer || 'Brak klienta'}) kończy się ${new Date(project.finish_date).toLocaleDateString('pl-PL')}. Adres: ${project.address || 'Brak adresu'}.`;
|
|
|
|
for (const user of recipients) {
|
|
await createNotification({
|
|
userId: user.id,
|
|
type: NOTIFICATION_TYPES.DUE_DATE_REMINDER,
|
|
title,
|
|
message,
|
|
resourceType: "project",
|
|
resourceId: project.project_id.toString(),
|
|
actionUrl: `/projects/${project.project_id}`,
|
|
priority: daysUntilDue === 1 ? "urgent" : "high"
|
|
});
|
|
|
|
console.log(`📢 Reminder sent to ${user.name} (${user.role}) for project: ${project.project_name}`);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`❌ Failed to send reminder for project ${project.project_id}:`, error);
|
|
}
|
|
}
|
|
|
|
// Run the script
|
|
sendDueDateReminders(); |