rewrite availability form

This commit is contained in:
Dobromir Popov
2024-03-03 02:52:41 +02:00
parent e52d962c49
commit 193dd91605
4 changed files with 229 additions and 314 deletions

View File

@ -1,70 +1,39 @@
import axiosInstance from '../../src/axiosSecure'; import axiosInstance from '../../src/axiosSecure';
import { useEffect, useState } from "react"; import { useEffect, useState, useCallback } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import DayOfWeek from "../DayOfWeek";
const common = require('src/helpers/common');
import { MobileTimePicker } from '@mui/x-date-pickers/MobileTimePicker';
import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
// import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import bg from 'date-fns/locale/bg';
import { bgBG } from '../x-date-pickers/locales/bgBG';
import TextField from '@mui/material/TextField';
import bg from 'date-fns/locale/bg'; // Bulgarian locale
import { bgBG } from '../x-date-pickers/locales/bgBG'; // Your custom translation file
import { ToastContainer } from 'react-toastify'; import { ToastContainer } from 'react-toastify';
import axios from 'axios'; const common = require('src/helpers/common');
const fetchConfig = async () => { const fetchConfig = async () => {
const config = await import('../../config.json'); const config = await import('../../config.json');
return config.default; return config.default;
}; };
/* export default function AvailabilityForm({ publisherId, existingItems, inline, onDone, date }) {
// ------------------ data model ------------------
model Availability {
id Int @id @default(autoincrement())
publisher Publisher @relation(fields: [publisherId], references: [id], onDelete: Cascade)
publisherId String
name String
dayofweek DayOfWeek
dayOfMonth Int?
weekOfMonth Int?
startTime DateTime
endTime DateTime
isactive Boolean @default(true)
type AvailabilityType @default(Weekly)
isWithTransport Boolean @default(false)
isFromPreviousAssignment Boolean @default(false)
isFromPreviousMonth Boolean @default(false)
repeatWeekly Boolean? // New field to indicate weekly repetition
repeatFrequency Int? // New field to indicate repetition frequency
endDate DateTime? // New field for the end date of repetition
@@map("Availability") 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 : [{
//enum for abailability type - day of week or day of month; and array of values publisherId: publisher.id,
const AvailabilityType = {
WeeklyRecurrance: 'WeeklyRecurrance',
ExactDate: 'ExactDate'
}
//const AvailabilityTypeValues = Object.values(AvailabilityType);
export default function AvailabilityForm({ publisherId, existingItem, inline, onDone, itemsForDay }) {
const [availability, setAvailability] = useState(existingItem || {
publisherId: publisherId || null,
name: "Нов", name: "Нов",
dayofweek: "Monday", dayofweek: "Monday",
dayOfMonth: null, dayOfMonth: null,
@ -74,47 +43,20 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
repeatWeekly: false, repeatWeekly: false,
endDate: null, endDate: null,
isFirst: false, isFirst: false,
}); isLast: false,
const [items, setItems] = useState(itemsForDay || []); // [existingItem, ...items] }]);
const [selectedType, setSelectedOption] = useState(AvailabilityType.WeeklyRecurrance);
const [isInline, setInline] = useState(inline || false); const [isInline, setInline] = useState(inline || false);
const [timeSlots, setTimeSlots] = useState([]);
const [isMobile, setIsMobile] = useState(false);
// Check screen width to determine if the device is mobile
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768); // 768px is a common breakpoint for mobile devices
};
// Call the function to setAvailability the initial state
handleResize();
// Add event listener
window.addEventListener('resize', handleResize);
// Cleanup
return () => window.removeEventListener('resize', handleResize);
}, []);
// Inside your component
const [config, setConfig] = useState(null); const [config, setConfig] = useState(null);
useEffect(() => { useEffect(() => {
fetchConfig().then(config => { fetchConfig().then(config => {
// Use config here to adjust form fields
console.log("UI config: ", config); console.log("UI config: ", config);
setConfig(config); setConfig(config);
}); });
}, []); }, []);
const [dataFetched, setDataFetched] = useState(false);
const router = useRouter();
const initialId = existingItem?.id || router.query.id;
const urls = {
apiUrl: "/api/data/availabilities/",
indexUrl: "/cart/availabilities"
};
// Define the minimum and maximum times // Define the minimum and maximum times
const minTime = new Date(); const minTime = new Date();
@ -123,66 +65,77 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
maxTime.setHours(20, 0, 0, 0); // 8:00 PM maxTime.setHours(20, 0, 0, 0); // 8:00 PM
//always setAvailability publisherId
useEffect(() => { useEffect(() => {
availability.publisherId = publisherId; const fetchItemFromDB = async () => {
console.log("availability.publisherId: ", availability.publisherId); const id = parseInt(router.query.id);
}, [availability]); if (existingItems.length == 0 && id) {
if (typeof window !== 'undefined') {
useEffect(() => {
// If component is not in inline mode and there's no existing availability, fetch the availability based on the query ID
// Fetch availability from DB only if it's not fetched yet, and there's no existing availability
if (!isInline && !existingItem && !dataFetched && router.query.id) {
fetchItemFromDB(parseInt(router.query.id.toString()));
setDataFetched(true); // Set data as fetched
}
}, [router.query.id, isInline, existingItem, dataFetched]);
}
// const [isEdit, setIsEdit] = useState(false);
const fetchItemFromDB = async (id) => {
try { try {
console.log("fetching availability " + id); const response = await axiosInstance.get(`/api/data/availabilities/${id}`);
const { data } = await axiosInstance.get(urls.apiUrl + id); setAvailabilities([response.data]);
data.startTime = formatTime(data.startTime); setEditMode(true);
data.endTime = formatTime(data.endTime);
setAvailability(data);
console.log(data);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
toast.error("Error fetching availability data.");
}
} }
}; };
const handleChange = ({ target }) => { fetchItemFromDB();
// const { name, value } = e.target; }, [router.query.id]);
// setItem((prev) => ({ ...prev, [name]: value }));
console.log("AvailabilityForm: handleChange: " + target.name + " = " + target.value);
setAvailability({ ...availability, [target.name]: target.value });
}
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
try { 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 (!availability.name) { if (shouldRecreate) {
// availability.name = "От календара"; // Delete existing availabilities if they have an ID
availability.name = common.getTimeFomatted(availability.startTime) + "-" + common.getTimeFomatted(availability.endTime); console.log("Recreating availabilities");
} await Promise.all(availabilities.filter(av => av.id).map(av => axiosInstance.delete(`${urls.apiUrl}${av.id}`)));
availability.dayofweek = common.getDayOfWeekNameEnEnum(availability.startTime); // Create new availabilities
if (availability.repeatWeekly) { const createdAvailabilities = await Promise.all(groupedTimeSlots.map(async group => {
availability.dayOfMonth = null; 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 { } else {
availability.endDate = null; // Update existing availabilities
availability.dayOfMonth = availability.startTime.getDate(); 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);
} }
delete availability.date; //remove date from availability as it is not part of the db model handleCompletion({ updated: true });
// ---------------------- CB UI -------------- } 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); const selectedSlots = timeSlots.filter(slot => slot.isChecked);
// Sort the selected intervals by start time // Sort the selected intervals by start time
const sortedSlots = [...selectedSlots].sort((a, b) => a.startTime - b.startTime); const sortedSlots = [...selectedSlots].sort((a, b) => a.startTime - b.startTime);
@ -209,83 +162,65 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
if (currentGroup.length > 0) { if (currentGroup.length > 0) {
groupedIntervals.push(currentGroup); groupedIntervals.push(currentGroup);
} }
return groupedIntervals;
// Create availability objects from grouped slots
const availabilities = groupedIntervals.map(group => {
const startTime = group[0].startTime;
const endTime = group[group.length - 1].endTime;
return {
publisherId: availability.publisherId,
startTime: startTime,
endTime: endTime,
isWithTransportIn: group[0].isFirst && timeSlots[0].isWithTransport,
isWithTransportOut: group[group.length - 1].isLast && timeSlots[timeSlots.length - 1].isWithTransport,
// Add other necessary fields, like isWithTransport if applicable
};
});
//if more than one interval, we delete and recreate the availability, as it is not possble to map them
if (availability.id && availabilities.length > 1) {
await axiosInstance.delete(urls.apiUrl + availability.id);
delete availability.id;
} }
// const firstSlotWithTransport = timeSlots[0].checked && timeSlots[0]?.isWithTransport; // const firstSlotWithTransport = timeSlots[0].checked && timeSlots[0]?.isWithTransport;
// const lastSlotWithTransport = timeSlots[timeSlots.length - 1].checked && timeSlots[timeSlots.length - 1]?.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);
availabilities.forEach(async av => { return {
// expand availability name: common.getTimeFomatted(startTime) + "-" + common.getTimeFomatted(endTime),
const avToStore = { publisherId: publisher.id,
...availability, startTime: startTime,
...av, endTime: endTime,
startTime: av.startTime, isWithTransportIn: group[0].isFirst && timeSlots[0].isWithTransport,
endTime: av.endTime, isWithTransportOut: group[group.length - 1].isLast && timeSlots[timeSlots.length - 1].isWithTransport,
name: "От календара", dayofweek: common.getDayOfWeekNameEnEnum(day.getDay()),
id: undefined, repeatWeekly: doRepeat,
dayOfMonth: doRepeat ? null : startTime.getDate(),
// isWithTransportIn: firstSlotWithTransport, endDate: doRepeat ? repeatUntil : null,
// isWithTransportOut: lastSlotWithTransport,
};
console.log("AvailabilityForm: handleSubmit: " + av);
if (availability.id) {
// UPDATE EXISTING ITEM
await axiosInstance.put(urls.apiUrl + availability.id, {
...avToStore,
});
} else {
// CREATE NEW ITEM
await axiosInstance.post(urls.apiUrl, avToStore);
}
handleCompletion(avToStore); // Assuming `handleCompletion` is defined to handle post-save logic
});
handleCompletion(availability);
} 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 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) => { const handleDelete = async (e) => {
e.preventDefault(); e.preventDefault();
try { try {
const deletePromises = availabilities.map(async (availability) => {
if (availability.id) { if (availability.id) {
// console.log("deleting publisher id = ", router.query.id, "; url=" + urls.apiUrl + router.query.id); // console.log("deleting publisher id = ", router.query.id, "; url=" + urls.apiUrl + router.query.id);
await axiosInstance.delete(urls.apiUrl + availability.id); await axiosInstance.delete(urls.apiUrl + availability.id);
}
});
await Promise.all(deletePromises);
toast.success("Записът изтрит", { toast.success("Записът изтрит", {
position: "bottom-center", position: "bottom-center",
}); });
handleCompletion({ deleted: true }); // Assuming handleCompletion is defined and properly handles post-deletion logic if (handleCompletion) {
handleCompletion({ deleted: true });
} }
} catch (error) { } catch (error) {
alert("Нещо се обърка при изтриването. Моля, опитайте отново или се свържете с нас"); alert("Нещо се обърка при изтриването. Моля, опитайте отново или се свържете с нас");
@ -305,48 +240,53 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
} }
} }
console.log("AvailabilityForm: publisherId: " + availability.publisherId + ", id: " + availability.id, ", inline: " + isInline); // console.log("AvailabilityForm: publisherId: " + publisher.id + ", id: " + availabilit .id, ", inline: " + isInline);
const generateTimeSlots = (start, end, increment, item) => { const generateTimeSlots = (start, end, increment, items) => {
const slots = []; const slots = [];
// Ensure we're working with the correct date base const baseDate = new Date(day || new Date());
const baseDate = new Date(item?.startTime || new Date()); baseDate.setHours(start, 0, 0, 0); // Initialize base date with start hour
baseDate.setHours(start, 0, 0, 0); // Set start time on the base date
let currentTime = baseDate.getTime(); let currentTime = baseDate.getTime();
const endDate = new Date(item?.startTime || new Date()); // Assuming end time is the same for all items, otherwise, this logic needs adjustment
endDate.setHours(end, 0, 0, 0); // Set end time on the same date const endDate = new Date(day || new Date());
endDate.setHours(end, 0, 0, 0);
const endTime = endDate.getTime(); const endTime = endDate.getTime();
// Parse availability's startTime and endTime into Date objects for comparison
const itemStartDate = new Date(item?.startTime);
const itemEndDate = new Date(item?.endTime);
while (currentTime < endTime) { while (currentTime < endTime) {
let slotStart = new Date(currentTime); let slotStart = new Date(currentTime);
let slotEnd = new Date(currentTime + increment * 60 * 60 * 1000); // Calculate slot end time let slotEnd = new Date(currentTime + increment * 60 * 60 * 1000);
// Check if the slot overlaps with the availability's time range // Determine if the slot is checked based on overlapping with any item time ranges
const isChecked = slotStart < itemEndDate && slotEnd > itemStartDate; const isChecked = items.some(item => {
const itemStartTime = new Date(item.startTime);
const itemEndTime = new Date(item.endTime);
return slotStart < itemEndTime && slotEnd > itemStartTime;
});
slots.push({ slots.push({
startTime: slotStart, startTime: slotStart,
endTime: slotEnd, endTime: slotEnd,
isChecked: isChecked, isChecked: isChecked,
}); });
currentTime += increment * 60 * 60 * 1000; // Move to the next slot
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[0].isFirst = true;
slots[slots.length - 1].isLast = true; slots[slots.length - 1].isLast = true;
slots[0].isWithTransport = item.isWithTransportIn; // Assuming isWithTransport flags are global settings, not unique per slot
slots[slots.length - 1].isWithTransport = item.isWithTransportOut; slots[0].isWithTransport = items[0]?.isWithTransportIn;
slots[slots.length - 1].isWithTransport = items[items.length - 1]?.isWithTransportOut;
}
return slots; return slots;
}; };
const TimeSlotCheckboxes = ({ slots, setSlots, item }) => { const TimeSlotCheckboxes = ({ slots, setSlots, items: [] }) => {
const [allDay, setAllDay] = useState(false); const [allDay, setAllDay] = useState(false);
const handleAllDayChange = (e) => { const handleAllDayChange = (e) => {
@ -378,6 +318,10 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
if ((changedSlot.isFirst || changedSlot.isLast) && !changedSlot.isChecked) { if ((changedSlot.isFirst || changedSlot.isLast) && !changedSlot.isChecked) {
changedSlot.isWithTransport = false; changedSlot.isWithTransport = false;
} }
//if no slots are checked, disable Update button
const anyChecked = updatedSlots.some(slot => slot.isChecked);
setCanUpdate(anyChecked);
setSlots(updatedSlots); setSlots(updatedSlots);
}; };
@ -434,35 +378,34 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
}; };
useEffect(() => { useEffect(() => {
setTimeSlots(generateTimeSlots(9, 18, 1.5, availability)); setTimeSlots(generateTimeSlots(9, 18, 1.5, availabilities));
}, []); }, []);
return ( return (
// <div style={{ width: isMobile ? '90%' : 'max-w-xs', margin: '0 auto' }} >
<div className="w-full"> <div className="w-full">
<ToastContainer></ToastContainer> <ToastContainer></ToastContainer>
<form id="formAv" className="form p-5 bg-white shadow-md rounded-lg" onSubmit={handleSubmit}> <form id="formAv" className="form p-5 bg-white shadow-md rounded-lg" onSubmit={handleSubmit}>
<h3 className="text-xl font-semibold mb-5 text-gray-800 border-b pb-2"> <h3 className="text-xl font-semibold mb-5 text-gray-800 border-b pb-2">
{availability.id ? "Редактирай" : "Нова"} възможност {editMode ? "Редактирай" : "Нова"} възможност
</h3> </h3>
<LocalizationProvider dateAdapter={AdapterDateFns} localeText={bgBG} adapterLocale={bg}> <LocalizationProvider dateAdapter={AdapterDateFns} localeText={bgBG} adapterLocale={bg}>
<div className="mb-2"> <div className="mb-2">
<DatePicker label="Изберете дата" value={availability.startTime} onChange={(value) => setAvailability({ ...availability, endTime: value })} /> <DatePicker label="Изберете дата" value={day} onChange={(value) => setDay({ value })} />
</div> </div>
<div> <div>
<div className="mb-1"> <div className="mb-1">
{/* Time slot checkboxes */} {/* Time slot checkboxes */}
<TimeSlotCheckboxes slots={timeSlots} setSlots={setTimeSlots} item={availability} /> <TimeSlotCheckboxes slots={timeSlots} setSlots={setTimeSlots} items={availabilities} />
</div> </div>
</div> </div>
<div className="mb-2"> <div className="mb-2">
<label className="checkbox-container"> <label className="checkbox-container">
<input type="checkbox" checked={availability.repeatWeekly} className="form-checkbox h-5 w-5 text-gray-600 mx-2" <input type="checkbox" checked={doRepeat} className="form-checkbox h-5 w-5 text-gray-600 mx-2"
onChange={() => setAvailability({ ...availability, repeatWeekly: !availability.repeatWeekly })} /> onChange={(e) => setDoRepeat(e.target.checked)} />
Повтаряй всяка {' '} Повтаряй всяка {' '}
{/* {availability.repeatWeekly && ( {/* {repeatWeekly && (
<select <select
style={{ style={{
appearance: 'none', appearance: 'none',
@ -480,8 +423,8 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
// className="appearance-none border border-black bg-transparent px-1 py-0 mx-0 mr-1 h-auto text-base text-center text-current align-middle cursor-pointer" // className="appearance-none border border-black bg-transparent px-1 py-0 mx-0 mr-1 h-auto text-base text-center text-current align-middle cursor-pointer"
//className="form-select mx-2 h-8 text-gray-600" //className="form-select mx-2 h-8 text-gray-600"
value={availability.repeatFrequency || 1} value={repeatFrequency || 1}
onChange={(e) => setAvailability({ ...availability, repeatFrequency: parseInt(e.target.value, 10) })} onChange={(e) => setRepeatFrequency(e.target.value)}
> >
<option value="1">1</option> <option value="1">1</option>
<option value="2">2</option> <option value="2">2</option>
@ -494,33 +437,26 @@ export default function AvailabilityForm({ publisherId, existingItem, inline, on
</label> </label>
</div> </div>
{false && availability.repeatWeekly && ( {false && repeatWeekly && (
<div className="mb-2"> <div className="mb-2">
<DatePicker label="До" value={availability.endDate} onChange={(value) => setAvailability({ ...availability, endDate: value })} /> <DatePicker label="До" value={repeatUntil} onChange={(value) => setRepeatUntil({ value })} />
</div> </div>
)} )}
</LocalizationProvider> </LocalizationProvider>
<div className="mb-2 hidden">
<div className="form-check">
<input className="checkbox form-input" type="checkbox" id="isactive" name="isactive" onChange={handleChange} checked={availability.isactive} autoComplete="off" />
<label className="label" htmlFor="isactive">активно</label>
</div>
</div>
{/* <input type="hidden" name="isactive" value={availability.isactive} /> */}
<div className="flex justify-between items-center flex-nowrap w-full p-1"> <div className="flex justify-between items-center flex-nowrap w-full p-1">
<button className="button border border-blue-500 text-blue-500 bg-transparent hover:text-white focus:outline-none focus:shadow-outline" onClick={() => handleCompletion()}> Отмени </button> <button className="button border border-blue-500 text-blue-500 bg-transparent hover:text-white focus:outline-none focus:shadow-outline" onClick={() => handleCompletion()}> Отмени </button>
{availability.id && ( {editMode && (
<><button className="button btn-outline bg-red-500 hover:bg-red-700 focus:outline-none focus:shadow-outline" type="button" onClick={handleDelete}> <><button className="button btn-outline bg-red-500 hover:bg-red-700 focus:outline-none focus:shadow-outline" type="button" onClick={handleDelete}>
Изтрий Изтрий
</button></> </button></>
)} )}
<button <button
className="button bg-blue-500 hover:bg-blue-700 focus:outline-none focus:shadow-outline" className={`button bg-blue-500 hover:bg-blue-700 focus:outline-none focus:shadow-outline ${!canUpdate ? 'opacity-50 cursor-not-allowed' : ''}`}
> {availability.id ? "Обнови" : "Запиши"} disabled={!canUpdate}
> {editMode ? "Обнови" : "Запиши"}
</button> </button>
</div> </div>
</form> </form>

