mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
(feat) improved calendars loading time
+ (feat) ability to choose a machine in the public calendar
This commit is contained in:
parent
d0239a0e25
commit
fea26285d6
@ -1,6 +1,8 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
- Report user's prepaid packs in the dashboard
|
- Report user's prepaid packs in the dashboard
|
||||||
- Ability to buy a new prepaid pack from the user's dashboard
|
- Ability to buy a new prepaid pack from the user's dashboard
|
||||||
|
- Improved public calendar loading time
|
||||||
|
- [TODO DEPLOY] `rails fablab:fix_availabilities` THEN `rails fablab:setup:build_places_cache`
|
||||||
|
|
||||||
- Use Time instead of DateTime objects
|
- Use Time instead of DateTime objects
|
||||||
- Fix a bug: missing statististics subtypes
|
- Fix a bug: missing statististics subtypes
|
||||||
@ -57,7 +59,6 @@
|
|||||||
- Fix a security issue: updated rack to 2.2.6.2 to fix [CVE-2022-44571](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-44571)
|
- Fix a security issue: updated rack to 2.2.6.2 to fix [CVE-2022-44571](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-44571)
|
||||||
- Fix a security issue: updated globalid to 1.0.1 to fix [CVE-2023-22799](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-22799)
|
- Fix a security issue: updated globalid to 1.0.1 to fix [CVE-2023-22799](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-22799)
|
||||||
- [TODO DEPLOY] `rails fablab:fix:invoice_items_in_error` THEN `rails fablab:fix_invoice_items` THEN `rails db:migrate`
|
- [TODO DEPLOY] `rails fablab:fix:invoice_items_in_error` THEN `rails fablab:fix_invoice_items` THEN `rails db:migrate`
|
||||||
- [TODO DEPLOY] `rails fablab:fix_availabilities` THEN `rails fablab:setup:build_places_cache`
|
|
||||||
|
|
||||||
## v5.6.5 2023 January 9
|
## v5.6.5 2023 January 9
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
* Controller used in the public calendar global
|
* Controller used in the public calendar global
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', 'moment', 'Availability', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise', 'iCalendarPromise', 'machineCategoriesPromise',
|
Application.Controllers.controller('CalendarController', ['$scope', '$state', '$aside', '$uibModal', 'moment', 'Availability', 'Setting', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', '_t', 'uiCalendarConfig', 'CalendarConfig', 'trainingsPromise', 'machinesPromise', 'spacesPromise', 'iCalendarPromise', 'machineCategoriesPromise',
|
||||||
function ($scope, $state, $aside, moment, Availability, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise, machineCategoriesPromise) {
|
function ($scope, $state, $aside, $uibModal, moment, Availability, Setting, growl, dialogs, bookingWindowStart, bookingWindowEnd, _t, uiCalendarConfig, CalendarConfig, trainingsPromise, machinesPromise, spacesPromise, iCalendarPromise, machineCategoriesPromise) {
|
||||||
/* PRIVATE STATIC CONSTANTS */
|
/* PRIVATE STATIC CONSTANTS */
|
||||||
let currentMachineEvent = null;
|
let currentMachineEvent = null;
|
||||||
machinesPromise.forEach(m => m.checked = true);
|
machinesPromise.forEach(m => m.checked = true);
|
||||||
@ -263,8 +263,33 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
|
|||||||
$state.go('app.public.training_show', { id: event.training_id });
|
$state.go('app.public.training_show', { id: event.training_id });
|
||||||
} else {
|
} else {
|
||||||
if (event.machine_ids) {
|
if (event.machine_ids) {
|
||||||
// TODO open modal to ask the user to select the machine to show
|
if (event.machine_ids.length === 1) {
|
||||||
$state.go('app.public.machines_show', { id: event.machine_ids[0] });
|
$state.go('app.public.machines_show', { id: event.machine_ids[0] });
|
||||||
|
} else {
|
||||||
|
// open a modal to ask the user to select the machine to show
|
||||||
|
const modalInstance = $uibModal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/calendar/chooseMachine.html',
|
||||||
|
size: 'md',
|
||||||
|
controller: ['$scope', 'machinesPromise', '$uibModalInstance', function ($scope, machinesPromise, $uibModalInstance) {
|
||||||
|
$scope.machines = machinesPromise.filter(m => event.machine_ids.includes(m.id));
|
||||||
|
$scope.selectMachine = function (machineId) {
|
||||||
|
$uibModalInstance.close(machineId);
|
||||||
|
};
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
}],
|
||||||
|
resolve: {
|
||||||
|
machinesPromise: ['Machine', function (Machine) {
|
||||||
|
return Machine.query().$promise;
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
modalInstance.result.then(function (res) {
|
||||||
|
$state.go('app.public.machines_show', { id: res });
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (event.space_id) {
|
} else if (event.space_id) {
|
||||||
$state.go('app.public.space_show', { id: event.space_id });
|
$state.go('app.public.space_show', { id: event.space_id });
|
||||||
}
|
}
|
||||||
|
@ -791,6 +791,10 @@ p, .widget p {
|
|||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-auto {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.media-xs {
|
.media-xs {
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
}
|
}
|
||||||
|
11
app/frontend/templates/calendar/chooseMachine.html
Normal file
11
app/frontend/templates/calendar/chooseMachine.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="text-center red" translate>
|
||||||
|
{{ 'app.public.calendar.choose_a_machine' }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<button class="block m-auto btn btn-default" style="margin-bottom: 5px" ng-repeat="machine in machines" type="button" ng-click="selectMachine(machine.id)" translate>{{machine.name}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.public.calendar.cancel' }}</button>
|
||||||
|
</div>
|
@ -22,16 +22,16 @@ module AvailabilityHelper
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def machines_slot_border_color(slot)
|
def machines_slot_border_color(slot, customer = nil)
|
||||||
if slot.is_reserved
|
if slot.reserved?
|
||||||
slot.current_user_slots_reservations_ids.empty? ? IS_FULL : IS_RESERVED_BY_CURRENT_USER
|
slot.reserved_users.include?(customer&.id) ? IS_RESERVED_BY_CURRENT_USER : IS_FULL
|
||||||
else
|
else
|
||||||
MACHINE_COLOR
|
MACHINE_COLOR
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def space_slot_border_color(slot)
|
def space_slot_border_color(slot)
|
||||||
if slot.is_reserved
|
if slot.reserved?
|
||||||
IS_RESERVED_BY_CURRENT_USER
|
IS_RESERVED_BY_CURRENT_USER
|
||||||
elsif slot.full?
|
elsif slot.full?
|
||||||
IS_FULL
|
IS_FULL
|
||||||
@ -41,7 +41,7 @@ module AvailabilityHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def trainings_events_border_color(availability)
|
def trainings_events_border_color(availability)
|
||||||
if availability.is_reserved
|
if availability.reserved?
|
||||||
IS_RESERVED_BY_CURRENT_USER
|
IS_RESERVED_BY_CURRENT_USER
|
||||||
elsif availability.full?
|
elsif availability.full?
|
||||||
IS_FULL
|
IS_FULL
|
||||||
|
@ -14,8 +14,6 @@ class Slot < ApplicationRecord
|
|||||||
|
|
||||||
after_create_commit :create_places_cache
|
after_create_commit :create_places_cache
|
||||||
|
|
||||||
attr_accessor :is_reserved, :machine, :space, :title, :can_modify, :current_user_slots_reservations_ids, :current_user_pending_reservations_ids
|
|
||||||
|
|
||||||
# @param reservable [Machine,Space,Training,Event,NilClass]
|
# @param reservable [Machine,Space,Training,Event,NilClass]
|
||||||
# @return [Integer] the total number of reserved places
|
# @return [Integer] the total number of reserved places
|
||||||
def reserved_places(reservable = nil)
|
def reserved_places(reservable = nil)
|
||||||
@ -29,7 +27,7 @@ class Slot < ApplicationRecord
|
|||||||
# @param reservables [Array<Machine,Space,Training,Event>,NilClass]
|
# @param reservables [Array<Machine,Space,Training,Event>,NilClass]
|
||||||
# @return [Array<Integer>] Collection of User's IDs
|
# @return [Array<Integer>] Collection of User's IDs
|
||||||
def reserved_users(reservables = nil)
|
def reserved_users(reservables = nil)
|
||||||
if reservable.nil?
|
if reservables.nil?
|
||||||
places.pluck('user_ids').flatten
|
places.pluck('user_ids').flatten
|
||||||
else
|
else
|
||||||
r_places = places.select do |p|
|
r_places = places.select do |p|
|
||||||
|
@ -11,7 +11,6 @@ class Availabilities::AvailabilitiesService
|
|||||||
other: Setting.get('visibility_others').to_i.months.since
|
other: Setting.get('visibility_others').to_i.months.since
|
||||||
}
|
}
|
||||||
@minimum_visibility = Setting.get('reservation_deadline').to_i.minutes.since
|
@minimum_visibility = Setting.get('reservation_deadline').to_i.minutes.since
|
||||||
@service = Availabilities::StatusService.new(current_user&.role)
|
|
||||||
@level = level
|
@level = level
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -39,16 +38,15 @@ class Availabilities::AvailabilitiesService
|
|||||||
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
||||||
# @option window [ActiveSupport::TimeWithZone] :start the beginning of the time window
|
# @option window [ActiveSupport::TimeWithZone] :start the beginning of the time window
|
||||||
# @option window [ActiveSupport::TimeWithZone] :end the end of the time window
|
# @option window [ActiveSupport::TimeWithZone] :end the end of the time window
|
||||||
# @param no_status [Boolean] should the availability/slot reservation status be computed?
|
def machines(machines, user, window)
|
||||||
def machines(machines, user, window, no_status: false)
|
|
||||||
ma_availabilities = Availability.includes(:machines_availabilities)
|
ma_availabilities = Availability.includes(:machines_availabilities)
|
||||||
.where('machines_availabilities.machine_id': machines.map(&:id))
|
.where('machines_availabilities.machine_id': machines.map(&:id))
|
||||||
availabilities = availabilities(ma_availabilities, 'machines', user, window[:start], window[:end])
|
availabilities = availabilities(ma_availabilities, 'machines', user, window[:start], window[:end])
|
||||||
|
|
||||||
if @level == 'slot'
|
if @level == 'slot'
|
||||||
availabilities.map(&:slots).flatten.map { |s| no_status ? s : @service.slot_reserved_status(s, user, (machines & s.availability.machines)) }
|
availabilities.map(&:slots).flatten
|
||||||
else
|
else
|
||||||
no_status ? availabilities : availabilities.map { |a| @service.availability_reserved_status(a, user, (machines & a.machines)) }
|
availabilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -58,16 +56,15 @@ class Availabilities::AvailabilitiesService
|
|||||||
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
||||||
# @option window [ActiveSupport::TimeWithZone] :start
|
# @option window [ActiveSupport::TimeWithZone] :start
|
||||||
# @option window [ActiveSupport::TimeWithZone] :end
|
# @option window [ActiveSupport::TimeWithZone] :end
|
||||||
# @param no_status [Boolean] should the availability/slot reservation status be computed?
|
def spaces(spaces, user, window)
|
||||||
def spaces(spaces, user, window, no_status: false)
|
|
||||||
sp_availabilities = Availability.includes('spaces_availabilities')
|
sp_availabilities = Availability.includes('spaces_availabilities')
|
||||||
.where('spaces_availabilities.space_id': spaces.map(&:id))
|
.where('spaces_availabilities.space_id': spaces.map(&:id))
|
||||||
availabilities = availabilities(sp_availabilities, 'space', user, window[:start], window[:end])
|
availabilities = availabilities(sp_availabilities, 'space', user, window[:start], window[:end])
|
||||||
|
|
||||||
if @level == 'slot'
|
if @level == 'slot'
|
||||||
availabilities.map(&:slots).flatten.map { |s| no_status ? s : @service.slot_reserved_status(s, user, (spaces & s.availability.spaces)) }
|
availabilities.map(&:slots).flatten
|
||||||
else
|
else
|
||||||
no_status ? availabilities : availabilities.map { |a| @service.availability_reserved_status(a, user, (spaces & a.spaces)) }
|
availabilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -77,15 +74,15 @@ class Availabilities::AvailabilitiesService
|
|||||||
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
||||||
# @option window [ActiveSupport::TimeWithZone] :start
|
# @option window [ActiveSupport::TimeWithZone] :start
|
||||||
# @option window [ActiveSupport::TimeWithZone] :end
|
# @option window [ActiveSupport::TimeWithZone] :end
|
||||||
def trainings(trainings, user, window, no_status: false)
|
def trainings(trainings, user, window)
|
||||||
tr_availabilities = Availability.includes('trainings_availabilities')
|
tr_availabilities = Availability.includes('trainings_availabilities')
|
||||||
.where('trainings_availabilities.training_id': trainings.map(&:id))
|
.where('trainings_availabilities.training_id': trainings.map(&:id))
|
||||||
availabilities = availabilities(tr_availabilities, 'training', user, window[:start], window[:end])
|
availabilities = availabilities(tr_availabilities, 'training', user, window[:start], window[:end])
|
||||||
|
|
||||||
if @level == 'slot'
|
if @level == 'slot'
|
||||||
availabilities.map(&:slots).flatten.map { |s| no_status ? s : @service.slot_reserved_status(s, user, (trainings & s.availability.trainings)) }
|
availabilities.map(&:slots).flatten
|
||||||
else
|
else
|
||||||
no_status ? availabilities : availabilities.map { |a| @service.availability_reserved_status(a, user, (trainings & a.trainings)) }
|
availabilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -95,14 +92,14 @@ class Availabilities::AvailabilitiesService
|
|||||||
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
# @param window [Hash] the time window the look through: {start: xxx, end: xxx}
|
||||||
# @option window [ActiveSupport::TimeWithZone] :start
|
# @option window [ActiveSupport::TimeWithZone] :start
|
||||||
# @option window [ActiveSupport::TimeWithZone] :end
|
# @option window [ActiveSupport::TimeWithZone] :end
|
||||||
def events(events, user, window, no_status: false)
|
def events(events, user, window)
|
||||||
ev_availabilities = Availability.includes('event').where('events.id': events.map(&:id))
|
ev_availabilities = Availability.includes('event').where('events.id': events.map(&:id))
|
||||||
availabilities = availabilities(ev_availabilities, 'event', user, window[:start], window[:end])
|
availabilities = availabilities(ev_availabilities, 'event', user, window[:start], window[:end])
|
||||||
|
|
||||||
if @level == 'slot'
|
if @level == 'slot'
|
||||||
availabilities.map(&:slots).flatten.map { |s| no_status ? s : @service.slot_reserved_status(s, user, [s.availability.event]) }
|
availabilities.map(&:slots).flatten
|
||||||
else
|
else
|
||||||
no_status ? availabilities : availabilities.map { |a| @service.availability_reserved_status(a, user, [a.event]) }
|
availabilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
class Availabilities::PublicAvailabilitiesService
|
class Availabilities::PublicAvailabilitiesService
|
||||||
def initialize(current_user)
|
def initialize(current_user)
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
@service = Availabilities::StatusService.new('public')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_availabilities(window, ids, events: false)
|
def public_availabilities(window, ids, events: false)
|
||||||
@ -12,17 +11,17 @@ class Availabilities::PublicAvailabilitiesService
|
|||||||
service = Availabilities::AvailabilitiesService.new(@current_user, level)
|
service = Availabilities::AvailabilitiesService.new(@current_user, level)
|
||||||
|
|
||||||
machines_slots = if Setting.get('machines_module')
|
machines_slots = if Setting.get('machines_module')
|
||||||
service.machines(Machine.where(id: ids[:machines]), @current_user, window, no_status: true)
|
service.machines(Machine.where(id: ids[:machines]), @current_user, window)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
spaces_slots = Setting.get('spaces_module') ? service.spaces(Space.where(id: ids[:spaces]), @current_user, window, no_status: true) : []
|
spaces_slots = Setting.get('spaces_module') ? service.spaces(Space.where(id: ids[:spaces]), @current_user, window) : []
|
||||||
trainings_slots = if Setting.get('trainings_module')
|
trainings_slots = if Setting.get('trainings_module')
|
||||||
service.trainings(Training.where(id: ids[:trainings]), @current_user, window, no_status: true)
|
service.trainings(Training.where(id: ids[:trainings]), @current_user, window)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
events_slots = events ? service.events(Event.all, @current_user, window, no_status: true) : []
|
events_slots = events ? service.events(Event.all, @current_user, window) : []
|
||||||
|
|
||||||
[].concat(trainings_slots).concat(events_slots).concat(machines_slots).concat(spaces_slots)
|
[].concat(trainings_slots).concat(events_slots).concat(machines_slots).concat(spaces_slots)
|
||||||
end
|
end
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
# TODO, remove this
|
|
||||||
# Provides helper methods checking reservation status of any availabilities
|
|
||||||
class Availabilities::StatusService
|
|
||||||
# @param current_user_role [String]
|
|
||||||
def initialize(current_user_role)
|
|
||||||
@current_user_role = current_user_role
|
|
||||||
@show_name = (%w[admin manager].include?(@current_user_role) || (current_user_role && Setting.get('display_name_enable')))
|
|
||||||
end
|
|
||||||
|
|
||||||
# check that the provided slot is reserved for the given reservable (machine, training or space).
|
|
||||||
# Mark it accordingly for display in the calendar
|
|
||||||
# @param slot [Slot]
|
|
||||||
# @param user [User] the customer
|
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
|
||||||
# @return [Slot]
|
|
||||||
def slot_reserved_status(slot, user, reservables)
|
|
||||||
if reservables.map(&:class).map(&:name).uniq.size > 1
|
|
||||||
raise TypeError('[Availabilities::StatusService#slot_reserved_status] reservables have differents types: ' \
|
|
||||||
"#{reservables.map(&:class).map(&:name).uniq} , with slot #{slot.id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
places = places(slot, reservables)
|
|
||||||
is_reserved = places.any? { |p| p['reserved_places'].positive? }
|
|
||||||
is_reserved_by_user = is_reserved && user && places.select { |p| p['user_ids'].include?(user.id) }.length.positive?
|
|
||||||
slot.is_reserved = is_reserved
|
|
||||||
slot.title = slot_title(slot, is_reserved, is_reserved_by_user, reservables)
|
|
||||||
slot.can_modify = true if %w[admin manager].include?(@current_user_role) || is_reserved
|
|
||||||
if is_reserved_by_user
|
|
||||||
user_reservations = Slots::ReservationsService.user_reservations(slot, user, reservables.first.class.name)
|
|
||||||
|
|
||||||
slot.current_user_slots_reservations_ids = user_reservations[:reservations].select('id').map(&:id)
|
|
||||||
slot.current_user_pending_reservations_ids = user_reservations[:pending].select('id').map(&:id)
|
|
||||||
end
|
|
||||||
slot
|
|
||||||
end
|
|
||||||
|
|
||||||
# check that the provided ability is reserved by the given user
|
|
||||||
# @param availability [Availability]
|
|
||||||
# @param user [User] the customer
|
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
|
||||||
# @return [Availability]
|
|
||||||
def availability_reserved_status(availability, user, reservables)
|
|
||||||
if reservables.map(&:class).map(&:name).uniq.size > 1
|
|
||||||
raise TypeError('[Availabilities::StatusService#availability_reserved_status] reservables have differents types: ' \
|
|
||||||
"#{reservables.map(&:class).map(&:name).uniq}, with availability #{availability.id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
slots = availability.slots.map do |slot|
|
|
||||||
slot_reserved_status(slot, user, reservables)
|
|
||||||
end
|
|
||||||
|
|
||||||
availability.is_reserved = slots.any?(&:is_reserved)
|
|
||||||
availability.current_user_slots_reservations_ids = slots.map(&:current_user_slots_reservations_ids).flatten
|
|
||||||
availability.current_user_pending_reservations_ids = slots.map(&:current_user_pending_reservations_ids).flatten
|
|
||||||
availability
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# @param slot [Slot]
|
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
|
||||||
# @return [Array<Hash>]
|
|
||||||
def places(slot, reservables)
|
|
||||||
places = []
|
|
||||||
reservables.each do |reservable|
|
|
||||||
places.push(slot.places.detect { |p| p['reservable_type'] == reservable.class.name && p['reservable_id'] == reservable.id })
|
|
||||||
end
|
|
||||||
places
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param slot [Slot]
|
|
||||||
# @param is_reserved [Boolean]
|
|
||||||
# @param is_reserved_by_user [Boolean]
|
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
|
||||||
def slot_title(slot, is_reserved, is_reserved_by_user, reservables)
|
|
||||||
name = reservables.map(&:name).join(', ')
|
|
||||||
if !is_reserved && !is_reserved_by_user
|
|
||||||
name
|
|
||||||
elsif is_reserved && !is_reserved_by_user
|
|
||||||
"#{name} #{@show_name ? "- #{slot_users_names(slot, reservables)}" : ''}"
|
|
||||||
else
|
|
||||||
"#{name} - #{I18n.t('availabilities.i_ve_reserved')}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @param slot [Slot]
|
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
|
||||||
# @return [String]
|
|
||||||
def slot_users_names(slot, reservables)
|
|
||||||
user_ids = slot.places
|
|
||||||
.select { |p| p['reservable_type'] == reservables.first.class.name && reservables.map(&:id).includes?(p['reservable_id']) }
|
|
||||||
.pluck('user_ids')
|
|
||||||
.flatten
|
|
||||||
User.where(id: user_ids).includes(:profile)
|
|
||||||
.map { |u| u&.profile&.full_name || I18n.t('availabilities.deleted_user') }
|
|
||||||
.join(', ')
|
|
||||||
end
|
|
||||||
end
|
|
@ -12,7 +12,7 @@ class Slots::ReservationsService
|
|||||||
def reservations(slots_reservations, reservables)
|
def reservations(slots_reservations, reservables)
|
||||||
reservable_types = reservables.map(&:class).map(&:name).uniq
|
reservable_types = reservables.map(&:class).map(&:name).uniq
|
||||||
if reservable_types.size > 1
|
if reservable_types.size > 1
|
||||||
raise TypeError("[Availabilities::StatusService#slot_reservations] reservables have differents types: #{reservable_types}")
|
raise TypeError("[Availabilities::ReservationsService#reservations] reservables have differents types: #{reservable_types}")
|
||||||
end
|
end
|
||||||
|
|
||||||
reservations = slots_reservations.includes(:reservation)
|
reservations = slots_reservations.includes(:reservation)
|
||||||
@ -75,14 +75,26 @@ class Slots::ReservationsService
|
|||||||
# @param slot [Slot]
|
# @param slot [Slot]
|
||||||
# @param user [User]
|
# @param user [User]
|
||||||
# @param reservable_type [String] 'Machine' | 'Space' | 'Training' | 'Event'
|
# @param reservable_type [String] 'Machine' | 'Space' | 'Training' | 'Event'
|
||||||
|
# @param reservable_id [Integer]
|
||||||
# @return [Hash{Symbol=>ActiveRecord::Relation<SlotsReservation>,ActiveRecord::Relation<CartItem::ReservationSlot>}]
|
# @return [Hash{Symbol=>ActiveRecord::Relation<SlotsReservation>,ActiveRecord::Relation<CartItem::ReservationSlot>}]
|
||||||
def user_reservations(slot, user, reservable_type)
|
def user_reservations(slot, user, reservable_type, reservable_id)
|
||||||
|
return { reservations: [], pending: [] } if user.nil?
|
||||||
|
|
||||||
reservations = SlotsReservation.includes(:reservation)
|
reservations = SlotsReservation.includes(:reservation)
|
||||||
.where(slot_id: slot.id, reservations: { statistic_profile_id: user.statistic_profile.id })
|
.where(slot_id: slot.id, reservations: {
|
||||||
|
statistic_profile_id: user.statistic_profile.id,
|
||||||
|
reservable_type: reservable_type,
|
||||||
|
reservable_id: reservable_id
|
||||||
|
})
|
||||||
relation = "cart_item_#{reservable_type&.downcase}_reservation".to_sym
|
relation = "cart_item_#{reservable_type&.downcase}_reservation".to_sym
|
||||||
table = (reservable_type == 'Event' ? 'cart_item_event_reservations' : 'cart_item_reservations').to_sym
|
table = (reservable_type == 'Event' ? 'cart_item_event_reservations' : 'cart_item_reservations').to_sym
|
||||||
|
id_key = (reservable_type == 'Event' ? 'event_id' : 'reservable_id').to_sym
|
||||||
|
type_key = (reservable_type == 'Event' ? {} : { reservable_type: reservable_type })
|
||||||
pending = CartItem::ReservationSlot.includes(relation)
|
pending = CartItem::ReservationSlot.includes(relation)
|
||||||
.where(slot_id: slot.id, table => { customer_profile_id: user.invoicing_profile.id })
|
.where(slot_id: slot.id, table => {
|
||||||
|
customer_profile_id: user.invoicing_profile.id,
|
||||||
|
id_key => reservable_id
|
||||||
|
}.merge!(type_key))
|
||||||
|
|
||||||
{
|
{
|
||||||
reservations: reservations,
|
reservations: reservations,
|
||||||
|
@ -9,15 +9,16 @@ class Slots::TitleService
|
|||||||
|
|
||||||
# @param slot [Slot]
|
# @param slot [Slot]
|
||||||
# @param reservables [Array<Machine, Space, Training, Event>]
|
# @param reservables [Array<Machine, Space, Training, Event>]
|
||||||
def slot_title(slot, reservables)
|
def call(slot, reservables = nil)
|
||||||
|
reservables = all_reservables(slot) if reservables.nil?
|
||||||
is_reserved = slot.reserved?
|
is_reserved = slot.reserved?
|
||||||
is_reserved_by_user = slot.reserved_users(reservables).include?(@user.id)
|
is_reserved_by_user = slot.reserved_users(reservables).include?(@user&.id)
|
||||||
|
|
||||||
name = reservables.map(&:name).join(', ')
|
name = reservables.map(&:name).join(', ')
|
||||||
if !is_reserved && !is_reserved_by_user
|
if !is_reserved && !is_reserved_by_user
|
||||||
name
|
name
|
||||||
elsif is_reserved && !is_reserved_by_user
|
elsif is_reserved && !is_reserved_by_user
|
||||||
"#{name} #{@show_name ? "- #{Slots::TitleService.slot_users_names(slot, reservables)}" : ''}"
|
"#{name} #{@show_name ? "- #{slot_users_names(slot, reservables)}" : ''}"
|
||||||
else
|
else
|
||||||
"#{name} - #{I18n.t('availabilities.i_ve_reserved')}"
|
"#{name} - #{I18n.t('availabilities.i_ve_reserved')}"
|
||||||
end
|
end
|
||||||
@ -34,4 +35,9 @@ class Slots::TitleService
|
|||||||
.map { |u| u&.profile&.full_name || I18n.t('availabilities.deleted_user') }
|
.map { |u| u&.profile&.full_name || I18n.t('availabilities.deleted_user') }
|
||||||
.join(', ')
|
.join(', ')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @param slot [Slot]
|
||||||
|
def all_reservables(slot)
|
||||||
|
slot.places.pluck('reservable_type', 'reservable_id').map { |r| r[0].classify.constantize.find(r[1]) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.slot_id slot.id
|
json.slot_id slot.id
|
||||||
json.can_modify slot.modifiable?(operator_role, @user.id, reservable)
|
json.can_modify slot.modifiable?(operator_role, @user&.id, reservable)
|
||||||
json.title Slots::TitleService.new(operator_role, @user).slot_title(slot, [reservable])
|
json.title Slots::TitleService.new(operator_role, @user).call(slot, [reservable])
|
||||||
json.start slot.start_at.iso8601
|
json.start slot.start_at.iso8601
|
||||||
json.end slot.end_at.iso8601
|
json.end slot.end_at.iso8601
|
||||||
json.is_reserved slot.reserved?(reservable)
|
json.is_reserved slot.reserved?(reservable)
|
||||||
@ -10,7 +10,7 @@ json.is_completed slot.full?(reservable)
|
|||||||
json.backgroundColor 'white'
|
json.backgroundColor 'white'
|
||||||
|
|
||||||
json.availability_id slot.availability_id
|
json.availability_id slot.availability_id
|
||||||
json.slots_reservations_ids slot.current_user_slots_reservations_ids #TODO, move this out of attr_accessor
|
json.slots_reservations_ids Slots::ReservationsService.user_reservations(slot, @user, reservable&.class&.name, reservable&.id)[:reservations]
|
||||||
|
|
||||||
json.tag_ids slot.availability.tag_ids
|
json.tag_ids slot.availability.tag_ids
|
||||||
json.tags slot.availability.tags do |t|
|
json.tags slot.availability.tags do |t|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
json.array!(@slots) do |slot|
|
json.array!(@slots) do |slot|
|
||||||
json.partial! 'api/availabilities/slot', slot: slot, operator_role: @operator_role, reservable: @machine
|
json.partial! 'api/availabilities/slot', slot: slot, operator_role: @operator_role, reservable: @machine
|
||||||
json.borderColor machines_slot_border_color(slot)
|
json.borderColor machines_slot_border_color(slot, @customer)
|
||||||
|
|
||||||
json.machine do
|
json.machine do
|
||||||
json.id @machine.id
|
json.id @machine.id
|
||||||
|
@ -32,27 +32,28 @@ json.array!(@availabilities) do |availability|
|
|||||||
|
|
||||||
# slot object ( here => availability = slot ), for daily view
|
# slot object ( here => availability = slot ), for daily view
|
||||||
elsif availability.instance_of? Slot
|
elsif availability.instance_of? Slot
|
||||||
json.title availability.title
|
slot = availability
|
||||||
json.tag_ids availability.availability.tag_ids
|
json.title Slots::TitleService.new(@user.role, @user).call(slot)
|
||||||
json.tags availability.availability.tags do |t|
|
json.tag_ids slot.availability.tag_ids
|
||||||
|
json.tags slot.availability.tags do |t|
|
||||||
json.id t.id
|
json.id t.id
|
||||||
json.name t.name
|
json.name t.name
|
||||||
end
|
end
|
||||||
json.is_reserved availability.reserved?
|
json.is_reserved slot.reserved?
|
||||||
json.is_completed availability.full?
|
json.is_completed slot.full?
|
||||||
case availability.availability.available_type
|
case slot.availability.available_type
|
||||||
when 'machines'
|
when 'machines'
|
||||||
json.machine_ids availability.availability.machines.map(&:id)
|
json.machine_ids slot.availability.machines.map(&:id)
|
||||||
json.borderColor machines_slot_border_color(availability)
|
json.borderColor machines_slot_border_color(slot)
|
||||||
when 'space'
|
when 'space'
|
||||||
json.space_id availability.availability.spaces.first.id
|
json.space_id slot.availability.spaces.first.id
|
||||||
json.borderColor space_slot_border_color(availability)
|
json.borderColor space_slot_border_color(slot)
|
||||||
when 'training'
|
when 'training'
|
||||||
json.training_id availability.availability.trainings.first.id
|
json.training_id slot.availability.trainings.first.id
|
||||||
json.borderColor trainings_events_border_color(availability.availability)
|
json.borderColor trainings_events_border_color(slot.availability)
|
||||||
when 'event'
|
when 'event'
|
||||||
json.event_id availability.availability.event.id
|
json.event_id slot.availability.event.id
|
||||||
json.borderColor trainings_events_border_color(availability.availability)
|
json.borderColor trainings_events_border_color(slot.availability)
|
||||||
else
|
else
|
||||||
json.title 'Unknown slot'
|
json.title 'Unknown slot'
|
||||||
end
|
end
|
||||||
|
@ -366,6 +366,8 @@ en:
|
|||||||
spaces: "Spaces"
|
spaces: "Spaces"
|
||||||
events: "Events"
|
events: "Events"
|
||||||
externals: "Other calendars"
|
externals: "Other calendars"
|
||||||
|
choose_a_machine: "Choose a machine"
|
||||||
|
cancel: "Cancel"
|
||||||
#list of spaces
|
#list of spaces
|
||||||
spaces_list:
|
spaces_list:
|
||||||
the_spaces: "The spaces"
|
the_spaces: "The spaces"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user