Merge commit '53729a5926cef1f5d155bc3aff90ccaee1b96c86'

This commit is contained in:
Dobromir Popov
2024-10-31 16:12:14 +02:00
18 changed files with 992 additions and 247 deletions

6
.gitignore vendored
View File

@ -35,7 +35,5 @@ content/output/*
public/content/output/* public/content/output/*
public/content/output/shifts 2024.1.json public/content/output/shifts 2024.1.json
!public/content/uploads/* !public/content/uploads/*
shift_generate_log_*.txt .aider*
.aider.input.history /shift_generate_log_*.txt
.aider.chat.history.md
.aider.tags.cache.v3/*

View File

@ -39,6 +39,8 @@ if [ "$UPDATE_CODE_FROM_GIT" = "true" ]; then
rsync -av --itemize-changes \ rsync -av --itemize-changes \
--exclude='package.json' \ --exclude='package.json' \
--exclude='package-lock.json' \ --exclude='package-lock.json' \
--exclude='/public/content/permits' \
--exclude='/public/content/uploads' \
/tmp/clone/ /app/ >> /app/logs/deploy.txt 2>&1 /tmp/clone/ /app/ >> /app/logs/deploy.txt 2>&1
# Check rsync exit status # Check rsync exit status

View File

@ -118,6 +118,11 @@ next start
export OPENAI_API_KEY=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 # personal export OPENAI_API_KEY=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 # personal
export OPENAI_API_KEY=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 # dev-bro export OPENAI_API_KEY=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 # dev-bro
aider --openai-api-key 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 --no-auto-commits --
## build - prod
npx run build
npm run prod
# ----------------------------------------------update PRISMA schema/sync database ----------------------------------------------- # # ----------------------------------------------update PRISMA schema/sync database ----------------------------------------------- #
# prisma migrate dev --create-only # prisma migrate dev --create-only
NODE_ENV=production npx prisma migrate deploy NODE_ENV=production npx prisma migrate deploy

View File

@ -51,10 +51,15 @@ const messages = {
const AvCalendar = ({ publisherId, events, selectedDate, cartEvents, lastPublishedDate }) => { const AvCalendar = ({ publisherId, events, selectedDate, cartEvents, lastPublishedDate }) => {
const [editLockedBefore, setEditLockedBefore] = useState(new Date(lastPublishedDate)); const [editLockedBefore, setEditLockedBefore] = useState(new Date(lastPublishedDate));
const [isAdmin, setIsAdmin] = useState(false); const [isAdmin, setIsAdmin] = useState(false);
// const [isPowerUser, setIsPowerUser] = useState(false);
// const [currentUserId, setCurrentUserId] = useState(null);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
setIsAdmin(await ProtectedRoute.IsInRole(UserRole.ADMIN)); setIsAdmin(await ProtectedRoute.IsInRole(UserRole.ADMIN));
// setIsPowerUser(await ProtectedRoute.IsInRole(UserRole.POWERUSER));
// // Assuming you have a way to get the current user's ID
// setCurrentUserId(await ProtectedRoute.GetCurrentUserId());
} catch (error) { } catch (error) {
console.error("Failed to check admin role:", error); console.error("Failed to check admin role:", error);
} }
@ -256,7 +261,9 @@ const AvCalendar = ({ publisherId, events, selectedDate, cartEvents, lastPublish
const enddate = common.setTimezone(end); const enddate = common.setTimezone(end);
if (!start || !end) return; if (!start || !end) return;
//readonly for past dates (ToDo: if not admin) //readonly for past dates (ToDo: if not admin or current user)
//const isCurrentUser = selectedEvents.some(event => event.userId === currentUserId);
if (!isAdmin) { if (!isAdmin) {
if (startdate < new Date() || end < new Date() || startdate > end) return; if (startdate < new Date() || end < new Date() || startdate > end) return;
//or if schedule is published (lastPublishedDate) //or if schedule is published (lastPublishedDate)

View File

@ -85,5 +85,10 @@ export async function serverSideAuth({ req, allowedRoles }) {
// Static method to check if the user has a specific role // Static method to check if the user has a specific role
ProtectedRoute.IsInRole = async (roleName) => { ProtectedRoute.IsInRole = async (roleName) => {
const session = await getSession(); const session = await getSession();
return session && session.user && session.user.role === roleName; return (session && session.user && session.user.role === roleName) || false;
}; };
ProtectedRoute.GetCurrentUserId = async () => {
const session = await getSession();
return session && session.user && session.user.id;
}

View File

@ -1,5 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { Publisher } from "@prisma/client" import { Publisher, UserRole } from "@prisma/client"
// import {IsDateXMonthsAgo} from "../../helpers/const" // import {IsDateXMonthsAgo} from "../../helpers/const"
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import toast from "react-hot-toast"; import toast from "react-hot-toast";
@ -97,26 +97,27 @@ export default function PublisherCard({ publisher }) {
</div> </div>
</a> </a>
<div className="absolute bottom-2 right-2"> <div className="absolute bottom-2 right-2">
<button onClick={() => { setIsModalOpen(true) }} aria-label="Изтрий Publisher"> <ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" ">
<svg className="w-5 h-6 text-red-500 hover:text-red-700" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> <button onClick={() => { setIsModalOpen(true) }} aria-label="Изтрий Publisher">
<path d="M10 11V17" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> <svg className="w-5 h-6 text-red-500 hover:text-red-700" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M14 11V17" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> <path d="M10 11V17" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4 7H20" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> <path d="M14 11V17" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M6 7H12H18V18C18 19.6569 16.6569 21 15 21H9C7.34315 21 6 19.6569 6 18V7Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> <path d="M4 7H20" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /> <path d="M6 7H12H18V18C18 19.6569 16.6569 21 15 21H9C7.34315 21 6 19.6569 6 18V7Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
{/* <path d="M8 9a1 1 0 000 2h4a1 1 0 100-2H8z" /> {/* <path d="M8 9a1 1 0 000 2h4a1 1 0 100-2H8z" />
<path fillRule="evenodd" d="M4.293 4.293A1 1 0 015.707 3.707L10 8l4.293-4.293a1 1 0 111.414 1.414L11.414 9l4.293 4.293a1 1 0 01-1.414 1.414L10 10.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 9 4.293 4.707a1 1 0 010-1.414z" clipRule="evenodd" /> */} <path fillRule="evenodd" d="M4.293 4.293A1 1 0 015.707 3.707L10 8l4.293-4.293a1 1 0 111.414 1.414L11.414 9l4.293 4.293a1 1 0 01-1.414 1.414L10 10.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 9 4.293 4.707a1 1 0 010-1.414z" clipRule="evenodd" /> */}
</svg> </svg>
</button> </button>
<ConfirmationModal <ConfirmationModal
isOpen={isModalOpen} isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)} onClose={() => setIsModalOpen(false)}
onConfirm={() => handleDelete(publisher.id)} onConfirm={() => handleDelete(publisher.id)}
message="Сигурни ли сте, че искате да изтриете този профил? Това действие не може да бъде отменено." message="Сигурни ли сте, че искате да изтриете този профил? Това действие не може да бъде отменено."
/> />
<ProtectedRoute> </ProtectedRoute>
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]} deniedMessage=" ">
<button onClick={() => handleLoginAs(publisher.id)}>Login as</button> <button onClick={() => handleLoginAs(publisher.id)}>Login as</button>
</ProtectedRoute> </ProtectedRoute>
</div> </div>

