mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-26 20:54:21 +01:00
Ask for confirmation before booking a slot for a member without the required tag
This commit is contained in:
parent
47a0fca481
commit
5f7287cec7
@ -5,6 +5,7 @@
|
|||||||
- Interface to manage partners
|
- Interface to manage partners
|
||||||
- Ability to define, per availability, a custom duration for the reservation slots
|
- Ability to define, per availability, a custom duration for the reservation slots
|
||||||
- Ability to promote a user to a higher role (member > manager > admin)
|
- Ability to promote a user to a higher role (member > manager > admin)
|
||||||
|
- Ask for confirmation before booking a slot for a member without the required tag
|
||||||
- Corrected the documentation about BOOK_SLOT_AT_SAME_TIME
|
- Corrected the documentation about BOOK_SLOT_AT_SAME_TIME
|
||||||
- Auto-adjusts text colors based on the selected theme colors
|
- Auto-adjusts text colors based on the selected theme colors
|
||||||
- Fix a bug: unable to change group if the previous was deactivated
|
- Fix a bug: unable to change group if the previous was deactivated
|
||||||
|
@ -74,38 +74,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
|||||||
* @param slot {Object} fullCalendar event object
|
* @param slot {Object} fullCalendar event object
|
||||||
*/
|
*/
|
||||||
$scope.validateSlot = function (slot) {
|
$scope.validateSlot = function (slot) {
|
||||||
let sameTimeReservations = [
|
validateTags(slot, function () {
|
||||||
'training_reservations',
|
validateSameTimeReservations(slot, function () {
|
||||||
'machine_reservations',
|
|
||||||
'space_reservations',
|
|
||||||
'events_reservations'
|
|
||||||
].map(function (k) {
|
|
||||||
return _.filter($scope.user[k], function(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.apply(null, 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;
|
slot.isValid = true;
|
||||||
return updateCartPrice();
|
updateCartPrice();
|
||||||
});
|
})
|
||||||
} else {
|
})
|
||||||
slot.isValid = true;
|
|
||||||
return updateCartPrice();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -330,6 +304,74 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that the current slot is reserved by a member with an authorized tag. Admin and managers can overpass
|
||||||
|
* the mismatch.
|
||||||
|
* @param slot {Object} fullCalendar event object.
|
||||||
|
* @param callback {function}
|
||||||
|
*/
|
||||||
|
const validateTags = function (slot, callback) {
|
||||||
|
const interTags = _.intersection.apply(null, [slot.tag_ids, $scope.user.tag_ids]);
|
||||||
|
if (slot.tag_ids.length === 0 || interTags.length > 0) {
|
||||||
|
if (typeof callback === 'function') callback();
|
||||||
|
} else {
|
||||||
|
// ask confirmation
|
||||||
|
const modalInstance = $uibModal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '<%= asset_path "shared/_reserve_slot_tags_mismatch.html" %>',
|
||||||
|
size: 'md',
|
||||||
|
controller: 'ReserveSlotTagsMismatchController',
|
||||||
|
resolve: {
|
||||||
|
slotTags: function() { return slot.tags; },
|
||||||
|
userTags: function () { return $scope.user.tags; },
|
||||||
|
userName: function () { return $scope.user.name; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
modalInstance.result.then(function(res) {
|
||||||
|
if (typeof callback === 'function') callback(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that no other reservations were made that conflict the current slot and alert the user about the conflict.
|
||||||
|
* If the user is an administrator or a manager, he can overpass the conflict.
|
||||||
|
* @param slot {Object} fullCalendar event object.
|
||||||
|
* @param callback {function}
|
||||||
|
*/
|
||||||
|
const validateSameTimeReservations = function (slot, callback) {
|
||||||
|
let sameTimeReservations = [
|
||||||
|
'training_reservations',
|
||||||
|
'machine_reservations',
|
||||||
|
'space_reservations',
|
||||||
|
'events_reservations'
|
||||||
|
].map(function (k) {
|
||||||
|
return _.filter($scope.user[k], function(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.apply(null, 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) {
|
||||||
|
if (typeof callback === 'function') callback(res);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (typeof callback === 'function') callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback triggered when the selected slot changed
|
* Callback triggered when the selected slot changed
|
||||||
*/
|
*/
|
||||||
@ -504,12 +546,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
|||||||
return Price.compute(mkRequestParams(r, $scope.coupon.applied), function (res) {
|
return Price.compute(mkRequestParams(r, $scope.coupon.applied), function (res) {
|
||||||
$scope.amountTotal = res.price;
|
$scope.amountTotal = res.price;
|
||||||
$scope.totalNoCoupon = res.price_without_coupon;
|
$scope.totalNoCoupon = res.price_without_coupon;
|
||||||
return setSlotsDetails(res.details);
|
setSlotsDetails(res.details);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// otherwise we alert, this error musn't occur when the current user is not admin
|
// otherwise we alert, this error musn't occur when the current user is not admin
|
||||||
growl.warning(_t('app.shared.cart.please_select_a_member_first'));
|
growl.warning(_t('app.shared.cart.please_select_a_member_first'));
|
||||||
return $scope.amountTotal = null;
|
$scope.amountTotal = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -518,7 +560,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
|||||||
angular.forEach(details.slots, function (s) {
|
angular.forEach(details.slots, function (s) {
|
||||||
if (moment(s.start_at).isSame(slot.start)) {
|
if (moment(s.start_at).isSame(slot.start)) {
|
||||||
slot.promo = s.promo;
|
slot.promo = s.promo;
|
||||||
return slot.price = s.price;
|
slot.price = s.price;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -739,10 +781,10 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller of modal for show reservations the same date at the same time
|
* Controller of the modal showing the reservations the same date at the same time
|
||||||
*/
|
*/
|
||||||
Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '$uibModalInstance', 'AuthService', 'sameTimeReservations', 'growl', '_t',
|
Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '$uibModalInstance', 'AuthService', 'sameTimeReservations',
|
||||||
function ($scope, $uibModalInstance, AuthService, sameTimeReservations, growl, _t) {
|
function ($scope, $uibModalInstance, AuthService, sameTimeReservations) {
|
||||||
$scope.sameTimeReservations = sameTimeReservations;
|
$scope.sameTimeReservations = sameTimeReservations;
|
||||||
$scope.bookSlotAtSameTime = Fablab.bookSlotAtSameTime;
|
$scope.bookSlotAtSameTime = Fablab.bookSlotAtSameTime;
|
||||||
$scope.isAuthorized = AuthService.isAuthorized;
|
$scope.isAuthorized = AuthService.isAuthorized;
|
||||||
@ -761,6 +803,31 @@ Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller of the modal showing the slot tags
|
||||||
|
*/
|
||||||
|
Application.Controllers.controller('ReserveSlotTagsMismatchController', ['$scope', '$uibModalInstance', 'AuthService', 'slotTags', 'userTags', 'userName',
|
||||||
|
function ($scope, $uibModalInstance, AuthService, slotTags, userTags, userName) {
|
||||||
|
$scope.slotTags = slotTags;
|
||||||
|
$scope.userTags = userTags;
|
||||||
|
$scope.userName = userName;
|
||||||
|
$scope.isAuthorized = AuthService.isAuthorized;
|
||||||
|
/**
|
||||||
|
* Confirmation callback
|
||||||
|
*/
|
||||||
|
$scope.ok = function () {
|
||||||
|
$uibModalInstance.close({});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Cancellation callback
|
||||||
|
*/
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller used to alert admin reserve slot without plan
|
* Controller used to alert admin reserve slot without plan
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
|
||||||
|
<h1 translate>{{ 'app.shared.cart.tags_mismatch' }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p ng-show="isAuthorized(['admin', 'manager'])" translate translate-values="{USER: userName}">{{ 'app.shared.cart.confirm_book_slot_tags_mismatch' }}</p>
|
||||||
|
<p ng-hide="isAuthorized(['admin', 'manager'])" translate>{{ 'app.shared.cart.unable_to_book_slot_tags_mismatch' }}</p>
|
||||||
|
<h3 translate>{{ 'app.shared.cart.slot_tags' }}</h3>
|
||||||
|
<ul class="list-unstyled" ng-show="slotTags.length > 0">
|
||||||
|
<li ng-repeat="t in slotTags">
|
||||||
|
<span class="label label-default">{{t.name}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<span ng-hide="slotTags.length > 0" translate>{{ 'app.shared.cart.no_tags' }}</span>
|
||||||
|
<h3 translate>{{ 'app.shared.cart.user_tags' }}</h3>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li ng-repeat="t in userTags" ng-show="userTags.length > 0">
|
||||||
|
<span class="label label-default">{{t.name}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<span ng-hide="userTags.length > 0" translate>{{ 'app.shared.cart.no_tags' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button ng-if="isAuthorized(['admin', 'manager'])" class="btn btn-info" ng-click="ok()" translate>{{ 'app.shared.buttons.confirm' }}</button>
|
||||||
|
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||||
|
</div>
|
@ -425,6 +425,12 @@ en:
|
|||||||
slot_at_same_time: "Conflict with others reservations"
|
slot_at_same_time: "Conflict with others reservations"
|
||||||
do_you_really_want_to_book_slot_at_same_time: "Do you really want to book this slot? Other bookings take place at the same time"
|
do_you_really_want_to_book_slot_at_same_time: "Do you really want to book this slot? Other bookings take place at the same time"
|
||||||
unable_to_book_slot_because_really_have_reservation_at_same_time: "Unable to book this slot because the following reservation occurs at the same time."
|
unable_to_book_slot_because_really_have_reservation_at_same_time: "Unable to book this slot because the following reservation occurs at the same time."
|
||||||
|
tags_mismatch: "Tags mismatch"
|
||||||
|
confirm_book_slot_tags_mismatch: "Do you really want to book this slot? {USER} does not have any of the required tags."
|
||||||
|
unable_to_book_slot_tags_mismatch: "Unable to book this slot because you don't have any of the required tags."
|
||||||
|
slot_tags: "Slot tags"
|
||||||
|
user_tags: "User tags"
|
||||||
|
no_tags: "No tags"
|
||||||
# feature-tour modal
|
# feature-tour modal
|
||||||
tour:
|
tour:
|
||||||
previous: "Previous"
|
previous: "Previous"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user