View File

@ -95,27 +95,19 @@ export default function AvailabilityList({ publisher, showNew }) {
<AvailabilityForm <AvailabilityForm
publisherId={publisher.id} publisherId={publisher.id}
inline={true} inline={true}
existingItem={selectedItem} existingItems={selectedItem ? [selectedItem] : []}
date={selectedItem ? new Date(selectedItem.startTime) : new Date()}
onDone={(item) => { onDone={(item) => {
toggleAv(); toggleAv();
setSelectedItem(null); setSelectedItem(null);
if (!item) { //get the updated list of availabilities from the server
// remove selected item from state axiosInstance.get(common.getBaseUrl("/api/data/availabilities"))
const updatedItems = items.filter(i => i.id !== selectedItem.id); .then(({ data: items }) => {
setItems([...updatedItems]); setItems(items);
return; })
}; .catch(error => {
const itemIndex = items.findIndex(i => i.id === item.id); // assuming each item has a unique 'id' property console.error("Error getting availabilities:", error);
});
if (itemIndex !== -1) {
// Replace the existing item with the updated item
const updatedItems = [...items];
updatedItems[itemIndex] = item;
setItems(updatedItems);
} else {
// Append the new item to the end of the list
setItems([...items, item]);
}
}} }}
/> />
)} )}