View File

@ -317,7 +317,7 @@ export default function PublisherForm({ item, me }) {
{/* ADMINISTRATORS ONLY */} {/* ADMINISTRATORS ONLY */}
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" " className=""> <ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]} deniedMessage=" " className="">
<div className="border border-blue-500 border-solid p-2"> <div className="border border-blue-500 border-solid p-2">
{/* prompt to install PWA */} {/* prompt to install PWA */}
<div className="mb-4"> <div className="mb-4">
@ -341,12 +341,16 @@ export default function PublisherForm({ item, me }) {
</div> </div>
<div className="mb-4"> <div className="mb-4">
<div className="form-check"> <div className="form-check">
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" ">
<input className="checkbox disabled" type="checkbox" id="isImported" name="isImported" onChange={handleChange} checked={publisher.isImported} autoComplete="off" />
<label className="label " htmlFor="isImported">Импортиран от график</label>
</ProtectedRoute>
<input className="checkbox" type="checkbox" id="isActive" name="isActive" onChange={handleChange} checked={publisher.isActive} autoComplete="off" /> <input className="checkbox" type="checkbox" id="isActive" name="isActive" onChange={handleChange} checked={publisher.isActive} autoComplete="off" />
<label className="label" htmlFor="isActive">Активен</label> <label className="label" htmlFor="isActive">Активен</label>
<input className="checkbox" type="checkbox" id="isTrained" name="isTrained" onChange={handleChange} checked={publisher.isTrained} autoComplete="off" /> <input className="checkbox" type="checkbox" id="isTrained" name="isTrained" onChange={handleChange} checked={publisher.isTrained} autoComplete="off" />
<label className="label" htmlFor="isTrained">Получил обучение</label> <label className="label" htmlFor="isTrained">Получил обучение</label>
<input className="checkbox disabled" type="checkbox" id="isImported" name="isImported" onChange={handleChange} checked={publisher.isImported} autoComplete="off" />
<label className="label " htmlFor="isImported">Импортиран от график</label>
</div> </div>
</div> </div>
<div className="mb-4"> <div className="mb-4">
@ -356,7 +360,9 @@ export default function PublisherForm({ item, me }) {
<option value={`${UserRole.USER}`}>Потребител</option> <option value={`${UserRole.USER}`}>Потребител</option>
<option value={`${UserRole.EXTERNAL}`}>Външен</option> <option value={`${UserRole.EXTERNAL}`}>Външен</option>
<option value={`${UserRole.POWERUSER}`}>Организатор</option> <option value={`${UserRole.POWERUSER}`}>Организатор</option>
<option value={`${UserRole.ADMIN}`}>Администратор</option> <ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=" ">
<option value={`${UserRole.ADMIN}`}>Администратор</option>
</ProtectedRoute>
{/* Add other roles as needed */} {/* Add other roles as needed */}
</select> </select>
</div> </div>

View File

@ -1,17 +1,22 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import common from 'src/helpers/common'; import common from 'src/helpers/common';
import axiosInstance from 'src/axiosSecure'; import axiosInstance from 'src/axiosSecure';
const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentChange }) => { const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentChange }) => {
const [shifts, setShifts] = React.useState([..._shifts]); const [shifts, setShifts] = useState([..._shifts]);
const [assignments, setAssignments] = useState(publisher.assignments || []);
//Refactor ToDo: show the whole month instead of just the current week by showing the shift start time in front of the rows, and show all shifts in the month from the first to the last week in the cell where we show one shift now //Refactor ToDo: show the whole month instead of just the current week by showing the shift start time in front of the rows, and show all shifts in the month from the first to the last week in the cell where we show one shift now
const monthInfo = common.getMonthDatesInfo(new Date(date)); let monthInfo = common.getMonthDatesInfo(new Date(date));
//if date is before monthInfo.firstMonday, get the previous month indexOf
if (date < monthInfo.firstMonday) {
monthInfo = common.getMonthDatesInfo(new Date(date.getFullYear(), date.getMonth() - 1, 1));
}
const monthShifts = shifts.filter(shift => { const monthShifts = shifts.filter(shift => {
const shiftDate = new Date(shift.startTime); const shiftDate = new Date(shift.startTime);
return shiftDate > monthInfo.firstDay && shiftDate < monthInfo.lastDay; return shiftDate > monthInfo.firstDay && shiftDate < monthInfo.lastSunday;
}); });
const weekShifts = monthShifts.filter(shift => { const weekShifts = monthShifts.filter(shift => {
const shiftDate = new Date(shift.startTime); const shiftDate = new Date(shift.startTime);
@ -38,11 +43,13 @@ const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentC
console.log("dayShifts:", dayShifts); console.log("dayShifts:", dayShifts);
const hasAssignment = (shiftId) => { const hasAssignment = (shiftId) => {
// return publisher.assignments.some(ass => ass.shift.id == shiftId);
return publisher.assignments?.some(ass => { return assignments.some(ass => ass.shift.id === shiftId);
//console.log(`Comparing: ${ass.shift.id} to ${shiftId}: ${ass.shift.id === shiftId}`); // // return publisher.assignments.some(ass => ass.shift.id == shiftId);
return ass.shift.id === shiftId; // return publisher.assignments?.some(ass => {
}); // //console.log(`Comparing: ${ass.shift.id} to ${shiftId}: ${ass.shift.id === shiftId}`);
// return ass.shift.id === shiftId;
// });
}; };
@ -63,6 +70,61 @@ const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentC
}; };
}, [onClose]); // Include onClose in the dependency array }, [onClose]); // Include onClose in the dependency array
function getColorForShift(shift) {
const assignedCount = shift.assignedCount || 0; // Assuming each shift has an assignedCount property
switch (assignedCount) {
case 0: return 'bg-blue-300';
case 1: return 'bg-green-300';
case 2: return 'bg-yellow-300';
case 3: return 'bg-orange-300';
case 4: return 'bg-red-200';
default: return 'bg-gray-300';
}
}
//ToDo: DRY - move to common
const addAssignment = async (publisher, shiftId) => {
try {
console.log(`calendar.idx: new assignment for publisher ${publisher.id} - ${publisher.firstName} ${publisher.lastName}`);
const newAssignment = {
publisher: { connect: { id: publisher.id } },
shift: { connect: { id: shiftId } },
isConfirmed: true
};
const { data } = await axiosInstance.post("/api/data/assignments", newAssignment);
// Update the 'publisher' property of the returned data with the full publisher object
data.publisher = publisher;
data.shift = shifts.find(shift => shift.id === shiftId);
publisher.assignments = [...publisher.assignments, data];
// handleAssignmentChange(publisher.id, 'add');
setAssignments(prevAssignments => [...prevAssignments, data]);
if (onAssignmentChange) { onAssignmentChange(publisher.id, 'add'); }
} catch (error) {
console.error("Error adding assignment:", error);
}
};
const removeAssignment = async (publisher, shiftId) => {
try {
const assignment = publisher.assignments.find(ass => ass.shift.id === shiftId);
console.log(`calendar.idx: remove assignment for shift ${shiftId}`);
const { data } = await axiosInstance.delete(`/api/data/assignments/${assignment.id}`);
//remove from local assignments:
publisher.assignments = publisher.assignments.filter(a => a.id !== assignment.id)
// Update local state
setAssignments(prevAssignments => prevAssignments.filter(a => a.id !== assignment.id));
//
// handleAssignmentChange(publisher.id, 'remove')
if (onAssignmentChange) {
onAssignmentChange(publisher.id, 'remove')
}
} catch (error) {
console.error("Error removing assignment:", error);
}
}
return ( return (
<div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50 z-50"> <div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-black bg-opacity-50 z-50">
<div className="relative bg-white p-8 rounded-lg shadow-xl max-w-xl w-full h-auto overflow-y-auto"> <div className="relative bg-white p-8 rounded-lg shadow-xl max-w-xl w-full h-auto overflow-y-auto">
@ -91,24 +153,23 @@ const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentC
> >
{common.getTimeRange(shift.startTime, shift.endTime)} {shift.id} {common.getTimeRange(shift.startTime, shift.endTime)} {shift.id}
{!assignmentExists && shift.isAvailable && ( {shift.isAvailable && (
<button onClick={() => { addAssignment(publisher, shift.id); }} <button
className="mt-2 bg-green-500 text-white p-1 rounded hover:bg-green-600 active:bg-green-700 focus:outline-none" onClick={() => {
if (assignmentExists) {
removeAssignment(publisher, shift.id);
} else {
addAssignment(publisher, shift.id);
}
}}
className={`mt-2 ${assignmentExists ? 'bg-red-500 hover:bg-red-600 active:bg-red-700' : 'bg-green-500 hover:bg-green-600 active:bg-green-700'} text-white p-1 rounded focus:outline-none`}
> >
добави {assignmentExists ? 'махни' : 'добави'}
</button>
)}
{assignmentExists && (
<button onClick={() => { removeAssignment(publisher, shift.id) }} // Implement the removeAssignment function
className="mt-2 bg-red-500 text-white p-1 rounded hover:bg-red-600 active:bg-red-700 focus:outline-none"
>
махни
</button> </button>
)} )}
</div> </div>
); );
} })}
)}
</div> </div>
))} ))}
</div> </div>
@ -133,57 +194,10 @@ const PublisherShiftsModal = ({ publisher, _shifts, onClose, date, onAssignmentC
</div> </div>
</div > </div >
); );
} }
function getColorForShift(shift) {
const assignedCount = shift.assignedCount || 0; // Assuming each shift has an assignedCount property
switch (assignedCount) {
case 0: return 'bg-blue-300';
case 1: return 'bg-green-300';
case 2: return 'bg-yellow-300';
case 3: return 'bg-orange-300';
case 4: return 'bg-red-200';
default: return 'bg-gray-300';
}
}
//ToDo: DRY - move to common
const addAssignment = async (publisher, shiftId) => {
try {
console.log(`calendar.idx: new assignment for publisher ${publisher.id} - ${publisher.firstName} ${publisher.lastName}`);
const newAssignment = {
publisher: { connect: { id: publisher.id } },
shift: { connect: { id: shiftId } },
isConfirmed: true
};
const { data } = await axiosInstance.post("/api/data/assignments", newAssignment);
// Update the 'publisher' property of the returned data with the full publisher object
data.publisher = publisher;
data.shift = shifts.find(shift => shift.id === shiftId);
publisher.assignments = [...publisher.assignments, data];
// handleAssignmentChange(publisher.id, 'add');
if (onAssignmentChange) { onAssignmentChange(publisher.id, 'add'); }
} catch (error) {
console.error("Error adding assignment:", error);
}
};
const removeAssignment = async (publisher, shiftId) => {
try {
const assignment = publisher.assignments.find(ass => ass.shift.id === shiftId);
console.log(`calendar.idx: remove assignment for shift ${shiftId}`);
const { data } = await axiosInstance.delete(`/api/data/assignments/${assignment.id}`);
//remove from local assignments:
publisher.assignments = publisher.assignments.filter(a => a.id !== assignment.id)
//
// handleAssignmentChange(publisher.id, 'remove')
if (onAssignmentChange) {
onAssignmentChange(publisher.id, 'remove')
}
} catch (error) {
console.error("Error removing assignment:", error);
}
}
export default PublisherShiftsModal; export default PublisherShiftsModal;

