| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /**
- * App Calendar
- */
-
- /**
- * ! If both start and end dates are same Full calendar will nullify the end date value.
- * ! Full calendar will end the event on a day before at 12:00:00AM thus, event won't extend to the end date.
- * ! We are getting events from a separate file named app-calendar-events.js. You can add or remove events from there.
- *
- **/
-
- 'use strict';
-
- document.addEventListener('DOMContentLoaded', function () {
- const direction = isRtl ? 'rtl' : 'ltr';
- (function () {
- // DOM Elements
- const calendarEl = document.getElementById('calendar');
- const appCalendarSidebar = document.querySelector('.app-calendar-sidebar');
- const addEventSidebar = document.getElementById('addEventSidebar');
- const appOverlay = document.querySelector('.app-overlay');
- const offcanvasTitle = document.querySelector('.offcanvas-title');
- const btnToggleSidebar = document.querySelector('.btn-toggle-sidebar');
- const btnSubmit = document.getElementById('addEventBtn');
- const btnDeleteEvent = document.querySelector('.btn-delete-event');
- const btnCancel = document.querySelector('.btn-cancel');
- const eventTitle = document.getElementById('eventTitle');
- const eventStartDate = document.getElementById('eventStartDate');
- const eventEndDate = document.getElementById('eventEndDate');
- const eventUrl = document.getElementById('eventURL');
- const eventLocation = document.getElementById('eventLocation');
- const eventDescription = document.getElementById('eventDescription');
- const allDaySwitch = document.querySelector('.allDay-switch');
- const selectAll = document.querySelector('.select-all');
- const filterInputs = Array.from(document.querySelectorAll('.input-filter'));
- const inlineCalendar = document.querySelector('.inline-calendar');
-
- // Calendar settings
- const calendarColors = {
- Business: 'primary',
- Holiday: 'success',
- Personal: 'danger',
- Family: 'warning',
- ETC: 'info'
- };
-
- // External jQuery Elements
- const eventLabel = $('#eventLabel'); // ! Using jQuery vars due to select2 jQuery dependency
- const eventGuests = $('#eventGuests'); // ! Using jQuery vars due to select2 jQuery dependency
-
- // Event Data
- let currentEvents = events; // Assuming events are imported from app-calendar-events.js
- let isFormValid = false;
- let eventToUpdate = null;
- let inlineCalInstance = null;
-
- // Offcanvas Instance
- const bsAddEventSidebar = new bootstrap.Offcanvas(addEventSidebar);
-
- //! TODO: Update Event label and guest code to JS once select removes jQuery dependency
- // Initialize Select2 with custom templates
- if (eventLabel.length) {
- function renderBadges(option) {
- if (!option.id) {
- return option.text;
- }
- var $badge =
- "<span class='badge badge-dot bg-" + $(option.element).data('label') + " me-2'> " + '</span>' + option.text;
-
- return $badge;
- }
- eventLabel.wrap('<div class="position-relative"></div>').select2({
- placeholder: 'Select value',
- dropdownParent: eventLabel.parent(),
- templateResult: renderBadges,
- templateSelection: renderBadges,
- minimumResultsForSearch: -1,
- escapeMarkup: function (es) {
- return es;
- }
- });
- }
-
- // Render guest avatars
- if (eventGuests.length) {
- function renderGuestAvatar(option) {
- if (!option.id) return option.text;
- return `
- <div class='d-flex flex-wrap align-items-center'>
- <div class='avatar avatar-xs me-2'>
- <img src='${assetsPath}img/avatars/${$(option.element).data('avatar')}'
- alt='avatar' class='rounded-circle' />
- </div>
- ${option.text}
- </div>`;
- }
- eventGuests.wrap('<div class="position-relative"></div>').select2({
- placeholder: 'Select value',
- dropdownParent: eventGuests.parent(),
- closeOnSelect: false,
- templateResult: renderGuestAvatar,
- templateSelection: renderGuestAvatar,
- escapeMarkup: function (es) {
- return es;
- }
- });
- }
-
- // Event start (flatpicker)
- if (eventStartDate) {
- var start = eventStartDate.flatpickr({
- monthSelectorType: 'static',
- static: true,
- enableTime: true,
- altFormat: 'Y-m-dTH:i:S',
- onReady: function (selectedDates, dateStr, instance) {
- if (instance.isMobile) {
- instance.mobileInput.setAttribute('step', null);
- }
- }
- });
- }
-
- // Event end (flatpicker)
- if (eventEndDate) {
- var end = eventEndDate.flatpickr({
- monthSelectorType: 'static',
- static: true,
- enableTime: true,
- altFormat: 'Y-m-dTH:i:S',
- onReady: function (selectedDates, dateStr, instance) {
- if (instance.isMobile) {
- instance.mobileInput.setAttribute('step', null);
- }
- }
- });
- }
-
- // Inline sidebar calendar (flatpicker)
- if (inlineCalendar) {
- inlineCalInstance = inlineCalendar.flatpickr({
- monthSelectorType: 'static',
- static: true,
- inline: true
- });
- }
-
- // Event click function
- function eventClick(info) {
- eventToUpdate = info.event;
- if (eventToUpdate.url) {
- info.jsEvent.preventDefault();
- window.open(eventToUpdate.url, '_blank');
- }
- bsAddEventSidebar.show();
- // For update event set offcanvas title text: Update Event
- if (offcanvasTitle) {
- offcanvasTitle.innerHTML = 'Update Event';
- }
- btnSubmit.innerHTML = 'Update';
- btnSubmit.classList.add('btn-update-event');
- btnSubmit.classList.remove('btn-add-event');
- btnDeleteEvent.classList.remove('d-none');
-
- eventTitle.value = eventToUpdate.title;
- start.setDate(eventToUpdate.start, true, 'Y-m-d');
- eventToUpdate.allDay === true ? (allDaySwitch.checked = true) : (allDaySwitch.checked = false);
- eventToUpdate.end !== null
- ? end.setDate(eventToUpdate.end, true, 'Y-m-d')
- : end.setDate(eventToUpdate.start, true, 'Y-m-d');
- eventLabel.val(eventToUpdate.extendedProps.calendar).trigger('change');
- eventToUpdate.extendedProps.location !== undefined
- ? (eventLocation.value = eventToUpdate.extendedProps.location)
- : null;
- eventToUpdate.extendedProps.guests !== undefined
- ? eventGuests.val(eventToUpdate.extendedProps.guests).trigger('change')
- : null;
- eventToUpdate.extendedProps.description !== undefined
- ? (eventDescription.value = eventToUpdate.extendedProps.description)
- : null;
- }
-
- // Modify sidebar toggler
- function modifyToggler() {
- const fcSidebarToggleButton = document.querySelector('.fc-sidebarToggle-button');
- fcSidebarToggleButton.classList.remove('fc-button-primary');
- fcSidebarToggleButton.classList.add('d-lg-none', 'd-inline-block', 'ps-0');
- while (fcSidebarToggleButton.firstChild) {
- fcSidebarToggleButton.firstChild.remove();
- }
- fcSidebarToggleButton.setAttribute('data-bs-toggle', 'sidebar');
- fcSidebarToggleButton.setAttribute('data-overlay', '');
- fcSidebarToggleButton.setAttribute('data-target', '#app-calendar-sidebar');
- fcSidebarToggleButton.insertAdjacentHTML(
- 'beforeend',
- '<i class="icon-base bx bx-menu icon-lg text-heading"></i>'
- );
- }
-
- // Filter events by calender
- function selectedCalendars() {
- let selected = [],
- filterInputChecked = [].slice.call(document.querySelectorAll('.input-filter:checked'));
-
- filterInputChecked.forEach(item => {
- selected.push(item.getAttribute('data-value'));
- });
-
- return selected;
- }
-
- // --------------------------------------------------------------------------------------------------
- // AXIOS: fetchEvents
- // * This will be called by fullCalendar to fetch events. Also this can be used to refetch events.
- // --------------------------------------------------------------------------------------------------
- function fetchEvents(info, successCallback) {
- let calendars = selectedCalendars();
- // We are reading event object from app-calendar-events.js file directly by including that file above app-calendar file.
- // You should make an API call, look into above commented API call for reference
- let selectedEvents = currentEvents.filter(function (event) {
- return calendars.includes(event.extendedProps.calendar.toLowerCase());
- });
- // if (selectedEvents.length > 0) {
- successCallback(selectedEvents);
- // }
- }
-
- // Init FullCalendar
- // ------------------------------------------------
- let calendar = new Calendar(calendarEl, {
- initialView: 'dayGridMonth',
- events: fetchEvents,
- plugins: [dayGridPlugin, interactionPlugin, listPlugin, timegridPlugin],
- editable: true,
- dragScroll: true,
- dayMaxEvents: 2,
- eventResizableFromStart: true,
- customButtons: {
- sidebarToggle: {
- text: 'Sidebar'
- }
- },
- headerToolbar: {
- start: 'sidebarToggle, prev,next, title',
- end: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
- },
- direction: direction,
- initialDate: new Date(),
- navLinks: true, // can click day/week names to navigate views
- eventClassNames: function ({ event: calendarEvent }) {
- const colorName = calendarColors[calendarEvent._def.extendedProps.calendar];
- // Background Color
- return ['bg-label-' + colorName];
- },
- dateClick: function (info) {
- let date = moment(info.date).format('YYYY-MM-DD');
- resetValues();
- bsAddEventSidebar.show();
-
- // For new event set offcanvas title text: Add Event
- if (offcanvasTitle) {
- offcanvasTitle.innerHTML = 'Add Event';
- }
- btnSubmit.innerHTML = 'Add';
- btnSubmit.classList.remove('btn-update-event');
- btnSubmit.classList.add('btn-add-event');
- btnDeleteEvent.classList.add('d-none');
- eventStartDate.value = date;
- eventEndDate.value = date;
- },
- eventClick: function (info) {
- eventClick(info);
- },
- datesSet: function () {
- modifyToggler();
- },
- viewDidMount: function () {
- modifyToggler();
- }
- });
-
- // Render calendar
- calendar.render();
- // Modify sidebar toggler
- modifyToggler();
-
- const eventForm = document.getElementById('eventForm');
- const fv = FormValidation.formValidation(eventForm, {
- fields: {
- eventTitle: {
- validators: {
- notEmpty: {
- message: 'Please enter event title '
- }
- }
- },
- eventStartDate: {
- validators: {
- notEmpty: {
- message: 'Please enter start date '
- }
- }
- },
- eventEndDate: {
- validators: {
- notEmpty: {
- message: 'Please enter end date '
- }
- }
- }
- },
- plugins: {
- trigger: new FormValidation.plugins.Trigger(),
- bootstrap5: new FormValidation.plugins.Bootstrap5({
- // Use this for enabling/changing valid/invalid class
- eleValidClass: '',
- rowSelector: function (field, ele) {
- // field is the field name & ele is the field element
- return '.form-control-validation';
- }
- }),
- submitButton: new FormValidation.plugins.SubmitButton(),
- // Submit the form when all fields are valid
- // defaultSubmit: new FormValidation.plugins.DefaultSubmit(),
- autoFocus: new FormValidation.plugins.AutoFocus()
- }
- })
- .on('core.form.valid', function () {
- // Jump to the next step when all fields in the current step are valid
- isFormValid = true;
- })
- .on('core.form.invalid', function () {
- // if fields are invalid
- isFormValid = false;
- });
-
- // Sidebar Toggle Btn
- if (btnToggleSidebar) {
- btnToggleSidebar.addEventListener('click', e => {
- btnCancel.classList.remove('d-none');
- });
- }
-
- // Add Event
- // ------------------------------------------------
- function addEvent(eventData) {
- // ? Add new event data to current events object and refetch it to display on calender
- // ? You can write below code to AJAX call success response
-
- currentEvents.push(eventData);
- calendar.refetchEvents();
-
- // ? To add event directly to calender (won't update currentEvents object)
- // calendar.addEvent(eventData);
- }
-
- // Update Event
- // ------------------------------------------------
- function updateEvent(eventData) {
- // ? Update existing event data to current events object and refetch it to display on calender
- // ? You can write below code to AJAX call success response
- eventData.id = parseInt(eventData.id);
- currentEvents[currentEvents.findIndex(el => el.id === eventData.id)] = eventData; // Update event by id
- calendar.refetchEvents();
-
- // ? To update event directly to calender (won't update currentEvents object)
- // let propsToUpdate = ['id', 'title', 'url'];
- // let extendedPropsToUpdate = ['calendar', 'guests', 'location', 'description'];
-
- // updateEventInCalendar(eventData, propsToUpdate, extendedPropsToUpdate);
- }
-
- // Remove Event
- // ------------------------------------------------
-
- function removeEvent(eventId) {
- // ? Delete existing event data to current events object and refetch it to display on calender
- // ? You can write below code to AJAX call success response
- currentEvents = currentEvents.filter(function (event) {
- return event.id != eventId;
- });
- calendar.refetchEvents();
-
- // ? To delete event directly to calender (won't update currentEvents object)
- // removeEventInCalendar(eventId);
- }
-
- // (Update Event In Calendar (UI Only)
- // ------------------------------------------------
- const updateEventInCalendar = (updatedEventData, propsToUpdate, extendedPropsToUpdate) => {
- const existingEvent = calendar.getEventById(updatedEventData.id);
-
- // --- Set event properties except date related ----- //
- // ? Docs: https://fullcalendar.io/docs/Event-setProp
- // dateRelatedProps => ['start', 'end', 'allDay']
- // eslint-disable-next-line no-plusplus
- for (var index = 0; index < propsToUpdate.length; index++) {
- var propName = propsToUpdate[index];
- existingEvent.setProp(propName, updatedEventData[propName]);
- }
-
- // --- Set date related props ----- //
- // ? Docs: https://fullcalendar.io/docs/Event-setDates
- existingEvent.setDates(updatedEventData.start, updatedEventData.end, {
- allDay: updatedEventData.allDay
- });
-
- // --- Set event's extendedProps ----- //
- // ? Docs: https://fullcalendar.io/docs/Event-setExtendedProp
- // eslint-disable-next-line no-plusplus
- for (var index = 0; index < extendedPropsToUpdate.length; index++) {
- var propName = extendedPropsToUpdate[index];
- existingEvent.setExtendedProp(propName, updatedEventData.extendedProps[propName]);
- }
- };
-
- // Remove Event In Calendar (UI Only)
- // ------------------------------------------------
- function removeEventInCalendar(eventId) {
- calendar.getEventById(eventId).remove();
- }
-
- // Add new event
- // ------------------------------------------------
- btnSubmit.addEventListener('click', e => {
- if (btnSubmit.classList.contains('btn-add-event')) {
- if (isFormValid) {
- let newEvent = {
- id: calendar.getEvents().length + 1,
- title: eventTitle.value,
- start: eventStartDate.value,
- end: eventEndDate.value,
- startStr: eventStartDate.value,
- endStr: eventEndDate.value,
- display: 'block',
- extendedProps: {
- location: eventLocation.value,
- guests: eventGuests.val(),
- calendar: eventLabel.val(),
- description: eventDescription.value
- }
- };
- if (eventUrl.value) {
- newEvent.url = eventUrl.value;
- }
- if (allDaySwitch.checked) {
- newEvent.allDay = true;
- }
- addEvent(newEvent);
- bsAddEventSidebar.hide();
- }
- } else {
- // Update event
- // ------------------------------------------------
- if (isFormValid) {
- let eventData = {
- id: eventToUpdate.id,
- title: eventTitle.value,
- start: eventStartDate.value,
- end: eventEndDate.value,
- url: eventUrl.value,
- extendedProps: {
- location: eventLocation.value,
- guests: eventGuests.val(),
- calendar: eventLabel.val(),
- description: eventDescription.value
- },
- display: 'block',
- allDay: allDaySwitch.checked ? true : false
- };
-
- updateEvent(eventData);
- bsAddEventSidebar.hide();
- }
- }
- });
-
- // Call removeEvent function
- btnDeleteEvent.addEventListener('click', e => {
- removeEvent(parseInt(eventToUpdate.id));
- // eventToUpdate.remove();
- bsAddEventSidebar.hide();
- });
-
- // Reset event form inputs values
- // ------------------------------------------------
- function resetValues() {
- eventEndDate.value = '';
- eventUrl.value = '';
- eventStartDate.value = '';
- eventTitle.value = '';
- eventLocation.value = '';
- allDaySwitch.checked = false;
- eventGuests.val('').trigger('change');
- eventDescription.value = '';
- }
-
- // When modal hides reset input values
- addEventSidebar.addEventListener('hidden.bs.offcanvas', function () {
- resetValues();
- });
-
- // Hide left sidebar if the right sidebar is open
- btnToggleSidebar.addEventListener('click', e => {
- if (offcanvasTitle) {
- offcanvasTitle.innerHTML = 'Add Event';
- }
- btnSubmit.innerHTML = 'Add';
- btnSubmit.classList.remove('btn-update-event');
- btnSubmit.classList.add('btn-add-event');
- btnDeleteEvent.classList.add('d-none');
- appCalendarSidebar.classList.remove('show');
- appOverlay.classList.remove('show');
- });
-
- // Calender filter functionality
- // ------------------------------------------------
- if (selectAll) {
- selectAll.addEventListener('click', e => {
- if (e.currentTarget.checked) {
- document.querySelectorAll('.input-filter').forEach(c => (c.checked = 1));
- } else {
- document.querySelectorAll('.input-filter').forEach(c => (c.checked = 0));
- }
- calendar.refetchEvents();
- });
- }
-
- if (filterInputs) {
- filterInputs.forEach(item => {
- item.addEventListener('click', () => {
- document.querySelectorAll('.input-filter:checked').length < document.querySelectorAll('.input-filter').length
- ? (selectAll.checked = false)
- : (selectAll.checked = true);
- calendar.refetchEvents();
- });
- });
- }
-
- // Jump to date on sidebar(inline) calendar change
- inlineCalInstance.config.onChange.push(function (date) {
- calendar.changeView(calendar.view.type, moment(date[0]).format('YYYY-MM-DD'));
- modifyToggler();
- appCalendarSidebar.classList.remove('show');
- appOverlay.classList.remove('show');
- });
- })();
- });
|