initial commit - code moved to separate repo

This commit is contained in:
Dobromir Popov
2024-02-22 04:19:38 +02:00
commit 560d503219
240 changed files with 105125 additions and 0 deletions

669
src/helpers/common.js Normal file
View File

@ -0,0 +1,669 @@
// import { format, startOfMonth, endOfMonth, eachDayOfInterval, startOfWeek, endOfWeek, isSameMonth } from 'date-fns';
const levenshtein = require('fastest-levenshtein');
const path = require("path");
const { PrismaClient } = require('@prisma/client');
const DayOfWeek = require("@prisma/client").DayOfWeek;
const winston = require('winston');
const logger = winston.createLogger({
level: 'info', // Set the default log level
format: winston.format.json(), // Choose a log format
transports: [
// Define where logs should be output (e.g., to a file or the console)
new winston.transports.Console(),
],
});
exports.logger = logger;
// import { DayOfWeek } from "@prisma/client";
//const UserRole = require("@prisma/client").UserRole; - does not allow usage of UserRole in code
// import { UserRole } from '@prisma/client';
// const getConfig = require("next/config");
// exports.nextconfig = getConfig();
// //const { serverRuntimeConfig } = getConfig();
// const dotenv = require("dotenv");
// dotenv.config();
// // dotenv.config({ path: ".env.local" });
exports.isValidPhoneNumber = function (phone) {
if (typeof phone !== 'string') {
return false; // or handle as you see fit
}
// Remove spaces and dashes for validation
const cleanedPhone = phone.replace(/\s|-/g, '');
// Check if the phone starts with '08' and has 10 digits
if (cleanedPhone.startsWith('08') && cleanedPhone.length === 10) {
return true;
}
// Check if the phone starts with '+359' and has the correct length
if (cleanedPhone.startsWith('+359') && cleanedPhone.length === 12) {
return true;
}
// If neither condition is met, the phone number is invalid
return false;
}
exports.getBaseUrl = function (relative = "") {
const host = process.env.NEXT_PUBLIC_HOST || '127.0.0.1';
const port = process.env.NEXT_PUBLIC_PORT ? `:${process.env.NEXT_PUBLIC_PORT}` : '';
const protocol = process.env.NEXT_PUBLIC_PROTOCOL || "https"
//const url = `${protocol}://${host}${port}/${relative.replace(/^\/|\/$/g, '')}/`;
const isRelativeEmpty = !relative || relative.trim() === '';
const formattedRelative = !isRelativeEmpty ? '/' + relative.replace(/^\/|\/$/g, '') : '';
const url = `${protocol}://${host}${port}${formattedRelative}`;
logger.debug("NODE_ENV = ", process.env.NODE_ENV, "protocol:", protocol);
logger.debug("getBaseURL = ", url);
return url;
};
let prisma;
exports.getPrismaClient = function getPrismaClient() {
if (!prisma) {
logger.debug("getPrismaClient: process.env.DATABASE_URL = ", process.env.DATABASE_URL);
prisma = new PrismaClient({
// Optional: Enable logging
// log: ['query', 'info', 'warn', 'error'],
datasources: { db: { url: process.env.DATABASE_URL } },
});
}
return prisma;
}
exports.removeAccentsAndSpecialCharacters = function (str) {
return str.normalize("NFD") // split an accented letter in the base letter and the accent
.replace(/[\u0300-\u036f]/g, "") // remove all previously split accents
.replace(/[^a-zA-Z0-9]/g, ""); // remove all chars not letters and not digits
}
Date.prototype.getDayEuropean = function () {
const day = this.getDay();
return (day === 0) ? 6 : day - 1; // Convert 0 (Sunday) to 6, and decrement other days by 1
};
// Helper function to convert month name to 0-based index
exports.getMonthIndex = function (monthName) {
const monthNames = ["януари", "февруари", "март", "април", "май", "юни", "юли", "август", "септември", "октомври", "ноември", "декември"];
return monthNames.indexOf(monthName.toLowerCase());
};
exports.getMonthName = function (monthIndex) {
const monthNames = ["януари", "февруари", "март", "април", "май", "юни", "юли", "август", "септември", "октомври", "ноември", "декември"];
return monthNames[monthIndex];
};
exports.getMonthNameEn = function (monthIndex) {
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
return monthNames[monthIndex];
};
exports.dayOfWeekNames = ["понеделник", "вторник", "сряда", "четвъртък", "петък", "събота", "неделя"];
exports.DaysOfWeekArray = [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday];
// Helper function to convert day of week name to 0-based index (Sunday = 0)
exports.getDayOfWeekIndex = function (dayOfWeekName) {
dayOfWeekName = dayOfWeekName.toLowerCase().trim();
const abbreviatedDayOfWeekNames = ["пон", "вт", "ср", "четв", "пет", "съб", "нед"];
const enDayOfWeekNames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"].map((d) => d.toLowerCase());
let index = exports.dayOfWeekNames.indexOf(dayOfWeekName);
// If not found in the full names, look in the abbreviated names
if (index === -1) {
index = abbreviatedDayOfWeekNames.indexOf(dayOfWeekName);
}
if (index === -1) {
index = enDayOfWeekNames.indexOf(dayOfWeekName);
}
logger.debug("getDayOfWeekIndex: dayOfWeekName = ", dayOfWeekName, "index = ", index);
return index;
};
exports.getDayOfWeekName = function (date) {
const dayOfWeekIndex = date.getDayEuropean();
return exports.dayOfWeekNames[dayOfWeekIndex];
};
exports.getDayOfWeekNameEnEnum = function (date) {
date = new Date(date);
const dayOfWeekIndex = date.getDayEuropean();
const dayOfWeekNames = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
//return enum instead of string
// const dayOfWeekNames: Record<number, DayOfWeek> = {
// 0: DayOfWeek.Monday,
// 1: DayOfWeek.Tuesday,
// 2: DayOfWeek.Wednesday,
// 3: DayOfWeek.Thursday,
// 4: DayOfWeek.Friday,
// 5: DayOfWeek.Saturday,
// 6: DayOfWeek.Sunday
// };
return dayOfWeekNames[dayOfWeekIndex];
}
/*
sets the date portion of the date object to the correct day of week, keeping the time portion
*/
exports.getDayOfWeekDate = function (dayOfWeekName, date = new Date()) {
const targetDay = exports.DaysOfWeekArray.indexOf(dayOfWeekName); // 0 = Sunday, 1 = Monday, etc.
if (targetDay === -1) {
throw new Error('Invalid day of week name provided');
}
// Adjust currentDay to match our custom mapping
let currentDay = date.getDayEuropean();
const daysDifference = targetDay - currentDay;
date.setDate(date.getDate() + daysDifference);
return date;
};
//common.getWeekOfMonth(date)
// exports.getWeekOfMonth = function (date) {
// // Copy date so don't modify original
// date = new Date(date);
// // Adjust to Monday of this week
// date.setDate(date.getDate() + 3 - (date.getDayEuropean() + 6) % 7);
// // Return week number
// const weekNumber = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000 / 7);
// return weekNumber;
// }
exports.getMonthDatesInfo = function (date) {
// get first day of the month
var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
// get first day of next month
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 1);
// lastDay = new Date(lastDay.getTime() - 1);
lastDay = new Date(lastDay.getTime() - 86400000); // 86400000 ms = 1 day
// // get the day of the week of the first day
var firstDayOfWeek = firstDay.getDay(); // 0 = Sunday, 1 = Monday, etc.
// calculate the date of the first Monday
var firstMonday = new Date(firstDay);
if (firstDayOfWeek !== 1) { // Check if the first day is not already a Monday
firstMonday.setDate(firstDay.getDate() + ((7 - firstDayOfWeek) % 7) + 1);
}
// get last Monday
var lastMonday = new Date(lastDay);
if (lastDay.getDay() !== 0) { // Check if the last day is not already a Sunday
lastMonday.setDate(lastDay.getDate() - ((lastDay.getDay() + 6) % 7));
} else {
lastMonday.setDate(lastDay.getDate() - 6);
}
// get Sunday of the last week
var lastSunday = new Date(lastMonday);
lastSunday.setDate(lastMonday.getDate() + 6);
var monthName = exports.getMonthName(date.getMonth());
// var firstDayNextMonth = new Date(date.getFullYear(), date.getMonth() + 1, 1);
// // Calculate the last Sunday of the current month
// // .getDay() returns 0 for Sunday, 1 for Monday, ..., 6 for Saturday
// var lastSunday = new Date(firstDayNextMonth);
// lastSunday.setDate(firstDayNextMonth.getDate() - firstDayNextMonth.getDay());
//logger.debug("Last Sunday: ", lastSunday);
return {
firstDay: firstDay,
lastDay: lastDay,
firstMonday: firstMonday,
lastMonday: lastMonday,
lastSunday: lastSunday,
date: date,
monthName: monthName,
year: date.getFullYear(),
nrOfWeeks: Math.ceil((lastMonday.getDate() - firstMonday.getDate()) / 7)
};
};
exports.getMonthlyScheduleRange = function (date) {
let info = exports.getMonthDatesInfo(date);
// get first day of the month
var firstDay = info.firstDay;
// get last sunday of the month
var lastSunday = info.lastSunday;
return {
firstDay: firstDay,
lastSunday: lastSunday
};
};
exports.getWeekNumber = function (date) {
let info = exports.getMonthDatesInfo(date);
// If the date is before the first full week, return 0
if (date < info.firstMonday) {
return 0;
}
// Calculate the week number based on the first full week
return Math.ceil((date.getDate() - info.firstMonday.getDate() + 1) / 7);
};
exports.getTimeRange = function (start, end) {
start = new Date(start);
end = new Date(end);
const startTime = start.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });
const endTime = end.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });
return `${startTime}-${endTime}`;
}
//get date in format сряда 1 юли 2020 г.
exports.getDateFormated = function (date) {
const dayOfWeekName = exports.getDayOfWeekName(date);
const day = date.getDate();
const monthName = exports.getMonthName(date.getMonth());
const year = date.getFullYear();
return `${dayOfWeekName} ${day} ${monthName} ${year} г.`;
}
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
slice(0, 10)
getISODateFromDateTime > getISODateOnly
*/
exports.toLocalISO = function (date) {
// Adjust for timezone and get ISO string without 'Z'
const offset = date.getTimezoneOffset() * 60000; // offset in milliseconds
return new Date(date.getTime() - offset).toISOString().slice(0, -1);
};
exports.getISODateOnly = function (date) {
// Use toLocalISO and get the date part only
return this.toLocalISO(date).split('T')[0];
};
exports.getDateFromDateTime = function (day) {
// Adjust for timezone and reset time to midnight
var date = new Date(day);
date.setHours(0, 0, 0, 0);
const offset = date.getTimezoneOffset() * 60000;
return new Date(date.getTime() - offset);
};
// ###########################################
// remove empty nodes from json
// Function to check if an object is empty or contains only empty nodes
exports.isEmpty = function (obj) {
for (const key in obj) {
if (typeof obj[key] === 'object') {
if (!exports.isEmpty(obj[key])) {
return false;
}
} else if (obj[key] !== '') {
return false;
}
}
return true;
}
// Function to remove empty nodes from a JSON object
exports.jsonRemoveEmptyNodes = function (obj) {
const newObj = {};
for (const key in obj) {
if (typeof obj[key] === 'object') {
const cleanedObj = exports.jsonRemoveEmptyNodes(obj[key]);
if (!exports.isEmpty(cleanedObj)) {
newObj[key] = cleanedObj;
}
} else if (obj[key] !== '') {
newObj[key] = obj[key];
}
}
return newObj;
}
// #region string functions
//input:Светослав и Сириел Георгиеви
//output: Светослав Георгиев, Сириел Георгиев
//input: Игор и Галина Москвини
//output: Игор Москвин, Галина Москвин
exports.separateFamilyMemberNames = function (namesArray) {
logger.debug("separateFamilyMemberNames: " + namesArray);
const result = [];
// itteraate over all names
for (let i = 0; i < namesArray.trim().split(' '); i++) {
const name = namesArray[i];
// if name contains " и " then split it into two names
// else just add it to the result
if (name.includes(" и ")) {
const namePairs = name.split(" и ");
const firstNames = namePairs.map((pair) => pair.split(" ")[0]);
// last name is only one. and it is shared for both firstnames
const lastName = namePairs[1].split(" ")[1];
for (let i = 0; i < namePairs.length; i++) {
let firstName = firstNames[i];
if (i === namePairs.length - 1 && namePairs.length % 2 !== 0) {
result.push(`${firstName} ${lastName}`);
} else {
result.push(`${firstName} ${lastName}`);
}
}
} else {
result.push(name);
}
}
//normalize names
for (var i = 0; i < result.length; i++) {
var name = result[i].trim();
//cut last letter of name if it is "a" or "и" (bulgarian feminine ending)
if (name.endsWith("a") || name.endsWith("и")) {
name = name.substring(0, name.length - 1);
}
result[i] = name;
}
return result;
}
exports.separateFamilyMemberNames2 = function (input) {
const nameParts = input.trim().split(' ');
let familyName = nameParts.pop();
const firstNames = nameParts.join(' ').split(' и ');
const result = firstNames.map(firstName => `${firstName} ${familyName}`);
return result;
}
exports.fuzzySearch = function (publishers, searchQuery, distanceThreshold = 0.9) {
const lowerCaseSearchQuery = searchQuery?.toLowerCase();
let results = publishers
.filter((p) => {
try {
const fullName = p.firstName.toLowerCase() + " " + p.lastName.toLowerCase();
const reversedFullName = p.lastName.toLowerCase() + " " + p.firstName.toLowerCase();
const distanceFullName = levenshtein.distance(fullName, lowerCaseSearchQuery);
const distanceReversedFullName = levenshtein.distance(reversedFullName, lowerCaseSearchQuery);
const similarityFullName = 1 - distanceFullName / Math.max(fullName.length, lowerCaseSearchQuery.length);
const similarityReversedFullName = 1 - distanceReversedFullName / Math.max(reversedFullName.length, lowerCaseSearchQuery.length);
// If total fullname length is less than 10 symbols, allow results that are 1 symbol difference
if ((fullName.length < 10 || reversedFullName.length < 10) && (distanceFullName <= 1 || distanceReversedFullName <= 1)) {
return true;
}
return similarityFullName >= distanceThreshold || similarityReversedFullName >= distanceThreshold
} catch (e) {
logger.error(e);
console.log(e);
return false;
}
})
.sort((a, b) => (a.similarity > b.similarity ? -1 : 1));
return (results && results.length > 0) ? results[0] : null;
}
exports.getCurrentNonthFormatted = function () {
const getCurrentYearMonth = () => {
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
return `${year}-${month}`;
}
return getCurrentYearMonth();
}
exports.getCurrentYearMonth = () => {
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
return `${year}-${month}`;
}
// 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.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;
};
exports.getTimeInMinutes = (dateOrTimestamp) => {
const date = new Date(dateOrTimestamp);
logger.debug("getTimeInMinutes: date = ", date);
return date.getHours() * 60 + date.getMinutes();
};
exports.parseBool = function (value) {
if (value === undefined) {
return false;
}
value = value || "false";
const truthyValues = ['1', 'y', 'yes', 'true', 't'];
return truthyValues.includes(String(value).toLowerCase());
}
exports.getStartOfWeek = function (date) {
const result = new Date(date); // create a copy of the input date
// If the day is Sunday (0), we set it to -6, otherwise, subtract the current day of the week from 1 (for Monday)
let daysToSubtract = result.getDay() === 0 ? 6 : result.getDay() - 1;
result.setDate(result.getDate() - daysToSubtract);
result.setHours(0, 0, 0, 0); // set time to midnight
return result;
}
exports.getEndOfWeek = function (date) {
const result = new Date(date);
// If the day is Sunday (0 in `getDay()`), no addition is needed. Otherwise, add 7 minus the current day of the week.
let daysToAdd = result.getDay() === 0 ? 0 : 7 - result.getDay();
result.setDate(result.getDate() + daysToAdd);
result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
return result;
}
exports.getStartOfMonth = function (date) {
date = new Date(date);
return new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);
}
exports.getEndOfMonth = function (date) {
date = new Date(date);
const result = new Date(date.getFullYear(), date.getMonth() + 1, 0); // 0th day of the next month
result.setHours(23, 59, 59, 999); // set time to the last millisecond of the day
return result;
}
exports.excelSerialDateToDate = function (serial) {
// Set base date as 1899-12-31 (Excel's zero date, ignoring leap year bug)
let baseDate = new Date(1899, 11, 31);
// Extract date and time fractions
let datePart = Math.floor(serial);
let timePart = serial - datePart;
// Compute the complete date
let dateInMs = baseDate.getTime() + datePart * 24 * 60 * 60 * 1000;
let timeInMs = timePart * 24 * 60 * 60 * 1000;
// Construct and return the final date
return new Date(dateInMs + timeInMs);
}
// Function to get a value from the session
exports.getSessionValue = function (session, fieldName, defaultValue = null) {
return session[fieldName] || defaultValue;
};
// Function to update a value in the session
exports.updateSessionValue = function (session, fieldName, value) {
session[fieldName] = value;
session.save(); // Save the session after updating
};
exports.copyToClipboard = function (event, text) {
let contentToCopy;
if (event) {
const spanElement = event.currentTarget;
contentToCopy = spanElement.textContent;
// Animation logic
const originalText = spanElement.textContent;
const originalColor = spanElement.style.backgroundColor; // Store the original color
spanElement.textContent = "имейла копиран";
spanElement.style.backgroundColor = "blue"; // Change color to "info" (blue in this case)
setTimeout(() => {
spanElement.textContent = originalText;
spanElement.style.backgroundColor = originalColor; // Reset the color back to original
}, 3000); // Reset the text and color back to original after 3 seconds
} else {
// If event is null, use the provided text parameter
if (!text) {
console.error("No text provided to copy to clipboard");
return; // Exit the function if no text is provided
}
contentToCopy = text;
}
// Copy contentToCopy to clipboard
const tempTextarea = document.createElement('textarea');
tempTextarea.value = contentToCopy;
document.body.appendChild(tempTextarea);
tempTextarea.select();
document.execCommand('copy');
document.body.removeChild(tempTextarea);
}
// User and auth functions
// import { getSession } from "next-auth/react";
// import { UserRole } from "@prisma/client";
//convert to es6 import
const { getSession } = require("next-auth/react");
const { UserRole } = require("@prisma/client");
exports.getUser = async function (req) {
// Use req if provided (server-side), otherwise call getSession without args (client-side)
const session = req ? await getSession({ req }) : await getSession();
return session?.user;
}
exports.isUserInRole = async function (req, allowedRoles = []) {
const user = await exports.getUser(req);
// Check if the user is authenticated
if (!user) {
return false;
}
// If no specific roles are required, return true as the user is authenticated
if (allowedRoles.length === 0) {
return true;
}
// Check if the user's role is among the allowed roles
// Ensure role exists and is a valid UserRole
const userRole = user.role;
return allowedRoles.includes(userRole);
}
// Utility functions for localStorage operations
exports.setLocalStorage = function (key, value) {
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(value));
}
};
exports.getLocalStorage = function (key, defaultValue) {
if (typeof window !== 'undefined') {
const stored = window.localStorage.getItem(key);
if (stored) {
try {
// Attempt to parse the stored JSON data
return JSON.parse(stored);
} catch (error) {
// Handle parsing error
console.error(`Error parsing JSON from localStorage for key '${key}':`, error);
// Return defaultValue or perform other error handling as needed
return defaultValue;
}
}
}
return defaultValue;
};