689
package-lock.json generated
View File

@ -100,6 +100,7 @@
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"depcheck": "^1.4.7", "depcheck": "^1.4.7",
"eslint-plugin-no-unsanitized": "^4.1.0",
"prisma": "^5.19.1" "prisma": "^5.19.1"
} }
}, },
@ -2128,6 +2129,200 @@
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz",
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
}, },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
"dev": true,
"peer": true,
"dependencies": {
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
"peer": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz",
"integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==",
"dev": true,
"peer": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/config-array": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
"integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint/object-schema": "^2.1.4",
"debug": "^4.3.1",
"minimatch": "^3.1.2"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-array/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"peer": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@eslint/config-array/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"peer": true
},
"node_modules/@eslint/eslintrc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
"integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"peer": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"peer": true
},
"node_modules/@eslint/eslintrc/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"peer": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"peer": true,
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/@eslint/eslintrc/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"peer": true
},
"node_modules/@eslint/js": {
"version": "9.10.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
"integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
"dev": true,
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/object-schema": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
"integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
"dev": true,
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
"integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
"dev": true,
"peer": true,
"dependencies": {
"levn": "^0.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@fast-csv/format": { "node_modules/@fast-csv/format": {
"version": "4.3.5", "version": "4.3.5",
"resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
@ -2293,6 +2488,34 @@
"react": ">= 16" "react": ">= 16"
} }
}, },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=12.22"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@humanwhocodes/retry": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
"integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true,
"peer": true,
"engines": {
"node": ">=18.18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@img/sharp-darwin-arm64": { "node_modules/@img/sharp-darwin-arm64": {
"version": "0.33.2", "version": "0.33.2",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz",
@ -4914,9 +5137,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.11.3", "version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -4933,6 +5156,16 @@
"acorn": "^8" "acorn": "^8"
} }
}, },
"node_modules/acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
"peer": true,
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": { "node_modules/acorn-walk": {
"version": "8.3.2", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
@ -6616,6 +6849,13 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
"peer": true
},
"node_modules/deepmerge": { "node_modules/deepmerge": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@ -7499,6 +7739,75 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/eslint": {
"version": "9.10.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
"integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0",
"@eslint/config-array": "^0.18.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "9.10.0",
"@eslint/plugin-kit": "^0.1.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.0.2",
"eslint-visitor-keys": "^4.0.0",
"espree": "^10.1.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
},
"bin": {
"eslint": "bin/eslint.js"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
},
"peerDependencies": {
"jiti": "*"
},
"peerDependenciesMeta": {
"jiti": {
"optional": true
}
}
},
"node_modules/eslint-plugin-no-unsanitized": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.0.tgz",
"integrity": "sha512-9A8Yrbkkex8e56ivxJ2f5dXN2Js2BmKC8QgmeYZjadyiGUngo3KLXDlq6ZzalmCHyLwLF5MoQLPR6FWlNc+Qbw==",
"dev": true,
"peerDependencies": {
"eslint": "^8 || ^9"
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -7512,6 +7821,178 @@
"node": ">=8.0.0" "node": ">=8.0.0"
} }
}, },
"node_modules/eslint-visitor-keys": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
"integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"peer": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/eslint/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/eslint/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"peer": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
"integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
"dev": true,
"peer": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint/node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"peer": true,
"dependencies": {
"is-glob": "^4.0.3"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/eslint/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/eslint/node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"peer": true
},
"node_modules/eslint/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"peer": true
},
"node_modules/eslint/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/espree": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
"integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
"dev": true,
"peer": true,
"dependencies": {
"acorn": "^8.12.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": { "node_modules/esprima": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@ -7525,6 +8006,29 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/esquery": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"dev": true,
"peer": true,
"dependencies": {
"estraverse": "^5.1.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/esquery/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/esrecurse": { "node_modules/esrecurse": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@ -7807,6 +8311,13 @@
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
}, },
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true,
"peer": true
},
"node_modules/fast-write-atomic": { "node_modules/fast-write-atomic": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/fast-write-atomic/-/fast-write-atomic-0.2.1.tgz", "resolved": "https://registry.npmjs.org/fast-write-atomic/-/fast-write-atomic-0.2.1.tgz",
@ -7839,6 +8350,19 @@
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.3.11.tgz",
"integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A==" "integrity": "sha512-Rr5QlUeGN1mbOHlaqcSYMKVpPbgLy0AWT/W0EHxA6NGI12yO1jpoui2zBBvU2G824ltM6Ut8BFgfHSBGfkmS0A=="
}, },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"peer": true,
"dependencies": {
"flat-cache": "^4.0.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/file-stream-rotator": { "node_modules/file-stream-rotator": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
@ -7935,7 +8459,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"optional": true, "devOptional": true,
"dependencies": { "dependencies": {
"locate-path": "^6.0.0", "locate-path": "^6.0.0",
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
@ -7962,6 +8486,27 @@
"node": ">= 10.13.0" "node": ">= 10.13.0"
} }
}, },
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"peer": true,
"dependencies": {
"flatted": "^3.2.9",
"keyv": "^4.5.4"
},
"engines": {
"node": ">=16"
}
},
"node_modules/flatted": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true,
"peer": true
},
"node_modules/fn.name": { "node_modules/fn.name": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
@ -9157,6 +9702,16 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.8.19"
}
},
"node_modules/indent-string": { "node_modules/indent-string": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@ -9533,7 +10088,7 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"optional": true, "devOptional": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@ -9881,6 +10436,13 @@
"bignumber.js": "^9.0.0" "bignumber.js": "^9.0.0"
} }
}, },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"peer": true
},
"node_modules/json-parse-even-better-errors": { "node_modules/json-parse-even-better-errors": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@ -9896,6 +10458,13 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
}, },
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true,
"peer": true
},
"node_modules/json-stringify-safe": { "node_modules/json-stringify-safe": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@ -10052,6 +10621,16 @@
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
}, },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"peer": true,
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/kleur": { "node_modules/kleur": {
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@ -10133,6 +10712,20 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"dev": true,
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/lie": { "node_modules/lie": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@ -10185,7 +10778,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"optional": true, "devOptional": true,
"dependencies": { "dependencies": {
"p-locate": "^5.0.0" "p-locate": "^5.0.0"
}, },
@ -10887,6 +11480,13 @@
"node": "^18 || >=20" "node": "^18 || >=20"
} }
}, },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true,
"peer": true
},
"node_modules/negotiator": { "node_modules/negotiator": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -14560,6 +15160,24 @@
"url": "https://github.com/sponsors/panva" "url": "https://github.com/sponsors/panva"
} }
}, },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"peer": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
"type-check": "^0.4.0",
"word-wrap": "^1.2.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/p-filter": { "node_modules/p-filter": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz",
@ -14585,7 +15203,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"optional": true, "devOptional": true,
"dependencies": { "dependencies": {
"yocto-queue": "^0.1.0" "yocto-queue": "^0.1.0"
}, },
@ -14600,7 +15218,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"optional": true, "devOptional": true,
"dependencies": { "dependencies": {
"p-limit": "^3.0.2" "p-limit": "^3.0.2"
}, },
@ -15072,6 +15690,16 @@
"preact": ">=10" "preact": ">=10"
} }
}, },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true,
"peer": true,
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/pretty-bytes": { "node_modules/pretty-bytes": {
"version": "5.6.0", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@ -16934,6 +17562,19 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/styled-jsx": { "node_modules/styled-jsx": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
@ -17491,6 +18132,13 @@
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
}, },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true,
"peer": true
},
"node_modules/thenify": { "node_modules/thenify": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@ -17862,6 +18510,19 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
}, },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "0.8.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@ -18631,6 +19292,16 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/wordwrap": { "node_modules/wordwrap": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
@ -19352,7 +20023,7 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"optional": true, "devOptional": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },

