(wip) - show repeating events in calendar (admin)
script to get jwt signature for Apple ID
This commit is contained in:
@ -1,67 +0,0 @@
|
|||||||
#!/bin/node
|
|
||||||
# https://gist.githubusercontent.com/balazsorban44/09613175e7b37ec03f676dcefb7be5eb/raw/b0d31aa0c7f58e0088fdf59ec30cad1415a3475b/apple-gen-secret.mjs
|
|
||||||
|
|
||||||
import { SignJWT } from "jose"
|
|
||||||
import { createPrivateKey } from "crypto"
|
|
||||||
|
|
||||||
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
||||||
console.log(`
|
|
||||||
Creates a JWT from the components found at Apple.
|
|
||||||
By default, the JWT has a 6 months expiry date.
|
|
||||||
Read more: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
node apple.mjs [--kid] [--iss] [--private_key] [--sub] [--expires_in] [--exp]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--help Print this help message
|
|
||||||
--kid, --key_id The key id of the private key
|
|
||||||
--iss, --team_id The Apple team ID
|
|
||||||
--private_key The private key to use to sign the JWT. (Starts with -----BEGIN PRIVATE KEY-----)
|
|
||||||
--sub, --client_id The client id to use in the JWT.
|
|
||||||
--expires_in Number of seconds from now when the JWT should expire. Defaults to 6 months.
|
|
||||||
--exp Future date in seconds when the JWT expires
|
|
||||||
`)
|
|
||||||
} else {
|
|
||||||
const args = process.argv.slice(2).reduce((acc, arg, i) => {
|
|
||||||
if (arg.match(/^--\w/)) {
|
|
||||||
const key = arg.replace(/^--/, "").toLowerCase()
|
|
||||||
acc[key] = process.argv[i + 3]
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const {
|
|
||||||
team_id,
|
|
||||||
iss = team_id,
|
|
||||||
|
|
||||||
private_key,
|
|
||||||
|
|
||||||
client_id,
|
|
||||||
sub = client_id,
|
|
||||||
|
|
||||||
key_id,
|
|
||||||
kid = key_id,
|
|
||||||
|
|
||||||
expires_in = 86400 * 180,
|
|
||||||
exp = Math.ceil(Date.now() / 1000) + expires_in,
|
|
||||||
} = args
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How long is the secret valid in seconds.
|
|
||||||
* @default 15780000
|
|
||||||
*/
|
|
||||||
const expiresAt = Math.ceil(Date.now() / 1000) + expires_in
|
|
||||||
const expirationTime = exp ?? expiresAt
|
|
||||||
console.log(`
|
|
||||||
Apple client secret generated. Valid until: ${new Date(expirationTime * 1000)}
|
|
||||||
|
|
||||||
${await new SignJWT({})
|
|
||||||
.setAudience("https://appleid.apple.com")
|
|
||||||
.setIssuer(iss)
|
|
||||||
.setIssuedAt()
|
|
||||||
.setExpirationTime(expirationTime)
|
|
||||||
.setSubject(sub)
|
|
||||||
.setProtectedHeader({ alg: "ES256", kid })
|
|
||||||
.sign(createPrivateKey(private_key.replace(/\\n/g, "\n")))}`)
|
|
||||||
}
|
|
@ -298,7 +298,7 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o
|
|||||||
|
|
||||||
return itemStart && itemEnd &&
|
return itemStart && itemEnd &&
|
||||||
(slotStart.getTime() < itemEnd.getTime()) &&
|
(slotStart.getTime() < itemEnd.getTime()) &&
|
||||||
(slotEnd.getTime() > itemStart.getTime());
|
(slotEnd.getTime() >= itemStart.getTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
slots.push({
|
slots.push({
|
||||||
|
@ -17,7 +17,7 @@ import { MdToday } from 'react-icons/md';
|
|||||||
import { useSwipeable } from 'react-swipeable';
|
import { useSwipeable } from 'react-swipeable';
|
||||||
import axiosInstance from '../../src/axiosSecure';
|
import axiosInstance from '../../src/axiosSecure';
|
||||||
// import { set, format, addDays } from 'date-fns';
|
// import { set, format, addDays } from 'date-fns';
|
||||||
import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns';
|
// import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns';
|
||||||
import { filter } from 'jszip';
|
import { filter } from 'jszip';
|
||||||
import e from 'express';
|
import e from 'express';
|
||||||
|
|
||||||
@ -170,6 +170,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
|
|||||||
};
|
};
|
||||||
const filterEvents = (evts, publisherId, startdate) => {
|
const filterEvents = (evts, publisherId, startdate) => {
|
||||||
setDate(startdate); // Assuming setDate is a function that sets some state or context
|
setDate(startdate); // Assuming setDate is a function that sets some state or context
|
||||||
|
const filterHHmm = new Date(startdate).getHours() * 100 + new Date(startdate).getMinutes();
|
||||||
|
|
||||||
// Filter events based on the publisher ID and the start date/time
|
// Filter events based on the publisher ID and the start date/time
|
||||||
const existingEvents = evts?.filter(event => {
|
const existingEvents = evts?.filter(event => {
|
||||||
@ -177,15 +178,15 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
|
|||||||
const isPublisherMatch = (event.publisher?.id || event.publisherId) === publisherId;
|
const isPublisherMatch = (event.publisher?.id || event.publisherId) === publisherId;
|
||||||
|
|
||||||
let isDateMatch;
|
let isDateMatch;
|
||||||
|
const eventDate = new Date(event.startTime);
|
||||||
if (event.repeatWeekly && event.date) {
|
if (event.repeatWeekly && event.date) {
|
||||||
// Compare only the time part
|
// Compare only the time part
|
||||||
const eventDate = new Date(event.startTime);
|
let evenStarttHHmm = eventDate.getHours() * 100 + eventDate.getMinutes();
|
||||||
const isSameHour = getHours(eventDate) === getHours(startdate);
|
let eventEndHHmm = eventDate.getHours() * 100 + eventDate.getMinutes();
|
||||||
const isSameMinute = getMinutes(eventDate) === getMinutes(startdate);
|
isDateMatch = filterHHmm >= evenStarttHHmm && filterHHmm <= eventEndHHmm;
|
||||||
isDateMatch = isSameHour && isSameMinute;
|
|
||||||
} else if (event.date) {
|
} else if (event.date) {
|
||||||
// Compare the full date
|
// Compare the full date. issameday is not working. do it manually
|
||||||
isDateMatch = isSameDay(new Date(event.date), startdate);
|
isDateMatch = eventDate.setHours(0, 0, 0, 0) === new Date(startdate).setHours(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isPublisherMatch && isDateMatch;
|
return isPublisherMatch && isDateMatch;
|
||||||
@ -203,7 +204,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
|
|||||||
maxTime.setHours(maxHour, 0, 0);
|
maxTime.setHours(maxHour, 0, 0);
|
||||||
const totalHours = maxHour - minHour;
|
const totalHours = maxHour - minHour;
|
||||||
|
|
||||||
const handleSelect = ({ start, end }) => {
|
const handleSelect = ({ mode, start, end }) => {
|
||||||
const startdate = typeof start === 'string' ? new Date(start) : start;
|
const startdate = typeof start === 'string' ? new Date(start) : start;
|
||||||
const enddate = typeof end === 'string' ? new Date(end) : end;
|
const enddate = typeof end === 'string' ? new Date(end) : end;
|
||||||
|
|
||||||
@ -230,6 +231,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
|
|||||||
setDate(start);
|
setDate(start);
|
||||||
|
|
||||||
// get exising events for the selected date
|
// get exising events for the selected date
|
||||||
|
//ToDo: properly fix this. filterEvents does not return the expcted results
|
||||||
let existingEvents = filterEvents(evts, publisherId, startdate);
|
let existingEvents = filterEvents(evts, publisherId, startdate);
|
||||||
// if existingEvents is empty - create new with the selected range
|
// if existingEvents is empty - create new with the selected range
|
||||||
if (existingEvents.length === 0) {
|
if (existingEvents.length === 0) {
|
||||||
|
40
pages/api/auth/apple-token.ts
Normal file
40
pages/api/auth/apple-token.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// pages/api/auth/apple-token.js
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
const dotenv = require("dotenv");
|
||||||
|
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method === 'GET') {
|
||||||
|
try {
|
||||||
|
const appleKey = fs.readFileSync(path.resolve('./_deploy/appleKey.p8'), 'utf8');
|
||||||
|
|
||||||
|
const token = jwt.sign({}, appleKey, {
|
||||||
|
algorithm: 'ES256',
|
||||||
|
expiresIn: '180d',
|
||||||
|
issuer: process.env.APPLE_TEAM_ID,
|
||||||
|
header: {
|
||||||
|
alg: 'ES256',
|
||||||
|
kid: process.env.APPLE_KEY_ID,
|
||||||
|
},
|
||||||
|
audience: 'https://appleid.apple.com',
|
||||||
|
subject: process.env.APPLE_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Redirect to Apple's authentication page, or send the token to the client to do so
|
||||||
|
console.log(token);
|
||||||
|
res.status(200).send({
|
||||||
|
message: 'Generated token for Apple Sign In',
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error signing in with Apple:', error);
|
||||||
|
res.status(500).send({ error: 'Failed to sign in with Apple' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle any non-GET requests
|
||||||
|
res.setHeader('Allow', ['GET']);
|
||||||
|
res.status(405).end(`Method ${req.method} Not Allowed`);
|
||||||
|
}
|
||||||
|
}
|
@ -161,8 +161,9 @@ export default async function handler(req, res) {
|
|||||||
res.status(200).json(publishers);
|
res.status(200).json(publishers);
|
||||||
break;
|
break;
|
||||||
case "filterPublishersNew":
|
case "filterPublishersNew":
|
||||||
|
let includeOldAvailabilities = common.parseBool(req.query.includeOldAvailabilities);
|
||||||
let results = await filterPublishersNew_Available(req.query.select, day,
|
let results = await filterPublishersNew_Available(req.query.select, day,
|
||||||
common.parseBool(req.query.isExactTime), common.parseBool(req.query.isForTheMonth));
|
common.parseBool(req.query.isExactTime), common.parseBool(req.query.isForTheMonth), includeOldAvailabilities);
|
||||||
res.status(200).json(results);
|
res.status(200).json(results);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -461,8 +462,8 @@ export async function getMonthlyStatistics(selectFields, filterDate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function filterPublishersNew_Available(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true) {
|
export async function filterPublishersNew_Available(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true, includeOldAvailabilities = false) {
|
||||||
return data.filterPublishersNew(selectFields, filterDate, isExactTime, isForTheMonth, isWithStats);
|
return data.filterPublishersNew(selectFields, filterDate, isExactTime, isForTheMonth, isWithStats, includeOldAvailabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
// availabilites filter:
|
// availabilites filter:
|
||||||
|
@ -76,15 +76,15 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
const [modalPub, setModalPub] = useState(null);
|
const [modalPub, setModalPub] = useState(null);
|
||||||
|
|
||||||
// ------------------ no assignments checkbox ------------------
|
// ------------------ no assignments checkbox ------------------
|
||||||
const [isCheckboxChecked, setIsCheckboxChecked] = useState(false);
|
const [filterShowWithoutAssignments, setFilterShowWithoutAssignments] = useState(false);
|
||||||
const handleCheckboxChange = (event) => {
|
const handleCheckboxChange = (event) => {
|
||||||
setIsCheckboxChecked(!isCheckboxChecked); // Toggle the checkbox state
|
setFilterShowWithoutAssignments(!filterShowWithoutAssignments); // Toggle the checkbox state
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("checkbox checked: " + isCheckboxChecked);
|
console.log("checkbox checked: " + filterShowWithoutAssignments);
|
||||||
handleCalDateChange(value); // Call handleCalDateChange whenever isCheckboxChecked changes
|
handleCalDateChange(value); // Call handleCalDateChange whenever isCheckboxChecked changes
|
||||||
}, [isCheckboxChecked]); // Dependency array
|
}, [filterShowWithoutAssignments]); // Dependency array
|
||||||
|
|
||||||
const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth());
|
const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth());
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -99,7 +99,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
var date = new Date(common.getDateFromDateTime(selectedDate));//ToDo: check if seting the timezone affects the selectedDate?!
|
var date = new Date(common.getDateFromDateTime(selectedDate));//ToDo: check if seting the timezone affects the selectedDate?!
|
||||||
var dateStr = common.getISODateOnly(date);
|
var dateStr = common.getISODateOnly(date);
|
||||||
console.log("Setting date to '" + date.toLocaleDateString() + "' from '" + selectedDate.toLocaleDateString() + "'. ISO: " + date.toISOString(), "locale ISO:", common.getISODateOnly(date));
|
console.log("Setting date to '" + date.toLocaleDateString() + "' from '" + selectedDate.toLocaleDateString() + "'. ISO: " + date.toISOString(), "locale ISO:", common.getISODateOnly(date));
|
||||||
if (isCheckboxChecked) {
|
if (filterShowWithoutAssignments) {
|
||||||
console.log(`getting unassigned publishers for ${common.getMonthName(date.getMonth())} ${date.getFullYear()}`);
|
console.log(`getting unassigned publishers for ${common.getMonthName(date.getMonth())} ${date.getFullYear()}`);
|
||||||
const { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=getUnassignedPublishers&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`);
|
const { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=getUnassignedPublishers&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`);
|
||||||
setAvailablePubs(availablePubsForDate);
|
setAvailablePubs(availablePubsForDate);
|
||||||
@ -110,7 +110,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
const { data: shiftsForDate } = await axiosInstance.get(`/api/?action=getShiftsForDay&date=${dateStr}`);
|
const { data: shiftsForDate } = await axiosInstance.get(`/api/?action=getShiftsForDay&date=${dateStr}`);
|
||||||
setShifts(shiftsForDate);
|
setShifts(shiftsForDate);
|
||||||
setIsPublished(shiftsForDate.some(shift => shift.isPublished));
|
setIsPublished(shiftsForDate.some(shift => shift.isPublished));
|
||||||
let { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=filterPublishersNew&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth`);
|
let { data: availablePubsForDate } = await axiosInstance.get(`/api/?action=filterPublishersNew&date=${dateStr}&select=id,firstName,lastName,isActive,desiredShiftsPerMonth&includeOldAvailabilities=${filterShowWithoutAssignments}`);
|
||||||
|
|
||||||
availablePubsForDate.forEach(pub => {
|
availablePubsForDate.forEach(pub => {
|
||||||
pub.canTransport = pub.availabilities.some(av =>
|
pub.canTransport = pub.availabilities.some(av =>
|
||||||
@ -664,9 +664,10 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
|
|
||||||
<h2 className="text-lg font-semibold mb-4">Достъпни за този ден: <span className="text-blue-600">{availablePubs.length}</span></h2>
|
<h2 className="text-lg font-semibold mb-4">Достъпни за този ден: <span className="text-blue-600">{availablePubs.length}</span></h2>
|
||||||
<label className="toggle pb-3">
|
<label className="toggle pb-3">
|
||||||
<input type="checkbox" className="toggle-checkbox" onChange={handleCheckboxChange} />
|
<input type="checkbox" className="toggle-checkbox" id="filterShowWithoutAssignments" onChange={handleCheckboxChange} />
|
||||||
<span className="toggle-slider m-1">без назначения за месеца</span>
|
<span className="toggle-slider m-1">без назначения за месеца</span>
|
||||||
|
<input type="checkbox" className="toggle-checkbox" id="filterIncludeOldAvailabilities" onChange={handleCheckboxChange} />
|
||||||
|
<span className="toggle-slider m-1">със стари предпочитания</span>
|
||||||
</label>
|
</label>
|
||||||
<ul className="w-full max-w-md">
|
<ul className="w-full max-w-md">
|
||||||
{Array.isArray(availablePubs) && availablePubs?.map((pub, index) => {
|
{Array.isArray(availablePubs) && availablePubs?.map((pub, index) => {
|
||||||
@ -891,6 +892,7 @@ export default function CalendarPage({ initialEvents, initialShifts }) {
|
|||||||
|
|
||||||
import axiosServer from '../../../src/axiosServer';
|
import axiosServer from '../../../src/axiosServer';
|
||||||
import { start } from 'repl';
|
import { start } from 'repl';
|
||||||
|
import { filter } from 'jszip';
|
||||||
export const getServerSideProps = async (context) => {
|
export const getServerSideProps = async (context) => {
|
||||||
const axios = await axiosServer(context);
|
const axios = await axiosServer(context);
|
||||||
// const baseUrl = common.getBaseUrl();
|
// const baseUrl = common.getBaseUrl();
|
||||||
|
@ -6,18 +6,11 @@ const levenshtein = require('fastest-levenshtein');
|
|||||||
|
|
||||||
const fs = typeof window === 'undefined' ? require('fs') : undefined;
|
const fs = typeof window === 'undefined' ? require('fs') : undefined;
|
||||||
const path = typeof window === 'undefined' ? require('path') : undefined;
|
const path = typeof window === 'undefined' ? require('path') : undefined;
|
||||||
|
const { PrismaClient, UserRole } = require('@prisma/client');
|
||||||
const { PrismaClient } = require('@prisma/client');
|
|
||||||
const DayOfWeek = require("@prisma/client").DayOfWeek;
|
const DayOfWeek = require("@prisma/client").DayOfWeek;
|
||||||
|
|
||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
// 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 { getSession } = require("next-auth/react");
|
||||||
const { UserRole } = require("@prisma/client");
|
|
||||||
// const { set } = require('date-fns');
|
|
||||||
|
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: 'info', // Set the default log level
|
level: 'info', // Set the default log level
|
||||||
@ -334,6 +327,18 @@ exports.compareTimes = function (time1, time2) {
|
|||||||
const time2String = `${getHours(time2)}:${getMinutes(time2)}`;
|
const time2String = `${getHours(time2)}:${getMinutes(time2)}`;
|
||||||
return time1String.localeCompare(time2String);
|
return time1String.localeCompare(time2String);
|
||||||
};
|
};
|
||||||
|
exports.normalizeTime = function (date, baseDate) {
|
||||||
|
// return set(baseDate, {
|
||||||
|
// hours: getHours(date),
|
||||||
|
// minutes: getMinutes(date),
|
||||||
|
// seconds: getSeconds(date),
|
||||||
|
// milliseconds: 0
|
||||||
|
// });
|
||||||
|
//don't use date-fns
|
||||||
|
let newDate = new Date(baseDate);
|
||||||
|
newDate.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), 0);
|
||||||
|
return newDate;
|
||||||
|
}
|
||||||
|
|
||||||
exports.getTimeRange = function (start, end) {
|
exports.getTimeRange = function (start, end) {
|
||||||
start = new Date(start);
|
start = new Date(start);
|
||||||
|
@ -228,7 +228,7 @@ async function getAvailabilities(userId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function filterPublishersNew(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true) {
|
async function filterPublishersNew(selectFields, filterDate, isExactTime = false, isForTheMonth = false, isWithStats = true, includeOldAvailabilities = false) {
|
||||||
|
|
||||||
filterDate = new Date(filterDate); // Convert to date object if not already
|
filterDate = new Date(filterDate); // Convert to date object if not already
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user