import { getToken } from "next-auth/jwt"; import { NextApiRequest, NextApiResponse } from 'next' import { DayOfWeek, AvailabilityType } from '@prisma/client'; const common = require('../../src/helpers/common'); const data = require('../../src/helpers/data'); const subq = require('../../prisma/bl/subqueries'); import fs from 'fs'; import path from 'path'; import { all } from "axios"; /** * * @param req import { NextApiRequest, NextApiResponse } from 'next' * @param res import { NextApiRequest, NextApiResponse } from 'next' */ export default async function handler(req, res) { const prisma = common.getPrismaClient(); // Retrieve and validate the JWT token const token = await getToken({ req: req }); if (!token) { // If no token or invalid token, return unauthorized status return res.status(401).json({ message: "Unauthorized to call this API endpoint" }); } else { // If token is valid, log the user //console.log("JWT | User: " + token.email); } var action = req.query.action; var filter = req.query.filter; let day: Date; let isExactTime; if (req.query.date) { day = new Date(req.query.date); //date.setDate(date.getDate()); // Subtract one day to get the correct date, as calendar sends wrong date (one day ahead) //date.setHours(0, 0, 0, 0); } if (req.query.filterDate) { day = new Date(req.query.filterDate); isExactTime = true; } let monthInfo = common.getMonthDatesInfo(day); const searchText = req.query.searchText?.normalize('NFC'); try { switch (action) { case "initDb": // Read the SQL script from the file const sqlFilePath = path.join(process.cwd(), 'prisma', 'data.sql'); const sql = fs.readFileSync(sqlFilePath, 'utf8'); // Execute the SQL script await prisma.$executeRawUnsafe(sql); res.status(200).json({ message: "SQL script executed successfully" }); break; case "deleteAllPublishers": //get filter and delete all publishers containing that in first name or last name await prisma.publisher.deleteMany({ where: { OR: [ { firstName: { contains: filter } }, { lastName: { contains: filter } }, ], }, }); res.status(200).json({ "message": "ok" }); break; case "deleteAllAvailabilities": //get filter and delete all publishers containing that in first name or last name await prisma.availability.deleteMany({ where: filter ? { OR: [ // { name: { contains: filter } }, { starTime: { lte: day } } ] } : {} }); res.status(200).json({ "message": "ok" }); break; //gets publisher by names with availabilities and assignments case "deleteAvailabilityForPublisher": let publisherId = req.query.publisherId; let dateFor; if (req.query.date) { dateFor = new Date(req.query.date); //get month info from date monthInfo = common.getMonthDatesInfo(dateFor); } const deleteFromPreviousAssignments = common.parseBool(req.query.deleteFromPreviousAssignments); // if datefor is not null/undefined, delete availabilities for that month try { await prisma.availability.deleteMany({ where: { publisherId: publisherId, startTime: { gte: monthInfo?.firstMonday }, endTime: { lte: monthInfo?.lastSunday } } }); if (deleteFromPreviousAssignments) { await prisma.availability.deleteMany({ where: { publisherId: publisherId, isFromPreviousAssignment: true } }); } // await prisma.availability.deleteMany({ // where: { // publisherId: publisherId // } // }); res.status(200).json({ "message": "ok" }); } catch (error) { console.error("Error deleting availability for publisher: " + publisherId + " error: " + error); res.status(500).json({ error }); } break; case "createAvailabilities": { const availabilities = req.body; //! console.log("createAvailabilities: " + JSON.stringify(availabilities)); try { let createResults = await prisma.availability.createMany({ data: availabilities }); res.status(200).json({ "message": "ok", "results": createResults }); } catch (error) { console.error("Error creating availabilities: " + error); res.status(500).json({ error }); } } break; case "getCalendarEvents": let events = await getCalendarEvents(req.query.publisherId, day); res.status(200).json(events); case "getPublisherInfo": let pubs = await filterPublishers("id,firstName,lastName,email".split(","), "", null, req.query.assignments || true, req.query.availabilities || true, false, req.query.id); res.status(200).json(pubs[0]); break; case "getMonthlyStatistics": let allpubs = await getMonthlyStatistics("id,firstName,lastName,email", day); res.status(200).json(allpubs); break; case "getUnassignedPublishers": //let monthInfo = common.getMonthDatesInfo(date); let allPubs = await filterPublishers("id,firstName,lastName,email,isActive".split(","), "", day, true, true, false); let unassignedPubs = allPubs.filter(pub => pub.currentMonthAssignments == 0 && pub.availabilities.length > 0); res.status(200).json(unassignedPubs); break; case "filterPublishers": const fetchAssignments = common.parseBool(req.query.assignments); const fetchAvailabilities = common.parseBool(req.query.availabilities); let publishers = await filterPublishers(req.query.select, searchText, day, fetchAssignments, fetchAvailabilities); //!console.log("publishers: (" + publishers.length + ") " + JSON.stringify(publishers.map(pub => pub.firstName + " " + pub.lastName))); res.status(200).json(publishers); break; case "filterPublishersNew": let includeOldAvailabilities = common.parseBool(req.query.includeOldAvailabilities); let results = await filterPublishersNew_Available(req.query.select, day, common.parseBool(req.query.isExactTime), common.parseBool(req.query.isForTheMonth), includeOldAvailabilities); res.status(200).json(results); break; // find publisher by full name or email case "findPublisher": const getAll = common.parseBool(req.query.all) || false; let publisher = await data.findPublisher(filter, req.query.email, req.query.select, getAll); res.status(200).json(publisher); break; case "getShiftsForDay": // Setting the range for a day: starting from the beginning of the date and ending just before the next date. let startOfDay = new Date(day.getFullYear(), day.getMonth(), day.getDate()); let endOfDay = new Date(day.getFullYear(), day.getMonth(), day.getDate(), 23, 59, 59, 999); const pubAvCount = await prisma.publisher.findMany({ select: { id: true, firstName: true, lastName: true, _count: { select: { availabilities: true, } } } }); let shiftsForDate = await prisma.shift.findMany({ where: { startTime: { gte: startOfDay, lt: endOfDay }, }, include: { assignments: { include: { publisher: subq.publisherSelect }, }, }, }); shiftsForDate.forEach(shift => { shift.assignments.forEach(assignment => { assignment.publisher.availabilityCount = pubAvCount.find(pub => pub.id === assignment.publisher.id)?._count?.availabilities || 0; }); } ); console.log("shiftsForDate(" + day + ") - " + shiftsForDate.length + " : " + JSON.stringify(shiftsForDate.map(shift => shift.id))); res.status(200).json(shiftsForDate); break; case "copyOldAvailabilities": //get all publishers that don't have availabilities for the current month monthInfo = common.getMonthDatesInfo(day); // await prisma.availability.deleteMany({ // where: { // startTime: { // gte: monthInfo.firstMonday, // }, // isFromPreviousMonth: true // } // }); let outdatedPubs = await prisma.publisher.findMany({ where: { availabilities: { none: { startTime: { gte: monthInfo.firstMonday, } } } }, select: { id: true, firstName: true, lastName: true, availabilities: true } }); outdatedPubs.forEach(async pub => { // avail.startTime >= monthInfo.firstMonday //get prev month date: let prevMonth = new Date(monthInfo.firstMonday); prevMonth.setMonth(prevMonth.getMonth() - 1); let prevMonthInfo = common.getMonthDatesInfo(prevMonth); pub.availabilities = pub.availabilities.filter(avail => avail.startTime > prevMonthInfo.firstMonday); console.log("" + pub.firstName + " " + pub.lastName + " copying " + pub.availabilities.length + " availabilities from previous months."); pub.availabilities.forEach(async avail => { //get the new date based on the day of week and week of month if (!avail.weekOfMonth) { avail.weekOfMonth = common.getWeekOfMonth(avail.startTime) } let origMonthInfo = common.getMonthDatesInfo(avail.startTime); let newStart = common.getDateFromWeekNrAndDayOfWeek(monthInfo.firstMonday, avail.weekOfMonth, avail.dayofweek, avail.startTime); //ToDo: fix double check. also check if we're in 5th week and the month has 4 weeks // const availability = await data.findPublisherAvailability(publisher.id, newStart); // if (availability) { // return; // } let newEnd = new Date(newStart.getTime()); newEnd.setHours(avail.endTime.getHours(), avail.endTime.getMinutes(), 0, 0); let data = { publisherId: pub.id, dayOfMonth: null, dayofweek: avail.dayofweek || common.getDayOfWeekNameEnEnumForDate(avail.startTime), weekOfMonth: avail.weekofMonth || common.getWeekOfMonth(avail.startTime), // null for auto generated availabilities //dateOfEntry: new Date(), //avail.dateOfEntry || avail.startTime, startTime: newStart, endTime: newEnd, type: AvailabilityType.Monthly, isFromPreviousMonth: true, name: avail.name || "старо предпочитание", parentAvailabilityId: avail.id, // parentAvailability: { // connect: { // id: avail.id // } // }, } await prisma.availability.create({ data: data }); //if month has 5 weeks and the monthInfo has 4 weeks copy the availabilities also from the 1st week to the 5th week if (monthInfo.nrOfWeeks == 5 && avail.weekOfMonth == 1 && origMonthInfo.nrOfWeeks == 4) { newStart = common.getDateFromWeekNrAndDayOfWeek(monthInfo.firstMonday, 5, avail.dayofweek, avail.startTime); newEnd = new Date(newStart.getTime()); newEnd.setHours(avail.endTime.getHours(), avail.endTime.getMinutes(), 0, 0); data.weekOfMonth = 5; data.startTime = newStart; data.endTime = newEnd; await prisma.availability.create({ data: data }); } }); }); //convert old assignments to availabilities res.status(200).json({ "message": "ok" }); break; case "deleteCopiedAvailabilities": //delete all availabilities that are copied from previous months monthInfo = common.getMonthDatesInfo(day); await prisma.availability.deleteMany({ where: { startTime: { gte: monthInfo.firstMonday, }, isFromPreviousMonth: true } }); case "replaceInAssignment": const { oldPublisherId, newPublisherId, shiftId } = req.method === "POST" ? req.body : req.query; const result = await replaceInAssignment(oldPublisherId, newPublisherId, shiftId); res.status(200).json(result); break; case "updateShifts": //get all shifts for the month and publish them (we pass date ) let isPublished = common.parseBool(req.query.isPublished); let updated = await prisma.shift.updateMany({ where: { startTime: { gte: new Date(monthInfo.firstMonday.getFullYear(), monthInfo.firstMonday.getMonth(), 1), lt: new Date(monthInfo.lastSunday.getFullYear(), monthInfo.lastSunday.getMonth() + 1, 1), } }, data: { isPublished: isPublished } }); console.log("Updated shifts: " + updated.count); res.status(200).json({ "message": "ok" }); break; case "getPossibleShiftPublisherEmails": const subscribedPublishers = await prisma.publisher.findMany({ where: { isSubscribedToCoverMe: true }, select: { id: true, firstName: true, lastName: true, email: true } }).then(pubs => { return pubs.map(pub => { return { id: pub.id, name: pub.firstName + " " + pub.lastName, email: pub.email } }); }); let shift = await prisma.shift.findUnique({ where: { id: parseInt(req.query.shiftId) } }); let availablePublishers = await filterPublishersNew_Available("id,firstName,lastName,email", new Date(shift.startTime), true, false); //return names and email info only availablePublishers = availablePublishers.map(pub => { return { id: pub.id, name: pub.firstName + " " + pub.lastName, email: pub.email } }); res.status(200).json({ shift, availablePublishers: availablePublishers, subscribedPublishers }); break; default: res.status(200).json({ "message": "no action '" + action + "' found" }); break; } } catch (error) { console.error("API: Error executing action: " + action + " with filter: " + filter + " error: " + error); res.status(500).json({ error }); } } export async function getMonthlyStatistics(selectFields, filterDate) { let publishers = []; selectFields = selectFields?.split(","); let selectBase = selectFields.reduce((acc, curr) => { acc[curr] = true; return acc; }, {}); selectBase.assignments = { select: { id: true, shift: { select: { id: true, startTime: true, endTime: true } } } }; let currentWeekStart: Date, currentWeekEnd: Date, currentMonthStart: Date, currentMonthEnd: Date, previousMonthStart: Date, previousMonthEnd: Date; let date = new Date(filterDate); date.setDate(filterDate.getDate()); currentWeekStart = common.getStartOfWeek(date); currentWeekEnd = common.getEndOfWeek(date); var monthInfo = common.getMonthDatesInfo(date); currentMonthStart = monthInfo.firstMonday; currentMonthEnd = monthInfo.lastSunday; date.setMonth(date.getMonth() - 1); monthInfo = common.getMonthDatesInfo(date); previousMonthStart = monthInfo.firstMonday; previousMonthEnd = monthInfo.lastSunday; const prisma = common.getPrismaClient(); publishers = await prisma.publisher.findMany({ select: { ...selectBase, } }); publishers.forEach(pub => { // Debug logs to help identify issues pub.currentWeekAssignments = pub.assignments.filter(assignment => { return assignment.shift.startTime >= currentWeekStart && assignment.shift.startTime <= currentWeekEnd; }).length; pub.currentMonthAssignments = pub.assignments.filter(assignment => { return assignment.shift.startTime >= currentMonthStart && assignment.shift.startTime <= currentMonthEnd; }).length; pub.previousMonthAssignments = pub.assignments.filter(assignment => { return assignment.shift.startTime >= previousMonthStart && assignment.shift.startTime <= previousMonthEnd; }).length; }); return publishers; } export async function filterPublishersNew_Available(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true, includeOldAvailabilities = false) { return data.filterPublishersNew(selectFields, filterDate, isExactTime, isForTheMonth, isWithStats, includeOldAvailabilities); } // availabilites filter: // 1. if dayOfMonth is null, match by day of week (enum) // 2. if dayOfMonth is not null, match by date // 3. if date is 00:00:00, match by date only (without time) // 4. if date is not 00:00:00, it should be in the range of start and end times // this way we distinguish between weekly availabiillities (entered without dayOfMonth) and old availabilities from previous months (entered with dayOfMonth, but we set it to null), // (To validate) we use useDateFilter in combination with the filterDate to get publishers without availabilities for the day: // 1: useDateFilter = false, filterDate = null - get all publishers with availabilities for the current month // 2: useDateFilter = false, filterDate = date - get all publishers with availabilities for the current month // 3: useDateFilter = true, filterDate = null - get all publishers with availabilities for the current month // 4: useDateFilter = true, filterDate = date - get all publishers with availabilities for the current month and filter by date export async function filterPublishers(selectFields, searchText, filterDate, fetchAssignments: boolean = true, fetchAvailabilities: boolean = true, useDateFilter = true, id = null) { let currentWeekStart: Date, currentWeekEnd: Date, currentMonthStart: Date, currentMonthEnd: Date, previousMonthStart: Date, previousMonthEnd: Date, filterDateEnd: Date, publishers = []; if (!filterDate) { useDateFilter = false; previousMonthStart = new Date(); previousMonthStart.setDate(1); previousMonthStart.setMonth(previousMonthStart.getMonth() - 1); } else { let date = new Date(filterDate.getTime()); //date.setDate(filterDate.getDate()); currentWeekStart = common.getStartOfWeek(date); currentWeekEnd = common.getEndOfWeek(date); var monthInfo = common.getMonthDatesInfo(date); currentMonthStart = monthInfo.firstMonday; currentMonthEnd = monthInfo.lastSunday; date.setMonth(date.getMonth() - 1); monthInfo = common.getMonthDatesInfo(date); previousMonthStart = monthInfo.firstMonday; previousMonthEnd = monthInfo.lastSunday; filterDateEnd = new Date(filterDate); filterDateEnd.setHours(23, 59, 59, 999); } let whereClause = {}; if (id) { whereClause = { id: String(id) } } const searchTextString = String(searchText || "").trim(); if (searchTextString !== "") { whereClause = { OR: [ { firstName: { contains: searchTextString } }, { lastName: { contains: searchTextString } }, ], }; } // Base select fields // Only attempt to split if selectFields is a string; otherwise, use it as it is. selectFields = typeof selectFields === 'string' ? selectFields.split(",") : selectFields; let selectBase = selectFields.reduce((acc, curr) => { acc[curr] = true; return acc; }, {}); // If assignments flag is true, fetch assignments if (fetchAssignments) { //!! WORKING CODE, but heavy on the DB !! selectBase.assignments = { select: { id: true, shift: { select: { id: true, startTime: true, endTime: true } } }, where: { shift: { OR: [ // { // startTime: { // gte: currentWeekStart, // lte: currentWeekEnd // } // }, // { // startTime: { // gte: currentMonthStart, // lte: currentMonthEnd // } // }, { startTime: { gte: previousMonthStart, // lte: previousMonthEnd } }, ] } } }; //selectBase.assignments = true; } let dayOfWeekEnum: DayOfWeek if (filterDate) { // Determine day of week using common function dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(filterDate); if (filterDate.getHours() > 21 || filterDate.getHours() < 6) { filterDate.setHours(0, 0, 0, 0); // Set to midnight } } // console.log(`filterDate: ${filterDate}`); // console.log(`filterDateEnd: ${filterDateEnd}`); if (filterDate && useDateFilter) { // Info, description and ToDo: // We should distinguish between availabilities with dayOfMonth and without // If dayOfMonth is null, we should match by day of week using the enum // If dayOfMonth is not null, we should match by date. // if date is 00:00:00, we should match by date only (without time) // if date is not 00:00:00, it should be in the range of start and end times // we should also include availabilities from previous assignments but not with preference - dayOfMonth is null. we shuold include them only if they match the day of week // and distinguish between weekly availabiillities (entered without dayOfMonth) and old availabilities from previous months (entered with dayOfMonth, but we set it to null), // which we count as weekly availabilities. We can use the type field for that //console.log(`filterDate: ${filterDate}. date: ${filterDate.getDate()}. dayOfWeekEnum: ${dayOfWeekEnum}. useDateFilter: ${useDateFilter}`); // we will have 3 cases: up-to date availabilities, old availabilities from previous months and availabilities from previous assignments but not with preference // we will use the type field to distinguish between them // up-to date availabilities will have type = 1 // old availabilities from previous months will have type = 2 - we want to drop that function to simplify the code and avoid confusion // availabilities from previous assignments but not with preference will have type = 3 // also, permanent weekly availabilities will have dayOfMonth = null and type = 0 // for 0 we will match by dayOfWeekEnum and times // for 1 we will match by exact date and times // for 2 we will match by dayofweek, weekOfMonth and times // for 3 we will match by dayofweek, weekOfMonth and times - this is the same as 2, but we will not count them as availabilities for the current month // generaion of schedule: /* option 1: fill from blank - first two places per shift, then more if possible option 2: fill from previous schedule , remove all assignments where new availabilities are not available and permanent availabilities to make room for changes (we want to shuffle if possible??? do we?) continue with option 1 from there which one depends on if we prioritize empty shifts or making sure everyone has up to date availabilities */ //substract the time difference between from ISO string and local time const offset = filterDate.getTimezoneOffset() * 60000; // offset in milliseconds var dateAsISO = new Date(filterDate.getTime() + offset); //if full day, match by date only if (filterDate.getHours() == 0 || dateAsISO.getHours() == 0) { whereClause["availabilities"] = { some: { OR: [ // Check only by date without considering time ( Assignments on specific days without time) { startTime: { gte: filterDate }, endTime: { lte: filterDateEnd }, } , // Check if dayOfMonth is null and match by day of week using the enum (Assigments every week) // This includes availabilities from previous assignments but not with preference { dayOfMonth: null, // includes monthly and weekly repeats dayofweek: dayOfWeekEnum, // ToDo: and weekOfMonth startTime: { lte: filterDate }, AND: [ { OR: [ // OR condition for repeatUntil to handle events that either end after filterDate or repeat forever { endDate: { gte: filterDate } }, { endDate: null } ] } ] } ] } }; } //if not full day, match by date and time else { //match exact time (should be same as data.findPublisherAvailability()) whereClause["availabilities"] = { some: { OR: [ // Check if dayOfMonth is set and filterDate is between start and end dates (Assignments on specific days AND time) { dayOfMonth: filterDate.getDate(), startTime: { lte: filterDate }, endTime: { gte: filterDate } }, // Check if dayOfMonth is null and match by day of week using the enum (Assigments every week) { dayOfMonth: null, dayofweek: dayOfWeekEnum, startTime: { gte: filterDate }, } ] } }; } } else { // we use month filter if date is passed and useDateFilter is false to get all publishers with availabilities for the current month if (fetchAvailabilities) { // If no filter date, return all publishers's availabilities for currentMonthStart whereClause["availabilities"] = { some: { OR: [ // Check if dayOfMonth is not null and startTime is after currentMonthStart (Assignments on specific days AND time) { dayOfMonth: { not: null }, startTime: { gte: currentMonthStart }, endTime: { lte: currentMonthEnd } }, // Check if dayOfMonth is null and match by day of week using the enum (Assigments every week) { dayOfMonth: null, } ] } }; //try here // selectBase.аvailabilities = { // select: { // dayofweek: true, // dayOfMonth: true, // startTime: true, // endTime: true, // weekOfMonth: true, // type: true // }, // where: { // OR: [ // { // startTime: { gte: currentMonthStart }, // endTime: { lte: currentMonthEnd } // } // ] // } // } } } //include availabilities if flag is true const prisma = common.getPrismaClient(); //why we need to get it again? publishers = await prisma.publisher.findMany({ where: whereClause, select: { ...selectBase, ...(fetchAvailabilities && { availabilities: true }), // ...(fetchAssignments && { // assignments: { // include: { // shift: true, // } // } // } // ) } }); console.log(`publishers: ${publishers.length}, WhereClause: ${JSON.stringify(whereClause)}`); if (filterDate) { if (fetchAssignments) { //get if publisher has assignments for current weekday, week, current month, previous month publishers.forEach(pub => { // Filter assignments for current day pub.currentDayAssignments = pub.assignments?.filter(assignment => { return assignment.shift.startTime >= filterDate && assignment.shift.startTime <= filterDateEnd; }).length; // Filter assignments for current week pub.currentWeekAssignments = pub.assignments?.filter(assignment => { return assignment.shift.startTime >= currentWeekStart && assignment.shift.startTime <= currentWeekEnd; }).length; // Filter assignments for current month pub.currentMonthAssignments = pub.assignments?.filter(assignment => { return assignment.shift.startTime >= currentMonthStart && assignment.shift.startTime <= currentMonthEnd; }).length; // Filter assignments for previous month pub.previousMonthAssignments = pub.assignments?.filter(assignment => { return assignment.shift.startTime >= previousMonthStart && assignment.shift.startTime <= previousMonthEnd; }).length; }); } } if (fetchAvailabilities) { //get the availabilities for the day. Calcullate: //1. how many days the publisher is available for the current month - only with dayOfMonth //2. how many days the publisher is available without dayOfMonth (previous months count) //3. how many hours in total the publisher is available for the current month publishers.forEach(pub => { pub.currentMonthAvailability = pub.availabilities?.filter(avail => { // return avail.dayOfMonth != null && avail.startTime >= currentMonthStart && avail.startTime <= currentMonthEnd; return avail.startTime >= currentMonthStart && avail.startTime <= currentMonthEnd; }) pub.currentMonthAvailabilityDaysCount = pub.currentMonthAvailability.length || 0; // pub.currentMonthAvailabilityDaysCount += pub.availabilities.filter(avail => { // return avail.dayOfMonth == null; // }).length; pub.currentMonthAvailabilityHoursCount = pub.currentMonthAvailability.reduce((acc, curr) => { return acc + (curr.endTime.getTime() - curr.startTime.getTime()) / (1000 * 60 * 60); }, 0); //if pub has ever filled the form - if has availabilities which are not from previous assignments pub.hasEverFilledForm = pub.availabilities?.some(avail => { return avail.isFromPreviousAssignments == false; }); //if pub has up-to-date availabilities (with dayOfMonth) for the current month pub.hasUpToDateAvailabilities = pub.availabilities?.some(avail => { return avail.dayOfMonth != null && avail.startTime >= currentMonthStart; // && avail.startTime <= currentMonthEnd; }); //if pub has availabilities for the current day pub.hasAvailabilityForCurrentDay = pub.availabilities?.some(avail => { return avail.startTime >= filterDate && avail.startTime <= filterDateEnd; }); }); if (filterDate && useDateFilter) { // Post filter for time if dayOfMonth is null // Modify the availabilities array of the filtered publishers publishers.forEach(pub => { pub.availabilities = pub.availabilities?.filter(avail => matchesAvailability(avail, filterDate)); }); } } return publishers; } function matchesAvailability(avail, filterDate) { // Setting the start and end time of the filterDate filterDate.setHours(0, 0, 0, 0); const filterDateEnd = new Date(filterDate); filterDateEnd.setHours(23, 59, 59, 999); // Return true if avail.startTime is between filterDate and filterDateEnd return avail.startTime >= filterDate && avail.startTime <= filterDateEnd; } async function getCalendarEvents(publisherId, date, availabilities = true, assignments = true) { const result = []; let pubs = await filterPublishers("id,firstName,lastName,email".split(","), "", date, assignments, availabilities, date ? true : false, publisherId); let publisher = pubs[0]; if (publisher) { if (availabilities) { publisher.availabilities?.forEach(item => { result.push({ ...item, title: common.getTimeFomatted(new Date(item.startTime)) + "-" + common.getTimeFomatted(new Date(item.endTime)), //item.name, date: new Date(item.startTime), startTime: new Date(item.startTime), endTime: new Date(item.endTime), publisherId: publisher.id, type: "availability", isFromPreviousAssignment: item.isFromPreviousAssignment, }); }); } if (assignments) { publisher.assignments?.forEach(item => { result.push({ ...item, title: common.getTimeFomatted(new Date(item.shift.startTime)) + "-" + common.getTimeFomatted(new Date(item.shift.endTime)), date: new Date(item.shift.startTime), startTime: new Date(item.shift.startTime), endTime: new Date(item.shift.endTime), publisherId: item.publisherid, type: "assignment", }); }); } } return result; } async function replaceInAssignment(oldPublisherId, newPublisherId, shiftId) { const prisma = common.getPrismaClient(); const result = await prisma.assignment.updateMany({ where: { publisherId: oldPublisherId, shiftId: shiftId }, data: { publisherId: newPublisherId, isConfirmed: false, isBySystem: true, isMailSent: false } }); return result; }