const TIME_FORMAT = 'HH:mm'; const FLYING_TEAM_LABEL = 'Flying Team'; const FLYING_TEAM_BORDER_COLOR = "orange"; var overlaps = (function () { function getPositions(elem) { var pos, width, height; pos = $(elem).position(); width = $(elem).width(); height = $(elem).height(); return [[pos.left, pos.left + width], [pos.top, pos.top + height]]; } function comparePositions(p1, p2) { var r1, r2; r1 = p1[0] < p2[0] ? p1 : p2; r2 = p1[0] < p2[0] ? p2 : p1; return r1[1] > r2[0] || r1[0] === r2[0]; } return function (x, y, element) { var pos1 = [[x, x + 100], [y, y + 20]], pos2 = getPositions(element); console.log(x, y, element); return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]); }; })(); var eventsCleared = []; var appointmentsCleared = []; function add_event(event, color, subjectId, locationId, boxBody) { var eventElement = $('<div class="fc-event ui-draggable ui-draggable-handle hidden-print">' + event.title + '<span style="float:right;padding-right:5px;">' + event.duration + '</span></div>' ) ; eventElement.removeData(); var constraintStart = $.fullCalendar.moment(event.appointment_start); var borderColor; var location = event.location; if (location === FLYING_TEAM_LABEL) { eventElement.css('border', '2px solid ' + FLYING_TEAM_BORDER_COLOR); location = event.flying_team_location + ' (FT)'; borderColor = FLYING_TEAM_BORDER_COLOR; } var event_title = event.short_title; if (event_title === undefined || event_title === "") { event_title = event.title; } event_data = { appointment_start: event.appointment_start, appointment_end: event.appointment_end, title: event_title, stick: true, duration: event.duration, original_duration: event.duration, subject: event.subject, subject_id: subjectId, location_id: locationId, id: event.id, appointment_id: event.appointment_id, link_id: event.link_id, link_who: event.link_who, link_when: event.link_when, location: event.location, flying_team_location: event.flying_team_location, constraint: { start: constraintStart, end: $.fullCalendar.moment(event.appointment_end) }, borderColor: borderColor } if(color != undefined){ event_data['color'] = color + " !important"; } eventElement.data('event', event_data); eventElement.draggable({ zIndex: 999, revert: true, revertDuration: 0, start: function (event, ui) { $(this).popover('disable'); } }); eventElement.css('background-color', color); eventElement.css('width', event.width); boxBody.append(eventElement); eventElement.popover({ container: 'body', trigger: 'click', content: location + ', appointment starts at ' + constraintStart.format(TIME_FORMAT) }); } function get_subjects_events(day) { $.get('/api/events/' + day, function (data) { $("#subjects").empty(); var availabilities = data.availabilities; $.each(availabilities, function (index, event) { event.backgroundColor = '#AAFFAA !important'; event.start = $.fullCalendar.moment(event.link_when); event.end = $.fullCalendar.moment(event.link_end); event.rendering = 'background'; event.resourceId = event.link_who.toString(); event.className = 'background-event'; $('#calendar').fullCalendar('renderEvent', event, true); }); var holidays = data.holidays; $.each(holidays, function (index, event) { if(event.kind == 'H'){ event.backgroundColor = '#FFAAAA !important'; }else{ event.backgroundColor = '#AAFFAA !important'; } event.start = $.fullCalendar.moment(event.link_when); event.end = $.fullCalendar.moment(event.link_end); event.rendering = 'background'; event.resourceId = event.link_who.toString(); event.className = 'background-event'; $('#calendar').fullCalendar('renderEvent', event, true); }); var subjects = data.data; $.each(subjects, function (index, subject) { var divSubject = $("<div class='col-md-4 col-lg-3 col-sm-12 subjects-events'/>"); var boxSubject = $("<div class='box box-primary'/>").css('border-top-color', subject.color); var boxBody = $("<div class='box-body' id='subject_" + subject.id + "'>"); var boxHeader = $("<div class='box-header with-border'/>"); //flags var flagTooltip = ''; if('flags' in subject){ for(i=0; i<subject.flags.length; i++){ var url = subject.flags[i]; flagTooltip+= `<img class='tooltip_image' src='${url}'/>`; } var title_subject = `<h4 class="subject_title" data-toggle="tooltip" data-html="true" title="${flagTooltip}">${subject.name} (${subject.start})</h4>`; }else{ var title_subject = `<h4 class="subject_title">${subject.name} (${subject.start})</h4>`; //wrap content } title_subject = $(`${title_subject} <span style='padding-right:5px;'>${subject.location}</span> <span style='padding-right:5px;'> <a title="Edit appointment" target="_blank" href="/appointments/edit/${subject.appointment_id}"> <i class="fa fa-pencil-square"></i> </a> </span>`); boxHeader.append(title_subject); title_subject.find('a[title]').tooltip(); $.each(subject.events, function (index_event, event) { if (event.link_when) { event.title = event.short_title; event.color = subject.color + ' !important'; event.start = $.fullCalendar.moment(event.link_when); event.end = $.fullCalendar.moment(event.link_end); event.resourceId = event.link_who.toString(); event.subject_id = subject.id; event.original_duration = event.duration; event.constraint = { start: $.fullCalendar.moment(event.appointment_start), end: $.fullCalendar.moment(event.appointment_end) }; if (event.start.valueOf() === event.end.valueOf()) { event.end = $.fullCalendar.moment(event.start); event.end.add(1, 'hour'); } $('#calendar').fullCalendar('renderEvent', event, true); } else { add_event(event, subject.color, subject.id, undefined, boxBody); } }); boxSubject.append(boxHeader); boxSubject.append(boxBody); divSubject.append(boxSubject); $("#subjects").append(divSubject); divSubject.droppable({ drop: function (event, ui) { } }); }); var location_based_general_appointments = data.generic; $.each(location_based_general_appointments, function (index, location) { //Subject Appointment List var divSubject = $("<div class='col-md-4 col-lg-3 col-sm-12 subjects-events'/>"); var boxSubject = $("<div class='box box-primary'/>").css('border-top-color', location.color); var boxBody = $("<div class='box-body' id='location_" + location.id + "'>"); var boxHeader = $("<div class='box-header with-border'/>"); var title_location = $(`<h4>${location.name}<span style='padding-right:5px;'>${location.location}</span></h4> <span style='padding-right:5px;'> <a title="Edit Appointment" target="_blank" href="/appointments/edit/${location.id}"> <i class="fa fa-pencil-square"></i> </a> </span>`); boxHeader.append(title_location); title_location.find('a[title]').tooltip(); $.each(location.events, function (index_event, event) { if (event.link_who) { event.title = event.short_title; event.color = location.color + ' !important'; event.start = $.fullCalendar.moment(event.link_when); event.end = $.fullCalendar.moment(event.link_end); event.resourceId = event.link_who.toString(); event.location_id = location.id; event.original_duration = event.duration; event.constraint = { start: $.fullCalendar.moment(event.appointment_start), end: $.fullCalendar.moment(event.appointment_end) }; if (event.start.valueOf() === event.end.valueOf()) { event.end = $.fullCalendar.moment(event.start); event.end.add(1, 'hour'); } $('#calendar').fullCalendar('renderEvent', event, true); } else { add_event(event, location.color, undefined, location.id, boxBody); } }); boxSubject.append(boxHeader); boxSubject.append(boxBody); divSubject.append(boxSubject); $("#subjects").append(divSubject); divSubject.droppable({ drop: function (event, ui) { } }); }); }); } function remove_event(event) { if(event.className.includes("background-event")){ //avoid removing availabilities return; } $('#calendar').fullCalendar('removeEvents', event.id); var selector; if (event.subject_id !== undefined) { selector = '#subject_' + event.subject_id; } else { selector = '#location_' + event.location_id; } var boxBody = $(selector); event.duration = event.original_duration; event.removed = true; //remove !important if(event.color != undefined){ event.color = event.color.substring(0, 7); } if (event.link_id !== undefined) { eventsCleared.push(event.link_id); } else { appointmentsCleared.push(event.appointment_id); } add_event(event, event.color, event.subject_id, event.location_id, boxBody); } $(document).ready(function () { $('#calendar').fullCalendar({ schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives', defaultView: 'agendaDay', eventDurationEditable: false, eventStartEditable: true, editable: true, selectable: true, eventOverlap: function (stillEvent, movingEvent) { if (stillEvent.rendering === "background") { return true; } return false; }, weekends: false, scrollTime: '08:00', slotDuration: '00:30', snapDuration: '00:05', displayEventTime: true, resourceOrder: 'role', resourceGroupField: 'role', dragRevertDuration: 0, minTime: "08:00:00", maxTime: "19:00:00", groupByResource: true, height: "auto", customButtons: { datePickerButton: { text: 'select', click: function () { var $btnCustom = $('.fc-datePickerButton-button'); if ($(".calendar-datepicker").length > 0) { $(".calendar-datepicker").remove(); } else { $btnCustom.after('<div class="calendar-datepicker"/>'); $(".calendar-datepicker").datepicker().on('changeDate', function (ev) { $('#calendar').fullCalendar('gotoDate', ev.date); $(".calendar-datepicker").remove(); }); } } }, save: { text: 'Save', click: function () { calendarEvents = $('#calendar').fullCalendar('clientEvents'); eventsToPersist = []; var saveButton = $(".fc-save-button"); var currentBorder = saveButton.css('border-color'); $.each(calendarEvents, function (i, calendar_event) { if (calendar_event.rendering !== "background") { eventsToPersist.push({ 'link_id': calendar_event.link_id, 'appointment_id': calendar_event.appointment_id, 'link_who': parseInt(calendar_event.resourceId), 'start': calendar_event.start.format() }); if (calendar_event.link_id !== undefined) { var index = eventsCleared.indexOf(calendar_event.link_id); if (index > -1) { eventsCleared.splice(index, 1); } } else { var index = appointmentsCleared.indexOf(calendar_event.appointment_id); if (index > -1) { appointmentsCleared.splice(index, 1); } } } }); $.post({ url: events_url, data: { events_to_persist: JSON.stringify(eventsToPersist), events_to_clear: JSON.stringify(eventsCleared), appointments_to_clear: JSON.stringify(appointmentsCleared) }, dataType: "json" }).done(function (data) { saveButton.css('border-color', 'green'); setTimeout(function () { saveButton.css('border-color', currentBorder); }, 200); }).error(function (data) { console.log(data); saveButton.css('border-color', 'red'); showErrorInfo("There was an unexpected problem with saving data. " + "Please contact administrators."); setTimeout(function () { saveButton.delay(200).css('border-color', currentBorder); }, 200); }); } }, clear: { text: 'Clear', click: function () { calendarEvents = $('#calendar').fullCalendar('clientEvents'); $.each(calendarEvents, function (i, calendar_event) { remove_event(calendar_event); }); } }, toPdf: { text: 'PDF', click: function () { var srcEl = document.getElementById("calendar"); var parent = srcEl.parentNode; var container = document.createElement("div"); container.style.backgroundColor = "#FFFFFF"; container.style.width = "3840px"; document.body.appendChild(container); container.appendChild(srcEl); var pdf = new jsPDF('l', 'mm', [1485, 270]); pdf.addHTML(container, function () { pdf.save('daily-planning.pdf'); parent.appendChild(srcEl); document.body.removeChild(container); }); } } }, viewRender: function (view, element) { var date = view.start.format('YYYY-MM-DD'); $('#calendar').fullCalendar('removeEvents'); get_subjects_events(date); }, businessHours: { start: '08:00', end: '19:00' }, header: { left: 'prev,next today', center: 'title, datePickerButton', right: 'save, clear, toPdf' }, droppable: true, resourceAreaWidth: '15%', resourceLabelText: 'Workers', refetchResourcesOnNavigate: true, resourceOrder: '-availability', resourceRender: function(resourceObj, labelTds, bodyTds) { //Calendar Columns var title = $(labelTds).text() //get content $(labelTds).text('') //empty content var flagTooltip = ''; if('flags' in resourceObj){ for(i=0; i<resourceObj.flags.length; i++){ var url = resourceObj.flags[i]; flagTooltip+= `<img class='tooltip_image' src='${url}'/>`; } $(labelTds).prepend(`<span class="column_title" data-toggle="tooltip" data-html="true" title="${flagTooltip}">${title}</span>`) //wrap content }else{ $(labelTds).prepend(`<span class="column_title">${title}</span>`) //wrap content } //buttons var worker_id = resourceObj.id; var span = $(`<div style='display:block;'></div>`); var add_extra_availability_link = `<a style="padding-right:5px;" title="Add Extra Availability or Holiday" target="_blank" href="/doctors/${worker_id}/holiday/add"><i class="fa fa-plus-square-o" aria-hidden="true"></i></a>`; var edit_worker_link = `<a style="padding-right:5px;" title="Edit Worker" target="_blank" href="/doctors/edit/${worker_id}"><i class="fa fa-pencil-square" aria-hidden="true"></i></a>`; $(span).append($(add_extra_availability_link)); $(span).append($(edit_worker_link)); $(labelTds).append(span); $(labelTds).find('a[title]').tooltip(); }, resources: function(callback){ setTimeout(function(){ var view = $('#calendar').fullCalendar('getView'); $.ajax({ url: resources_url, type: 'GET', cache: false, data: { start_date: view.start.format('YYYY-MM-DD'), } }).then(function(resources){ //Filter out roles var checked_roles = $('.role_list_item > input:checked').map( (i,e) => e.value).toArray(); resources = resources.filter(resource => checked_roles.includes(resource.role)); callback(resources) }); }, 0); }, events: [], eventRender: function (event, element) { if (event.rendering !== 'background') { var content = element.popover({ title: event.short_title, container: 'body', placement: 'bottom', trigger: 'click', content: '<ul>' + '<li>' + (event.subject !== undefined ? event.subject : 'NO SUBJECT') + '</li>' + '<li>' + event.start.format(TIME_FORMAT) + ' - ' + event.end.format(TIME_FORMAT) + '</li>' + (event.subject !== undefined ? '<li>Appointment starts: ' + event.constraint.start.format(TIME_FORMAT) + '</li>' : '') + '<li>Location: ' + (event.location !== FLYING_TEAM_LABEL ? event.location : event.flying_team_location + ' (FT)') + '</li>' + '</ul>', html: true }); } else { } }, selectAllow: function (selectInfo) { return false; }, eventDragStop: function (event, jsEvent, ui, view) { // var x = isElemOverDiv(jsEvent.clientX, jsEvent.clientY, $('#subjects')); $('.popover').popover('hide'); var x = overlaps(jsEvent.clientX, jsEvent.clientY, $('#subjects')); if (x) { remove_event(event); } }, eventDragStart: function (event, jsEvent, view) { $('.popover').popover('hide'); }, selectHelper: true, drop: function (date, jsEvent, ui, resourceId) { $(this).remove(); }, eventAfterAllRender: function(view){ //RESIZE COLUMNS AND ENABLE HORIZONTAL SCROLL window.onresize = function(event) { resizeCalendarColumns(); }; setTimeout(function() {resizeCalendarColumns()}, 100); } }); }) ; //RESIZE COLUMNS AND ENABLE HORIZONTAL SCROLL function resizeCalendarColumns(){ if($('.fc-resource-cell').width() <= 150){ $('.fc-view-container').width(200*$('.fc-resource-cell').length); $('#calendar').css('overflow-x', 'scroll'); } if($('#calendar').width() > 200*$('.fc-resource-cell').length){ $('.fc-view-container').width("100%"); $('#calendar').css('overflow-x', 'null'); } }