From d90f65f4b73900c78235806dec68f0927b95e00c Mon Sep 17 00:00:00 2001 From: Lixian Zhou Date: Tue, 17 Mar 2026 14:58:11 -0400 Subject: [PATCH] fix --- app/services/reminderService.js | 120 ++++++++---------- .../center-calendar/CenterCalendar.js | 56 +++++++- 2 files changed, 104 insertions(+), 72 deletions(-) diff --git a/app/services/reminderService.js b/app/services/reminderService.js index 05f74ef..8badca2 100644 --- a/app/services/reminderService.js +++ b/app/services/reminderService.js @@ -3,35 +3,39 @@ const Vehicle = db.vehicle; const Customer = db.customer; const CalendarEvent = db.calendar_event; const moment = require('moment'); -const siteMap = { - 'ws1': 1, - 'worldshine1': 1, - 'ws2': 2, - 'worldshine2': 2, - 'ws3': 3, - 'worldshine3': 3, - 'worldshine4': 4, - 'ws4': 4, - 'worldshine.mayo.llc': 1 +const toSiteNumber = (value) => { + const parsed = Number(value); + return Number.isInteger(parsed) && parsed > 0 ? parsed : null; }; -const getSite = () => { - console.log('here', __dirname); - let site = 1; - const arr = __dirname.split('/'); - for (const key of Object.keys(siteMap)) { - if (arr.includes(key)) { - site = siteMap[key]; - break; - } - } - return site; -} +const getAllSites = async () => { + const [customerSites, vehicleSites, eventSites] = await Promise.all([ + Customer.distinct('site'), + Vehicle.distinct('site'), + CalendarEvent.distinct('site') + ]); + const normalizedSites = [...new Set([...customerSites, ...vehicleSites, ...eventSites] + .map(toSiteNumber) + .filter(Boolean))]; + return normalizedSites.length > 0 ? normalizedSites : [1]; +}; class ReminderService { parseDate(dateString) { + if (!dateString || typeof dateString !== 'string') return null; const [month, day, year] = dateString.split('/').map(Number); - return new Date(year, month-1,day); + if (!month || !day || !year) return null; + const parsedDate = new Date(year, month-1,day); + return Number.isNaN(parsedDate.getTime()) ? null : parsedDate; + } + + parseMonthDay(dateString) { + if (!dateString || typeof dateString !== 'string') return { month: -1, day: -1 }; + const [month, day] = dateString.split('/').map(Number); + return { + month: Number.isInteger(month) ? month : -1, + day: Number.isInteger(day) ? day : -1 + }; } async createVehicleRelatedEvents(daysAhead = 30) { @@ -39,15 +43,18 @@ class ReminderService { const futureDate = new Date(); futureDate.setDate(today.getDate() + daysAhead); // check 30 days ahead - const vehicles = await Vehicle.find({status: 'active', site: getSite()}); - const allVehiclesCalendarEvent = await CalendarEvent.find({type: 'reminder', status: 'active', site: getSite(), target_type: 'vehicle'}); - console.log('vehicles', vehicles); - for (const vehicle of vehicles) { - const insuranceExpireDate = this.parseDate(vehicle.insurance_expire_on); - const titleRegistrationDate = this.parseDate(vehicle.title_registration_on); + const sites = await getAllSites(); + for (const site of sites) { + const vehicles = await Vehicle.find({status: 'active', site}); + const allVehiclesCalendarEvent = await CalendarEvent.find({type: 'reminder', status: 'active', site, target_type: 'vehicle'}); + for (const vehicle of vehicles) { + const insuranceExpireDate = this.parseDate(vehicle.insurance_expire_on); + const titleRegistrationDate = this.parseDate(vehicle.title_registration_on); + if (!insuranceExpireDate || !titleRegistrationDate) continue; const titleExpireDate = new Date(titleRegistrationDate); titleExpireDate.setFullYear(titleExpireDate.getFullYear() + 1); const emissionTestDate = this.parseDate(vehicle.emission_test_on); + if (!emissionTestDate) continue; const emissionTestExpireDate = new Date(emissionTestDate); emissionTestExpireDate.setFullYear(titleExpireDate.getFullYear() + 1); const newDate = new Date(insuranceExpireDate); @@ -70,7 +77,7 @@ class ReminderService { target_name: vechile.vehicle_number, event_reminder_type: 'insurance_expire', status: 'active', - site: getSite(), + site, edit_date: new Date(), create_date: new Date(), create_by: 'system', @@ -94,7 +101,7 @@ class ReminderService { target_name: vechile.vehicle_number, event_reminder_type: 'emission_test', status: 'active', - site: getSite(), + site, edit_date: new Date(), create_date: new Date(), create_by: 'system', @@ -118,7 +125,7 @@ class ReminderService { target_name: vechile.vehicle_number, event_reminder_type: 'title_expire', status: 'active', - site: getSite(), + site, edit_date: new Date(), create_date: new Date(), create_by: 'system', @@ -127,6 +134,7 @@ class ReminderService { }) await calendarEvent.save(calendarEvent); } + } } } } @@ -143,44 +151,17 @@ class ReminderService { upcomingDates.push(targetDate) } - const allCustomers = await Customer.find({ site: getSite()}); - console.log('allCustomers', allCustomers); - const allExistingBirthdayReminders = await CalendarEvent.find({ type: 'reminder', status: 'active', rrule: 'FREQ=YEARLY', event_reminder_type: 'birthday', site: getSite() }); - const allExistingPaymentReminders = await CalendarEvent.find({ type: 'reminder', status: 'active', event_reminder_type: 'payment', site: getSite() }); + const sites = await getAllSites(); + for (const site of sites) { + const allCustomers = await Customer.find({ site }); + const allExistingBirthdayReminders = await CalendarEvent.find({ type: 'reminder', status: 'active', rrule: 'FREQ=YEARLY', event_reminder_type: 'birthday', site }); + const allExistingPaymentReminders = await CalendarEvent.find({ type: 'reminder', status: 'active', event_reminder_type: 'payment', site }); - for (const customer of allCustomers) { - const [month, day, year] = customer?.birth_date?.split('/').map(Number) || [-1, -1, -1]; - const [month1, day1, year1] = customer?.payment_due_date?.split('/').map(Number) || [-1, -1, -1]; + for (const customer of allCustomers) { + const { month, day } = this.parseMonthDay(customer?.birth_date); + const { month: month1, day: day1 } = this.parseMonthDay(customer?.payment_due_date); for (const targetDate of upcomingDates) { - if (month === targetDate.getMonth() + 1 && day === targetDate.getDate()) { - console.log('targetDate found', targetDate); - console.log('are you sure you find', allExistingBirthdayReminders.find((reminder) => reminder.target_uuid === customer?.id)); - if (!allExistingBirthdayReminders.find((reminder) => reminder.target_uuid === customer?.id)) { - console.log(123123); - const calendarEvent = new CalendarEvent({ - title: `${customer?.name}'s Birthday`, - description: `Happy Birthday to ${customer?.name}`, - type: 'reminder', - start_time: targetDate, - stop_time: new Date(targetDate.getTime() + 10*60*1000), - color: 'orange', - target_type: 'customer', - target_uuid: customer?.id, - target_name: customer?.name, - event_reminder_type: 'birthday', - rrule: 'FREQ=YEARLY', - status: 'active', - site: getSite(), - edit_date: new Date(), - create_date: new Date(), - create_by: 'system', - edit_by: 'system', - edit_history: [{employee: 'system', date: new Date()}] - }); - console.log('start to create', calendarEvent); - await calendarEvent.save(calendarEvent); - } - } + // Birthday reminders are rendered dynamically in Important Dates calendar. if (month1 === targetDate.getMonth() + 1 && day1 === targetDate.getDate()) { if (!allExistingPaymentReminders.find((reminder) => reminder.target_uuid === customer?.id)) { const calendarEvent = new CalendarEvent({ @@ -195,7 +176,7 @@ class ReminderService { target_name: customer?.name, event_reminder_type: 'payment', status: 'active', - site: getSite(), + site, edit_date: new Date(), create_date: new Date(), create_by: 'system', @@ -206,6 +187,7 @@ class ReminderService { } } } + } } } } diff --git a/client/src/components/center-calendar/CenterCalendar.js b/client/src/components/center-calendar/CenterCalendar.js index 2e09acc..24d5a37 100644 --- a/client/src/components/center-calendar/CenterCalendar.js +++ b/client/src/components/center-calendar/CenterCalendar.js @@ -217,6 +217,46 @@ const EventsCalendar = () => { return instances; }; + const buildDynamicBirthdayEvents = (customerList = [], rangeFrom, rangeTo) => { + if (!rangeFrom || !rangeTo) return []; + const from = new Date(rangeFrom); + const to = new Date(rangeTo); + const startYear = from.getFullYear(); + const endYear = to.getFullYear(); + const dynamicEvents = []; + + customerList.forEach((customer) => { + const birthDate = `${customer?.birth_date || ''}`; + const [month, day] = birthDate.split('/').map(Number); + if (!month || !day) return; + + for (let year = startYear; year <= endYear; year++) { + const birthday = new Date(year, month - 1, day); + if (Number.isNaN(birthday.getTime())) continue; + if (birthday < from || birthday > to) continue; + dynamicEvents.push({ + id: `dynamic-birthday-${customer.id}-${year}`, + type: 'reminder', + status: 'active', + title: `${customer?.name}'s Birthday`, + description: `Happy Birthday to ${customer?.name}`, + start_time: birthday, + stop_time: new Date(birthday.getTime() + 10 * 60 * 1000), + color: 'member_related', + target_type: 'customer', + target_uuid: customer?.id, + target_name: customer?.name, + event_reminder_type: 'birthday', + rrule: 'FREQ=YEARLY', + create_by: 'system-dynamic', + edit_by: 'system-dynamic' + }); + } + }); + + return dynamicEvents; + }; + const eventTypeMap = { medicalCalendar: 'medical', activitiesCalendar: 'activity', @@ -426,11 +466,21 @@ const EventsCalendar = () => { } } else { // Expand recurring event rules into individual instances and merge with regular events - const recurInstances = allEventRecurrences + let recurInstances = allEventRecurrences .filter(rule => rule.type === eventTypeMap[currentTab] && rule.status === 'active') .flatMap(rule => expandRecurrence(rule, fromDate, toDate)); - const originalEvents = [...allEvents, ...recurInstances]; - let filteredEvents = originalEvents?.filter(item => item.type === eventTypeMap[currentTab])?.map(item => { + let baseEvents = [...allEvents]; + if (currentTab === 'reminderDatesCalendar') { + // Birthday reminders are rendered dynamically from customer data. + baseEvents = baseEvents.filter((item) => item?.event_reminder_type !== 'birthday'); + recurInstances = recurInstances.filter((item) => item?.event_reminder_type !== 'birthday'); + } + const originalEvents = [...baseEvents, ...recurInstances]; + const dynamicBirthdayEvents = currentTab === 'reminderDatesCalendar' + ? buildDynamicBirthdayEvents(customers, fromDate, toDate) + : []; + const mergedEvents = [...originalEvents, ...dynamicBirthdayEvents]; + let filteredEvents = mergedEvents?.filter(item => item.type === eventTypeMap[currentTab])?.map(item => { // For Important Dates, remap old blue/orange colors to new member_related/vehicle_maintenance let eventColor = item?.color; if (currentTab === 'reminderDatesCalendar') {