1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

fix machine reservation calendar

This commit is contained in:
Sylvain 2020-09-30 16:32:01 +02:00
parent f6183dbafb
commit 5a0185dd48
3 changed files with 100 additions and 88 deletions

View File

@ -495,7 +495,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = FREE_SLOT_BORDER_COLOR;
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_reserve');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
@ -506,11 +506,11 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
slot.borderColor = FREE_SLOT_BORDER_COLOR;
slot.title = '';
slot.isValid = false;
slot.id = null;
slot.slot_id = null;
slot.is_reserved = false;
slot.can_modify = false;
slot.offered = false;
return updateCalendar();
updateEvents(slot);
};
/**
@ -524,7 +524,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee';
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_change');
return updateCalendar();
updateEvents($scope.selectedEvent);
};
/**
@ -534,34 +534,44 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.title = '';
updateEvents($scope.events.placable);
}
if (!$scope.events.placable || ($scope.events.placable._id !== $scope.selectedEvent._id)) {
$scope.selectedEvent.backgroundColor = '#bbb';
$scope.selectedEvent.title = _t('app.logged.machines_reserve.i_shift');
updateEvents($scope.selectedEvent);
}
return updateCalendar();
};
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyMachineSlot = function () {
$scope.events.placable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = $scope.events.modifiable.borderColor;
$scope.events.placable.id = $scope.events.modifiable.id;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
$scope.events.placable.user = angular.copy($scope.events.modifiable.user);
const save = {
slotId: $scope.events.modifiable.slot_id,
borderColor: $scope.events.modifiable.borderColor,
user: angular.copy($scope.events.modifiable.user),
title: $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available')
};
$scope.events.modifiable.backgroundColor = 'white';
$scope.events.modifiable.title = '';
$scope.events.modifiable.borderColor = FREE_SLOT_BORDER_COLOR;
$scope.events.modifiable.id = null;
$scope.events.modifiable.slot_id = null;
$scope.events.modifiable.is_reserved = false;
$scope.events.modifiable.can_modify = false;
updateEvents($scope.events.modifiable);
return updateCalendar();
$scope.events.placable.title = save.title;
$scope.events.placable.backgroundColor = 'white';
$scope.events.placable.borderColor = save.borderColor;
$scope.events.placable.slot_id = save.slotId;
$scope.events.placable.is_reserved = true;
$scope.events.placable.can_modify = true;
$scope.events.placable.user = angular.copy(save.user);
updateEvents($scope.events.placable);
refetchCalendar();
};
/**
@ -575,7 +585,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.events.modifiable.title = $scope.currentUser.id === $scope.events.modifiable.user.id ? _t('app.logged.machines_reserve.i_ve_reserved') : _t('app.logged.machines_reserve.not_available');
$scope.events.modifiable.backgroundColor = 'white';
return updateCalendar();
updateEvents($scope.events.placable, $scope.events.modifiable);
};
/**
@ -648,7 +658,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.selectedPlan = null;
}
return refetchCalendar();
refetchCalendar();
};
/**
@ -662,11 +672,13 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
Availability.machine({ machineId: $stateParams.id }, function (availabilities) {
$scope.eventSources.push({
events: availabilities,
textColor: 'black'
});
$scope.eventSources.push({
events: function (start, end, timezone, callback) {
Availability.machine({ machineId: $stateParams.id }, function (availabilities) {
callback(availabilities);
});
},
textColor: 'black'
});
if ($scope.currentUser.role !== 'admin') {
@ -680,7 +692,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
* if it's too late).
*/
var calendarEventClickCb = function (event, jsEvent, view) {
const calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event;
return $scope.selectionTime = new Date();
};
@ -690,7 +702,7 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element) {
const eventRenderCb = function (event, element) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = '';
for (let tag of Array.from(event.tags)) {
@ -708,28 +720,30 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
* @param reservation {Object}
* @param user {Object} user associated with the slot
*/
var updateMachineSlot = function (slot, reservation, user) {
const updateMachineSlot = function (slot, reservation, user) {
angular.forEach(reservation.slots, function (s) {
if (slot.start.isSame(s.start_at)) {
slot.id = s.id;
return slot.user = user;
slot.slot_id = s.id;
slot.user = user;
}
});
updateEvents(slot);
};
/**
* Update the calendar's display to render the new attributes of the events
* @param events Object[] events to update in full-calendar
*/
var updateCalendar = function () { uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents'); };
const updateEvents = function (...events) {
const realEvents = events.filter(e => !_.isNil(e));
uiCalendarConfig.calendars.calendar.fullCalendar('updateEvents', realEvents);
};
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = function () {
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents');
});
const refetchCalendar = function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents');
};
// !!! MUST BE CALLED AT THE END of the controller

View File

@ -10,7 +10,7 @@
* 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', 'AuthService', 'helpers', '_t',
Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs', 'growl', 'Auth', 'Price', 'Wallet', 'CustomAsset', 'Slot', 'AuthService', 'helpers', '_t',
function ($rootScope, $uibModal, dialogs, growl, Auth, Price, Wallet, CustomAsset, Slot, AuthService, helpers, _t) {
return ({
restrict: 'E',
@ -78,8 +78,8 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
validateSameTimeReservations(slot, function () {
slot.isValid = true;
updateCartPrice();
})
})
});
});
};
/**
@ -121,15 +121,15 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// first, we ensure that a user was selected (admin/manager) or logged (member)
const isSelectedUser = Object.keys($scope.user).length > 0;
// all slots are in future
const areFutureSlots = _.every($scope.events.reserved, function(s) {
const areFutureSlots = _.every($scope.events.reserved, function (s) {
return s.start.isAfter();
});
if (isSelectedUser && areFutureSlots) {
return $scope.modePlans = true;
} else if (!isSelectedUser){
} else if (!isSelectedUser) {
// otherwise we alert, this error musn't occur when the current user hasn't the admin role
return growl.error(_t('app.shared.cart.please_select_a_member_first'));
} else if (!areFutureSlots){
} else if (!areFutureSlots) {
return growl.error(_t('app.shared.cart.unable_to_select_plan_if_slots_in_the_past'));
}
};
@ -140,7 +140,6 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.payCart = function () {
// first, we check that a user was selected
if (Object.keys($scope.user).length > 0) {
// check selected user has a subscription, if any slot is restricted for subscriptions
const slotValidations = [];
let slotNotValid;
@ -148,16 +147,16 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.events.reserved.forEach(function (slot) {
if (slot.plan_ids.length > 0) {
if (
($scope.selectedPlan && _.include(slot.plan_ids, $scope.selectedPlan.id)) ||
($scope.user.subscribed_plan && _.include(slot.plan_ids, $scope.user.subscribed_plan.id))
($scope.selectedPlan && _.includes(slot.plan_ids, $scope.selectedPlan.id)) ||
($scope.user.subscribed_plan && _.includes(slot.plan_ids, $scope.user.subscribed_plan.id))
) {
slotValidations.push(true);
} else {
slotNotValid = slot;
if ($scope.selectedPlan && !_.include(slot.plan_ids, $scope.selectedPlan.id)) {
if ($scope.selectedPlan && !_.includes(slot.plan_ids, $scope.selectedPlan.id)) {
slotNotValidError = 'selectedPlanError';
}
if ($scope.user.subscribed_plan && !_.include(slot.plan_ids, $scope.user.subscribed_plan.id)) {
if ($scope.user.subscribed_plan && !_.includes(slot.plan_ids, $scope.user.subscribed_plan.id)) {
slotNotValidError = 'userPlanError';
}
if (!$scope.selectedPlan || !$scope.user.subscribed_plan) {
@ -178,11 +177,11 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
size: 'md',
controller: 'ReserveSlotWithoutPlanController',
resolve: {
slot: function() { return slotNotValid; },
slotNotValidError: function() { return slotNotValidError; },
slot: function () { return slotNotValid; },
slotNotValidError: function () { return slotNotValidError; }
}
});
modalInstance.result.then(function(res) {
modalInstance.result.then(function (res) {
return paySlots();
});
}
@ -199,7 +198,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
* When modifying an already booked reservation, confirm the modification.
*/
$scope.modifySlot = function () {
Slot.update({ id: $scope.events.modifiable.id }, {
Slot.update({ id: $scope.events.modifiable.slot_id }, {
slot: {
start_at: $scope.events.placable.start,
end_at: $scope.events.placable.end,
@ -216,23 +215,23 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
};
// -> reset the 'moving' status
$scope.events.placable = null;
return $scope.events.modifiable = null;
$scope.events.modifiable = null;
}
, function (err) { // failure
growl.error(_t('app.shared.cart.unable_to_change_the_reservation'));
return console.error(err);
console.error(err);
});
};
/**
* Cancel the current booking modification, reseting the whole process
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
* @param [event] {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.cancelModifySlot = function (event) {
if (event) { event.preventDefault(); }
if (typeof $scope.onSlotModifyCancel === 'function') { $scope.onSlotModifyCancel(); }
$scope.events.placable = null;
return $scope.events.modifiable = null;
$scope.events.modifiable = null;
};
/**
@ -242,7 +241,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.removeSlotToPlace = function (e) {
e.preventDefault();
if (typeof $scope.onSlotModifyUnselect === 'function') { $scope.onSlotModifyUnselect(); }
return $scope.events.placable = null;
$scope.events.placable = null;
};
/**
@ -251,7 +250,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
*/
$scope.tagMissmatch = function () {
if ($scope.events.placable.tag_ids.length === 0) { return false; }
for (let tag of Array.from($scope.events.modifiable.tags)) {
for (const tag of Array.from($scope.events.modifiable.tags)) {
if (!Array.from($scope.events.placable.tag_ids).includes(tag.id)) {
return true;
}
@ -271,7 +270,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
return false;
}
};
/* PRIVATE SCOPE */
@ -322,16 +321,16 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
size: 'md',
controller: 'ReserveSlotTagsMismatchController',
resolve: {
slotTags: function() { return slot.tags; },
slotTags: function () { return slot.tags; },
userTags: function () { return $scope.user.tags; },
userName: function () { return $scope.user.name; }
}
});
modalInstance.result.then(function(res) {
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.
@ -346,12 +345,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
'space_reservations',
'events_reservations'
].map(function (k) {
return _.filter($scope.user[k], function(r) {
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) {
@ -361,17 +360,17 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
size: 'md',
controller: 'ReserveSlotSameTimeController',
resolve: {
sameTimeReservations: function() { return sameTimeReservations; },
sameTimeReservations: function () { return sameTimeReservations; },
bookOverlappingSlotsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'book_overlapping_slots' }).$promise; }]
}
});
modalInstance.result.then(function(res) {
modalInstance.result.then(function (res) {
if (typeof callback === 'function') callback(res);
});
} else {
if (typeof callback === 'function') callback();
}
}
};
/**
* Callback triggered when the selected slot changed
@ -381,13 +380,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// if this slot is restricted for subscribers...
if ($scope.slot.plan_ids.length > 0) {
// ... we select all the plans matching these restrictions...
const _plans = _.filter($scope.plans, function (p) { return _.include($scope.slot.plan_ids, p.id) });
const _plans = _.filter($scope.plans, function (p) { return _.includes($scope.slot.plan_ids, p.id); });
// ... and we group these plans, by Group...
$scope.slot.plansGrouped = [];
$scope.slot.group_ids = [];
for (let group of Array.from($scope.groups)) {
for (const group of Array.from($scope.groups)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of Array.from(_plans)) {
for (const plan of Array.from(_plans)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
@ -400,13 +399,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
}
$scope.slot.group_ids = $scope.slot.plansGrouped.map(function(g) { return g.id; });
$scope.slot.group_ids = $scope.slot.plansGrouped.map(function (g) { return g.id; });
}
if (!$scope.slot.is_reserved && !$scope.events.modifiable && !$scope.slot.is_completed) {
// slot is not reserved and we are not currently modifying a slot
// -> can be added to cart or removed if already present
const index = $scope.events.reserved.indexOf($scope.slot);
const index = _.findIndex($scope.events.reserved, (e) => e._id === $scope.slot._id);
if (index === -1) {
if (($scope.limitToOneSlot === 'true') && $scope.events.reserved[0]) {
// if we limit the number of slots in the cart to 1, and there is already
@ -437,7 +436,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
} else if ($scope.slot.is_reserved && $scope.events.modifiable && ($scope.slot.is_reserved._id === $scope.events.modifiable._id)) {
// slot is reserved and currently modified
// -> we cancel the modification
return $scope.cancelModifySlot();
$scope.cancelModifySlot();
} else if ($scope.slot.is_reserved && (slotCanBeModified($scope.slot) || slotCanBeCanceled($scope.slot)) && !$scope.events.modifiable && ($scope.events.reserved.length === 0)) {
// slot is reserved and is ok to be modified or cancelled
// but we are not currently running a modification or having any slots in the cart
@ -456,9 +455,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// the user has chosen an action, so we proceed
if (type === 'move') {
if (typeof $scope.onSlotStartToModify === 'function') { $scope.onSlotStartToModify(); }
return $scope.events.modifiable = $scope.slot;
$scope.events.modifiable = $scope.slot;
} else if (type === 'cancel') {
return dialogs.confirm(
dialogs.confirm(
{
resolve: {
object () {
@ -470,7 +469,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
},
function () { // cancel confirmed
Slot.cancel({ id: $scope.slot.id }, function () { // successfully canceled
Slot.cancel({ id: $scope.slot.slot_id }, function () { // successfully canceled
growl.success(_t('app.shared.cart.reservation_was_cancelled_successfully'));
if (typeof $scope.onSlotCancelSuccess === 'function') { return $scope.onSlotCancelSuccess(); }
}
@ -494,7 +493,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.events.moved = null;
$scope.events.paid = [];
$scope.events.modifiable = null;
return $scope.events.placable = null;
$scope.events.placable = null;
};
/**
@ -666,7 +665,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
};
}
]
}).result['finally'](null).then(function (reservation) { afterPayment(reservation); });
}).result.finally(null).then(function (reservation) { afterPayment(reservation); });
};
/**
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
@ -735,7 +734,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
}
]
}).result['finally'](null).then(function (reservation) { afterPayment(reservation); });
}).result.finally(null).then(function (reservation) { afterPayment(reservation); });
};
/**
* Actions to run after the payment was successful
@ -756,22 +755,22 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
/**
* Actions to pay slots
*/
const paySlots = function() {
const paySlots = function () {
const reservation = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan);
return Wallet.getWalletByUser({ user_id: $scope.user.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.amountTotal, wallet.amount);
if ((AuthService.isAuthorized(['member']) && amountToPay > 0)
|| (AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
if ((AuthService.isAuthorized(['member']) && amountToPay > 0) ||
(AuthService.isAuthorized('manager') && $scope.user.id === $rootScope.currentUser.id && amountToPay > 0)) {
if ($scope.settings.online_payment_module !== 'true') {
growl.error(_t('app.shared.cart.online_payment_disabled'));
} else {
return payByStripe(reservation);
}
} else {
if (AuthService.isAuthorized(['admin'])
|| (AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id)
|| amountToPay === 0) {
if (AuthService.isAuthorized(['admin']) ||
(AuthService.isAuthorized('manager') && $scope.user.id !== $rootScope.currentUser.id) ||
amountToPay === 0) {
return payOnSite(reservation);
}
}
@ -798,17 +797,16 @@ Application.Controllers.controller('ReserveSlotSameTimeController', ['$scope', '
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
/**
* Controller of the modal showing the slot tags
*/
@ -823,13 +821,13 @@ Application.Controllers.controller('ReserveSlotTagsMismatchController', ['$scope
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
@ -845,12 +843,12 @@ Application.Controllers.controller('ReserveSlotWithoutPlanController', ['$scope'
*/
$scope.ok = function () {
$uibModalInstance.close({});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
json.array!(@slots) do |slot|
json.id slot.id if slot.id
json.slot_id slot.id if slot.id
json.can_modify slot.can_modify
json.title slot.title
json.start slot.start_at.iso8601