View File

@ -16,6 +16,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 } from 'date-fns';
// Set moment to use the Bulgarian locale // Set moment to use the Bulgarian locale
moment.locale('bg'); moment.locale('bg');
@ -43,7 +44,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
const [evts, setEvents] = useState(events); // Existing events const [evts, setEvents] = useState(events); // Existing events
const [displayedEvents, setDisplayedEvents] = useState(evts); // Events to display in the calendar const [displayedEvents, setDisplayedEvents] = useState(evts); // Events to display in the calendar
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedEvent, setSelectedEvent] = useState(null); const [selectedEvents, setSelectedEvents] = useState([]);
const [visibleRange, setVisibleRange] = useState(() => { const [visibleRange, setVisibleRange] = useState(() => {
const start = new Date(); const start = new Date();
start.setDate(1); // Set to the first day of the current month start.setDate(1); // Set to the first day of the current month
@ -182,18 +183,21 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
// get exising events for the selected date // get exising events for the selected date
const existingEvents = evts?.filter(event => event.publisherId === publisherId && event.startTime === start.toDateString()); const existingEvents = evts?.filter(event => event.publisherId === publisherId && event.startTime === start.toDateString());
setSelectedEvent({ console.log("handleSelect: " + existingEvents);
date: start, setSelectedEvents(existingEvents);
startTime: start,
endTime: end,
dayOfMonth: start.getDate(),
isactive: true,
publisherId: publisherId,
// Add any other initial values needed
//set dayOfMonth to null, so that we repeat the availability every week
dayOfMonth: null,
}); // setSelectedEvent({
// date: start,
// startTime: start,
// endTime: end,
// dayOfMonth: start.getDate(),
// isactive: true,
// publisherId: publisherId,
// // Add any other initial values needed
// //set dayOfMonth to null, so that we repeat the availability every week
// dayOfMonth: null,
// });
setIsModalOpen(true); setIsModalOpen(true);
}; };
@ -214,7 +218,7 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
delete eventForEditing.type; delete eventForEditing.type;
delete eventForEditing.publisher delete eventForEditing.publisher
console.log("handleEventClick: " + eventForEditing); console.log("handleEventClick: " + eventForEditing);
setSelectedEvent(eventForEditing); setSelectedEvents([eventForEditing]);
setIsModalOpen(true); setIsModalOpen(true);
}; };
@ -223,22 +227,6 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
setIsModalOpen(false); setIsModalOpen(false);
if (dialogEvent === null || dialogEvent === undefined) { if (dialogEvent === null || dialogEvent === undefined) {
} else { } else {
// if (dialogEvent.deleted) {
// // Remove the old event from the calendar
// setEvents(currentEvents => currentEvents.filter(e => e.id !== selectedEvent.id));
// }
// else {
// // Update the event data
// dialogEvent.start = dialogEvent.startTime;
// dialogEvent.end = dialogEvent.endTime;
// // Update the events array by first removing the old event and then adding the updated one
// setEvents(currentEvents => {
// const filteredEvents = currentEvents?.filter(e => e.id !== selectedEvent.id) || [];
// return [...filteredEvents, dialogEvent];
// });
// }
//refresh the events from the server
let e = await axiosInstance.get(`/api/?action=getCalendarEvents&publisherId=${publisherId}`); let e = await axiosInstance.get(`/api/?action=getCalendarEvents&publisherId=${publisherId}`);
var newEvents = e.data; var newEvents = e.data;
// set start and end to Date objects for all events. Fix for the calendar component // set start and end to Date objects for all events. Fix for the calendar component
@ -468,7 +456,8 @@ const AvCalendar = ({ publisherId, events, selectedDate }) => {
<div className="modal-content"> <div className="modal-content">
<AvailabilityForm <AvailabilityForm
publisherId={publisherId} publisherId={publisherId}
existingItem={selectedEvent} existingItems={selectedEvents}
date={date}
onDone={handleDialogClose} onDone={handleDialogClose}
inline={true} inline={true}
// Pass other props as needed // Pass other props as needed

View File

@ -14,8 +14,6 @@ export default function NewPage(props: ICartEventPageProps) {
<Layout> <Layout>
<div className="h-5/6 grid place-items-center"> <div className="h-5/6 grid place-items-center">
<CartEventForm props={props} /> <CartEventForm props={props} />
{/*
<AvailabilityForm id={item.id} publisherId={item.publisherId} /> */}
</div> </div>
</Layout> </Layout>
); );