diff --git a/components/availability/AvailabilityForm.js b/components/availability/AvailabilityForm.js index b911438..79b1345 100644 --- a/components/availability/AvailabilityForm.js +++ b/components/availability/AvailabilityForm.js @@ -11,6 +11,7 @@ import { ToastContainer } from 'react-toastify'; const common = require('src/helpers/common'); //todo import Availability type from prisma schema import { isBefore, addMinutes, isAfter, isEqual, set, getHours, getMinutes, getSeconds } from 'date-fns'; //ToDo obsolete +import { stat } from 'fs'; const { DateTime, FixedOffsetZone } = require('luxon'); @@ -21,7 +22,7 @@ const fetchConfig = async () => { return config.default; }; -export default function AvailabilityForm({ publisherId, existingItems, inline, onDone, date, datePicker = false }) { +export default function AvailabilityForm({ publisherId, existingItems, inline, onDone, date, cartEvent, datePicker = false }) { const router = useRouter(); const urls = { @@ -67,14 +68,13 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o }, []); - // Define the minimum and maximum times - const minTime = new Date(); - minTime.setHours(9, 0, 0, 0); // 8:00 AM - const maxTime = new Date(); - maxTime.setHours(19, 30, 0, 0); // 8:00 PM + // get cart event or set default time for Sofia timezone + const minTime = cartEvent?.startTime || DateTime.now().set({ hour: 8, minute: 0, second: 0, millisecond: 0, zone: 'Europe/Sofia' }).toJSDate(); + const maxTime = cartEvent?.endTime || DateTime.now().set({ hour: 20, minute: 0, second: 0, millisecond: 0, zone: 'Europe/Sofia' }).toJSDate(); useEffect(() => { - setTimeSlots(generateTimeSlots(minTime, maxTime, 90, availabilities)); + setTimeSlots(generateTimeSlots(new Date(minTime), new Date(maxTime), cartEvent.shiftDuration, availabilities)); + console.log("AvailabilityForm: minTime: " + common.getTimeFormatted(minTime) + ", maxTime: " + common.getTimeFormatted(maxTime), ", " + cartEvent.shiftDuration + " min. shifts"); }, []); @@ -189,34 +189,11 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o // Common function to set shared properties function setSharedAvailabilityProperties(availability, group, timeSlots) { - // Define a fixed offset for Sofia (+2 hours from UTC, ignoring DST) - const fixedZone = FixedOffsetZone.instance(120); // Offset in minutes - - // availability.startTime = group[0].startTime; - // availability.endTime = group[group.length - 1].endTime; - // Adjust start time - let startTime = DateTime.fromJSDate(group[0].startTime, { zone: 'utc' }) - .setZone(fixedZone, { keepLocalTime: true }); - startTime = startTime.set({ - hour: group[0].startTime.getUTCHours(), - minute: group[0].startTime.getUTCMinutes(), - second: group[0].startTime.getUTCSeconds() - }); - - // Adjust end time - let endTime = DateTime.fromJSDate(group[group.length - 1].endTime, { zone: 'utc' }) - .setZone(fixedZone, { keepLocalTime: true }); - endTime = endTime.set({ - hour: group[group.length - 1].endTime.getUTCHours(), - minute: group[group.length - 1].endTime.getUTCMinutes(), - second: group[group.length - 1].endTime.getUTCSeconds() - }); - - // Update the availability object with the new times - availability.startTime = startTime.toJSDate(); - availability.endTime = endTime.toJSDate(); - - availability.name = common.getTimeFomatted(group[0].startTime) + "-" + common.getTimeFomatted(group[group.length - 1].endTime); + let startTime = common.setTimeHHmm(new Date(availability.startTime || day), common.getTimeFormatted(group[0].startTime)); + let endTime = common.setTimeHHmm(new Date(availability.endTime || day), common.getTimeFormatted(group[group.length - 1].endTime)); + availability.startTime = startTime; + availability.endTime = endTime; + availability.name = common.getTimeFormatted(group[0].startTime) + "--" + common.getTimeFormatted(group[group.length - 1].endTime); availability.isWithTransportIn = group[0].isFirst && timeSlots[0].isWithTransport; availability.isWithTransportOut = group[group.length - 1].isLast && timeSlots[timeSlots.length - 1].isWithTransport; @@ -308,27 +285,19 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o let currentTime = start; //const baseDate = new Date(Date.UTC(2000, 0, 1, 0, 0, 0)); - const baseDate = new Date(start); + //const baseDate = new Date(start); while (isBefore(currentTime, end)) { - let slotStart = normalizeTime(currentTime, baseDate); - let slotEnd = normalizeTime(addMinutes(currentTime, increment), baseDate); + let slotStart = currentTime; + let slotEnd = addMinutes(currentTime, increment); const isChecked = items.some(item => { - let itemStart = item.startTime ? normalizeTime(new Date(item.startTime), baseDate) : null; - let itemEnd = item.endTime ? normalizeTime(new Date(item.endTime), baseDate) : null; - - return itemStart && itemEnd && - (slotStart.getTime() < itemEnd.getTime()) && - (slotEnd.getTime() > itemStart.getTime()); - }); - - slots.push({ - startTime: slotStart, - endTime: slotEnd, - isChecked: isChecked, + return item.startTime && item.endTime && + (slotStart.getTime() < item.endTime.getTime()) && + (slotEnd.getTime() > item.startTime.getTime()); }); + slots.push({ startTime: slotStart, endTime: slotEnd, isChecked: isChecked, }); currentTime = addMinutes(currentTime, increment); } @@ -342,16 +311,6 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o return slots; }; - // Normalize the time part of a date by using a base date - function normalizeTime(date, baseDate) { - return set(baseDate, { - hours: getHours(date), - minutes: getMinutes(date), - seconds: getSeconds(date), - milliseconds: 0 - }); - } - const TimeSlotCheckboxes = ({ slots, setSlots, items: [] }) => { const [allDay, setAllDay] = useState(slots.every(slot => slot.isChecked)); const handleAllDayChange = (e) => { diff --git a/components/calendar/avcalendar.tsx b/components/calendar/avcalendar.tsx index f0d47e0..662047f 100644 --- a/components/calendar/avcalendar.tsx +++ b/components/calendar/avcalendar.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, use } from 'react'; import { Calendar, momentLocalizer, dateFnsLocalizer } from 'react-big-calendar'; import 'react-big-calendar/lib/css/react-big-calendar.css'; import AvailabilityForm from '../availability/AvailabilityForm'; @@ -18,11 +18,13 @@ import { MdToday } from 'react-icons/md'; import { useSwipeable } from 'react-swipeable'; import axiosInstance from '../../src/axiosSecure'; +import { set } from 'date-fns'; +import { get } from 'http'; // import { set, format, addDays } from 'date-fns'; // import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns'; -import { filter } from 'jszip'; -import e from 'express'; +// import { filter } from 'jszip'; +// import e from 'express'; @@ -46,7 +48,7 @@ const messages = { // Any other labels you want to translate... }; -const AvCalendar = ({ publisherId, events, selectedDate }) => { +const AvCalendar = ({ publisherId, events, selectedDate, cartEvents }) => { const isAdmin = ProtectedRoute.IsInRole(UserRole.ADMIN); @@ -65,7 +67,15 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { return { start, end }; }); + const [cartEvent, setCartEvent] = useState(null); + function getCartEvent(date) { + const dayOfWeek = common.getDayOfWeekNameEnEnumForDate(date); + const ce = cartEvents?.find(e => e.dayofweek === dayOfWeek); + return ce; + } + useEffect(() => { setCartEvent(getCartEvent(date)); }, + [date, selectedEvents]); // Update internal state when `events` prop changes useEffect(() => { @@ -113,6 +123,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { //setDisplayedEvents(evts); }, [visibleRange, evts, currentView]); + // todo: review that const handlers = useSwipeable({ onSwipedLeft: () => navigate('NEXT'), onSwipedRight: () => navigate('PREV'), @@ -201,18 +212,13 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { return existingEvents; }; - // Define min and max times - const minHour = 8; // 8:00 AM - const maxHour = 20; // 8:00 PM - const minTime = new Date(); - minTime.setHours(minHour, 0, 0); - const maxTime = new Date(); - maxTime.setHours(maxHour, 0, 0); - const totalHours = maxHour - minHour; + + // const totalHours = maxHour - minHour; const handleSelect = ({ mode, start, end }) => { - const startdate = typeof start === 'string' ? new Date(start) : start; - const enddate = typeof end === 'string' ? new Date(end) : end; + //we set the time to proper timezone + const startdate = common.setTimezone(start); + const enddate = common.setTimezone(end); if (!start || !end) return; //readonly for past dates (ToDo: if not admin) @@ -224,27 +230,10 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { end = common.setTimeHHmm(startdate, "23:59"); } - const startMinutes = common.getTimeInMinutes(start); - const endMinutes = common.getTimeInMinutes(end); - - // Adjust start and end times to be within min and max hours - if (startMinutes < common.getTimeInMinutes(common.setTimeHHmm(start, minHour))) { - start = common.setTimeHHmm(start, minHour); - } - if (endMinutes > common.getTimeInMinutes(common.setTimeHHmm(end, maxHour))) { - end = common.setTimeHHmm(end, maxHour); - } - setDate(start); - - // get exising events for the selected date - //ToDo: properly fix this. filterEvents does not return the expcted results let existingEvents = filterEvents(evts, publisherId, startdate); - // if existingEvents is empty - create new with the selected range - // if (existingEvents.length === 0) { - // existingEvents = [{ startTime: start, endTime: end }]; - // } console.log("handleSelect: " + existingEvents); + setCartEvent(getCartEvent(date)); setSelectedEvents(existingEvents); setIsModalOpen(true); }; @@ -509,8 +498,8 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { onSelectSlot={handleSelect} onSelectEvent={handleEventClick} style={{ height: '100%', width: '100%' }} - min={minTime} // Set minimum time - max={maxTime} // Set maximum time + min={cartEvent?.startTime} // Set minimum time + max={cartEvent?.endTime} // Set maximum time messages={messages} view={currentView} views={['month', 'week', 'agenda']} @@ -541,6 +530,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => { date={date} onDone={handleDialogClose} inline={true} + cartEvent={cartEvent} // Pass other props as needed /> diff --git a/components/cartevent/CartEventForm.tsx b/components/cartevent/CartEventForm.tsx index d6d6fdd..b8defce 100644 --- a/components/cartevent/CartEventForm.tsx +++ b/components/cartevent/CartEventForm.tsx @@ -69,8 +69,8 @@ export default function CartEventForm(props: IProps) { try { console.log("fetching cart event from component " + router.query.id); const { data } = await axiosInstance.get(urls.apiUrl + id); - data.startTime = common.formatTimeHHmm(data.startTime) - data.endTime = common.formatTimeHHmm(data.endTime) + data.startTime = common.getTimeFormatted(data.startTime) + data.endTime = common.getTimeFormatted(data.endTime) setEvt(data); console.log("id:" + evt.id); diff --git a/components/publisher/PublisherSearchBox.js b/components/publisher/PublisherSearchBox.js index 19f06ac..fe21a12 100644 --- a/components/publisher/PublisherSearchBox.js +++ b/components/publisher/PublisherSearchBox.js @@ -11,9 +11,15 @@ function PublisherSearchBox({ id, selectedId, onChange, isFocused, filterDate, s const [searchResults, setSearchResults] = useState([]); const [selectedDate, setSelectedDate] = useState(filterDate); + + // useEffect(() => { + // fetchPublishers(); + // }, []); // Empty dependency array ensures this useEffect runs only once + + // Update publishers when filterDate or showList changes useEffect(() => { fetchPublishers(); - }, []); // Empty dependency array ensures this useEffect runs only once + }, [filterDate, showList]); const fetchPublishers = async () => { console.log("fetchPublishers called"); @@ -60,10 +66,7 @@ function PublisherSearchBox({ id, selectedId, onChange, isFocused, filterDate, s // console.log("filterDate changed = ", filterDate); // }, [filterDate]); - // Update publishers when filterDate or showList changes - useEffect(() => { - fetchPublishers(); - }, [filterDate, showList]); + // Update selectedItem when selectedId changes and also at the initial load useEffect(() => { diff --git a/pages/api/email.ts b/pages/api/email.ts index c9914d7..b8c2161 100644 --- a/pages/api/email.ts +++ b/pages/api/email.ts @@ -161,7 +161,7 @@ export default async function handler(req, res) { newPubs: newPubs, placeName: assignment.shift.cartEvent.location.name, dateStr: common.getDateFormated(assignment.shift.startTime), - time: common.formatTimeHHmm(assignment.shift.startTime), + time: common.getTimeFormatted(assignment.shift.startTime), sentDate: common.getDateFormated(new Date()) }; @@ -383,7 +383,7 @@ export default async function handler(req, res) { email: pubsToSend[i].email, placeName: assignment.shift.cartEvent.location.name, dateStr: common.getDateFormated(assignment.shift.startTime), - time: common.formatTimeHHmm(assignment.shift.startTime), + time: common.getTimeFormatted(assignment.shift.startTime), sentDate: common.getDateFormated(new Date()) }; let results = emailHelper.SendEmailHandlebars( diff --git a/pages/dash.tsx b/pages/dash.tsx index 568b1d5..fa8beb1 100644 --- a/pages/dash.tsx +++ b/pages/dash.tsx @@ -15,13 +15,14 @@ import { getServerSession } from "next-auth/next" import PublisherSearchBox from '../components/publisher/PublisherSearchBox'; import PublisherInlineForm from '../components/publisher/PublisherInlineForm'; +import CartEventForm from "components/cartevent/CartEventForm"; interface IProps { initialItems: Availability[]; initialUserId: string; } -export default function IndexPage({ initialItems, initialUserId }: IProps) { +export default function IndexPage({ initialItems, initialUserId, cartEvents }: IProps) { const { data: session } = useSession(); const [userName, setUserName] = useState(session?.user?.name); const [userId, setUserId] = useState(initialUserId); @@ -78,7 +79,7 @@ export default function IndexPage({ initialItems, initialUserId }: IProps) {