Newer
Older
// Availability editor using fullcalendar v5
// This code was initially based on the availability editor from pretalx (https://github.com/pretalx/pretalx)
// Copyright 2017-2019, Tobias Kunze
// Original Copyrights licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
// It was significantly changed to deal with the newer fullcalendar version, event specific timezones,
// to remove the dependency to moments timezone and improve the visualization of deletion
function createAvailabilityEditors(timezone, language, startDate, endDate, slotResolution='00:30:00') {
$("input.availabilities-editor-data").each(function () {
const eventColor = '#28B62C';
let data_field = $(this);
let editor = $('<div class="availabilities-editor">');
editor.attr("data-name", data_field.attr("name"));
data_field.after(editor);
data_field.hide();
// Add inputs to add slots without the need to click and drag
let manualSlotAdderSource = "<form id='formManualAdd'><table class='table table-responsive mb-0'><tr>" +
"<td style='vertical-align: middle;'><input type='datetime-local' id='inputStart' value='" + startDate + "' min='" + startDate + "' max='" + endDate + "'></td>" +
"<td style='vertical-align: middle;'><i class=\"fas fa-long-arrow-alt-right\"></i></td>" +
"<td style='vertical-align: middle;'><input type='datetime-local' id='inputEnd' value='" + endDate + "' min='" + startDate + "' max='" + endDate + "'></td>" +
"<td><button class='btn btn-primary' type='submit'><i class=\"fas fa-plus\"></i></button></td></tr></table></form>";
let manualSlotAdder = $(manualSlotAdderSource);
editor.after(manualSlotAdder);
$('#formManualAdd').submit(function(event) {
add($('#inputStart').val(), $('#inputEnd').val());
event.preventDefault();
});
let editable = !Boolean(data_field.attr("disabled"));
let data = JSON.parse(data_field.attr("value"));
let events = data.availabilities.map(function (e) {
start = moment(e.start);
end = moment(e.end);
allDay = start.format("HHmmss") === 0 && end.format("HHmmss") === 0;
return {
id: e.id,
start: start.format(),
end: end.format(),
allDay: allDay,
title: ""
};
});
let eventMarkedForDeletion = undefined;
let eventMarkedForDeletionEl = undefined;
let newEventsCounter = 0;
let plan = new FullCalendar.Calendar(editor[0], {
timeZone: timezone,
buttonIcons: {
prev: 'ignore fa-solid fa-angle-left',
next: 'ignore fa-solid fa-angle-right',
},
locale: language,
schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
editable: editable,
selectable: editable,
headerToolbar: false,
initialView: 'timeGridWholeEvent',
timeGridWholeEvent: {
type: 'timeGrid',
visibleRange: {
start: startDate,
end: endDate,
allDaySlot: true,
events: data.availabilities,
eventBackgroundColor: eventColor,
select: function (info) {
add(info.start, info.end);
},
eventClick: function (info) {
if (eventMarkedForDeletion !== undefined && (eventMarkedForDeletion.id === info.event.id)) {
info.event.remove();
eventMarkedForDeletion = undefined;
eventMarkedForDeletionEl = undefined;
save_events();
} else {
resetDeletionCandidate();
makeDeletionCandidate(info.el);
eventMarkedForDeletion = info.event;
eventMarkedForDeletionEl = info.el;
}
},
selectOverlap: false,
eventOverlap: false,
eventChange: save_events,
slotDuration: slotResolution,
});
plan.render();
function add(start, end) {
resetDeletionCandidate();
plan.addEvent({
title: "",
start: start,
end: end,
id: 'new' + newEventsCounter
})
newEventsCounter++;
save_events();
}
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
function makeDeletionCandidate(el) {
el.classList.add("deleteEvent");
$(el).find(".fc-event-title").html("<i class='fas fa-trash'></i> <i class='fas fa-question'></i>");
}
function resetDeletionCandidate() {
if (eventMarkedForDeletionEl !== undefined) {
eventMarkedForDeletionEl.classList.remove("deleteEvent");
$(eventMarkedForDeletionEl).find(".fc-event-title").html("");
}
eventMarkedForDeletionEl = undefined;
eventMarkedForDeletion = undefined;
}
function save_events() {
data = {
availabilities: plan.getEvents().map(function (e) {
let id = e.id;
if(e.id.startsWith("new"))
id = "";
return {
id: id,
// Make sure these timestamps are correctly interpreted as localized ones
// by removing the UTC-signaler ("Z" at the end)
// A bit dirty, but still more elegant than creating a timestamp with the
// required format manually
start: e.start.toISOString().replace("Z", ""),
end: e.end.toISOString().replace("Z", ""),
allDay: e.allDay,
}
}),
}
data_field.attr("value", JSON.stringify(data));
}
});
}