From 3d29f90dbd180ab8442c7d81a2a7aacefa149658 Mon Sep 17 00:00:00 2001 From: Rutwik Hiwalkar Date: Tue, 7 May 2024 19:46:18 +0530 Subject: [PATCH] chore: upgrade calendar component to latest feature parity for, - toolbar actions - show/hide weekend - navigate through calendar and click events --- .../js/frappe/views/calendar/calendar.js | 127 +++++++++--------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/frappe/public/js/frappe/views/calendar/calendar.js b/frappe/public/js/frappe/views/calendar/calendar.js index cd0402eda1..0116201953 100644 --- a/frappe/public/js/frappe/views/calendar/calendar.js +++ b/frappe/public/js/frappe/views/calendar/calendar.js @@ -1,6 +1,12 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt +import { Calendar as FullCalendar } from "@fullcalendar/core"; +import dayGridPlugin from "@fullcalendar/daygrid"; +import listPlugin from "@fullcalendar/list"; +import timeGridPlugin from "@fullcalendar/timegrid"; +import interactionPlugin from "@fullcalendar/interaction"; + frappe.provide("frappe.views.calendar"); frappe.provide("frappe.views.calendars"); @@ -99,18 +105,6 @@ frappe.views.CalendarView = class CalendarView extends frappe.views.ListView { } }); } - - get required_libs() { - let assets = [ - "assets/frappe/js/lib/fullcalendar/fullcalendar.min.css", - "assets/frappe/js/lib/fullcalendar/fullcalendar.min.js", - ]; - let user_language = frappe.boot.lang; - if (user_language && user_language !== "en") { - assets.push("assets/frappe/js/lib/fullcalendar/locale-all.js"); - } - return assets; - } }; frappe.views.Calendar = class Calendar { @@ -133,10 +127,10 @@ frappe.views.Calendar = class Calendar { } get_default_options() { return new Promise((resolve) => { - let defaultView = localStorage.getItem("cal_defaultView"); + let initialView = localStorage.getItem("cal_initialView"); let weekends = localStorage.getItem("cal_weekends"); let defaults = { - defaultView: defaultView ? defaultView : "month", + initialView: initialView ? initialView : "dayGridMonth", weekends: weekends ? weekends : true, }; resolve(defaults); @@ -162,13 +156,13 @@ frappe.views.Calendar = class Calendar { }); $(this.parent).on("show", function () { - me.$cal.fullCalendar("refetchEvents"); + me.$cal.fullCalendar.refetchEvents(); }); } make() { this.$wrapper = this.parent; - this.$cal = $("
").appendTo(this.$wrapper); + this.$cal = $("
").appendTo(this.$wrapper); this.footnote_area = frappe.utils.set_footnote( this.footnote_area, this.$wrapper, @@ -176,7 +170,9 @@ frappe.views.Calendar = class Calendar { ); this.footnote_area.css({ "border-top": "0px" }); - this.$cal.fullCalendar(this.cal_options); + this.fullCalendar = new FullCalendar(this.$cal[0], this.cal_options); + this.fullCalendar.render(); + this.set_css(); } setup_view_mode_button(defaults) { @@ -194,37 +190,42 @@ frappe.views.Calendar = class Calendar { const me = this; let btn_group = me.$wrapper.find(".fc-button-group"); btn_group.on("click", ".btn", function () { - let value = $(this).hasClass("fc-agendaWeek-button") - ? "agendaWeek" - : $(this).hasClass("fc-agendaDay-button") - ? "agendaDay" - : "month"; - me.set_localStorage_option("cal_defaultView", value); + let value = $(this).hasClass("fc-dayGridWeek-button") + ? "dayGridWeek" + : $(this).hasClass("fc-dayGridDay-button") + ? "dayGridDay" + : "dayGridMonth"; + me.set_localStorage_option("cal_initialView", value); }); me.$wrapper.on("click", ".btn-weekend", function () { me.cal_options.weekends = !me.cal_options.weekends; - me.$cal.fullCalendar("option", "weekends", me.cal_options.weekends); + //me.fullCalendar("option", "weekends", ); + me.fullCalendar.setOption("weekends", me.cal_options.weekends); me.set_localStorage_option("cal_weekends", me.cal_options.weekends); me.set_css(); me.setup_view_mode_button(me.cal_options); }); } set_css() { - // flatify buttons this.$wrapper - .find("button.fc-state-default") - .removeClass("fc-state-default") + .find("button.fc-button") + .removeClass("fc-button") + .removeClass("fc-button-primary") .addClass("btn btn-default"); this.$wrapper - .find(".fc-month-button, .fc-agendaWeek-button, .fc-agendaDay-button") + .find(".fc-dayGridMonth-button, .fc-dayGridWeek-button, .fc-dayGridDay-button") .wrapAll('
'); + // add icons this.$wrapper .find(".fc-prev-button span") .attr("class", "") .html(frappe.utils.icon("left")); + + this.$wrapper.find(".fc-prev-button").css("margin-right", 5 + "px"); + this.$wrapper .find(".fc-next-button span") .attr("class", "") @@ -235,7 +236,7 @@ frappe.views.Calendar = class Calendar { this.$wrapper.find(".fc-day-number").wrap('
'); var btn_group = this.$wrapper.find(".fc-button-group"); - btn_group.find(".fc-state-active").addClass("active"); + btn_group.find(".fc-button-active").addClass("active"); btn_group.find(".btn").on("click", function () { btn_group.find(".btn").removeClass("active"); @@ -244,93 +245,95 @@ frappe.views.Calendar = class Calendar { } get_system_datetime(date) { - date._offset = moment(date).tz(frappe.sys_defaults.time_zone)._offset; - return frappe.datetime.convert_to_system_tz(moment(date).locale("en")); + return frappe.datetime.convert_to_system_tz(new Date(date)); } setup_options(defaults) { var me = this; defaults.meridiem = "false"; this.cal_options = { + plugins: [dayGridPlugin, listPlugin, timeGridPlugin, interactionPlugin], + initialView: defaults.initialView || "dayGridMonth", locale: frappe.boot.lang, - header: { - left: "prev, title, next", - right: "today, month, agendaWeek, agendaDay", + headerToolbar: { + left: "prev,next", + center: "title", + right: "today,dayGridMonth,dayGridWeek,dayGridDay", }, editable: true, + droppable: true, selectable: true, - selectHelper: true, + selectMirror: true, forceEventDuration: true, - displayEventTime: true, - defaultView: defaults.defaultView, + displayEventTime: false, weekends: defaults.weekends, nowIndicator: true, + themeSystem: null, buttonText: { today: __("Today"), month: __("Month"), week: __("Week"), day: __("Day"), }, - events: function (start, end, timezone, callback) { + events: function (info, successCallback, failureCallback) { return frappe.call({ method: me.get_events_method || "frappe.desk.calendar.get_events", type: "GET", - args: me.get_args(start, end), + args: me.get_args(info.startStr, info.endStr), callback: function (r) { var events = r.message || []; events = me.prepare_events(events); - callback(events); + successCallback(events); }, }); }, displayEventEnd: true, - eventRender: function (event, element) { - element.attr("title", event.tooltip); - }, - eventClick: function (event) { + eventClick: function (info) { // edit event description or delete - var doctype = event.doctype || me.doctype; + var doctype = info.doctype || me.doctype; if (frappe.model.can_read(doctype)) { - frappe.set_route("Form", doctype, event.name); + frappe.set_route("Form", doctype, info.event.id); } }, - eventDrop: function (event, delta, revertFunc) { - me.update_event(event, revertFunc); + eventDrop: function (info) { + me.update_event(info.event, info.revert); }, - eventResize: function (event, delta, revertFunc) { - me.update_event(event, revertFunc); + eventResize: function (info) { + me.update_event(info.event, info.revert); }, - select: function (startDate, endDate, jsEvent, view) { - if (view.name === "month" && endDate - startDate === 86400000) { + select: function (info) { + if (info.view.type === "dayGridMonth" && info.end - info.start === 86400000) { // detect single day click in month view return; } var event = frappe.model.get_new_doc(me.doctype); - event[me.field_map.start] = me.get_system_datetime(startDate); + event[me.field_map.start] = me.get_system_datetime(info.startStr); - if (me.field_map.end) event[me.field_map.end] = me.get_system_datetime(endDate); + if (me.field_map.end) + event[me.field_map.end] = me.get_system_datetime(info.endStr); if (me.field_map.allDay) { - var all_day = startDate._ambigTime && endDate._ambigTime ? 1 : 0; + var all_day = info.start._ambigTime && info.end._ambigTime ? 1 : 0; event[me.field_map.allDay] = all_day; if (all_day) event[me.field_map.end] = me.get_system_datetime( - moment(endDate).subtract(1, "s") + moment(info.endStr).subtract(1, "s") ); } frappe.set_route("Form", me.doctype, event.name); }, - dayClick: function (date, jsEvent, view) { - if (view.name === "month") { - const $date_cell = $("td[data-date=" + date.format("YYYY-MM-DD") + "]"); + dateClick: function (info) { + if (info.view.type === "dayGridMonth") { + const $date_cell = $( + "td[data-date=" + info.date.toISOString().slice(0, 10) + "]" + ); if ($date_cell.hasClass("date-clicked")) { - me.$cal.fullCalendar("changeView", "agendaDay"); - me.$cal.fullCalendar("gotoDate", date); + me.fullCalendar.changeView("timeGridDay", info.date); me.$wrapper.find(".date-clicked").removeClass("date-clicked"); // update "active view" btn @@ -361,7 +364,7 @@ frappe.views.Calendar = class Calendar { return args; } refresh() { - this.$cal.fullCalendar("refetchEvents"); + this.fullCalendar.refetchEvents(); } prepare_events(events) { var me = this;