recurring events
This commit is contained in:
BIN
app/.DS_Store
vendored
BIN
app/.DS_Store
vendored
Binary file not shown.
137
app/controllers/calendar-event-recur.controller.js
Normal file
137
app/controllers/calendar-event-recur.controller.js
Normal file
@@ -0,0 +1,137 @@
|
||||
const { splitSite } = require("../middlewares");
|
||||
const db = require("../models");
|
||||
const CalendarEventRecur = db.calendar_event_recur;
|
||||
|
||||
// Create a new Recurring Event Rule
|
||||
exports.create = (req, res) => {
|
||||
if (!req.body.rrule || !req.body.start_repeat_date) {
|
||||
res.status(400).send({ message: "rrule and start_repeat_date are required!" });
|
||||
return;
|
||||
}
|
||||
const site = splitSite.findSiteNumber(req);
|
||||
const recur = new CalendarEventRecur({
|
||||
title: req.body.title,
|
||||
type: req.body.type,
|
||||
description: req.body.description,
|
||||
color: req.body.color,
|
||||
status: req.body.status || 'active',
|
||||
target_type: req.body.target_type,
|
||||
target_uuid: req.body.target_uuid,
|
||||
target_name: req.body.target_name,
|
||||
event_location: req.body.event_location,
|
||||
event_reminder_type: req.body.event_reminder_type,
|
||||
meal_type: req.body.meal_type,
|
||||
activity_category: req.body.activity_category,
|
||||
ingredients: req.body.ingredients,
|
||||
rrule: req.body.rrule,
|
||||
start_repeat_date: req.body.start_repeat_date,
|
||||
end_repeat_date: req.body.end_repeat_date,
|
||||
indefinite_repeat: req.body.indefinite_repeat || false,
|
||||
create_by: req.body.create_by,
|
||||
create_date: req.body.create_date,
|
||||
edit_by: req.body.edit_by,
|
||||
edit_date: req.body.edit_date,
|
||||
edit_history: req.body.edit_history,
|
||||
site
|
||||
});
|
||||
recur
|
||||
.save(recur)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: err.message || "Some error occurred while creating the Recurring Event Rule."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Get all active recurring event rules
|
||||
exports.getAll = (req, res) => {
|
||||
var condition = { status: 'active' };
|
||||
condition = splitSite.splitSiteGet(req, condition);
|
||||
CalendarEventRecur.find(condition)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: err.message || "Some error occurred while retrieving Recurring Event Rules."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Get one by ID
|
||||
exports.getOne = (req, res) => {
|
||||
const id = req.params.id;
|
||||
CalendarEventRecur.findById(id)
|
||||
.then(data => {
|
||||
if (!data)
|
||||
res.status(404).send({ message: "Not found Recurring Event Rule with id " + id });
|
||||
else res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({ message: "Error retrieving Recurring Event Rule with id=" + id });
|
||||
});
|
||||
};
|
||||
|
||||
// Update by ID
|
||||
exports.update = (req, res) => {
|
||||
if (!req.body) {
|
||||
return res.status(400).send({ message: "Data to update can not be empty!" });
|
||||
}
|
||||
const id = req.params.id;
|
||||
CalendarEventRecur.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot update Recurring Event Rule with id=${id}. Maybe it was not found!`
|
||||
});
|
||||
} else res.send({ success: true, message: "Recurring Event Rule was updated successfully." });
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Error updating Recurring Event Rule with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Disable (set status to inactive)
|
||||
exports.disable = (req, res) => {
|
||||
const id = req.params.id;
|
||||
CalendarEventRecur.findByIdAndUpdate(id, { ...req.body, status: 'inactive' }, { useFindAndModify: false })
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot disable Recurring Event Rule with id=${id}. Maybe it was not found!`
|
||||
});
|
||||
} else res.send({ success: true, message: "Recurring Event Rule was disabled successfully." });
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Error disabling Recurring Event Rule with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Delete by ID
|
||||
exports.delete = (req, res) => {
|
||||
const id = req.params.id;
|
||||
CalendarEventRecur.findByIdAndRemove(id)
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot delete Recurring Event Rule with id=${id}. Maybe it was not found!`
|
||||
});
|
||||
} else {
|
||||
res.send({ message: "Recurring Event Rule was deleted successfully!" });
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Could not delete Recurring Event Rule with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
48
app/models/calendar-event-recur.model.js
Normal file
48
app/models/calendar-event-recur.model.js
Normal file
@@ -0,0 +1,48 @@
|
||||
module.exports = mongoose => {
|
||||
var editHistorySchema = mongoose.Schema({
|
||||
employee: String,
|
||||
date: Date
|
||||
});
|
||||
var schema = mongoose.Schema(
|
||||
{
|
||||
// Event template data
|
||||
title: String,
|
||||
type: String,
|
||||
description: String,
|
||||
color: String,
|
||||
status: String,
|
||||
// value could be ['active', 'inactive']
|
||||
target_type: String,
|
||||
target_uuid: String,
|
||||
target_name: String,
|
||||
event_location: String,
|
||||
event_reminder_type: String,
|
||||
meal_type: String,
|
||||
activity_category: String,
|
||||
ingredients: String,
|
||||
// Recurrence fields
|
||||
rrule: String,
|
||||
// rrule could be 'FREQ=DAILY', 'FREQ=WEEKLY', 'FREQ=MONTHLY', 'FREQ=YEARLY'
|
||||
start_repeat_date: Date,
|
||||
end_repeat_date: Date,
|
||||
indefinite_repeat: Boolean,
|
||||
// Metadata
|
||||
create_by: String,
|
||||
create_date: Date,
|
||||
edit_by: String,
|
||||
edit_date: Date,
|
||||
edit_history: [{
|
||||
type: editHistorySchema
|
||||
}],
|
||||
site: Number
|
||||
},
|
||||
{ collection: 'calendar_event_recur', timestamps: true }
|
||||
);
|
||||
schema.method("toJSON", function() {
|
||||
const { __v, _id, ...object } = this.toObject();
|
||||
object.id = _id;
|
||||
return object;
|
||||
});
|
||||
const CalendarEventRecur = mongoose.model("calendar_event_recur", schema);
|
||||
return CalendarEventRecur;
|
||||
};
|
||||
@@ -19,6 +19,7 @@ db.center_phone = require("./center-phone.model")(mongoose);
|
||||
db.message_token = require("./message-token.model")(mongoose);
|
||||
db.sent_message = require("./sent-message.model")(mongoose);
|
||||
db.calendar_event = require("./calendar-event.model")(mongoose);
|
||||
db.calendar_event_recur = require("./calendar-event-recur.model")(mongoose);
|
||||
db.doctemplate = require("./doctemplate.model")(mongoose);
|
||||
db.exceltemplate = require("./xlsxtemplate.model")(mongoose);
|
||||
db.timedata = require("./timedata.model")(mongoose);
|
||||
|
||||
19
app/routes/calendar-event-recur.route.js
Normal file
19
app/routes/calendar-event-recur.route.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const { authJwt } = require("../middlewares");
|
||||
module.exports = app => {
|
||||
const controller = require("../controllers/calendar-event-recur.controller.js");
|
||||
app.use((req, res, next) => {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
var router = require("express").Router();
|
||||
router.get("/", [authJwt.verifyToken], controller.getAll);
|
||||
router.post("/", [authJwt.verifyToken], controller.create);
|
||||
router.get("/:id", [authJwt.verifyToken], controller.getOne);
|
||||
router.put("/:id", [authJwt.verifyToken], controller.update);
|
||||
router.put("/:id/disable", [authJwt.verifyToken], controller.disable);
|
||||
router.delete("/:id", [authJwt.verifyToken], controller.delete);
|
||||
app.use('/api/event-recurrences', router);
|
||||
};
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.57cff37a.css",
|
||||
"main.js": "/static/js/main.e64707d9.js",
|
||||
"main.js": "/static/js/main.1a821513.js",
|
||||
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
|
||||
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
|
||||
"index.html": "/index.html",
|
||||
"main.57cff37a.css.map": "/static/css/main.57cff37a.css.map",
|
||||
"main.e64707d9.js.map": "/static/js/main.e64707d9.js.map",
|
||||
"main.1a821513.js.map": "/static/js/main.1a821513.js.map",
|
||||
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.57cff37a.css",
|
||||
"static/js/main.e64707d9.js"
|
||||
"static/js/main.1a821513.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.e64707d9.js"></script><link href="/static/css/main.57cff37a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.1a821513.js"></script><link href="/static/css/main.57cff37a.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
3
app/views/static/js/main.1a821513.js
Normal file
3
app/views/static/js/main.1a821513.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
client/.DS_Store
vendored
BIN
client/.DS_Store
vendored
Binary file not shown.
@@ -90,6 +90,14 @@ const EventsCalendar = () => {
|
||||
const [newReminderTitleCategory, setNewReminderTitleCategory] = useState('');
|
||||
const [newReminderAssociatedEntity, setNewReminderAssociatedEntity] = useState(null);
|
||||
|
||||
// Repeat date fields (new modal)
|
||||
const [newRepeatStartDate, setNewRepeatStartDate] = useState(null);
|
||||
const [newRepeatEndDate, setNewRepeatEndDate] = useState(null);
|
||||
const [newIndefiniteRepeat, setNewIndefiniteRepeat] = useState(false);
|
||||
|
||||
// Event recurrences data
|
||||
const [allEventRecurrences, setAllEventRecurrences] = useState([]);
|
||||
|
||||
// Edit modal state (for non-medical tabs)
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [editingEventId, setEditingEventId] = useState(null);
|
||||
@@ -108,6 +116,11 @@ const EventsCalendar = () => {
|
||||
// Important Dates edit fields
|
||||
const [editReminderTitleCategory, setEditReminderTitleCategory] = useState('');
|
||||
const [editReminderAssociatedEntity, setEditReminderAssociatedEntity] = useState(null);
|
||||
// Repeat date fields (edit modal)
|
||||
const [editRepeatStartDate, setEditRepeatStartDate] = useState(null);
|
||||
const [editRepeatEndDate, setEditRepeatEndDate] = useState(null);
|
||||
const [editIndefiniteRepeat, setEditIndefiniteRepeat] = useState(false);
|
||||
const [editingRecurId, setEditingRecurId] = useState(null); // tracks if editing a recurrence rule
|
||||
|
||||
// Helper function to format name from "lastname, firstname" to "firstname lastname"
|
||||
const formatFullName = (name) => {
|
||||
@@ -143,6 +156,56 @@ const EventsCalendar = () => {
|
||||
return doctorName ? `${fullName} - ${doctorName}` : fullName;
|
||||
};
|
||||
|
||||
// Helper: expand a recurring rule into individual event instances for a date range
|
||||
const expandRecurrence = (rule, rangeFrom, rangeTo) => {
|
||||
const instances = [];
|
||||
const startDate = new Date(rule.start_repeat_date);
|
||||
const endDate = new Date(rule.end_repeat_date);
|
||||
const from = new Date(rangeFrom);
|
||||
const to = new Date(rangeTo);
|
||||
|
||||
// Determine the effective range (overlap of rule range and visible range)
|
||||
const effectiveStart = from > startDate ? from : startDate;
|
||||
const effectiveEnd = to < endDate ? to : endDate;
|
||||
if (effectiveStart > effectiveEnd) return instances;
|
||||
|
||||
const freq = rule.rrule; // e.g. 'FREQ=DAILY', 'FREQ=WEEKLY', 'FREQ=MONTHLY', 'FREQ=YEARLY'
|
||||
let current = new Date(startDate);
|
||||
|
||||
// Limit to 1000 instances max to avoid infinite loops
|
||||
let count = 0;
|
||||
while (current <= effectiveEnd && count < 1000) {
|
||||
if (current >= effectiveStart) {
|
||||
const dateStr = moment(current).format('YYYY-MM-DD');
|
||||
instances.push({
|
||||
...rule,
|
||||
id: `recur-${rule.id}-${dateStr}`,
|
||||
_recur_id: rule.id,
|
||||
start_time: new Date(current),
|
||||
stop_time: new Date(current),
|
||||
});
|
||||
}
|
||||
// Advance to next occurrence
|
||||
if (freq === 'FREQ=DAILY') {
|
||||
current = new Date(current.getTime() + 24 * 60 * 60 * 1000);
|
||||
} else if (freq === 'FREQ=WEEKLY') {
|
||||
current = new Date(current.getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
} else if (freq === 'FREQ=MONTHLY') {
|
||||
const next = new Date(current);
|
||||
next.setMonth(next.getMonth() + 1);
|
||||
current = next;
|
||||
} else if (freq === 'FREQ=YEARLY') {
|
||||
const next = new Date(current);
|
||||
next.setFullYear(next.getFullYear() + 1);
|
||||
current = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return instances;
|
||||
};
|
||||
|
||||
const eventTypeMap = {
|
||||
medicalCalendar: 'medical',
|
||||
activitiesCalendar: 'activity',
|
||||
@@ -261,8 +324,21 @@ const EventsCalendar = () => {
|
||||
|
||||
useEffect(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => setAllEvents(data?.data));
|
||||
EventsService.getAllEventRecurrences().then(data => setAllEventRecurrences(data?.data || []));
|
||||
}, [fromDate, toDate]);
|
||||
|
||||
// Auto-fill repeat start date from event date when repeat option is selected (new modal)
|
||||
useEffect(() => {
|
||||
if (newEventRecurring && newEventStartDateTime) {
|
||||
setNewRepeatStartDate(new Date(newEventStartDateTime));
|
||||
}
|
||||
if (!newEventRecurring) {
|
||||
setNewRepeatStartDate(null);
|
||||
setNewRepeatEndDate(null);
|
||||
setNewIndefiniteRepeat(false);
|
||||
}
|
||||
}, [newEventRecurring, newEventStartDateTime]);
|
||||
|
||||
useEffect(() => {
|
||||
setNewEventType(eventTypeMap[currentTab]);
|
||||
if (currentTab === 'medicalCalendar') {
|
||||
@@ -313,9 +389,13 @@ const EventsCalendar = () => {
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
const originalEvents = [...allEvents];
|
||||
let filteredEvents = originalEvents?.filter(item => item.type === eventTypeMap[currentTab])?.map(item => {
|
||||
} else {
|
||||
// Expand recurring event rules into individual instances and merge with regular events
|
||||
const recurInstances = allEventRecurrences
|
||||
.filter(rule => rule.type === eventTypeMap[currentTab] && rule.status === 'active')
|
||||
.flatMap(rule => expandRecurrence(rule, fromDate, toDate));
|
||||
const originalEvents = [...allEvents, ...recurInstances];
|
||||
let filteredEvents = originalEvents?.filter(item => item.type === eventTypeMap[currentTab])?.map(item => {
|
||||
// For Important Dates, remap old blue/orange colors to new member_related/vehicle_maintenance
|
||||
let eventColor = item?.color;
|
||||
if (currentTab === 'reminderDatesCalendar') {
|
||||
@@ -369,7 +449,7 @@ const EventsCalendar = () => {
|
||||
|
||||
setEvents(filteredEvents);
|
||||
}
|
||||
}, [customers, resources, timeData, currentTab, allEvents, showDeletedItems, selectedColorFilters])
|
||||
}, [customers, resources, timeData, currentTab, allEvents, allEventRecurrences, showDeletedItems, selectedColorFilters])
|
||||
|
||||
|
||||
|
||||
@@ -429,6 +509,22 @@ const EventsCalendar = () => {
|
||||
}
|
||||
|
||||
const disableEvent = (id) => {
|
||||
// Handle recurring event instances
|
||||
const isRecurInstance = typeof id === 'string' && id.startsWith('recur-');
|
||||
if (isRecurInstance) {
|
||||
const currentEvent = events.find(item => item.id === id);
|
||||
const recurId = currentEvent?._recur_id || id.split('-')[1];
|
||||
EventsService.disableEventRecurrence(recurId, { status: 'inactive' }).then(() => {
|
||||
Promise.all([
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }),
|
||||
EventsService.getAllEventRecurrences()
|
||||
]).then(([eventsRes, recurRes]) => {
|
||||
setAllEvents(eventsRes.data);
|
||||
setAllEventRecurrences(recurRes.data || []);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
const currentEvent = events.find(item => item.id === id);
|
||||
EventsService.disableEvent(id, { status: 'inactive', edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
|
||||
edit_date: new Date(),
|
||||
@@ -479,9 +575,34 @@ const EventsCalendar = () => {
|
||||
|
||||
// Edit modal functions (for non-medical tabs)
|
||||
const openEditModal = (calendarEvent) => {
|
||||
const eventData = allEvents.find(e => e.id === calendarEvent.id) || calendarEvent;
|
||||
setEditingEventId(eventData.id);
|
||||
setEditEventStartDateTime(eventData.start_time ? new Date(eventData.start_time) : new Date());
|
||||
// Check if this is a recurring event instance (ID starts with 'recur-')
|
||||
const isRecurInstance = typeof calendarEvent.id === 'string' && calendarEvent.id.startsWith('recur-');
|
||||
let eventData;
|
||||
|
||||
if (isRecurInstance) {
|
||||
// Find the recurrence rule using the _recur_id stored on expanded instances
|
||||
const recurId = calendarEvent._recur_id || calendarEvent.id.split('-')[1];
|
||||
const recurRule = allEventRecurrences.find(r => r.id === recurId);
|
||||
if (recurRule) {
|
||||
eventData = { ...recurRule };
|
||||
setEditingRecurId(recurId);
|
||||
setEditingEventId(null);
|
||||
setEditRepeatStartDate(recurRule.start_repeat_date ? new Date(recurRule.start_repeat_date) : null);
|
||||
setEditRepeatEndDate(recurRule.end_repeat_date ? new Date(recurRule.end_repeat_date) : null);
|
||||
setEditIndefiniteRepeat(recurRule.indefinite_repeat || false);
|
||||
} else {
|
||||
return; // Can't find rule, bail
|
||||
}
|
||||
} else {
|
||||
eventData = allEvents.find(e => e.id === calendarEvent.id) || calendarEvent;
|
||||
setEditingEventId(eventData.id);
|
||||
setEditingRecurId(null);
|
||||
setEditRepeatStartDate(null);
|
||||
setEditRepeatEndDate(null);
|
||||
setEditIndefiniteRepeat(false);
|
||||
}
|
||||
|
||||
setEditEventStartDateTime(eventData.start_time ? new Date(eventData.start_time) : (eventData.start_repeat_date ? new Date(eventData.start_repeat_date) : new Date()));
|
||||
|
||||
if (currentTab === 'activitiesCalendar') {
|
||||
setEditEventTitle(eventData.title || '');
|
||||
@@ -491,6 +612,7 @@ const EventsCalendar = () => {
|
||||
} else if (currentTab === 'incidentsCalendar') {
|
||||
setEditAttendanceCustomer(eventData.target_uuid ? { value: eventData.target_uuid, label: eventData.target_name } : null);
|
||||
setEditAttendanceReason(eventData.description || '');
|
||||
setEditEventRecurring(eventData.rrule || '');
|
||||
} else if (currentTab === 'mealPlanCalendar') {
|
||||
setEditEventTitle(eventData.title || '');
|
||||
setEditMealType(eventData.meal_type || '');
|
||||
@@ -501,6 +623,13 @@ const EventsCalendar = () => {
|
||||
setEditReminderAssociatedEntity(eventData.target_uuid ? { value: eventData.target_uuid, label: eventData.target_name } : null);
|
||||
setEditEventRecurring(eventData.rrule || '');
|
||||
}
|
||||
|
||||
// If editing a regular event that has an rrule, populate repeat date fields
|
||||
if (!isRecurInstance && eventData.rrule) {
|
||||
setEditRepeatStartDate(eventData.start_time ? new Date(eventData.start_time) : null);
|
||||
setEditRepeatEndDate(null);
|
||||
setEditIndefiniteRepeat(false);
|
||||
}
|
||||
|
||||
setShowEditModal(true);
|
||||
};
|
||||
@@ -508,6 +637,7 @@ const EventsCalendar = () => {
|
||||
const closeEditModal = () => {
|
||||
setShowEditModal(false);
|
||||
setEditingEventId(null);
|
||||
setEditingRecurId(null);
|
||||
setEditEventTitle('');
|
||||
setEditEventStartDateTime(null);
|
||||
setEditEventLocation('');
|
||||
@@ -519,6 +649,9 @@ const EventsCalendar = () => {
|
||||
setEditMealIngredients('');
|
||||
setEditReminderTitleCategory('');
|
||||
setEditReminderAssociatedEntity(null);
|
||||
setEditRepeatStartDate(null);
|
||||
setEditRepeatEndDate(null);
|
||||
setEditIndefiniteRepeat(false);
|
||||
};
|
||||
|
||||
const handleEditSave = () => {
|
||||
@@ -538,7 +671,6 @@ const EventsCalendar = () => {
|
||||
activity_category: editActivityCategory,
|
||||
color: editActivityCategory,
|
||||
event_location: editEventLocation,
|
||||
rrule: editEventRecurring || undefined,
|
||||
};
|
||||
} else if (currentTab === 'incidentsCalendar') {
|
||||
data = {
|
||||
@@ -560,7 +692,6 @@ const EventsCalendar = () => {
|
||||
meal_type: editMealType,
|
||||
color: editMealType === 'breakfast' ? 'brown' : (editMealType === 'lunch' ? 'green' : 'red'),
|
||||
ingredients: editMealIngredients,
|
||||
rrule: editEventRecurring || undefined,
|
||||
};
|
||||
} else if (currentTab === 'reminderDatesCalendar') {
|
||||
const isMemberCategory = ['birthday', 'adcaps_completion', 'center_qualification_expiration'].includes(editReminderTitleCategory);
|
||||
@@ -573,17 +704,73 @@ const EventsCalendar = () => {
|
||||
target_type: isMemberCategory ? 'customer' : 'vehicle',
|
||||
target_uuid: editReminderAssociatedEntity?.value,
|
||||
target_name: editReminderAssociatedEntity?.label,
|
||||
rrule: editEventRecurring || undefined,
|
||||
color: isMemberCategory ? 'member_related' : 'vehicle_maintenance',
|
||||
};
|
||||
}
|
||||
|
||||
EventsService.updateEvent(editingEventId, data).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setAllEvents(data.data);
|
||||
const refreshAll = () => {
|
||||
Promise.all([
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }),
|
||||
EventsService.getAllEventRecurrences()
|
||||
]).then(([eventsRes, recurRes]) => {
|
||||
setAllEvents(eventsRes.data);
|
||||
setAllEventRecurrences(recurRes.data || []);
|
||||
closeEditModal();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// If editing a recurrence rule
|
||||
if (editingRecurId) {
|
||||
const recurData = {
|
||||
...data,
|
||||
rrule: editEventRecurring,
|
||||
start_repeat_date: editRepeatStartDate,
|
||||
end_repeat_date: editIndefiniteRepeat ? new Date('2099-12-31') : editRepeatEndDate,
|
||||
indefinite_repeat: editIndefiniteRepeat,
|
||||
};
|
||||
delete recurData.start_time;
|
||||
delete recurData.stop_time;
|
||||
|
||||
if (!editEventRecurring) {
|
||||
// Changed from recurring to one-time: delete recurrence rule, create regular event
|
||||
EventsService.deleteEventRecurrence(editingRecurId).then(() => {
|
||||
const eventData = { ...data };
|
||||
delete eventData.rrule;
|
||||
eventData.start_time = editEventStartDateTime;
|
||||
eventData.stop_time = editEventStartDateTime;
|
||||
eventData.type = eventTypeMap[currentTab];
|
||||
eventData.status = 'active';
|
||||
EventsService.createNewEvent(eventData).then(() => refreshAll());
|
||||
});
|
||||
} else {
|
||||
EventsService.updateEventRecurrence(editingRecurId, recurData).then(() => refreshAll());
|
||||
}
|
||||
} else if (editingEventId) {
|
||||
// Editing a regular event
|
||||
if (editEventRecurring) {
|
||||
// Changed from one-time to recurring: create recurrence rule, delete regular event
|
||||
const recurData = {
|
||||
...data,
|
||||
type: eventTypeMap[currentTab],
|
||||
status: 'active',
|
||||
rrule: editEventRecurring,
|
||||
start_repeat_date: editRepeatStartDate || editEventStartDateTime,
|
||||
end_repeat_date: editIndefiniteRepeat ? new Date('2099-12-31') : editRepeatEndDate,
|
||||
indefinite_repeat: editIndefiniteRepeat,
|
||||
create_by: data.edit_by,
|
||||
create_date: new Date(),
|
||||
};
|
||||
delete recurData.start_time;
|
||||
delete recurData.stop_time;
|
||||
EventsService.createEventRecurrence(recurData).then(() => {
|
||||
EventsService.disableEvent(editingEventId, {}).then(() => refreshAll());
|
||||
});
|
||||
} else {
|
||||
// Still one-time, just update normally
|
||||
delete data.rrule;
|
||||
EventsService.updateEvent(editingEventId, data).then(() => refreshAll());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleColorFilter = (colorValue) => {
|
||||
@@ -902,6 +1089,9 @@ const handleClose = () => {
|
||||
setNewEventEndDateTime(undefined);
|
||||
setNewEventColor('');
|
||||
setNewEventRecurring(undefined);
|
||||
setNewRepeatStartDate(null);
|
||||
setNewRepeatEndDate(null);
|
||||
setNewIndefiniteRepeat(false);
|
||||
setNewEventReminderType('');
|
||||
// Reset medical fields
|
||||
setNewEventCustomer('');
|
||||
@@ -975,7 +1165,6 @@ const handleSave = () => {
|
||||
activity_category: newActivityCategory,
|
||||
color: newActivityCategory, // category maps to color for display
|
||||
event_location: newEventLocation,
|
||||
rrule: newEventRecurring,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1004,7 +1193,6 @@ const handleSave = () => {
|
||||
meal_type: newMealType,
|
||||
color: newMealType === 'breakfast' ? 'brown' : (newMealType === 'lunch' ? 'green' : 'red'),
|
||||
ingredients: newMealIngredients,
|
||||
rrule: newEventRecurring,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1020,17 +1208,42 @@ const handleSave = () => {
|
||||
target_type: isMemberCategory ? 'customer' : 'vehicle',
|
||||
target_uuid: newReminderAssociatedEntity?.value,
|
||||
target_name: newReminderAssociatedEntity?.label,
|
||||
rrule: newEventRecurring,
|
||||
color: isMemberCategory ? 'member_related' : 'vehicle_maintenance',
|
||||
};
|
||||
}
|
||||
|
||||
EventsService.createNewEvent(data).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setAllEvents(data.data);
|
||||
handleClose();
|
||||
})
|
||||
});
|
||||
// If a repeat option is selected, save to calendar_event_recur collection
|
||||
if (newEventRecurring && currentTab !== 'medicalCalendar') {
|
||||
const recurData = {
|
||||
...data,
|
||||
rrule: newEventRecurring,
|
||||
start_repeat_date: newRepeatStartDate || newEventStartDateTime,
|
||||
end_repeat_date: newIndefiniteRepeat ? new Date('2099-12-31') : newRepeatEndDate,
|
||||
indefinite_repeat: newIndefiniteRepeat,
|
||||
};
|
||||
// Remove rrule from main event data — recurring logic goes in calendar_event_recur only
|
||||
delete recurData.start_time;
|
||||
delete recurData.stop_time;
|
||||
EventsService.createEventRecurrence(recurData).then(() => {
|
||||
Promise.all([
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }),
|
||||
EventsService.getAllEventRecurrences()
|
||||
]).then(([eventsRes, recurRes]) => {
|
||||
setAllEvents(eventsRes.data);
|
||||
setAllEventRecurrences(recurRes.data || []);
|
||||
handleClose();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// One-time event — save to calendar_event as before (strip rrule)
|
||||
delete data.rrule;
|
||||
EventsService.createNewEvent(data).then(() => {
|
||||
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then((data) => {
|
||||
setAllEvents(data.data);
|
||||
handleClose();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get reminder title label
|
||||
@@ -1219,6 +1432,39 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
{newEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatStartDate}
|
||||
onChange={setNewRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatEndDate}
|
||||
onChange={setNewRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={newIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={newIndefiniteRepeat} onChange={(e) => {
|
||||
setNewIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setNewRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1244,14 +1490,56 @@ const getReminderTitleLabel = (value) => {
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Reason</div>
|
||||
<input type="text" className="form-control" placeholder="Enter reason" value={newAttendanceReason || ''} onChange={e => setNewAttendanceReason(e.target.value)}/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Repeat</div>
|
||||
<select className="form-control" value={newEventRecurring} onChange={(e) => setNewEventRecurring(e.target.value)}>
|
||||
<option value="">No (One-time)</option>
|
||||
<option value="FREQ=DAILY">Daily</option>
|
||||
<option value="FREQ=WEEKLY">Weekly</option>
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
{newEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Reason</div>
|
||||
<input type="text" className="form-control" placeholder="Enter reason" value={newAttendanceReason || ''} onChange={e => setNewAttendanceReason(e.target.value)}/>
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatStartDate}
|
||||
onChange={setNewRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatEndDate}
|
||||
onChange={setNewRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={newIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={newIndefiniteRepeat} onChange={(e) => {
|
||||
setNewIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setNewRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Meal Item Fields */}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Meal Item Fields */}
|
||||
{currentTab === 'mealPlanCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
@@ -1285,6 +1573,39 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
{newEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatStartDate}
|
||||
onChange={setNewRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatEndDate}
|
||||
onChange={setNewRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={newIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={newIndefiniteRepeat} onChange={(e) => {
|
||||
setNewIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setNewRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Ingredients</div>
|
||||
<input type="text" className="form-control" placeholder="Enter ingredients" value={newMealIngredients || ''} onChange={e => setNewMealIngredients(e.target.value)}/>
|
||||
@@ -1362,6 +1683,39 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
{newEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatStartDate}
|
||||
onChange={setNewRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={newRepeatEndDate}
|
||||
onChange={setNewRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={newIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={newIndefiniteRepeat} onChange={(e) => {
|
||||
setNewIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setNewRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
@@ -1437,6 +1791,38 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
{editEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatStartDate}
|
||||
onChange={setEditRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatEndDate}
|
||||
onChange={setEditRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={editIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={editIndefiniteRepeat} onChange={(e) => {
|
||||
setEditIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setEditRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1462,14 +1848,55 @@ const getReminderTitleLabel = (value) => {
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Reason</div>
|
||||
<input type="text" className="form-control" placeholder="Enter reason" value={editAttendanceReason || ''} onChange={e => setEditAttendanceReason(e.target.value)}/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Repeat</div>
|
||||
<select className="form-control" value={editEventRecurring} onChange={(e) => setEditEventRecurring(e.target.value)}>
|
||||
<option value="">No (One-time)</option>
|
||||
<option value="FREQ=DAILY">Daily</option>
|
||||
<option value="FREQ=WEEKLY">Weekly</option>
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
{editEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Reason</div>
|
||||
<input type="text" className="form-control" placeholder="Enter reason" value={editAttendanceReason || ''} onChange={e => setEditAttendanceReason(e.target.value)}/>
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatStartDate}
|
||||
onChange={setEditRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatEndDate}
|
||||
onChange={setEditRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={editIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={editIndefiniteRepeat} onChange={(e) => {
|
||||
setEditIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setEditRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Meal Item Edit Fields */}
|
||||
{/* Meal Item Edit Fields */}
|
||||
{currentTab === 'mealPlanCalendar' && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
@@ -1503,6 +1930,38 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=MONTHLY">Monthly</option>
|
||||
</select>
|
||||
</div>
|
||||
{editEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatStartDate}
|
||||
onChange={setEditRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatEndDate}
|
||||
onChange={setEditRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={editIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={editIndefiniteRepeat} onChange={(e) => {
|
||||
setEditIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setEditRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Ingredients</div>
|
||||
<input type="text" className="form-control" placeholder="Enter ingredients" value={editMealIngredients || ''} onChange={e => setEditMealIngredients(e.target.value)}/>
|
||||
@@ -1580,6 +2039,38 @@ const getReminderTitleLabel = (value) => {
|
||||
<option value="FREQ=YEARLY">Yearly</option>
|
||||
</select>
|
||||
</div>
|
||||
{editEventRecurring && (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">Start Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatStartDate}
|
||||
onChange={setEditRepeatStartDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<div className="field-label">End Repeat Date</div>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
selected={editRepeatEndDate}
|
||||
onChange={setEditRepeatEndDate}
|
||||
dateFormat="MM/dd/yyyy"
|
||||
disabled={editIndefiniteRepeat}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={editIndefiniteRepeat} onChange={(e) => {
|
||||
setEditIndefiniteRepeat(e.target.checked);
|
||||
if (e.target.checked) setEditRepeatEndDate(new Date('2099-12-31'));
|
||||
}} />
|
||||
Indefinite Repeat
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
|
||||
@@ -36,6 +36,27 @@ const assignTransportationToEvents = (data) => {
|
||||
return http.post(`/events/assign`, data);
|
||||
}
|
||||
|
||||
// Event Recurrence API
|
||||
const getAllEventRecurrences = () => {
|
||||
return http.get('/event-recurrences');
|
||||
};
|
||||
|
||||
const createEventRecurrence = (data) => {
|
||||
return http.post('/event-recurrences', data);
|
||||
};
|
||||
|
||||
const updateEventRecurrence = (id, data) => {
|
||||
return http.put(`/event-recurrences/${id}`, data);
|
||||
};
|
||||
|
||||
const disableEventRecurrence = (id, data) => {
|
||||
return http.put(`/event-recurrences/${id}/disable`, data);
|
||||
};
|
||||
|
||||
const deleteEventRecurrence = (id) => {
|
||||
return http.delete(`/event-recurrences/${id}`);
|
||||
};
|
||||
|
||||
const formatDate = (date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
@@ -333,6 +354,12 @@ export const EventsService = {
|
||||
getTimeData,
|
||||
getByCustomer,
|
||||
site,
|
||||
// Event Recurrence
|
||||
getAllEventRecurrences,
|
||||
createEventRecurrence,
|
||||
updateEventRecurrence,
|
||||
disableEventRecurrence,
|
||||
deleteEventRecurrence,
|
||||
// New option names
|
||||
languageSupportOptions,
|
||||
labelOptions,
|
||||
|
||||
@@ -235,6 +235,7 @@ require("./app/routes/resource.routes")(app);
|
||||
require("./app/routes/center-phone.routes")(app);
|
||||
require("./app/routes/message-token.routes")(app);
|
||||
require("./app/routes/calendar-event.route")(app);
|
||||
require("./app/routes/calendar-event-recur.route")(app);
|
||||
require("./app/routes/doctemplate.route")(app);
|
||||
require("./app/routes/xlsxtemplate.route")(app);
|
||||
require("./app/routes/timedata.routes")(app);
|
||||
|
||||
Reference in New Issue
Block a user