View File

@ -118,6 +118,7 @@
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"depcheck": "^1.4.7", "depcheck": "^1.4.7",
"eslint-plugin-no-unsanitized": "^4.1.0",
"prisma": "^5.19.1" "prisma": "^5.19.1"
} }
} }

View File

@ -12,7 +12,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { all } from "axios"; import { all } from "axios";
import { logger } from "src/helpers/common"; import { logger } from "src/helpers/common";
import { excel } from "src/helpers/excel"; import { ExportPublishersToExcel } from "src/helpers/excel";
/** /**
* *
@ -435,7 +435,7 @@ export default async function handler(req, res) {
res.status(200).json(await dataHelper.getAllPublishersWithStatisticsMonth(day, noEndDate)); res.status(200).json(await dataHelper.getAllPublishersWithStatisticsMonth(day, noEndDate));
case "exportPublishersExcel": case "exportPublishersExcel":
try { try {
await excel.ExportPublishersToExcel(req, res); await ExportPublishersToExcel(req, res);
} catch (error) { } catch (error) {
console.error(JSON.stringify(error)); console.error(JSON.stringify(error));
} }

View File

@ -143,7 +143,7 @@ function flattenRegistry(dayKey) {
return Object.values(weekEntries).flat(); return Object.values(weekEntries).flat();
} }
async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, autoFill = false, forDay, algType = 0) { async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, autoFill = false, forDay, algType = 0, until) {
let missingPublishers = []; let missingPublishers = [];
let publishersWithChangedPref = []; let publishersWithChangedPref = [];
@ -176,7 +176,14 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
if (forDay) { if (forDay) {
day = monthInfo.date; day = monthInfo.date;
endDate.setDate(monthInfo.date.getDate() + 1); if (until === undefined) {
const oneDayInMs = 24 * 60 * 60 * 1000;
endDate = new Date(monthInfo.date.getTime() + oneDayInMs);
}
else {
endDate = new Date(until);
}
dayNr = monthInfo.date.getDate(); dayNr = monthInfo.date.getDate();
weekNr = common.getWeekNumber(monthInfo.date); weekNr = common.getWeekNumber(monthInfo.date);
} }
@ -184,7 +191,7 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
let publishersThisWeek = []; let publishersThisWeek = [];
// # # # # # # # # # # #
// 0. generate shifts and assign publishers from the previous month if still available // 0. generate shifts and assign publishers from the previous month if still available
while (day < endDate) { while (day < endDate) {
let availablePubsForTheDay = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', day, false, false, false, true, false); let availablePubsForTheDay = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', day, false, false, false, true, false);
@ -262,7 +269,7 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
//ToDo: check if getAvailablePublishersForShift is working correctly. It seems not to! //ToDo: check if getAvailablePublishersForShift is working correctly. It seems not to!
let availablePublishers = await getAvailablePublishersForShiftNew(shiftStart, shiftEnd, availablePubsForTheDay, publishersThisWeek); let availablePublishers = await getAvailablePublishersForShiftNew(shiftStart, shiftEnd, availablePubsForTheDay, publishersThisWeek);
console.log("shift " + __shiftName + " needs " + publishersNeeded + " publishers, available: " + availablePublishers.length + " for the day: " + availablePubsForTheDay.length); console.log("shift " + __shiftName + " needs " + publishersNeeded + " publishers, available: " + availablePublishers.length + ", for the day: " + availablePubsForTheDay.length);
const createdShift = await prisma.shift.create({ const createdShift = await prisma.shift.create({
data: { data: {
@ -294,21 +301,31 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration); shiftEnd.setMinutes(shiftStart.getMinutes() + event.shiftDuration);
} }
day.setDate(day.getDate() + 1);
dayNr++; if (forDay) { break; }
if (common.DaysOfWeekArray[day.getDayEuropean()] === DayOfWeek.Sunday) { else {
weekNr++; day.setDate(day.getDate() + 1);
publishersThisWeek = []; dayNr++;
publishers.forEach(p => p.currentWeekAssignments = 0); if (common.DaysOfWeekArray[day.getDayEuropean()] === DayOfWeek.Sunday) {
weekNr++;
publishersThisWeek = [];
publishers.forEach(p => p.currentWeekAssignments = 0);
}
} }
if (forDay) break; }
let from = monthInfo.firstMonday, to = monthInfo.lastSunday;
if (forDay) {
from = day;
to = endDate;
} }
let allShifts = await prisma.shift.findMany({ let allShifts = await prisma.shift.findMany({
where: { where: {
startTime: { startTime: {
gte: monthInfo.firstMonday, gte: from,
lt: monthInfo.lastSunday, lt: to,
}, },
}, },
include: { include: {
@ -320,17 +337,23 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
}, },
}); });
let publishersToday = []; let publishersToday = [];
let rankedPublishers = []; let rankedPublishers = [];
// # # # # # # # # # # #
// Second pass - prioritize shifts with transport where it is needed // Second pass - prioritize shifts with transport where it is needed
if (forDay) { }
else {
day = new Date(monthInfo.firstMonday);
dayNr = 1;
weekNr = 1;
}
console.log("\r\n\r\n\r\n" + "# ".repeat(50)); console.log("\r\n\r\n\r\n" + "# ".repeat(50));
console.log("Second pass - fix transports " + monthInfo.monthName + " " + monthInfo.year); console.log("Second pass - fix transports " + day.toLocaleDateString());
day = new Date(monthInfo.firstMonday);
dayNr = 1;
weekNr = 1;
while (day < endDate) { while (day < endDate) {
let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day); let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day);
let event = events.find((event) => event.dayofweek == common.DaysOfWeekArray[day.getDayEuropean()]); let event = events.find((event) => event.dayofweek == common.DaysOfWeekArray[day.getDayEuropean()]);
@ -371,7 +394,7 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
else if (publishersNeeded > 0) { else if (publishersNeeded > 0) {
console.log("shift " + shift.name + " requires transport (" + transportCapable.length + " transport capable)"); console.log("shift " + shift.name + " requires transport (" + transportCapable.length + " transport capable)");
let availablePubsForTheShift = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', shift.startTime, true, false, false, true, false); let availablePubsForTheShift = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type,familyHeadId', shift.startTime, true, false, false, true, false);
let availablePublishers = availablePubsForTheShift.filter(p => { let availablePublishers = availablePubsForTheShift.filter(p => {
const hasTransportInAvailability = shift.transportIn && p.availabilities.some(avail => avail.isWithTransportIn); const hasTransportInAvailability = shift.transportIn && p.availabilities.some(avail => avail.isWithTransportIn);
@ -386,45 +409,35 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
} else if (algType == 1) { } else if (algType == 1) {
rankedPublishers = await RankPublishersForShiftWeighted([...availablePublishers], scheduledPubsPerDayAndWeek, day, weekNr); rankedPublishers = await RankPublishersForShiftWeighted([...availablePublishers], scheduledPubsPerDayAndWeek, day, weekNr);
} }
// if (rankedPublishers.length > 0) {
// const newAssignment = await prisma.assignment.create({
// data: {
// shift: {
// connect: {
// id: shift.id,
// },
// },
// publisher: {
// connect: {
// id: rankedPublishers[0].id,
// },
// },
// isWithTransport: true,
// isConfirmed: true,
// isBySystem: false,
// },
// });
// shift.assignments.push(newAssignment);
// publishersToday.push(rankedPublishers[0].id);
// updateRegistry(rankedPublishers[0].id, day, weekNr);
// }
AddPublisherAssignment(prisma, event, shift, availablePublishers, rankedPublishers, publishersToday, day, weekNr); AddPublisherAssignment(prisma, event, shift, availablePublishers, rankedPublishers, publishersToday, day, weekNr);
} }
} }
} }
day.setDate(day.getDate() + 1); if (forDay) { break; }
else {
day.setDate(day.getDate() + 1);
}
} }
// Fill the rest of the shifts
// # # # # # # # # # # #
// 3. Fill the rest of the shifts
let goal = 1; let goal = 1;
while (goal <= 4) { while (goal <= 4) {
console.log("\r\n\r\n\r\n" + "# ".repeat(50));
console.log("Filling shifts with " + goal + " publishers " + monthInfo.monthName + " " + monthInfo.year); if (forDay) {
day = new Date(monthInfo.firstMonday); }
dayNr = 1; else {
weekNr = 1; day = new Date(monthInfo.firstMonday);
dayNr = 1;
weekNr = 1;
}
console.log("\r\n\r\n" + "# ".repeat(50));
console.log("Filling shifts with " + goal + " publishers | " + day.toLocaleDateString());
while (day < endDate) { while (day < endDate) {
let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day); let dayOfWeekEnum = common.getDayOfWeekNameEnEnumForDate(day);
let event = events.find((event) => event.dayofweek == common.DaysOfWeekArray[day.getDayEuropean()]); let event = events.find((event) => event.dayofweek == common.DaysOfWeekArray[day.getDayEuropean()]);
@ -452,7 +465,7 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
console.log("Filling shift " + shift.name + " with " + goal + " publishers"); console.log("Filling shift " + shift.name + " with " + goal + " publishers");
let publishersNeeded = event.numberOfPublishers - shift.assignments.length; let publishersNeeded = event.numberOfPublishers - shift.assignments.length;
if (publishersNeeded > 0 && shift.assignments.length < goal) { if (publishersNeeded > 0 && shift.assignments.length < goal) {
let availablePubsForTheShift = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type', shift.startTime, true, false, false, true, false); let availablePubsForTheShift = await data.filterPublishersNew('id,firstName,lastName,email,isActive,desiredShiftsPerMonth,lastLogin,type,familyHeadId', shift.startTime, true, false, false, true, false);
let availablePublishers = await FilterInappropriatePublishers([...availablePubsForTheShift], publishersToday, shift); let availablePublishers = await FilterInappropriatePublishers([...availablePubsForTheShift], publishersToday, shift);
if (algType == 0) { if (algType == 0) {
@ -461,12 +474,17 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
rankedPublishers = await RankPublishersForShiftWeighted([...availablePublishers], scheduledPubsPerDayAndWeek, day, weekNr); rankedPublishers = await RankPublishersForShiftWeighted([...availablePublishers], scheduledPubsPerDayAndWeek, day, weekNr);
} }
AddPublisherAssignment(prisma, event, shift, availablePublishers, rankedPublishers, publishersToday, day, weekNr); await AddPublisherAssignment(prisma, event, shift, availablePublishers, rankedPublishers, publishersToday, day, weekNr);
} }
} }
} }
day.setDate(day.getDate() + 1); if (forDay) { break; }
else {
day.setDate(day.getDate() + 1);
}
} }
goal += 1; goal += 1;
} }
@ -486,14 +504,18 @@ async function GenerateSchedule(axios, date, copyFromPreviousMonth = false, auto
async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheShift, rankedPublishers, publishersToday, day, weekNr) { async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheShift, rankedPublishers, publishersToday, day, weekNr) {
if (rankedPublishers.length == 0) { if (rankedPublishers.length == 0) {
console.log("No available publishers for shift " + shift.name); console.log("! ! ! No available publishers for shift " + shift.name + " ! ! !");
} else { } else {
for (let i = 0; i < rankedPublishers.length; i++) { for (let i = 0; i < rankedPublishers.length; i++) {
let mainPublisher = rankedPublishers[i]; let mainPublisher = rankedPublishers[i];
let familyMembers = availablePubsForTheShift.filter(p => (p.familyHeadId && p.familyHeadId === mainPublisher.familyHeadId) || (p.familyHeadId === mainPublisher.id)); let familyMembers = availablePubsForTheShift.filter(p => (p.id !== mainPublisher.id && (p.id === mainPublisher.familyHeadId) || (p.familyHeadId === mainPublisher.id)));
if (familyMembers.length > 0 && (shift.assignments.length + familyMembers.length + 1) <= event.numberOfPublishers) { if (familyMembers.length > 0 && (shift.assignments.length + familyMembers.length + 1) <= event.numberOfPublishers) {
console.log("Assigning " + mainPublisher.firstName + " " + mainPublisher.lastName + " and family members to " + new Date(shift.startTime).getDate() + " " + shift.name); console.log("Assigning " + mainPublisher.firstName + " " + mainPublisher.lastName + " and " + familyMembers.length + " available family members to " + new Date(shift.startTime).getDate() + " " + shift.name);
const hasTransportInAvailability = shift.transportIn && mainPublisher.availabilities.some(avail => avail.isWithTransportIn);
const hasTransportOutAvailability = shift.transportOut && mainPublisher.availabilities.some(avail => avail.isWithTransportOut);
const newAssignment = await prisma.assignment.create({ const newAssignment = await prisma.assignment.create({
data: { data: {
shift: { shift: {
@ -508,7 +530,7 @@ async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheS
}, },
isConfirmed: false, isConfirmed: false,
isBySystem: false, isBySystem: false,
isWithTransport: shift.requiresTransport, isWithTransport: (hasTransportInAvailability || hasTransportOutAvailability),
}, },
}); });
shift.assignments.push(newAssignment); shift.assignments.push(newAssignment);
@ -540,6 +562,10 @@ async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheS
break; break;
} else if (familyMembers.length == 0) { } else if (familyMembers.length == 0) {
console.log("Assigning " + mainPublisher.firstName + " " + mainPublisher.lastName + " to " + new Date(shift.startTime).getDate() + " " + shift.name); console.log("Assigning " + mainPublisher.firstName + " " + mainPublisher.lastName + " to " + new Date(shift.startTime).getDate() + " " + shift.name);
const hasTransportInAvailability = shift.transportIn && mainPublisher.availabilities.some(avail => avail.isWithTransportIn);
const hasTransportOutAvailability = shift.transportOut && mainPublisher.availabilities.some(avail => avail.isWithTransportOut);
const newAssignment = await prisma.assignment.create({ const newAssignment = await prisma.assignment.create({
data: { data: {
shift: { shift: {
@ -554,7 +580,7 @@ async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheS
}, },
isConfirmed: false, isConfirmed: false,
isBySystem: false, isBySystem: false,
isWithTransport: shift.requiresTransport, isWithTransport: (hasTransportInAvailability || hasTransportOutAvailability),
}, },
}); });
shift.assignments.push(newAssignment); shift.assignments.push(newAssignment);
@ -564,17 +590,16 @@ async function AddPublisherAssignment(prisma, event, shift, availablePubsForTheS
} }
} }
} }
} }
async function FilterInappropriatePublishers(availablePublishers, pubsToExclude, shift) { async function FilterInappropriatePublishers(availablePublishers, pubsToExclude, shift, maxShifts = 0) {
//ToDo: Optimization: store number of publishers, so we process the shifts from least to most available publishers later. //ToDo: Optimization: store number of publishers, so we process the shifts from least to most available publishers later.
let goodPublishers = availablePublishers.filter(p => { let goodPublishers = availablePublishers.filter(p => {
const isNotAssigned = !shift.assignments.some(a => a.publisher?.id === p.id); const isNotAssigned = !shift.assignments.some(a => a.publisher?.id === p.id);
const isNotAssignedToday = !pubsToExclude.includes(p.id); const isNotAssignedToday = !pubsToExclude.includes(p.id);
const isAssignedEnough = p.currentMonthAssignments >= p.desiredShiftsPerMonth const isAssignedEnough = p.currentMonthAssignments >= p.desiredShiftsPerMonth
|| p.currentMonthAssignments >= 6; // overwrite the desiredShiftsPerMonth to max 6 shifts per month || p.currentMonthAssignments >= maxShifts; // overwrite the desiredShiftsPerMonth to max 10 shifts per month
return isNotAssigned && isNotAssignedToday && !isAssignedEnough; return isNotAssigned && isNotAssignedToday && (!isAssignedEnough || maxShifts == 0);
}); });
return goodPublishers; return goodPublishers;
} }
@ -696,10 +721,17 @@ async function RankPublishersForShiftWeighted(publishers, scheduledPubsPerDayAnd
const result = calculateScoreAndPenalties(p); const result = calculateScoreAndPenalties(p);
p.score = result.score; p.score = result.score;
p.penalties = result.penalties; p.penalties = result.penalties;
p.finalScore = p.score;
if (p.finalScore > 0) {
p.penalties.forEach(penalty => {
p.finalScore *= penalty.penalty;
});
}
}); });
// Sort publishers based on score // Sort publishers based on score
let ranked = publishers.sort((a, b) => b.score - a.score); let ranked = publishers.sort((a, b) => b.finalScore - a.finalScore);
// Log the scores and penalties of the top publisher // Log the scores and penalties of the top publisher
if (ranked.length > 0) { if (ranked.length > 0) {

View File

@ -625,7 +625,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
async function setAvailabilityBlockDate(AvailabilityBlockDate: Date): Promise<void> { async function setAvailabilityBlockDate(AvailabilityBlockDate: Date): Promise<void> {
// set AvailabilityBlockDate to the selected date // set AvailabilityBlockDate to the selected date
let monthInfo = common.getMonthInfo(value); let monthInfo = common.getMonthInfo(value);
await axiosInstance.put(`/api/?action=settings&key=AvailabilityBlockDate&value=${common.getISODateOnly(monthInfo.lastSunday)}`) await axiosInstance.put(`/api/?action=settings&key=AvailabilityBlockDate&value=${common.getISODateOnly(value)}`)
.then((response) => { .then((response) => {
console.log("AvailabilityBlockDate set to:", response.data); console.log("AvailabilityBlockDate set to:", response.data);
// setShifts([...shifts, response.data]); // setShifts([...shifts, response.data]);
@ -703,9 +703,11 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
<button className="block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 flex items-center" onClick={() => generateShifts("genEmptyDay", false, false, true)}> <button className="block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 flex items-center" onClick={() => generateShifts("genEmptyDay", false, false, true)}>
{isLoading('genEmptyDay') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-plus mr-2"></i>)} {isLoading('genEmptyDay') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-plus mr-2"></i>)}
създай празни ({value.getDate()}-ти) </button> създай празни ({value.getDate()}-ти) </button>
<button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={() => generateShifts("genDay", false, true, true)}>
<button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={() => generateShifts("genDay", false, true, true, 1)}>
{isLoading('genDay') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-cogs mr-2"></i>)} {isLoading('genDay') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-cogs mr-2"></i>)}
Генерирай смени ({value.getDate()}-ти) </button> Генерирай смени ({value.getDate()}-ти) </button>
<button className="block px-4 py-2 text-sm text-red-500 hover:bg-gray-100" <button className="block px-4 py-2 text-sm text-red-500 hover:bg-gray-100"
onClick={() => openConfirmModal( onClick={() => openConfirmModal(
'Сигурни ли сте че искате да изтриете смените и назначения на този ден?', 'Сигурни ли сте че искате да изтриете смените и назначения на този ден?',
@ -749,7 +751,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
{isLoading('fetchShifts') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-sync-alt mr-2"></i>)} презареди</button> {isLoading('fetchShifts') ? (<i className="fas fa-sync-alt fa-spin mr-2"></i>) : (<i className="fas fa-sync-alt mr-2"></i>)} презареди</button>
{/* <button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={generateMonthlyStatistics}><i className="fas fa-chart-bar mr-2"></i> Генерирай статистика</button> {/* <button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={generateMonthlyStatistics}><i className="fas fa-chart-bar mr-2"></i> Генерирай статистика</button>
<button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={copyOldAvailabilities}><i className="fas fa-copy mr-2"></i> Прехвърли предпочитанията</button> */} <button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={copyOldAvailabilities}><i className="fas fa-copy mr-2"></i> Прехвърли предпочитанията</button> */}
<button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={setAvailabilityBlockDate}><i className="fas fa-copy mr-2"></i> Блокирай предпочитанията до края на {selectedMonth + 1} м.</button> <button className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onClick={setAvailabilityBlockDate}><i className="fas fa-copy mr-2"></i> Блокирай предпочитанията до {value.getDate()}-ти</button>
</div> </div>
</div> </div>
)} )}

