Skip to content
Snippets Groups Projects
daily_planning.js 21.6 KiB
Newer Older
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 = [];
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;
    }
        appointment_start: event.appointment_start,
        appointment_end: event.appointment_end,
        stick: true,
        duration: event.duration,
        original_duration: event.duration,
        subject: event.subject,
        subject_id: subjectId,
        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.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,
Valentin Groues's avatar
Valentin Groues committed
        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 () {
Piotr Gawron's avatar
Piotr Gawron committed
                    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'),
                                    }
                                    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)
                                });                  
        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');
    }
}