'use strict' Application.Controllers.controller "EventsController", ["$scope", "$state", 'Event', 'categoriesPromise', 'themesPromise', 'ageRangesPromise' , ($scope, $state, Event, categoriesPromise, themesPromise, ageRangesPromise) -> ### PUBLIC SCOPE ### ## 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 ## # Adds a resultset of events to the bottom of the page, grouped by month ## $scope.loadMoreEvents = -> Event.query Object.assign({page: $scope.page}, $scope.filters), (data) -> $scope.events = $scope.events.concat data groupEvents($scope.events) $scope.page += 1 if (!data[0] || data[0].nb_total_events <= $scope.events.length) $scope.noMoreResults = true ## # Callback to redirect the user to the specified event page # @param event {{id:number}} ## $scope.showEvent = (event) -> $state.go('app.public.events_show', {id: event.id}) ## # Callback to refresh the events list according to the filters set ## $scope.filterEvents = -> # reinitialize results datasets $scope.page = 1 $scope.eventsGroupByMonth = {} $scope.events = [] $scope.monthOrder = [] $scope.noMoreResults = false # run a search query Event.query Object.assign({page: $scope.page}, $scope.filters), (data) -> $scope.events = data groupEvents(data) $scope.page += 1 if (!data[0] || data[0].nb_total_events <= $scope.events.length) $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 = (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 ## initialize = -> $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 {Array} Events retrived from the API ## groupEvents = (events) -> if events.length > 0 eventsGroupedByMonth = _.groupBy(events, (obj) -> _.map ['month', 'year'], (key, value) -> obj[key] ) $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth) monthsOrder = _.sortBy _.keys($scope.eventsGroupByMonth), (k)-> monthYearArray = k.split(',') date = new Date() date.setMonth(monthYearArray[0]) date.setYear(monthYearArray[1]) return -date.getTime() $scope.monthOrder = monthsOrder ## !!! MUST BE CALLED AT THE END of the controller initialize() ] Application.Controllers.controller "ShowEventController", ["$scope", "$state", "$stateParams", "Event", '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'priceCategoriesPromise', ($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, growl, _t, Wallet, helpers, priceCategoriesPromise) -> ### PUBLIC SCOPE ### ## reservations for the currently shown event $scope.reservations = [] ## user to deal with $scope.ctrl = member: {} ## parameters for a new reservation $scope.reserve = nbPlaces: normal: [] nbReservePlaces: 0 tickets: {} toReserve: false amountTotal : 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 ## List of price categories for the events $scope.priceCategories = priceCategoriesPromise ## # Callback to delete the provided event (admins only) # @param event {$resource} angular's Event $resource ## $scope.deleteEvent = (event) -> event.$delete -> $state.go('app.public.events_list') ## # Callback to call when the number of tickets to book changes in the current booking ## $scope.changeNbPlaces = -> # compute the total remaing places remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces for ticket of $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 availables seats for full price tickets fullPriceRemains = $scope.reserve.nbReservePlaces + remain $scope.reserve.nbPlaces.normal = [0..fullPriceRemains] # update the available seats for other prices tickets for key of $scope.reserve.nbPlaces if key != 'normal' priceRemain = $scope.reserve.tickets[key] + remain $scope.reserve.nbPlaces[key] = [0..priceRemain] # recompute the total price $scope.computeEventAmount() ## # Callback to reset the current reservation parameters # @param e {Object} see https://docs.angularjs.org/guide/expression#-event- ## $scope.cancelReserve = (e)-> e.preventDefault() resetEventReserve() ## # Callback to allow the user to set the details for his reservation ## $scope.reserveEvent = -> if $scope.event.nb_total_places > 0 $scope.reserveSuccess = false if !$scope.isAuthenticated() $scope.login null, (user)-> $scope.reserve.toReserve = !$scope.reserve.toReserve if user.role isnt 'admin' $scope.ctrl.member = user 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 = -> resetEventReserve() $scope.reserveSuccess = false if $scope.ctrl.member Member.get {id: $scope.ctrl.member.id}, (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 = -> # first, we check that a user was selected if Object.keys($scope.ctrl.member).length > 0 reservation = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) -> amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount) if $scope.currentUser.role isnt 'admin' and amountToPay > 0 payByStripe(reservation) else if $scope.currentUser.role is 'admin' or amountToPay is 0 payOnSite(reservation) else # otherwise we alert, this error musn't occur when the current user is not admin growl.error(_t('please_select_a_member_first')) ## # Callback to validate the booking of a free event ## $scope.validReserveEvent = -> reservation = user_id: $scope.ctrl.member.id reservable_id: $scope.event.id reservable_type: 'Event' slots_attributes: [] nb_reserve_places: $scope.reserve.nbReservePlaces tickets_attributes: [] # a single slot is used for events reservation.slots_attributes.push start_at: $scope.event.start_date end_at: $scope.event.end_date availability_id: $scope.event.availability.id # iterate over reservations per prices for price_id, seats of $scope.reserve.tickets reservation.tickets_attributes.push event_price_category_id: price_id booked: seats # set the attempting marker $scope.attempting = true # save the reservation to the API Reservation.save reservation: reservation, (reservation) -> # reservation successfull afterPayment(reservation) $scope.attempting = false , (response)-> # reservation failed $scope.alerts = [] $scope.alerts.push msg: response.data.card[0] type: 'danger' # unset the attempting marker $scope.attempting = false ## # 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 {{id:number, reservable_id:number, nb_reserve_places:number}} # @param e {Object} see https://docs.angularjs.org/guide/expression#-event- ## $scope.modifyReservation = (reservation, e)-> e.preventDefault() e.stopPropagation() index = $scope.reservations.indexOf(reservation) $uibModal.open templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>' resolve: event: -> $scope.event reservation: -> reservation controller: ['$scope', '$uibModalInstance', 'event', 'reservation', 'Reservation', ($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 e in event.recurrence_events if e.nb_free_places > reservation.total_booked_seats $scope.reservation.reservable_id = e.id break # Callback to validate the new reservation's date $scope.ok = -> eventToPlace = null angular.forEach event.recurrence_events, (e)-> if e.id is parseInt($scope.reservation.reservable_id, 10) eventToPlace = e $scope.reservation.slots[0].start_at = eventToPlace.start_date $scope.reservation.slots[0].end_at = eventToPlace.end_date $scope.reservation.slots[0].availability_id = eventToPlace.availability_id $scope.reservation.slots_attributes = $scope.reservation.slots $scope.attempting = true Reservation.update {id: reservation.id}, {reservation: $scope.reservation}, (reservation) -> $uibModalInstance.close(reservation) $scope.attempting = true , (response)-> $scope.alerts = [] angular.forEach response, (v, k)-> angular.forEach v, (err)-> $scope.alerts.push({msg: k+': '+err, type: 'danger'}) $scope.attempting = false # Callback to cancel the modification $scope.cancel = -> $uibModalInstance.dismiss('cancel') ] .result['finally'](null).then (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, (e)-> if e.id is parseInt(reservation.reservable.id, 10) 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 {{total_booked_seats:number}} ## $scope.reservationCanModify = (reservation)-> isAble = false angular.forEach $scope.event.recurrence_events, (e)-> isAble = true if e.nb_free_places > reservation.total_booked_seats isAble ## # Compute the total amount for the current reservation according to the previously set parameters # and assign the result in $scope.reserve.amountTotal ## $scope.computeEventAmount = -> # first we check that a user was selected if Object.keys($scope.ctrl.member).length > 0 r = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) Price.compute mkRequestParams(r, $scope.coupon.applied), (res) -> $scope.reserve.amountTotal = res.price else $scope.reserve.amountTotal = null ## # Return the URL allowing to share the current project on the Facebook social network ## $scope.shareOnFacebook = -> '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 = -> 'https://twitter.com/intent/tweet?url='+encodeURIComponent($state.href('app.public.events_show', {id: $scope.event.id}, {absolute: true})); ## # 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 = (category_id) -> for cat in $scope.priceCategories if cat.id == category_id return cat.conditions ### PRIVATE SCOPE ### ## # Kind of constructor: these actions will be realized first when the controller is loaded ## initialize = -> # set the controlled user as the current user if the current user is not an admin if $scope.currentUser if $scope.currentUser.role isnt 'admin' $scope.ctrl.member = $scope.currentUser # initialize the "reserve" object with the event's data resetEventReserve() # if non-admin, 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', (newValue, oldValue) -> unless newValue == null and oldValue == null $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) ## getReservations = (reservable_id, reservable_type, user_id)-> Reservation.query(reservable_id: reservable_id, reservable_type: reservable_type, user_id: user_id).$promise.then (reservations)-> $scope.reservations = reservations ## # Create an hash map implementing the Reservation specs # @param member {Object} User as retreived from the API: current user / selected user if current is admin # @param reserve {Object} Reservation parameters (places...) # @param event {Object} Current event # @return {{user_id:Number, reservable_id:Number, reservable_type:String, slots_attributes:Array, nb_reserve_places:Number, nb_reserve_reduced_places:Number}} ## mkReservation = (member, reserve, event) -> reservation = user_id: member.id reservable_id: event.id reservable_type: 'Event' slots_attributes: [] nb_reserve_places: reserve.nbReservePlaces tickets_attributes: [] reservation.slots_attributes.push start_at: event.start_date end_at: event.end_date availability_id: event.availability.id offered: event.offered || false for evt_px_cat in event.prices booked = reserve.tickets[evt_px_cat.id] if booked > 0 reservation.tickets_attributes.push event_price_category_id: evt_px_cat.id booked: booked 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 # @return {{reservation:Object, coupon_code:string}} ## mkRequestParams = (reservation, coupon) -> params = reservation: reservation coupon_code: (coupon.code if coupon) params ## # Set the current reservation to the default values. This implies to reservation form to be hidden. ## resetEventReserve = -> if $scope.event $scope.reserve = nbPlaces: normal: [0..$scope.event.nb_free_places] nbReservePlaces: 0 tickets: {} toReserve: false amountTotal : 0 totalSeats: 0 for evt_px_cat in $scope.event.prices $scope.reserve.nbPlaces[evt_px_cat.id] = [0..$scope.event.nb_free_places] $scope.reserve.tickets[evt_px_cat.id] = 0 $scope.event.offered = false ## # Open a modal window which trigger the stripe payment process # @param reservation {Object} to book ## payByStripe = (reservation) -> $uibModal.open templateUrl: '<%= asset_path "stripe/payment_modal.html" %>' size: 'md' resolve: reservation: -> reservation price: -> Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise wallet: -> Wallet.getWalletByUser({user_id: reservation.user_id}).$promise cgv: -> CustomAsset.get({name: 'cgv-file'}).$promise objectToPay: -> eventToReserve: $scope.event reserve: $scope.reserve member: $scope.ctrl.member coupon: -> $scope.coupon.applied controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$locale', '$filter', 'coupon', ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $locale, $filter, coupon) -> # user wallet amount $scope.walletAmount = wallet.amount # Price $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # CGV $scope.cgv = cgv.custom_asset # Reservation $scope.reservation = reservation $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM $scope.numberFilter = $filter('number') # Callback for the stripe payment authorization $scope.payment = (status, response) -> if response.error growl.error(response.error.message) else $scope.attempting = true $scope.reservation.card_token = response.id Reservation.save mkRequestParams($scope.reservation, coupon), (reservation) -> $uibModalInstance.close(reservation) , (response)-> $scope.alerts = [] $scope.alerts.push msg: response.data.card[0] type: 'danger' $scope.attempting = false ] .result['finally'](null).then (reservation)-> afterPayment(reservation) ## # Open a modal window which trigger the local payment process # @param reservation {Object} to book ## payOnSite = (reservation) -> $uibModal.open templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>' size: 'sm' resolve: reservation: -> reservation price: -> Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise wallet: -> Wallet.getWalletByUser({user_id: reservation.user_id}).$promise coupon: -> $scope.coupon.applied controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', '$locale', 'helpers', '$filter', 'coupon', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, $locale, helpers, $filter, coupon) -> # user wallet amount $scope.walletAmount = wallet.amount # Price $scope.price = price.price # price to pay $scope.amount = helpers.getAmountToPay(price.price, wallet.amount) # Reservation $scope.reservation = reservation $scope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM $scope.numberFilter = $filter('number') # Button label if $scope.amount > 0 $scope.validButtonName = _t('confirm_payment_of_html', {ROLE:$scope.currentUser.role, AMOUNT:$filter('currency')($scope.amount)}, "messageformat") else if price.price > 0 and $scope.walletAmount == 0 $scope.validButtonName = _t('confirm_payment_of_html', {ROLE:$scope.currentUser.role, AMOUNT:$filter('currency')(price.price)}, "messageformat") else $scope.validButtonName = _t('confirm') # Callback to validate the payment $scope.ok = -> $scope.attempting = true Reservation.save mkRequestParams($scope.reservation, coupon), (reservation) -> $uibModalInstance.close(reservation) $scope.attempting = true , (response)-> $scope.alerts = [] angular.forEach response, (v, k)-> angular.forEach v, (err)-> $scope.alerts.push msg: k+': '+err type: 'danger' $scope.attempting = false # Callback to cancel the payment $scope.cancel = -> $uibModalInstance.dismiss('cancel') ] .result['finally'](null).then (reservation)-> afterPayment(reservation) ## # What to do after the payment was successful # @param resveration {Object} booked reservation ## afterPayment = (reservation)-> $scope.event.nb_free_places = $scope.event.nb_free_places - reservation.nb_reserve_places - reservation.nb_reserve_reduced_places resetEventReserve() $scope.reserveSuccess = true $scope.reservations.push reservation if $scope.currentUser.role == 'admin' $scope.ctrl.member = null ## !!! MUST BE CALLED AT THE END of the controller initialize() ]