import axiosInstance from '../../src/axiosSecure'; import { useEffect, useState, useCallback } from "react"; import toast from "react-hot-toast"; import { useRouter } from "next/router"; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3'; import bg from 'date-fns/locale/bg'; import { bgBG } from '../x-date-pickers/locales/bgBG'; import { ToastContainer } from 'react-toastify'; const common = require('src/helpers/common'); const fetchConfig = async () => { const config = await import('../../config.json'); return config.default; }; export default function AvailabilityForm({ publisherId, existingItems, inline, onDone, date }) { const router = useRouter(); const urls = { apiUrl: "/api/data/availabilities/", indexUrl: "/cart/availabilities" }; const [editMode, setEditMode] = useState(existingItems.length > 0); const [publisher, setPublisher] = useState({ id: publisherId }); const [day, setDay] = useState(new Date(date || new Date())); const [doRepeat, setDoRepeat] = useState(false); const [repeatFrequency, setRepeatFrequency] = useState(1); const [repeatUntil, setRepeatUntil] = useState(null); const [canUpdate, setCanUpdate] = useState(true); const [timeSlots, setTimeSlots] = useState([]); const [availabilities, setAvailabilities] = useState(existingItems && existingItems.length > 0 ? existingItems : [{ publisherId: publisher.id, name: "Нов", dayofweek: "Monday", dayOfMonth: null, startTime: "08:00", endTime: "20:00", isactive: true, repeatWeekly: false, endDate: null, isFirst: false, isLast: false, }]); const [isInline, setInline] = useState(inline || false); const [config, setConfig] = useState(null); useEffect(() => { fetchConfig().then(config => { console.log("UI config: ", config); setConfig(config); }); }, []); // Define the minimum and maximum times const minTime = new Date(); minTime.setHours(8, 0, 0, 0); // 8:00 AM const maxTime = new Date(); maxTime.setHours(20, 0, 0, 0); // 8:00 PM useEffect(() => { const fetchItemFromDB = async () => { const id = parseInt(router.query.id); if (existingItems.length == 0 && id) { try { const response = await axiosInstance.get(`/api/data/availabilities/${id}`); setAvailabilities([response.data]); setEditMode(true); } catch (error) { console.error(error); toast.error("Error fetching availability data."); } } }; fetchItemFromDB(); }, [router.query.id]); const handleSubmit = async (e) => { e.preventDefault(); try { const groupedTimeSlots = mergeCheckedTimeSlots(timeSlots); // Determine if we need to delete and recreate, or just update const shouldRecreate = availabilities.length !== groupedTimeSlots.length || availabilities.some(av => !av.id); if (shouldRecreate) { // Delete existing availabilities if they have an ID console.log("Recreating availabilities"); await Promise.all(availabilities.filter(av => av.id).map(av => axiosInstance.delete(`${urls.apiUrl}${av.id}`))); // Create new availabilities const createdAvailabilities = await Promise.all(groupedTimeSlots.map(async group => { const newAvailability = createAvailabilityFromGroup(group, publisher.id); const response = await axiosInstance.post(urls.apiUrl, newAvailability); return response.data; // Assuming the new availability is returned })); setAvailabilities(createdAvailabilities); } else { // Update existing availabilities console.log("Updating existing availabilities"); const updatedAvailabilities = await Promise.all(availabilities.map(async (availability, index) => { const group = groupedTimeSlots[index]; const updatedAvailability = updateAvailabilityFromGroup(availability, group); await axiosInstance.put(`${urls.apiUrl}${availability.id}`, updatedAvailability); return updatedAvailability; })); setAvailabilities(updatedAvailabilities); } handleCompletion({ updated: true }); } catch (error) { alert("Нещо се обърка. Моля, опитайте отново по-късно."); // toast.error("Нещо се обърка. Моля, опитайте отново по-късно."); // console.error(error.message); // try { // const { data: session, status } = useSession(); // const userId = session.user.id; // axiosInstance.post('/log', { message: error.message, userId: userId }); // } // catch (err) { // console.error("Error logging error: ", err); // } } }; function mergeCheckedTimeSlots(timeSlots) { const selectedSlots = timeSlots.filter(slot => slot.isChecked); // Sort the selected intervals by start time const sortedSlots = [...selectedSlots].sort((a, b) => a.startTime - b.startTime); // Group continuous slots const groupedIntervals = []; let currentGroup = [sortedSlots[0]]; for (let i = 1; i < sortedSlots.length; i++) { const previousSlot = currentGroup[currentGroup.length - 1]; const currentSlot = sortedSlots[i]; // Calculate the difference in hours between slots const difference = (currentSlot.startTime - previousSlot.endTime) / (60 * 60 * 1000); // Assuming each slot represents an exact match to the increment (1.5 hours), we group them if (difference === 0) { currentGroup.push(currentSlot); } else { groupedIntervals.push(currentGroup); currentGroup = [currentSlot]; } } // Don't forget the last group if (currentGroup.length > 0) { groupedIntervals.push(currentGroup); } return groupedIntervals; } // const firstSlotWithTransport = timeSlots[0].checked && timeSlots[0]?.isWithTransport; // const lastSlotWithTransport = timeSlots[timeSlots.length - 1].checked && timeSlots[timeSlots.length - 1]?.isWithTransport; function createAvailabilityFromGroup(group) { const startTime = new Date(day); startTime.setTime(group[0].startTime) const endTime = new Date(day); endTime.setTime(group[group.length - 1].endTime); return { name: common.getTimeFomatted(startTime) + "-" + common.getTimeFomatted(endTime), publisherId: publisher.id, startTime: startTime, endTime: endTime, isWithTransportIn: group[0].isFirst && timeSlots[0].isWithTransport, isWithTransportOut: group[group.length - 1].isLast && timeSlots[timeSlots.length - 1].isWithTransport, dayofweek: common.getDayOfWeekNameEnEnum(day.getDay()), repeatWeekly: doRepeat, dayOfMonth: doRepeat ? null : startTime.getDate(), endDate: doRepeat ? repeatUntil : null, }; } function updateAvailabilityFromGroup(availability, group) { availability.name = common.getTimeFomatted(startTime) + "-" + common.getTimeFomatted(endTime); availability.startTime.setTime(group[0].startTime); availability.endTime.setTime(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; availability.repeatWeekly = doRepeat; availability.dayOfMonth = doRepeat ? null : group.startTime.getDate(); availability.endDate = doRepeat ? repeatUntil : null; return availability; } const handleDelete = async (e) => { e.preventDefault(); try { const deletePromises = availabilities.map(async (availability) => { if (availability.id) { // console.log("deleting publisher id = ", router.query.id, "; url=" + urls.apiUrl + router.query.id); await axiosInstance.delete(urls.apiUrl + availability.id); } }); await Promise.all(deletePromises); toast.success("Записът изтрит", { position: "bottom-center", }); if (handleCompletion) { handleCompletion({ deleted: true }); } } catch (error) { alert("Нещо се обърка при изтриването. Моля, опитайте отново или се свържете с нас"); console.log(JSON.stringify(error)); toast.error(error.response?.data?.message || "An error occurred"); } }; const handleCompletion = async (result) => { console.log("AvailabilityForm: handleCompletion"); if (isInline) { if (onDone) { onDone(result); } } else { router.push(urls.indexUrl); } } // console.log("AvailabilityForm: publisherId: " + publisher.id + ", id: " + availabilit .id, ", inline: " + isInline); const generateTimeSlots = (start, end, increment, items) => { const slots = []; const baseDate = new Date(day || new Date()); baseDate.setHours(start, 0, 0, 0); // Initialize base date with start hour let currentTime = baseDate.getTime(); // Assuming end time is the same for all items, otherwise, this logic needs adjustment const endDate = new Date(day || new Date()); endDate.setHours(end, 0, 0, 0); const endTime = endDate.getTime(); while (currentTime < endTime) { let slotStart = new Date(currentTime); let slotEnd = new Date(currentTime + increment * 60 * 60 * 1000); // Determine if the slot is checked based on overlapping with any item time ranges const isChecked = items.some(item => { const itemStartTime = new Date(item.startTime); const itemEndTime = new Date(item.endTime); return slotStart < itemEndTime && slotEnd > itemStartTime; }); slots.push({ startTime: slotStart, endTime: slotEnd, isChecked: isChecked, }); currentTime += increment * 60 * 60 * 1000; } // Assign 'isFirst' and 'isLast' based on the slot's position in the array if (slots.length > 0) { slots[0].isFirst = true; slots[slots.length - 1].isLast = true; // Assuming isWithTransport flags are global settings, not unique per slot slots[0].isWithTransport = items[0]?.isWithTransportIn; slots[slots.length - 1].isWithTransport = items[items.length - 1]?.isWithTransportOut; } return slots; }; const TimeSlotCheckboxes = ({ slots, setSlots, items: [] }) => { const [allDay, setAllDay] = useState(false); const handleAllDayChange = (e) => { const updatedSlots = slots.map(slot => ({ ...slot, isChecked: e.target.checked, })); setSlots(updatedSlots); setAllDay(e.target.checked) console.log("handleAllDayChange: allDay: " + allDay + ", updatedSlots: " + JSON.stringify(updatedSlots)); }; useEffect(() => { console.log("allDay updated to: ", allDay); const updatedSlots = slots.map(slot => ({ ...slot, isChecked: allDay })); //setSlots(updatedSlots); }, [allDay]); const handleSlotCheckedChange = (changedSlot) => { const updatedSlots = slots.map(slot => { if (slot.startTime === changedSlot.startTime && slot.endTime === changedSlot.endTime) { return { ...slot, isChecked: !slot.isChecked }; } return slot; }); // If slot is either first or last and it's being unchecked, also uncheck and disable transport if ((changedSlot.isFirst || changedSlot.isLast) && !changedSlot.isChecked) { changedSlot.isWithTransport = false; } //if no slots are checked, disable Update button const anyChecked = updatedSlots.some(slot => slot.isChecked); setCanUpdate(anyChecked); setSlots(updatedSlots); }; const handleTransportChange = (changedSlot) => { const updatedSlots = slots.map(slot => { if (slot.startTime === changedSlot.startTime && slot.endTime === changedSlot.endTime) { return { ...slot, isWithTransport: !slot.isWithTransport }; } return slot; }); setSlots(updatedSlots); }; return ( <> {slots.map((slot, index) => { const slotLabel = `${slot.startTime.getHours()}:${slot.startTime.getMinutes() === 0 ? '00' : slot.startTime.getMinutes()} до ${slot.endTime.getHours()}:${slot.endTime.getMinutes() === 0 ? '00' : slot.endTime.getMinutes()}`; slot.transportNeeded = slot.isFirst || slot.isLast; // Determine if the current slot is the first or the last return (
{/* Conditionally render transport checkbox based on slot being first or last */} {slot.transportNeeded && ( )}
); })} ); }; useEffect(() => { setTimeSlots(generateTimeSlots(9, 18, 1.5, availabilities)); }, []); return (

{editMode ? "Редактирай" : "Нова"} възможност

setDay({ value })} />
{/* Time slot checkboxes */}
{false && repeatWeekly && (
setRepeatUntil({ value })} />
)}
{editMode && ( <> )}
); }