diff --git a/CHANGELOG.md b/CHANGELOG.md index 57ebfbbb7..81396d814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog Fab Manager +- Ability to configure allowing or preventing member book a machine/formation/event slot if he already have a reservation the same day at the same time - Ability to create and delete periodic calendar availabilities (recurrence) - Ability to fully customize the home page - Automated setup assistant @@ -45,6 +46,7 @@ - [TODO DEPLOY] add the `PHONE_REQUIRED` environment variable (see [doc/environment.md](doc/environment.md#PHONE_REQUIRED) for configuration details) - [TODO DEPLOY] add the `EVENTS_IN_CALENDAR` environment variable (see [doc/environment.md](doc/environment.md#EVENTS_IN_CALENDAR) for configuration details) - [TODO DEPLOY] add the `USER_CONFIRMATION_NEEDED_TO_SIGN_IN` environment variable (see [doc/environment.md](doc/environment.md#USER_CONFIRMATION_NEEDED_TO_SIGN_IN) for configuration details) +- [TODO DEPLOY] add the `BOOK_SLOT_AT_SAME_TIME` environment variable (see [doc/environment.md](doc/environment.md#BOOK_SLOT_AT_SAME_TIME) for configuration details) - [TODO DEPLOY] -> (only dev) `bundle install && yarn install` - [TODO DEPLOY] `rake db:migrate && rake db:seed` - [TODO DEPLOY] `rake fablab:fix:name_stylesheet` @@ -140,7 +142,7 @@ ## v4.0.4 2019 August 14 - Fix a bug: #140 VAT rate is erroneous in invoices. - Note: this bug was introduced in v4.0.3 and requires (if you are on v4.0.3) to regenerate the invoices since August 1st (if + Note: this bug was introduced in v4.0.3 and requires (if you are on v4.0.3) to regenerate the invoices since August 1st (if - [TODO DEPLOY] `rake fablab:maintenance:regenerate_invoices[2019,8]` ## v4.0.3 2019 August 01 @@ -184,7 +186,7 @@ - Refactored user's profile to keep invoicing data after an user was deleted - Refactored user's profile to keep statistical data after an user was deleted - Ability to delete an user (fixes #129 and #120) -- Ask user acceptance before deposing analytics cookies +- Ask user acceptance before deposing analytics cookies - Fix a bug: (spanish) some translations are not loaded correctly - Fix a bug: some users may not appear in the admin's general listing - Fix a bug: Availabilities export report an erroneous number of reservations for machine availabilities (#131) @@ -381,8 +383,8 @@ - Fix a security issue: sprockets < 2.12.5 has a security vulnerability as described in [CVE-2018-3760](https://nvd.nist.gov/vuln/detail/CVE-2018-3760) - Ensure elasticSearch indices are started with green status on new installations - Refactored User.to_json to remove code duplication -- Fixed syntax and typos in README -- [TODO DEPLOY] **IMPORTANT** Please read [elastic_upgrade.md](doc/elastic_upgrade.md) for instructions on upgrading ElasticSearch. +- Fixed syntax and typos in README +- [TODO DEPLOY] **IMPORTANT** Please read [elastic_upgrade.md](doc/elastic_upgrade.md) for instructions on upgrading ElasticSearch. - [TODO DEPLOY] `rake fablab:fix:categories_slugs` - [TODO DEPLOY] -> (only dev) `bundle install` - [TODO DEPLOY] `rake db:seed` @@ -395,7 +397,7 @@ - Set Stripe API version, all fab-managers has to use this version because codebase relies on it - Fix a security issue: OmniAuth < 1.3.2 has a security vulnerability described in [CVE-2017-18076](https://nvd.nist.gov/vuln/detail/CVE-2017-18076) - Fix a security issue: rack-protection < 1.5.5 has a security vulnerability described in [CVE-2018-1000119](https://nvd.nist.gov/vuln/detail/CVE-2018-1000119) -- Fix a security issue: http gem < 0.7.3 has a security vulnerability described in [CVE-2015-1828](https://nvd.nist.gov/vuln/detail/CVE-2015-1828), updates twitter gem as a dependency +- Fix a security issue: http gem < 0.7.3 has a security vulnerability described in [CVE-2015-1828](https://nvd.nist.gov/vuln/detail/CVE-2015-1828), updates twitter gem as a dependency ## v2.6.3 2018 January 2 @@ -451,12 +453,12 @@ ## v2.5.13 2017 September 11 -- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with recursive events which the initial event was deleted +- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with recursive events which the initial event was deleted ## v2.5.12 2017 September 11 - Fix a bug: Long words overflow from homepage's events blocks -- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with non-recursive events +- Fix a bug: ActiveRecord::RecordNotFound when running rake task fix:recursive_events_over_DST with non-recursive events ## v2.5.11 2017 September 7 diff --git a/app/assets/javascripts/controllers/events.js.erb b/app/assets/javascripts/controllers/events.js.erb index 1de803312..4e1717060 100644 --- a/app/assets/javascripts/controllers/events.js.erb +++ b/app/assets/javascripts/controllers/events.js.erb @@ -246,13 +246,34 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' $scope.reserveSuccess = false; if (!$scope.isAuthenticated()) { return $scope.login(null, function (user) { - $scope.reserve.toReserve = !$scope.reserve.toReserve; if (user.role !== 'admin') { return $scope.ctrl.member = user; } + const sameTimeReservations = findReservationsAtSameTime(); + if (sameTimeReservations.length > 0) { + showReserveSlotSameTimeModal(sameTimeReservations, function(res) { + return $scope.reserve.toReserve = !$scope.reserve.toReserve; + }); + } else { + return $scope.reserve.toReserve = !$scope.reserve.toReserve; + } }); } else { - return $scope.reserve.toReserve = !$scope.reserve.toReserve; + if ($scope.currentUser.role === 'admin') { + return $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) { + return $scope.reserve.toReserve = !$scope.reserve.toReserve; + }); + } else { + return $scope.reserve.toReserve = !$scope.reserve.toReserve; + } + }); + } } } }; @@ -798,6 +819,49 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' } }; + /** + * find user reservations the same date at the same time with event + * + */ + var findReservationsAtSameTime = function () { + let sameTimeReservations = [ + 'training_reservations', + 'machine_reservations', + 'space_reservations', + 'events_reservations' + ].map(k => { + return _.filter($scope.ctrl.member[k], 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(...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 + */ + var showReserveSlotSameTimeModal = function(sameTimeReservations, callback) { + const modalInstance = $uibModal.open({ + animation: true, + templateUrl: '<%= asset_path "shared/_reserve_slot_same_time.html" %>', + size: 'md', + controller: 'ReserveSlotSameTimeController', + resolve: { + sameTimeReservations: function() { return sameTimeReservations; }, + } + }); + modalInstance.result.then(callback); + }; + // !!! MUST BE CALLED AT THE END of the controller return initialize(); } @@ -879,4 +943,3 @@ Application.Controllers.controller('DeleteRecurrentEventController', ['$scope', } } ]); - diff --git a/app/assets/javascripts/controllers/machines.js.erb b/app/assets/javascripts/controllers/machines.js.erb index 733b1dca1..ea6f3c9f7 100644 --- a/app/assets/javascripts/controllers/machines.js.erb +++ b/app/assets/javascripts/controllers/machines.js.erb @@ -579,7 +579,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat }); if ($scope.currentUser.role !== 'admin') { - return $scope.ctrl.member = $scope.currentUser; + return Member.get({ id: $scope.currentUser.id }, function (member) { $scope.ctrl.member = member; }); } }; diff --git a/app/assets/javascripts/directives/cart.js.erb b/app/assets/javascripts/directives/cart.js.erb index ff2c4a810..816253088 100644 --- a/app/assets/javascripts/directives/cart.js.erb +++ b/app/assets/javascripts/directives/cart.js.erb @@ -10,8 +10,8 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'helpers', '_t', - function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, helpers, _t) { +Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'helpers', '_t', '$uibModal', + function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, helpers, _t, $uibModal) { return ({ restrict: 'E', scope: { @@ -72,8 +72,38 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', * @param slot {Object} fullCalendar event object */ $scope.validateSlot = function (slot) { - slot.isValid = true; - return updateCartPrice(); + let sameTimeReservations = [ + 'training_reservations', + 'machine_reservations', + 'space_reservations', + 'events_reservations' + ].map(k => { + return _.filter($scope.user[k], r => { + return slot.start.isSame(r.start_at) || + (slot.end.isAfter(r.start_at) && slot.end.isBefore(r.end_at)) || + (slot.start.isAfter(r.start_at) && slot.start.isBefore(r.end_at)) || + (slot.start.isBefore(r.start_at) && slot.end.isAfter(r.end_at)); + }); + }); + sameTimeReservations = _.union(...sameTimeReservations); + if (sameTimeReservations.length > 0) { + const modalInstance = $uibModal.open({ + animation: true, + templateUrl: '<%= asset_path "shared/_reserve_slot_same_time.html" %>', + size: 'md', + controller: 'ReserveSlotSameTimeController', + resolve: { + sameTimeReservations: function() { return sameTimeReservations; }, + } + }); + modalInstance.result.then(function(res) { + slot.isValid = true; + return updateCartPrice(); + }); + } else { + slot.isValid = true; + return updateCartPrice(); + } }; /** @@ -614,3 +644,25 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs', }); } ]); + +/** + * Controller of modal for show reservations the same date at the same time + */ +Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '$uibModalInstance', 'sameTimeReservations', 'growl', '_t', + function ($scope, $uibModalInstance, sameTimeReservations, growl, _t) { + $scope.sameTimeReservations = sameTimeReservations; + $scope.bookSlotAtSameTime = Fablab.bookSlotAtSameTime; + /** + * Confirmation callback + */ + $scope.ok = function () { + $uibModalInstance.close({}); + } + /** + * Cancellation callback + */ + $scope.cancel = function () { + $uibModalInstance.dismiss('cancel'); + } + } +]); diff --git a/app/assets/templates/shared/_reserve_slot_same_time.html.erb b/app/assets/templates/shared/_reserve_slot_same_time.html.erb new file mode 100644 index 000000000..16f8b9e00 --- /dev/null +++ b/app/assets/templates/shared/_reserve_slot_same_time.html.erb @@ -0,0 +1,18 @@ +
{{ 'app.shared.cart.do_you_really_want_to_book_slot_at_same_time' }}
+{{ 'app.shared.cart.unable_to_book_slot_because_really_have_reservation_at_same_time' }}
+