/* eslint-disable camelcase, handle-callback-err, no-return-assign, no-undef, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Event', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'growl', function ($scope, $state, Event, categoriesPromise, themesPromise, ageRangesPromise, growl) { /* PUBLIC SCOPE */ /** * Callback triggered by react components */ $scope.onError = function (message) { growl.error(message); }; // The events displayed on the page $scope.events = []; // The currently displayed page number $scope.page = 1; // List of categories for the events $scope.categories = categoriesPromise; // List of events themes $scope.themes = themesPromise; // List of age ranges $scope.ageRanges = ageRangesPromise; // Hide or show the 'load more' button $scope.noMoreResults = false; // Active filters for the events list $scope.filters = { category_id: null, theme_id: null, age_range_id: null }; $scope.monthNames = [<%= I18n.t('date.month_names')[1..-1].map { |m| "\"#{m}\"" }.join(', ') %>]; /** * Adds a resultset of events to the bottom of the page, grouped by month */ $scope.loadMoreEvents = function () { $scope.page += 1; return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) { $scope.events = $scope.events.concat(data); groupEvents($scope.events); if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) { return $scope.noMoreResults = true; } }); }; /** * Callback to redirect the user to the specified event page * @param event {{id:number}} */ $scope.showEvent = function (event) { $state.go('app.public.events_show', { id: event.id }); }; /** * Callback to refresh the events list according to the filters set */ $scope.filterEvents = function () { // reinitialize results datasets $scope.page = 1; $scope.eventsGroupByMonth = {}; $scope.featuredEevent = null; $scope.events = []; $scope.monthOrder = []; $scope.noMoreResults = false; // run a search query return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) { $scope.events = data; groupEvents(data); if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) { return $scope.noMoreResults = true; } }); }; /** * Test if the provided event occurs on a single day or on many days * @param event {{start_date:Date, end_date:Date}} Event object as retreived from the API * @return {boolean} false if the event occurs on many days */ $scope.onSingleDay = function (event) { moment(event.start_date).isSame(event.end_date, 'day'); }; /* PRIVATE SCOPE */ /** * Kind of constructor: these actions will be realized first when the controller is loaded */ const initialize = function () { $scope.filterEvents(); }; /** * Group the provided events by month/year and concat them with existing results * Then compute the ordered list of months for the complete resultset. * Affect the resulting events groups in $scope.eventsGroupByMonth and the ordered month keys in $scope.monthOrder. * @param events {Array} Events retrieved from the API */ const groupEvents = function (events) { if (events.length > 0) { const eventsGroupedByMonth = _.groupBy(events, function (obj) { return _.map(['month_id', 'year'], function (key) { return obj[key]; }); }); $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth); $scope.monthOrder = Object.keys($scope.eventsGroupByMonth); $scope.featuredEevent = _.minBy(events.filter(e => moment(e.start_date).isAfter()), e => e.start_date); } }; // # !!! MUST BE CALLED AT THE END of the controller initialize(); } ]); Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$rootScope', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'SlotsReservation', 'eventPromise', 'growl', '_t', 'Wallet', 'AuthService', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise', 'LocalPayment', function ($scope, $state,$rootScope, Event, $uibModal, Member, Reservation, Price, CustomAsset, SlotsReservation, eventPromise, growl, _t, Wallet, AuthService, helpers, dialogs, priceCategoriesPromise, settingsPromise, LocalPayment) { /* PUBLIC SCOPE */ // reservations for the currently shown event $scope.reservations = []; // current date & time $scope.now = moment(); // user to deal with $scope.ctrl = { member: {} }; // parameters for a new reservation $scope.reserve = { nbPlaces: { normal: [] }, nbReservePlaces: 0, tickets: {}, toReserve: false, amountTotal: 0, totalNoCoupon: 0, totalSeats: 0 }; // Discount coupon to apply to the basket, if any $scope.coupon = { applied: null }; // Get the details for the current event (event's id is recovered from the current URL) $scope.event = eventPromise; $scope.eventEndDateTime = moment(`${eventPromise.end_date}T${eventPromise.end_time}:00`); // the application global settings $scope.settings = settingsPromise; // List of price categories for the events $scope.priceCategories = priceCategoriesPromise; // Global config: is the user authorized to change his bookings slots? $scope.enableBookingMove = (settingsPromise.booking_move_enable === 'true'); // Global config: delay in hours before a booking while changing the booking slot is forbidden $scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay); // Global config: is the user authorized to cancel his booking slots? $scope.enableBookingCancel = settingsPromise.booking_cancel_enable === 'true'; // Global config: delay in hours from now when restrictions occurs about cancelling reservations $scope.cancelBookingDelay = parseInt(settingsPromise.booking_cancel_delay); // Message displayed to the end user about rules that applies to events reservations $scope.eventExplicationsAlert = settingsPromise.event_explications_alert; // Global config: is the user validation required ? $scope.enableUserValidationRequired = settingsPromise.user_validation_required === 'true'; // online payments (by card) $scope.onlinePayment = { showModal: false, cartItems: undefined }; /** * Callback to delete the provided event (admins only) */ $scope.deleteEvent = function () { // open a confirmation dialog const modalInstance = $uibModal.open({ animation: true, templateUrl: '/events/deleteRecurrent.html', size: 'md', controller: 'DeleteRecurrentEventController', resolve: { eventPromise: ['Event', function (Event) { return Event.get({ id: $scope.event.id }).$promise; }] } }); // once the dialog was closed, do things depending on the result modalInstance.result.then(function (res) { if (res.status === 'success') { $state.go('app.public.events_list'); } }); }; /** * Callback to call when the number of tickets to book changes in the current booking */ $scope.changeNbPlaces = function () { // compute the total remaining places let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces; for (let ticket in $scope.reserve.tickets) { remain -= $scope.reserve.tickets[ticket]; } // we store the total number of seats booked, this is used to know if the 'pay' button must be shown $scope.reserve.totalSeats = $scope.event.nb_free_places - remain; // update the available seats for full price tickets const fullPriceRemains = $scope.reserve.nbReservePlaces + remain; $scope.reserve.nbPlaces.normal = __range__(0, fullPriceRemains, true); // update the available seats for other prices tickets for (let key in $scope.reserve.nbPlaces) { if (key !== 'normal') { const priceRemain = $scope.reserve.tickets[key] + remain; $scope.reserve.nbPlaces[key] = __range__(0, priceRemain, true); } } // recompute the total price return $scope.computeEventAmount(); }; /** * Callback to reset the current reservation parameters * @param e {Object} see https://docs.angularjs.org/guide/expression#-event- */ $scope.cancelReserve = function (e) { e.preventDefault(); return resetEventReserve(); }; $scope.isUserValidatedByType = () => { return helpers.isUserValidatedByType($scope.ctrl.member, $scope.settings, 'event'); }; $scope.isShowReserveEventButton = () => { return $scope.event.nb_free_places > 0 && !$scope.reserve.toReserve && $scope.now.isBefore($scope.eventEndDateTime) && helpers.isUserValidatedByType($scope.ctrl.member, $scope.settings, 'event'); }; /** * Callback to allow the user to set the details for his reservation */ $scope.reserveEvent = function () { if ($scope.event.nb_total_places > 0) { $scope.reserveSuccess = false; if (!$scope.isAuthenticated()) { $scope.login(null, function (user) { if (user.role !== 'admin' || user.role !== 'manager') { $scope.ctrl.member = user; } const sameTimeReservations = findReservationsAtSameTime(); if (sameTimeReservations.length > 0) { showReserveSlotSameTimeModal(sameTimeReservations, function(res) { $scope.reserve.toReserve = !$scope.reserve.toReserve; }); } else { $scope.reserve.toReserve = !$scope.reserve.toReserve; } }); } else { if (AuthService.isAuthorized(['admin', 'manager'])) { $scope.reserve.toReserve = !$scope.reserve.toReserve; } else { Member.get({ id: $scope.currentUser.id }, function (member) { $scope.ctrl.member = member; const sameTimeReservations = findReservationsAtSameTime(); if (sameTimeReservations.length > 0) { showReserveSlotSameTimeModal(sameTimeReservations, function(res) { $scope.reserve.toReserve = !$scope.reserve.toReserve; }); } else { $scope.reserve.toReserve = !$scope.reserve.toReserve; } }); } } } }; /** * Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's * reservations. (admins only) */ $scope.updateMember = function () { resetEventReserve(); $scope.reserveSuccess = false; if ($scope.ctrl.member) { Member.get({ id: $scope.ctrl.member.id }, function (member) { $scope.ctrl.member = member; getReservations($scope.event.id, 'Event', $scope.ctrl.member.id); }); } }; /** * Callback to trigger the payment process of the current reservation */ $scope.payEvent = function () { // first, we check that a user was selected if (Object.keys($scope.ctrl.member).length > 0) { const reservation = mkReservation($scope.reserve, $scope.event); return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) { const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount); if ((AuthService.isAuthorized('member') && amountToPay > 0) || (AuthService.isAuthorized(['manager', 'admin']) && $scope.ctrl.member.id === $rootScope.currentUser.id && amountToPay > 0)) { if (settingsPromise.online_payment_module !== 'true') { growl.error(_t('app.public.events_show.online_payment_disabled')); } else { return payOnline(reservation); } } else { if (AuthService.isAuthorized(['manager', 'admin']) && $scope.ctrl.member.id !== $rootScope.currentUser.id || amountToPay === 0) { return payOnSite(reservation); } } }); } else { // otherwise we alert, this error musn't occur when the current user is not admin return growl.error(_t('app.public.events_show.please_select_a_member_first')); } }; /** * Callback to validate the booking of a free event */ $scope.validReserveEvent = function () { const reservation = mkReservation($scope.reserve, $scope.event) const cartItems = mkCartItems(reservation, $scope.coupon.applied); // set the attempting marker $scope.attempting = true; // save the reservation to the API return LocalPayment.confirm(cartItems, function (reservation) { // reservation successful afterPayment(reservation); return $scope.attempting = false; } , function (response) { // reservation failed growl.error(response && response.data && _.keys(response.data)[0] && response.data[_.keys(response.data)[0]][0] || 'server error'); // unset the attempting marker $scope.attempting = false; }) }; /** * Callback to cancel a reservation * @param reservation {Reservation} */ $scope.cancelReservation = function(reservation) { dialogs.confirm({ resolve: { object: function() { return { title: _t('app.public.events_show.cancel_the_reservation'), msg: _t('app.public.events_show.do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets') }; } } }, function() { // cancel confirmed SlotsReservation.cancel({ id: reservation.slots_reservations_attributes[0].id }, function() { // successfully canceled let index; growl.success(_t('app.public.events_show.reservation_was_successfully_cancelled')); index = $scope.reservations.indexOf(reservation); $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats; $scope.reservations[index].slots_reservations_attributes[0].canceled_at = new Date(); }, function(error) { growl.warning(_t('app.public.events_show.cancellation_failed')); }); }); }; /** * Test if the provided reservation has been cancelled * @param reservation {Reservation} * @returns {boolean} */ $scope.isCancelled = function(reservation) { return !!(reservation.slots_reservations_attributes[0].canceled_at); } /** * Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose * a new date for his reservation (if any available) * @param reservation {Reservation} */ $scope.modifyReservation = function (reservation) { const index = $scope.reservations.indexOf(reservation); return $uibModal.open({ templateUrl: '/events/modify_event_reservation_modal.html', resolve: { event () { return $scope.event; }, reservation () { return reservation; } }, controller: ['$scope', '$uibModalInstance', 'event', 'reservation', 'Reservation', function ($scope, $uibModalInstance, event, reservation, Reservation) { // we copy the controller's resolved parameters into the scope $scope.event = event; $scope.reservation = angular.copy(reservation); // set the reservable_id to the first available event for (evt of Array.from(event.recurrence_events)) { if (evt.nb_free_places > reservation.total_booked_seats) { $scope.reservation.reservable_id = evt.id; break; } } // Callback to validate the new reservation's date $scope.ok = function () { let eventToPlace = null; angular.forEach(event.recurrence_events, function (e) { if (e.id === parseInt($scope.reservation.reservable_id, 10)) { return eventToPlace = e; } }); $scope.reservation.slots_reservations_attributes[0].slot_id = eventToPlace.slot_id; $scope.attempting = true; Reservation.update({ id: reservation.id }, { reservation: $scope.reservation }, function (reservation) { $uibModalInstance.close(reservation); $scope.attempting = true; } , function (response) { $scope.alerts = []; angular.forEach(response, function (v, k) { angular.forEach(v, function (err) { $scope.alerts.push({ msg: k + ': ' + err, type: 'danger' }); }); }); $scope.attempting = false; }); }; // Callback to cancel the modification $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; } ] }) .result['finally'](null).then(function (reservation) { // remove the reservation from the user's reservations list for this event (occurrence) $scope.reservations.splice(index, 1); // add the number of places transfered (to the new date) to the total of free places for this event $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats; // remove the number of places transfered from the total of free places of the receiving occurrance angular.forEach($scope.event.recurrence_events, function (e) { if (e.id === parseInt(reservation.reservable.id, 10)) { return e.nb_free_places = e.nb_free_places - reservation.total_booked_seats; } }); }); }; /** * Checks if the provided reservation is able to be moved (date change) * @param reservation {Reservation} */ $scope.reservationCanModify = function (reservation) { if (AuthService.isAuthorized(['admin', 'manager'])) return true; const slotStart = moment(reservation.slots_reservations_attributes[0].slot_attributes.start_at); const now = moment(); let isAble = false; angular.forEach($scope.event.recurrence_events, function (e) { if (e.nb_free_places >= reservation.total_booked_seats) { return isAble = true; } }); return (isAble && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay)); }; /** * Checks if the provided reservation is able to be cancelled * @param reservation {Reservation} */ $scope.reservationCanCancel = function(reservation) { if (AuthService.isAuthorized(['admin', 'manager'])) return true; const slotStart = moment(reservation.slots_reservations_attributes[0].slot_attributes.start_at); const now = moment(); return $scope.enableBookingCancel && slotStart.diff(now, "hours") >= $scope.cancelBookingDelay; }; /** * Compute the total amount for the current reservation according to the previously set parameters * and assign the result in $scope.reserve.amountTotal */ $scope.computeEventAmount = function () { // first we check that a user was selected if (Object.keys($scope.ctrl.member).length > 0) { const r = mkReservation($scope.reserve, $scope.event); return Price.compute(mkCartItems(r, $scope.coupon.applied), function (res) { $scope.reserve.amountTotal = res.price; return $scope.reserve.totalNoCoupon = res.price_without_coupon; }); } else { return $scope.reserve.amountTotal = null; } }; /** * Return the URL allowing to share the current project on the Facebook social network */ $scope.shareOnFacebook = function () { return `https://www.facebook.com/share.php?u=${$state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }).replace('#', '%23')}`; }; /** * Return the URL allowing to share the current project on the Twitter social network */ $scope.shareOnTwitter = function () { return `https://twitter.com/intent/tweet?url=${encodeURIComponent($state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }))}&text=${encodeURIComponent($scope.event.title)}`; }; /** * Return the textual description of the conditions applyable to the given price's category * @param category_id {number} ID of the price's category */ $scope.getPriceCategoryConditions = function (category_id) { for (let cat of Array.from($scope.priceCategories)) { if (cat.id === category_id) { return cat.conditions; } } }; /** * This will open/close the online payment modal */ $scope.toggleOnlinePaymentModal = (beforeApply) => { setTimeout(() => { $scope.onlinePayment.showModal = !$scope.onlinePayment.showModal; if (typeof beforeApply === 'function') { beforeApply(); } $scope.$apply(); }, 50); }; /** * Invoked atfer a successful card payment * @param invoice {*} the invoice */ $scope.afterOnlinePaymentSuccess = (invoice) => { $scope.toggleOnlinePaymentModal(); afterPayment(invoice); }; /** * Invoked when something wrong occurred during the payment dialog initialization * @param message {string} */ $scope.onOnlinePaymentError = (message) => { growl.error(message); }; /* PRIVATE SCOPE */ /** * Kind of constructor: these actions will be realized first when the controller is loaded */ const initialize = function () { // set the controlled user as the current user if the current user is not an admin if ($scope.currentUser) { if ($scope.currentUser.role !== 'admin') { $scope.ctrl.member = $scope.currentUser; } } // initialize the "reserve" object with the event's data resetEventReserve(); // get the current user's reservations into $scope.reservations if ($scope.currentUser) { getReservations($scope.event.id, 'Event', $scope.currentUser.id); } // watch when a coupon is applied to re-compute the total price $scope.$watch('coupon.applied', function (newValue, oldValue) { if ((newValue !== null) || (oldValue !== null)) { return $scope.computeEventAmount(); } }); }; /** * Retrieve the reservations for the couple event / user * @param reservable_id {number} the current event id * @param reservable_type {string} 'Event' * @param user_id {number} the user's id (current or managed) */ const getReservations = function (reservable_id, reservable_type, user_id) { Reservation.query({ reservable_id, reservable_type, user_id }).$promise.then(function (reservations) { $scope.reservations = reservations; }); }; /** * Create a hash map implementing the Reservation specs * @param reserve {Object} Reservation parameters (places...) * @param event {Object} Current event * @return {{reservation: Reservation}} */ const mkReservation = function (reserve, event) { const reservation = { reservable_id: event.id, reservable_type: 'Event', slots_reservations_attributes: [], nb_reserve_places: reserve.nbReservePlaces, tickets_attributes: [] }; reservation.slots_reservations_attributes.push({ offered: event.offered || false, slot_id: event.availability.slot_id }); for (let evt_px_cat of Array.from(event.event_price_categories_attributes)) { const booked = reserve.tickets[evt_px_cat.id]; if (booked > 0) { reservation.tickets_attributes.push({ event_price_category_id: evt_px_cat.id, booked }); } } return { reservation }; }; /** * Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object * @param reservation {Object} as returned by mkReservation() * @param coupon {Object} Coupon as returned from the API * @param paymentMethod {string} 'card' | '' * @return {ShoppingCart} */ const mkCartItems = function (reservation, coupon, paymentMethod = '') { return { customer_id: $scope.ctrl.member.id, items: [reservation], coupon_code: ((coupon ? coupon.code : undefined)), payment_method: paymentMethod, }; }; /** * Set the current reservation to the default values. This implies the reservation form to be hidden. */ const resetEventReserve = function () { if ($scope.event) { $scope.reserve = { nbPlaces: { normal: __range__(0, $scope.event.nb_free_places, true) }, nbReservePlaces: 0, tickets: {}, toReserve: false, amountTotal: 0, totalSeats: 0 }; for (let evt_px_cat of Array.from($scope.event.event_price_categories_attributes)) { $scope.reserve.nbPlaces[evt_px_cat.id] = __range__(0, $scope.event.nb_free_places, true); $scope.reserve.tickets[evt_px_cat.id] = 0; } return $scope.event.offered = false; } }; /** * Open a modal window which trigger the stripe payment process * @param reservation {Object} to book */ const payOnline = function (reservation) { // check that the online payment is enabled if (settingsPromise.online_payment_module !== 'true') { growl.error(_t('app.shared.cart.online_payment_disabled')); } else { $scope.toggleOnlinePaymentModal(() => { $scope.onlinePayment.cartItems = mkCartItems(reservation, $scope.coupon.applied, 'card'); }); } }; /** * Open a modal window which trigger the local payment process * @param reservation {Object} to book */ const payOnSite = function (reservation) { $uibModal.open({ templateUrl: '/shared/valid_reservation_modal.html', size: 'sm', resolve: { reservation () { return reservation; }, price () { return Price.compute(mkCartItems(reservation, $scope.coupon.applied)).$promise; }, wallet () { return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }).$promise; }, coupon () { return $scope.coupon.applied; }, cartItems () { return mkCartItems(reservation, $scope.coupon.applied); }, event () { return $scope.event; } }, controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'LocalPayment', 'wallet', 'helpers', '$filter', 'coupon', 'cartItems', 'event', function ($scope, $uibModalInstance, $state, reservation, price, Auth, LocalPayment, wallet, helpers, $filter, coupon, cartItems, event) { // User's wallet amount $scope.wallet = wallet; // Price $scope.price = price.price; // Cart items $scope.cartItems = cartItems; // price to pay $scope.amount = helpers.getAmountToPay(price.price, wallet.amount); // Reservation $scope.reservation = reservation; // the event $scope.bookedEvent = event; // Used in wallet info template to interpolate some translations $scope.numberFilter = $filter('number'); // Button label if ($scope.amount > 0) { $scope.validButtonName = _t('app.public.events_show.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }); } else { if ((price.price > 0) && ($scope.walletAmount === 0)) { $scope.validButtonName = _t('app.public.events_show.confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) }); } else { $scope.validButtonName = _t('app.shared.buttons.confirm'); } } // Callback to validate the payment $scope.ok = function () { $scope.attempting = true; return LocalPayment.confirm(cartItems, function (reservation) { $uibModalInstance.close(reservation); return $scope.attempting = true; } , function (response) { $scope.alerts = []; angular.forEach(response, function (v, k) { angular.forEach(v, function (err) { $scope.alerts.push({ msg: k + ': ' + err, type: 'danger' }); }); }); return $scope.attempting = false; }); }; // Callback to cancel the payment return $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; } ] }) .result['finally'](null).then(function (reservation) { afterPayment(reservation); }); }; /** * What to do after the payment was successful * @param invoice {Object} the invoice for the booked reservation */ const afterPayment = function (invoice) { Reservation.get({ id: invoice.main_object.id }, function (reservation) { $scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats; $scope.reservations.push(reservation); }); resetEventReserve(); $scope.reserveSuccess = true; $scope.coupon.applied = null; if ($scope.currentUser.role === 'admin') { return $scope.ctrl.member = null; } }; /** * Find user's reservations, the same date at the same time, with event */ const findReservationsAtSameTime = function () { let sameTimeReservations = [ 'training_reservations', 'machine_reservations', 'space_reservations', 'events_reservations' ].map(function(k) { return _.filter($scope.ctrl.member[k], function(r) { if (r.reservable_type === 'Event' && r.reservable.id === $scope.event.id) { return false; } return moment($scope.event.start_time).isSame(r.start_at) || (moment($scope.event.end_time).isAfter(r.start_at) && moment($scope.event.end_time).isBefore(r.end_at)) || (moment($scope.event.start_time).isAfter(r.start_at) && moment($scope.event.start_time).isBefore(r.end_at)) || (moment($scope.event.start_time).isBefore(r.start_at) && moment($scope.event.end_time).isAfter(r.end_at)); }); }); return _.union.apply(null, sameTimeReservations); }; /** * A modal for show reservations the same date at the same time * * @param sameTimeReservations {Array} reservations the same date at the same time * @param callback {function} callback will invoke when user confirm */ const showReserveSlotSameTimeModal = function(sameTimeReservations, callback) { const modalInstance = $uibModal.open({ animation: true, templateUrl: '/shared/_reserve_slot_same_time.html', size: 'md', controller: 'ReserveSlotSameTimeController', resolve: { sameTimeReservations: function() { return sameTimeReservations; }, bookOverlappingSlotsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'book_overlapping_slots' }).$promise; }] } }); modalInstance.result.then(callback); }; // !!! MUST BE CALLED AT THE END of the controller return initialize(); } ]); function __range__ (left, right, inclusive) { let range = []; let ascending = left < right; let end = !inclusive ? right : ascending ? right + 1 : right - 1; for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { range.push(i); } return range; } /** * Controller used in the event deletion modal window */ Application.Controllers.controller('DeleteRecurrentEventController', ['$scope', '$uibModalInstance', 'Event', 'eventPromise', 'growl', '_t', function ($scope, $uibModalInstance, Event, eventPromise, growl, _t) { // is the current event (to be deleted) recurrent? $scope.isRecurrent = eventPromise.recurrence_events.length > 0; // with recurrent slots: how many slots should we delete? $scope.deleteMode = 'single'; /** * Confirmation callback */ $scope.ok = function () { const { id, start_at, end_at } = eventPromise; // the admin has confirmed, delete the slot Event.delete( { id, mode: $scope.deleteMode }, function (res) { // delete success if (res.deleted > 1) { growl.success(_t( 'app.public.events_show.events_deleted', {COUNT: res.deleted - 1} )); } else { growl.success(_t( 'app.public.events_show.event_successfully_deleted' )); } $uibModalInstance.close({ status: 'success', events: res.details.map(function (d) { return d.event.id }) }); }, function (res) { // not everything was deleted const { data } = res; if (data.total > 1) { growl.warning(_t( 'app.public.events_show.events_not_deleted', {TOTAL: data.total, COUNT: data.total - data.deleted} )); } else { growl.error(_t( 'app.public.events_show.unable_to_delete_the_event' )); } $uibModalInstance.close({ status: 'failed', availabilities: data.details.filter(function (d) { return d.status }).map(function (d) { return d.event.id }) }); }); } /** * Cancellation callback */ $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); } } ]);