Merge branch 'main' into feature-fixStats

This commit is contained in:
Dobromir Popov
2024-05-11 16:33:41 +03:00
67 changed files with 2703 additions and 881 deletions

View File

@ -10,7 +10,7 @@ const { PrismaClient, UserRole } = require('@prisma/client');
const DayOfWeek = require("@prisma/client").DayOfWeek;
const winston = require('winston');
const { getSession } = require("next-auth/react");
const { DateTime, FixedOffsetZone } = require('luxon');
const logger = winston.createLogger({
level: 'info', // Set the default log level
@ -36,6 +36,27 @@ exports.logger = logger;
// dotenv.config();
// // dotenv.config({ path: ".env.local" });
exports.adjustUtcTimeToSofia = function (time) {
// Convert the Date object to a Luxon DateTime object in UTC
let result = DateTime.fromJSDate(time, { zone: 'utc' });
// Convert to Sofia time, retaining the local time as provided
result = result.setZone('Europe/Sofia', { keepLocalTime: true });
// Set hours, minutes, and seconds to match the input time
result = result.set({
hour: time.getHours(),
minute: time.getMinutes(),
second: time.getSeconds()
});
return result.toJSDate();
};
exports.adjustTimeToUTC = function (time) {
let result = DateTime.fromJSDate(time, { zone: 'Europe/Sofia' });
result = result.setZone('utc', { keepLocalTime: true });
return result.toJSDate();
};
exports.isValidPhoneNumber = function (phone) {
if (typeof phone !== 'string') {
return false; // or handle as you see fit
@ -56,17 +77,6 @@ exports.isValidPhoneNumber = function (phone) {
// If neither condition is met, the phone number is invalid
return false;
}
exports.setBaseUrl = function (req) {
const protocol = req.headers['x-forwarded-proto'] || 'http';
const host = req.headers.host;
const baseUrl = `${protocol}://${host}`;
// Write the baseUrl to the file
if (req != null) {
fs.writeFileSync(path.join(__dirname, 'baseUrl.txt'), baseUrl, 'utf8');
}
return baseUrl;
};
exports.getBaseUrl = function (relative = "", req = null) {
return process.env.NEXT_PUBLIC_PUBLIC_URL + relative;
@ -329,14 +339,8 @@ exports.compareTimes = function (time1, time2) {
const time2String = `${getHours(time2)}:${getMinutes(time2)}`;
return time1String.localeCompare(time2String);
};
exports.normalizeTime = function (date, baseDate) {
// return set(baseDate, {
// hours: getHours(date),
// minutes: getMinutes(date),
// seconds: getSeconds(date),
// milliseconds: 0
// });
//don't use date-fns
let newDate = new Date(baseDate);
newDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), 0);
return newDate;
@ -366,10 +370,7 @@ exports.getDateFormatedShort = function (date) {
}
exports.getTimeFomatted = function (date) {
return date.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });//timeZone: 'local'
}
/*Todo: remove:
toISOString
@ -528,7 +529,7 @@ exports.fuzzySearch = function (publishers, searchQuery, distanceThreshold = 0.9
}
exports.getCurrentNonthFormatted = function () {
exports.getCurrentMonthFormatted = function () {
const getCurrentYearMonth = () => {
const currentDate = new Date();
const year = currentDate.getFullYear();
@ -544,51 +545,140 @@ exports.getCurrentYearMonth = () => {
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
return `${year}-${month}`;
}
exports.getTimeFormated = function (date) {
return this.formatTimeHHmm(date);
}
// format date to 'HH:mm' time string required by the time picker
exports.formatTimeHHmm = function (input) {
// Check if the input is a string or a Date object
const date = (typeof input === 'string') ? new Date(input) : input;
return date.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
timeZone: 'Europe/Sofia'
}).substring(0, 5);
}
//parse 'HH:mm' time string to date object
exports.parseTimeHHmm = (timeString) => {
// If timeString is already a date, return it as is
if (timeString instanceof Date) {
return timeString;
}
// new date FNs
/**
* Parses an input into a Luxon DateTime object, setting the timezone to 'Europe/Sofia' while keeping the local time.
* @param {string|Date} input - The input date string or JavaScript Date object.
* @returns {DateTime} - A Luxon DateTime object with the timezone set to 'Europe/Sofia', preserving the local time.
*/
const parseDate = (input) => {
let dateTime;
const [hours, minutes] = timeString.split(':');
const date = new Date();
date.setHours(hours);
date.setMinutes(minutes);
return date;
}
exports.setTimeHHmm = (date, timeStringOrHours) => {
const newDate = new Date(date);
if (typeof timeStringOrHours === 'string' && timeStringOrHours.includes(':')) {
// If hours is a string in "HH:mm" format
const [h, m] = timeStringOrHours.split(':');
newDate.setHours(parseInt(h, 10), parseInt(m, 10), 0, 0);
if (input instanceof DateTime) {
// If input is already a Luxon DateTime, we adjust the zone only.
dateTime = input.setZone('Europe/Sofia');
} else if (typeof input === 'string' || input instanceof Date) {
// Create a DateTime from the input assuming local timezone to preserve local time when changing the zone.
dateTime = DateTime.fromJSDate(new Date(input), { zone: 'local' });
dateTime = dateTime.setZone('Europe/Sofia');
} else {
// If hours and minutes are provided separately
newDate.setHours(parseInt(timeStringOrHours, 10), 0, 0, 0);
// Use the current time if no input is given, considered as local time.
dateTime = DateTime.local().setZone('Europe/Sofia');
}
return newDate;
// Set the timezone to 'Europe/Sofia' while keeping the original local time.
return dateTime.setZone('Europe/Sofia', { keepLocalTime: true });
};
exports.parseDate = parseDate;
// Set timezone to 'Europe/Sofia' without translating time
exports.setTimezone = (input) => {
let dateTime = parseDate(input);
dateTime = dateTime.setZone('Europe/Sofia', { keepLocalTime: true });
return dateTime.toJSDate();
};
exports.setTime = (baseDateTime, timeDateTime) => {
// Ensure both inputs are DateTime objects
baseDateTime = parseDate(baseDateTime);
timeDateTime = parseDate(timeDateTime);
return baseDateTime.set({
hour: timeDateTime.hour,
minute: timeDateTime.minute,
second: timeDateTime.second,
millisecond: timeDateTime.millisecond
});
};
// Format date to a specified format, defaulting to 'HH:mm'
exports.getTimeFormatted = (input, format = 'HH:mm') => {
const dateTime = parseDate(input);
return dateTime.toFormat(format);
};
// Set time in 'HH:mm' format to a date and return as JS Date in Sofia timezone
exports.setTimeHHmm = (input, timeString) => {
let dateTime = parseDate(input);
const [hour, minute] = timeString.split(':').map(Number);
dateTime = dateTime.set({ hour, minute });
return dateTime.toJSDate();
};
// Parse 'HH:mm' time string to a JS Date object in Sofia timezone for today
exports.parseTimeHHmm = (timeString) => {
const dateTime = DateTime.now({ zone: 'Europe/Sofia' });
const [hour, minute] = timeString.split(':').map(Number);
return dateTime.set({ hour, minute }).toJSDate();
};
function isTimeBetween(startTime, endTime, checkTime) {
const start = new Date(0, 0, 0, startTime.getHours(), startTime.getMinutes());
const end = new Date(0, 0, 0, endTime.getHours(), endTime.getMinutes());
const check = new Date(0, 0, 0, checkTime.getHours(), checkTime.getMinutes());
// If the end time is less than the start time, it means the time range spans midnight
if (end < start) {
// Check time is between start and midnight or between midnight and end
return (check >= start && check <= new Date(0, 0, 1, 0, 0, 0)) || (check >= new Date(0, 0, 0, 0, 0, 0) && check <= end);
} else {
return check >= start && check <= end;
}
}
exports.isTimeBetween = isTimeBetween;
// ToDo: update all uses of this function to use the new one
// exports.getTimeFormatted = function (date) {
// const dateTime = DateTime.fromJSDate(date, { zone: 'Europe/Sofia' });
// return dateTime.toFormat('HH:mm');
// };
// exports.setTimeHHmm = (date, timeStringOrHours) => {
// const newDate = new Date(date);
// if (typeof timeStringOrHours === 'string' && timeStringOrHours.includes(':')) {
// // If hours is a string in "HH:mm" format
// const [h, m] = timeStringOrHours.split(':');
// newDate.setHours(parseInt(h, 10), parseInt(m, 10), 0, 0);
// } else {
// // If hours and minutes are provided separately
// newDate.setHours(parseInt(timeStringOrHours, 10), 0, 0, 0);
// }
// return newDate;
// };
// // format date to 'HH:mm' time string required by the time picker
// exports.formatTimeHHmm = function (input) {
// // Check if the input is a string or a Date object
// const date = (typeof input === 'string') ? new Date(input) : input;
// return date.toLocaleTimeString('en-US', {
// hour12: false,
// hour: '2-digit',
// minute: '2-digit',
// timeZone: 'Europe/Sofia'
// }).substring(0, 5);
// }
// //parse 'HH:mm' time string to date object
// exports.parseTimeHHmm = (timeString) => {
// // If timeString is already a date, return it as is
// if (timeString instanceof Date) {
// return timeString;
// }
// const [hours, minutes] = timeString.split(':');
// const date = new Date();
// date.setHours(hours);
// date.setMinutes(minutes);
// return date;
// }
exports.getTimeInMinutes = (dateOrTimestamp) => {
const date = new Date(dateOrTimestamp);
@ -775,8 +865,13 @@ exports.convertDatesToISOStrings = function (obj) {
return obj;
}
// if (obj instanceof Date) {
// return obj.toISOString();
// }
if (obj instanceof Date) {
return obj.toISOString();
// Convert the Date object to a Luxon DateTime in UTC
const utcDate = DateTime.fromJSDate(obj, { zone: 'utc' });
return utcDate.toISO(); // Output in UTC as ISO string
}
if (Array.isArray(obj)) {
@ -793,8 +888,43 @@ exports.convertDatesToISOStrings = function (obj) {
return obj;
}
function adjustDateForDST(date, timezone) {
// Convert the date to the specified timezone
let dateTime = DateTime.fromJSDate(date, { zone: timezone });
// Check if the original date is in DST
const isOriginalDST = dateTime.isInDST;
// Check if the current date in the same timezone is in DST
const isNowDST = DateTime.now().setZone(timezone).isInDST;
// Compare and adjust if necessary
if (isOriginalDST && !isNowDST) {
// If original date was in DST but now is not, subtract one hour
dateTime = dateTime.minus({ hours: 1 });
} else if (!isOriginalDST && isNowDST) {
// If original date was not in DST but now is, add one hour
dateTime = dateTime.plus({ hours: 1 });
}
// Return the adjusted date as a JavaScript Date
return dateTime.toJSDate();
}
exports.adjustDateForDST = adjustDateForDST;
exports.base64ToUint8Array = function (base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = atob(base64);
const buffer = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
buffer[i] = rawData.charCodeAt(i);
}
return buffer;
}
// exports.getInitials = function (names) {
// const parts = names.split(' '); // Split the full name into parts
// if (parts.length === 0) {

View File

@ -158,7 +158,7 @@ async function getAvailabilities(userId) {
...item,
startTime: item.startTime.toISOString(),
endTime: item.endTime.toISOString(),
name: common.getTimeFomatted(item.startTime) + "-" + common.getTimeFomatted(item.endTime),
name: common.getTimeFormatted(item.startTime) + "-" + common.getTimeFormatted(item.endTime),
//endDate can be null
endDate: item.endDate ? item.endDate.toISOString() : null,
type: 'availability',
@ -214,7 +214,7 @@ async function getAvailabilities(userId) {
endTime: item.shift.endTime.toISOString(),
// name: item.shift.publishers.map(p => p.firstName + " " + p.lastName).join(", "),
//name: item.shift.assignments.map(a => a.publisher.firstName[0] + " " + a.publisher.lastName).join(", "),
name: common.getTimeFomatted(new Date(item.shift.startTime)) + "-" + common.getTimeFomatted(new Date(item.shift.endTime)),
name: common.getTimeFormatted(new Date(item.shift.startTime)) + "-" + common.getTimeFormatted(new Date(item.shift.endTime)),
type: 'assignment',
//delete shift object
shift: null,
@ -614,7 +614,7 @@ function convertShiftDates(assignments) {
}
async function getCalendarEvents(publisherId, date, availabilities = true, assignments = true) {
async function getCalendarEvents(publisherId, availabilities = true, assignments = true, includeUnpublished = false) {
const result = [];
// let pubs = await filterPublishers("id,firstName,lastName,email".split(","), "", date, assignments, availabilities, date ? true : false, publisherId);
@ -647,6 +647,7 @@ async function getCalendarEvents(publisherId, date, availabilities = true, assig
assignments: {
select: {
id: true,
// publisherId: true,
shift: {
select: {
id: true,
@ -665,7 +666,7 @@ async function getCalendarEvents(publisherId, date, availabilities = true, assig
publisher.availabilities?.forEach(item => {
result.push({
...item,
title: common.getTimeFomatted(new Date(item.startTime)) + "-" + common.getTimeFomatted(new Date(item.endTime)), //item.name,
title: common.getTimeFormatted(new Date(item.startTime)) + "-" + common.getTimeFormatted(new Date(item.endTime)), //item.name,
date: new Date(item.startTime),
startTime: new Date(item.startTime),
endTime: new Date(item.endTime),
@ -681,23 +682,21 @@ async function getCalendarEvents(publisherId, date, availabilities = true, assig
//only published shifts
publisher.assignments?.filter(
assignment => assignment.shift.isPublished
assignment => assignment.shift.isPublished || includeUnpublished
).forEach(item => {
result.push({
...item,
title: common.getTimeFomatted(new Date(item.shift.startTime)) + "-" + common.getTimeFomatted(new Date(item.shift.endTime)),
title: common.getTimeFormatted(new Date(item.shift.startTime)) + "-" + common.getTimeFormatted(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,
// publisherId: item.publisherId,
publisherId: publisher.id,
type: "assignment",
});
});
}
}
return result;
}