diff --git a/components/calendar/avcalendar.tsx b/components/calendar/avcalendar.tsx index c631c72..577c53e 100644 --- a/components/calendar/avcalendar.tsx +++ b/components/calendar/avcalendar.tsx @@ -274,6 +274,11 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { //if event is not active - show in gray let bgColorClass = 'bg-gray-500'; // Default color for inactive events var bgColor = event.isActive ? "" : "bg-gray-500"; + //ToDo: fix this. maybe we're missing some properties + // if (event.isFromPreviousMonth) { + // // set opacity to 0.5 + // bgColor = "bg-orange-500"; + // } if (event.type === "assignment") { bgColor = event.isBySystem ? "bg-red-500" : (event.isConfirmed ? "bg-green-500" : "bg-yellow-500"); @@ -297,11 +302,14 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { } } + eventStyle = { ...style, // backgroundColor: bgColorClass, //height: "50px", //color: 'white', + //if (event.isFromPreviousAssignment) { set opacity to 0.5 } + // opacity: event.isFromPreviousMonth ? 0.5 : 1, whiteSpace: 'normal', // Allow the text to wrap to the next line overflow: 'hidden', // Hide overflowed content textOverflow: 'ellipsis' // Add ellipsis to text that's too long to fit diff --git a/pages/api/index.ts b/pages/api/index.ts index 7e4658f..4aa8313 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -1,6 +1,6 @@ import { getToken } from "next-auth/jwt"; import { NextApiRequest, NextApiResponse } from 'next' -import { DayOfWeek } from '@prisma/client'; +import { DayOfWeek, AvailabilityType } from '@prisma/client'; const common = require('../../src/helpers/common'); const data = require('../../src/helpers/data'); const subq = require('../../prisma/bl/subqueries'); @@ -221,7 +221,7 @@ export default async function handler(req, res) { // isFromPreviousMonth: true // } // }); - let oldpubs = await prisma.publisher.findMany({ + let outdatedPubs = await prisma.publisher.findMany({ where: { availabilities: { none: { @@ -238,28 +238,55 @@ export default async function handler(req, res) { availabilities: true } }); - oldpubs.forEach(async pub => { + 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 - let newStart = common.getDateFromWeekNrAndDayOfWeek(avail.weekNr, avail.dayofweek, avail.startTime); + 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); - await prisma.availability.create({ - data: { - publisherId: pub.id, - dayOfMonth: null, - dayofweek: avail.dayofweek || common.getDayOfWeekNameEnEnum(avail.startTime), - weekOfMonth: avail.weekofMonth || common.getWeekOfMonth(avail.startTime), - dateOfEntry: new Date(), //avail.dateOfEntry || avail.startTime, - startTime: avail.startTime, - endTime: avail.endTime, - weekNr: avail.weekNr, - type: 3, - isFromPreviousMonth: true, - name: avail.name || "старо предпочитание", - } - }); + let data = { + publisherId: pub.id, + dayOfMonth: null, + dayofweek: avail.dayofweek || common.getDayOfWeekNameEnEnum(avail.startTime), + weekOfMonth: avail.weekofMonth || common.getWeekOfMonth(avail.startTime), + dateOfEntry: new Date(), //avail.dateOfEntry || avail.startTime, + startTime: newStart, + endTime: newEnd, + type: AvailabilityType.Monthly, + isFromPreviousMonth: true, + name: avail.name || "старо предпочитание", + parentAvailabilityId: 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 }); + } + }); }); @@ -531,8 +558,8 @@ export async function filterPublishers(selectFields, searchText, filterDate, fet // 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, weeknr and times - // for 3 we will match by dayofweek, weeknr and times - this is the same as 2, but we will not count them as availabilities for the current month + // 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: @@ -562,7 +589,7 @@ export async function filterPublishers(selectFields, searchText, filterDate, fet { dayOfMonth: null, dayofweek: dayOfWeekEnum, - // ToDo: and weekNr + // ToDo: and weekOfMonth //startTime: { gte: currentMonthStart }, } ] @@ -614,7 +641,7 @@ export async function filterPublishers(selectFields, searchText, filterDate, fet // dayOfMonth: true, // startTime: true, // endTime: true, - // weekNr: true, + // weekOfMonth: true, // type: true // }, // where: { diff --git a/pages/cart/publishers/stats.tsx b/pages/cart/publishers/stats.tsx index c0161a9..d881b1f 100644 --- a/pages/cart/publishers/stats.tsx +++ b/pages/cart/publishers/stats.tsx @@ -107,8 +107,13 @@ export const getServerSideProps = async (context) => { } } }); + //remove availabilities that isFromPreviousAssignment + publisher.availabilities = publisher.availabilities.filter(availability => !availability.isFromPreviousAssignment); + }); + //remove publishers without availabilities + publishers = publishers.filter(publisher => publisher.availabilities.length > 0); return { props: { diff --git a/prisma/migrations/20240325214807_misc_renames/migration.sql b/prisma/migrations/20240325214807_misc_renames/migration.sql index 2a2f6e7..890be7a 100644 --- a/prisma/migrations/20240325214807_misc_renames/migration.sql +++ b/prisma/migrations/20240325214807_misc_renames/migration.sql @@ -4,12 +4,18 @@ - You are about to drop the column `isTentative` on the `Assignment` table. All the data in the column will be lost. */ + -- AlterTable -ALTER TABLE `Assignment` - ADD COLUMN `isBySystem` BOOLEAN NOT NULL DEFAULT false; +ALTER TABLE `Assignment` + ADD COLUMN `isBySystem` BOOLEAN NOT NULL DEFAULT FALSE; + +-- Depending on your DBMS, you might need to execute one statement at a time. +-- Especially, the UPDATE statement should be run separately. UPDATE `Assignment` SET `isBySystem` = isTentative; -ALTER TABLE `Assignment` DROP COLUMN `isTentative`, +-- Drop the isTentative column +ALTER TABLE `Assignment` DROP COLUMN `isTentative`; -- AlterTable -ALTER TABLE `Report` ADD COLUMN `type` ENUM('ServiceReport', 'Experience', 'Feedback_Problem', 'Feedback_Suggestion', 'Feedback') NOT NULL DEFAULT 'ServiceReport'; +ALTER TABLE `Report` + ADD COLUMN `type` ENUM('ServiceReport', 'Experience', 'Feedback_Problem', 'Feedback_Suggestion', 'Feedback') NOT NULL DEFAULT 'ServiceReport'; diff --git a/prisma/migrations/20240328162213_add_availability_self_ref/migration.sql b/prisma/migrations/20240328162213_add_availability_self_ref/migration.sql new file mode 100644 index 0000000..c2d2a1e --- /dev/null +++ b/prisma/migrations/20240328162213_add_availability_self_ref/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE `availability` ADD COLUMN `parentAvailabilityId` INTEGER NULL; + +-- AddForeignKey +ALTER TABLE `Availability` ADD CONSTRAINT `Availability_parentAvailabilityId_fkey` FOREIGN KEY (`parentAvailabilityId`) REFERENCES `Availability`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 91b56e0..b76fc17 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -141,7 +141,10 @@ model Availability { repeatWeekly Boolean? // New field to indicate weekly repetition // until now dayofweek was used for repetition when dayOfMonth is null repeatFrequency Int? // New field to indicate repetition frequency endDate DateTime? // New field for the end date of repetition - dateOfEntry DateTime? //NEW v1.0.1 trade storage for intuintivity + dateOfEntry DateTime? //NEW v1.0.1 + parentAvailabilityId Int? + parentAvailability Availability? @relation("ParentAvailability", fields: [parentAvailabilityId], references: [id]) + ChildAvailabilities Availability[] @relation("ParentAvailability") } model CartEvent { diff --git a/server.js b/server.js index f420718..324eb33 100644 --- a/server.js +++ b/server.js @@ -84,6 +84,8 @@ nextApp next(); }); server.use("/favicon.ico", express.static("styles/favicon_io/favicon.ico")); + // server.use("/robots.txt", express.static("styles/favicon_io/robots.txt")); + // server.use("/sitemap.xml", express.static("styles/favicon_io/sitemap.xml")); server.get("/last_schedule_json", (req, res) => { // var data = JSON.parse(fs.readFileSync("./content/sources/march_flat.json", "utf8")); @@ -557,7 +559,7 @@ nextApp }); //check if ssl is enabled - if (process.env.SSL_ENABLED === "true") { + if (process.env.SSL_ENABLED === "true" || 1 == 1) { console.log("SSL_ENABLED = true"); // Redirect from http to https // server.use((req, res, next) => { diff --git a/src/helpers/common.js b/src/helpers/common.js index d706006..a72ab2f 100644 --- a/src/helpers/common.js +++ b/src/helpers/common.js @@ -248,6 +248,33 @@ exports.getWeekOfMonth = function (inputDate) { return weekNumber; }; +exports.getDateFromWeekNrAndDayOfWeek = function (firstMonday, weekNr, dayOfWeekEnum, startTime) { + firstMonday = new Date(firstMonday); + startTime = new Date(startTime); + if (!weekNr || weekNr < 1 || weekNr > 5) { + weekNr = this.getWeekOfMonth(startTime); + } + //get int from dayOfWeekEnum + let dayOfWeekNr = this.getDayOfWeekIndex(dayOfWeekEnum); + if (dayOfWeekNr < 0 || dayOfWeekNr > 6) { + dayOfWeekNr = 0; + } + + // Calculate the day offset from the first Monday of the month + // Note: Assuming dayOfWeekEnum starts from 0 (Monday) to 6 (Sunday) + const daysFromFirstMonday = (weekNr - 1) * 7 + dayOfWeekNr; + + // Calculate the new date + let newStart = new Date(firstMonday); + newStart.setDate(firstMonday.getDate() + daysFromFirstMonday); + + // Extract time from startTime and apply it to newStart + const time = new Date(startTime); + newStart.setHours(time.getHours(), time.getMinutes(), time.getSeconds()); + + return newStart; +} + exports.getMonthDatesInfo = function (date) { // get first day of the month var firstDay = new Date(date.getFullYear(), date.getMonth(), 1); @@ -285,6 +312,9 @@ exports.getMonthDatesInfo = function (date) { // lastSunday.setDate(firstDayNextMonth.getDate() - firstDayNextMonth.getDay()); //logger.debug("Last Sunday: ", lastSunday); + const diffInDays = (lastSunday - firstMonday) / (1000 * 60 * 60 * 24); + // Calculate number of weeks, rounding up for partial weeks + const nrOfWeeks = Math.ceil((diffInDays + 1) / 7); return { firstDay: firstDay, @@ -295,7 +325,7 @@ exports.getMonthDatesInfo = function (date) { date: date, monthName: monthName, year: date.getFullYear(), - nrOfWeeks: Math.ceil((lastMonday.getDate() - firstMonday.getDate()) / 7) + nrOfWeeks: nrOfWeeks }; }; exports.getMonthInfo = exports.getMonthDatesInfo; diff --git a/src/helpers/data.js b/src/helpers/data.js index af888ce..dc39ff2 100644 --- a/src/helpers/data.js +++ b/src/helpers/data.js @@ -79,10 +79,7 @@ async function findPublisher(names, email, select, getAll = false) { } async function findPublisherAvailability(publisherId, date) { - const prisma = common.getPrismaClient(); - const dayOfWeek = common.getDayOfWeekNameEnEnum(date); // Assuming common.getDayOfWeekNameEnEnum returns the day of week - //const weekOfMonth = common.getWeekOfMonth(date); // Assuming common.getWeekOfMonth returns the week of month date = new Date(date); // Convert to date object if not already const hours = date.getHours(); const minutes = date.getMinutes(); @@ -90,32 +87,24 @@ async function findPublisherAvailability(publisherId, date) { const potentialAvailabilities = await prisma.availability.findMany({ where: { publisherId: publisherId, - OR: [ + AND: [ // Ensure both conditions must be met { - // Exact date match startTime: { - gte: new Date(date.setHours(0, 0, 0, 0)), - lt: new Date(date.setHours(23, 59, 59, 999)) - } + lte: new Date(date), // startTime is less than or equal to the date + }, }, { - // Correct day of week and before the date, with endDate consideration - dayofweek: dayOfWeek, - OR: [ - { - endDate: null - }, - { - endDate: { - gt: date - } - } - ] - } - ] + endTime: { + gte: new Date(date), // endTime is greater than or equal to the date + }, + }, + ], } }); + if (potentialAvailabilities.length === 0) { + return null; // No availability found + } // Filter the results based on time and other criteria when not exact date match const availability = potentialAvailabilities.find(avail => { const availStartHours = avail.startTime.getHours(); diff --git a/src/helpers/excel.js b/src/helpers/excel.js index fd361bc..30efda8 100644 --- a/src/helpers/excel.js +++ b/src/helpers/excel.js @@ -481,23 +481,22 @@ exports.processEvents = async function (events, year, monthNumber, progressCallb // create availability with the same date as the event. //ToDo: add parameter to control if we want to create availability for each event. can be done whe we import previous shifts. - // if (createAvailabilities) { - // const dayofWeek = common.getDayOfWeekNameEnEnum(date); - // const availability = await prisma.availability.create({ - // data: { - // publisherId: publisher.id, - // //date: date, - // dayofweek: dayofWeek, - // startTime: startTime, - // endTime: endTime, - // name: `от предишен график, ${publisher.firstName} ${publisher.lastName}`, - // isFromPreviousAssignment: true, - // isActive: true, - // }, - // }); - // console.log(`Created WEEKLY availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`); - // } - // const personResponse = await axiosInstance.post("/publishers", manualPub); + if (createAvailabilities) { + const dayofWeek = common.getDayOfWeekNameEnEnum(date); + const availability = await prisma.availability.create({ + data: { + publisherId: publisher.id, + dayofweek: dayofWeek, + startTime: startTime, + endTime: endTime, + name: `от график, ${publisher.firstName} ${publisher.lastName}`, + isFromPreviousAssignment: true, + isActive: true, + }, + }); + console.log(`Created WEEKLY availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`); + } + const personResponse = await axiosInstance.post("/publishers", manualPub); // let personId = personResponse.data.id; } catch (e) { @@ -533,23 +532,26 @@ exports.processEvents = async function (events, year, monthNumber, progressCallb }); //ToDo: fix findPublisherAvailability and creation of availabilities // check if there is an availability for this publisher on this date, and if not, create one - // const availability = await data.findPublisherAvailability(publisher.id, start); - // if (!availability && createAvailabilities) { - // const dayofWeek = common.getDayOfWeekNameEnEnum(date); - // const availability = await prisma.availability.create({ - // data: { - // publisherId: publisher.id, - // //date: date, - // dayofweek: dayofWeek, - // //weekOfMonth: common.getWeekOfMonth(date), - // startTime: start, - // endTime: end, - // name: `от предишен график, ${publisher.firstName} ${publisher.lastName}`, - // isFromPreviousAssignment: true, - // }, - // }); - // console.log(`Created WEEKLY availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`); - // } + //ToDo: check if that works + const availability = await data.findPublisherAvailability(publisher.id, start); + if (!availability && createAvailabilities) { + const dayofWeek = common.getDayOfWeekNameEnEnum(date); + const availability = await prisma.availability.create({ + data: { + publisherId: publisher.id, + //date: date, + dayofweek: dayofWeek, + //weekOfMonth: common.getWeekOfMonth(date), + startTime: start, + endTime: end, + name: `от предишен график, ${publisher.firstName} ${publisher.lastName}`, + isFromPreviousAssignment: true, + isWithTransportIn: isWithTransport && event.shiftNr == 1, + isWithTransportOut: isWithTransport && event.shiftNr > 1, + }, + }); + console.log(`Created SYSTEM availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`); + } console.log(`Created assignment with ID ${assignment.id} for date '${date.toDateString()}' and location '${event.placeOfEvent}'. publisher: ${publisher.firstName} ${publisher.lastName}}`); } diff --git a/src/helpers/imports.js b/src/helpers/imports.js deleted file mode 100644 index a866ff6..0000000 --- a/src/helpers/imports.js +++ /dev/null @@ -1,4 +0,0 @@ -//??? can we consolidate all imports into one file? -import ProtectedRoute from '../../../components/protectedRoute'; -import axiosInstance from '../../../src/axiosSecure'; -import Layout from "../../../components/layout"; \ No newline at end of file