View File

@ -219,7 +219,7 @@ export default function DashboardPage({ initialItems, initialUserId, cartEvents,
<Layout> <Layout>
<ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER, UserRole.USER, UserRole.EXTERNAL]} deniedMessage=""> <ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER, UserRole.USER, UserRole.EXTERNAL]} deniedMessage="">
<h1 className="pt-2 pb-1 text-xl font-bold text-center">Графика на {userName}</h1> <h1 className="pt-2 pb-1 text-xl font-bold text-center">Графика на {userName}</h1>
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=""> <ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]} deniedMessage="">
<PublisherSearchBox selectedId={userId} infoText="" onChange={handleUserSelection} /> <PublisherSearchBox selectedId={userId} infoText="" onChange={handleUserSelection} />
</ProtectedRoute> </ProtectedRoute>
<div className="flex flex-row md:flex-row mt-4 xs:mt-1 space-y-4 md:space-y-0 md:space-x-4 h-[calc(100vh-10rem)]"> <div className="flex flex-row md:flex-row mt-4 xs:mt-1 space-y-4 md:space-y-0 md:space-x-4 h-[calc(100vh-10rem)]">
@ -318,7 +318,7 @@ export const getServerSideProps = async (context) => {
if (blockedDate) { if (blockedDate) {
blockedDate.value = new Date(blockedDate.value); blockedDate.value = new Date(blockedDate.value);
lastPublishedDate = lastPublishedDate > blockedDate.value ? lastPublishedDate : blockedDate.value; lastPublishedDate = blockedDate.value;
} }
let messages = await prisma.message.findMany({ let messages = await prisma.message.findMany({
@ -333,8 +333,9 @@ export const getServerSideProps = async (context) => {
}); });
messages = messages.filter((message) => { messages = messages.filter((message) => {
return (!message.Survey.publicFrom || message.Survey.publicFrom >= common.getStartOfDay(new Date())) let now = new Date();
&& (!message.Survey.publicUntil || message.Survey.publicUntil <= common.getEndOfDay(new Date())) return (!message.Survey.publicFrom || message.Survey.publicFrom <= common.getStartOfDay(now))
&& (!message.Survey.publicUntil || message.Survey.publicUntil >= common.getEndOfDay(now))
}); });
messages = common.convertDatesToISOStrings(messages); messages = common.convertDatesToISOStrings(messages);
messages = messages.map(message => { messages = messages.map(message => {

View File

@ -49,7 +49,7 @@ const PDFViewerPage = ({ pdfFiles }) => {
return ( return (
<Layout> <Layout>
<h1 className="text-3xl font-bold p-4 pt-8">Разрешителни</h1> <h1 className="text-3xl font-bold p-4 pt-8">Разрешителни</h1>
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage=""> <ProtectedRoute allowedRoles={[UserRole.ADMIN, UserRole.POWERUSER]} deniedMessage="">
<div className="border border-blue-500 p-4 rounded shadow-md"> <div className="border border-blue-500 p-4 rounded shadow-md">
<div className="mb-6"> <div className="mb-6">
<p className="text-lg mb-2">За да качите файл кликнете на бутона по-долу и изберете файл от вашия компютър.</p> <p className="text-lg mb-2">За да качите файл кликнете на бутона по-долу и изберете файл от вашия компютър.</p>

View File

@ -17,7 +17,7 @@ const CREDENTIALS_PATH = path.join(
); );
//generates iCalendar file for a single shift //generates iCalendar file for a single shift
GenerateICS = function (shifts) { const GenerateICS = function (shifts) {
// https://stackoverflow.com/questions/3665115/create-a-file-in-memory-for-user-to-download-not-through-server // https://stackoverflow.com/questions/3665115/create-a-file-in-memory-for-user-to-download-not-through-server
var content = "BEGIN:VCALENDAR\n"; var content = "BEGIN:VCALENDAR\n";
@ -174,76 +174,76 @@ GenerateICS = function (shifts) {
// const TOKEN_PATH = path.join(process.cwd(), 'content/token.json'); // const TOKEN_PATH = path.join(process.cwd(), 'content/token.json');
// const CREDENTIALS_PATH = path.join(process.cwd(), 'content/client_secret_926212607479-d3m8hm8f8esp3rf1639prskn445sa01v.apps.googleusercontent.com.json'); // const CREDENTIALS_PATH = path.join(process.cwd(), 'content/client_secret_926212607479-d3m8hm8f8esp3rf1639prskn445sa01v.apps.googleusercontent.com.json');
createEvent = async function createEvent(id) { // const createEvent = async function createEvent(id) {
// var eventTest2 = { // // var eventTest2 = {
// 'summary': 'Google I/O 2015', // // 'summary': 'Google I/O 2015',
// 'location': '800 Howard St., San Francisco, CA 94103', // // 'location': '800 Howard St., San Francisco, CA 94103',
// 'description': 'A chance to hear more about Google\'s developer products.', // // 'description': 'A chance to hear more about Google\'s developer products.',
// 'start': { // // 'start': {
// 'dateTime': '2015-05-28T09:00:00-07:00', // // 'dateTime': '2015-05-28T09:00:00-07:00',
// 'timeZone': 'America/Los_Angeles', // // 'timeZone': 'America/Los_Angeles',
// }, // // },
// 'end': { // // 'end': {
// 'dateTime': '2015-05-28T17:00:00-07:00', // // 'dateTime': '2015-05-28T17:00:00-07:00',
// 'timeZone': 'America/Los_Angeles', // // 'timeZone': 'America/Los_Angeles',
// }, // // },
// 'recurrence': [ // // 'recurrence': [
// 'RRULE:FREQ=DAILY;COUNT=2' // // 'RRULE:FREQ=DAILY;COUNT=2'
// ], // // ],
// 'attendees': [ // // 'attendees': [
// { 'email': '' }, // // { 'email': '' },
// ], // // ],
// 'reminders': { // // 'reminders': {
// 'useDefault': false, // // 'useDefault': false,
// 'overrides': [ // // 'overrides': [
// { 'method': 'email', 'minutes': 24 * 60 }, // // { 'method': 'email', 'minutes': 24 * 60 },
// { 'method': 'popup', 'minutes': 10 }, // // { 'method': 'popup', 'minutes': 10 },
// ], // // ],
// }, // // },
// }; // // };
// var errors; // // var errors;
// var auth = await exports.authorize().then((auth) => { // // var auth = await exports.authorize().then((auth) => {
// const calendar = google.calendar({ version: 'v3', auth }); // // const calendar = google.calendar({ version: 'v3', auth });
// const res = calendar.events.insert({ // // const res = calendar.events.insert({
// calendarId: 'primary', // // calendarId: 'primary',
// resource: eventTest, // // resource: eventTest,
// }, (err, event) => { // // }, (err, event) => {
// if (err) { // // if (err) {
// console.log(`There was an error creating the event: ${err}`); // // console.log(`There was an error creating the event: ${err}`);
// return; // // return;
// } // // }
// console.log(`Event created: ${event.data.htmlLink}`); // // console.log(`Event created: ${event.data.htmlLink}`);
// }); // // });
// }).catch(console.error).then((err) => { // // }).catch(console.error).then((err) => {
// errors = err; // // errors = err;
// }); // // });
// return errors; // // return errors;
// } // // }
// authorizeNew = async function authorizeNew() { // // authorizeNew = async function authorizeNew() {
// let client = await loadSavedCredentialsIfExist(); // // let client = await loadSavedCredentialsIfExist();
// if (client) { // // if (client) {
// return client; // // return client;
// } // // }
// // Set up the Google Calendar API client // // // Set up the Google Calendar API client
// const calendar = google.calendar({ version: 'v3', auth }); // // const calendar = google.calendar({ version: 'v3', auth });
// // Load the client secrets from a JSON file // // // Load the client secrets from a JSON file
fs.readFile("./path/to/client_secret.json", (err, content) => { // fs.readFile("./path/to/client_secret.json", (err, content) => {
if (err) return console.error("Error loading client secret file:", err); // if (err) return console.error("Error loading client secret file:", err);
// Authorize a client with the loaded credentials // // Authorize a client with the loaded credentials
authorizeOA(JSON.parse(content), calendar.calendarList.list); // authorizeOA(JSON.parse(content), calendar.calendarList.list);
}); // });
if (client.credentials) { // if (client.credentials) {
await saveCredentials(client); // await saveCredentials(client);
} // }
return client; // return client;
}; // };
// var googletoken = axiosInstance.get("/content/google_token.json"); // var googletoken = axiosInstance.get("/content/google_token.json");
// console.log("googletoken: " + googletoken); // console.log("googletoken: " + googletoken);
@ -372,7 +372,7 @@ async function importModules() {
}; };
} }
createEvent = async (event) => { const createEvent = async (event) => {
const { open, getPort } = await importModules(); const { open, getPort } = await importModules();
@ -474,7 +474,7 @@ createEvent = async (event) => {
} }
}; };
SaveEventsInGoogleCalendar = async function SaveEventsInGoogleCalendar(events) { const SaveEventsInGoogleCalendar = async function SaveEventsInGoogleCalendar(events) {
// Load client secrets from a local file. // Load client secrets from a local file.
try { try {
const content = await fs.readFile(CREDENTIALS_PATH); const content = await fs.readFile(CREDENTIALS_PATH);

View File

@ -5,7 +5,7 @@ const path = require('path');
const { MailtrapClient } = require("mailtrap"); const { MailtrapClient } = require("mailtrap");
const nodemailer = require("nodemailer"); const nodemailer = require("nodemailer");
const CON = require("./const"); const CON = require("./const");
const CAL = require("./calendar"); const { GenerateICS } = require("./calendar");
const common = require("./common"); const common = require("./common");
const Handlebars = require('handlebars'); const Handlebars = require('handlebars');