1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

fixes fullCalendar for admin agenda

This commit is contained in:
Sylvain 2020-09-30 11:23:42 +02:00
parent 5caceec57b
commit 8ee91ef7d5
5 changed files with 87 additions and 97 deletions

View File

@ -8,7 +8,7 @@
- Updated jquery-minicolors to 2.3.5
- Updated angular-bootstrap-switch to 0.5.2
- Updated bootstrap-switch to 3.4.0
- Updated fullCalendar to 2.9.1
- Updated fullCalendar to 3.10.2
## v4.5.6 2020 September 1st

View File

@ -46,11 +46,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.eventsInCalendar = (settingsPromise.events_in_calendar === 'true');
// bind the availabilities slots with full-Calendar events
$scope.eventSources = [];
$scope.eventSources.push({
$scope.eventSources = [{
url: '/api/availabilities',
textColor: 'black'
});
}];
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
@ -101,7 +100,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
{ id: slot.slot_id },
function (data, status) { // success
// update the canceled_at attribute
for (let resa of Array.from($scope.reservations)) {
for (const resa of Array.from($scope.reservations)) {
if (resa.slot_id === data.id) {
resa.canceled_at = data.canceled_at;
break;
@ -185,9 +184,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
},
function () {
// the admin has confirmed, remove the plan
const plans = _.drop($scope.availability.plan_ids, plan.id);
_.drop($scope.availability.plan_ids, plan.id);
return Availability.update({ id: $scope.availability.id }, { availability: { plans_attributes: [{ id: plan.id, _destroy: true }] } }
Availability.update({ id: $scope.availability.id }, { availability: { plans_attributes: [{ id: plan.id, _destroy: true }] } }
, function (data, status) { // success
// update the plan_ids attribute
$scope.availability.plan_ids = data.plan_ids;
@ -282,7 +281,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
});
// once the dialog was closed, do things depending on the result
modalInstance.result.then(function (res) {
if (res.status == 'success') {
if (res.status === 'success') {
$scope.availability = null;
}
for (const availability of res.availabilities) {
@ -355,7 +354,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('calendar') < 0) {
uitour.start();
}
}
};
/* PRIVATE SCOPE */
@ -380,12 +379,12 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
*
* @returns {array}
*/
var availabilityPlans = function() {
const availabilityPlans = function () {
const plansClassifiedByGroup = [];
const _plans = _.filter(plansPromise, function (p) { return _.include($scope.availability.plan_ids, p.id) });
for (let group of Array.from(groupsPromise)) {
const _plans = _.filter(plansPromise, function (p) { return _.includes($scope.availability.plan_ids, p.id); });
for (const group of Array.from(groupsPromise)) {
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) {
@ -424,9 +423,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
backdrop: 'static',
keyboard: false,
resolve: {
start() { return start; },
end() { return end; },
slots() { return Math.ceil(slots); },
start () { return start; },
end () { return end; },
slots () { return Math.ceil(slots); },
machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }],
trainingsPromise: ['Training', function (Training) { return Training.query().$promise; }],
spacesPromise: ['Space', function (Space) { return Space.query().$promise; }],
@ -434,7 +433,8 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
plansPromise: ['Plan', function (Plan) { return Plan.query().$promise; }],
groupsPromise: ['Group', function (Group) { return Group.query().$promise; }],
slotDurationPromise: ['Setting', function (Setting) { return Setting.get({ name: 'slot_duration' }).$promise; }]
} });
}
});
// when the modal is closed, we send the slot to the server for saving
modalInstance.result.then(
function (availability) {
@ -471,10 +471,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
$scope.availability.plans = availabilityPlans();
if ($scope.availabilityDom) {
$scope.availabilityDom.classList.remove("fc-selected")
$scope.availabilityDom.classList.remove('fc-selected');
}
$scope.availabilityDom = jsEvent.target.closest('.fc-event');
$scope.availabilityDom.classList.add("fc-selected")
$scope.availabilityDom.classList.add('fc-selected');
// if the user has clicked on the delete event button, delete the event
if ($(jsEvent.target).hasClass('remove-event')) {
@ -494,14 +494,13 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
if (event.available_type !== 'event') {
element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>');
}
if (event.tags.length > 0) {
if (event.tags && event.tags.length > 0) {
let html = '';
for (let tag of Array.from(event.tags)) {
for (const tag of Array.from(event.tags)) {
html += `<span class='label label-success text-white'>${tag.name}</span> `;
}
element.find('.fc-title').append(`<br/>${html}`);
}
// force return to prevent coffee-script auto-return to return random value (possiblity falsy)
};
/**
@ -509,10 +508,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* @see https://fullcalendar.io/docs/resource_data/loading/
*/
const loadingCb = function (isLoading, view) {
if (isLoading) {
if (isLoading && uiCalendarConfig.calendars.calendar) {
// we remove existing events when fetching starts to prevent duplicates
uiCalendarConfig.calendars.calendar.fullCalendar('removeEvents');
}
};
@ -520,7 +518,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
* Triggered when the view is changed
* @see https://fullcalendar.io/docs/v3/viewRender#v2
*/
const viewRenderCb = function(view, element) {
const viewRenderCb = function (view, element) {
// we unselect the current event to keep consistency
$scope.availability = null;
$scope.availabilityDom = null;
@ -633,7 +631,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
/**
* Select/unselect all the machines
*/
$scope.toggleAll = function() {
$scope.toggleAll = function () {
const count = $scope.selectedMachines.length;
$scope.selectedMachines = [];
$scope.selectedMachinesBinding = {};
@ -641,9 +639,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.machines.forEach(function (machine) {
$scope.selectedMachines.push(machine);
$scope.selectedMachinesBinding[machine.id] = true;
})
});
}
}
};
/**
* Adds or removes the provided plan from the current slot
@ -661,7 +659,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
/**
* Select/unselect all the plans
*/
$scope.toggleAllPlans = function() {
$scope.toggleAllPlans = function () {
const count = $scope.selectedPlans.length;
$scope.selectedPlans = [];
$scope.selectedPlansBinding = {};
@ -669,7 +667,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
plansPromise.forEach(function (plan) {
$scope.selectedPlans.push(plan);
$scope.selectedPlansBinding[plan.id] = true;
})
});
}
};
@ -738,7 +736,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/
$scope.isTypeDivided = function () {
return isTypeDivided($scope.availability.available_type);
}
};
/* PRIVATE SCOPE */
@ -754,7 +752,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
}
// when disable is only subscriptions option, reset all selected plans
$scope.$watch('isOnlySubscriptions', function(value) {
$scope.$watch('isOnlySubscriptions', function (value) {
if (!value) {
$scope.selectedPlans = [];
$scope.selectedPlansBinding = {};
@ -762,14 +760,14 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
});
// group plans by Group
for (let group of groupsPromise.filter(g => !g.disabled)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (let plan of plansPromise.filter(g => !g.disabled)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
$scope.plansClassifiedByGroup.push(groupObj);
}
for (const group of groupsPromise.filter(g => !g.disabled)) {
const groupObj = { id: group.id, name: group.name, plans: [] };
for (const plan of plansPromise.filter(g => !g.disabled)) {
if (plan.group_id === group.id) { groupObj.plans.push(plan); }
}
if (groupObj.plans.length > 0) {
$scope.plansClassifiedByGroup.push(groupObj);
}
}
// When the slot duration changes, we increment the availability to match the value
@ -783,9 +781,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
// When the number of slot changes, we increment the availability to match the value
$scope.$watch('slots_nb', function (newValue, oldValue, scope) {
start = moment($scope.start);
start.add($scope.availability.slot_duration * newValue, 'minutes');
$scope.end = start.toDate();
start = moment($scope.start);
start.add($scope.availability.slot_duration * newValue, 'minutes');
$scope.end = start.toDate();
});
// When we configure a machine/space availability, do not let the user change the end time, as the total
@ -802,7 +800,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.end = moment($scope.start).add(upper, 'minutes').toDate();
$scope.slots_nb = upperSlots;
} else {
$scope.slots_nb = slotsCurrentRange;
$scope.slots_nb = slotsCurrentRange;
}
$scope.availability.end_at = $scope.end;
} else {
@ -843,8 +841,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
* Test if the provided availability type is divided in slots
*/
const isTypeDivided = function (type) {
return ((type === 'machines') || (type === 'space'));
}
return ((type === 'machines') || (type === 'space'));
};
/**
* Validates that a machine or more was/were selected before continuing to step 3 (adjust time + tags)
@ -890,7 +888,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.availability.slot_duration = parseInt(slotDurationPromise.setting.value, 10);
}
$scope.step++;
}
};
/**
* Compute the various occurrences of the availability, according to the recurrence settings
@ -922,7 +920,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.reservableName = '';
switch ($scope.availability.available_type) {
case 'machines':
$scope.reservableName = localizedList($scope.selectedMachines)
$scope.reservableName = localizedList($scope.selectedMachines);
break;
case 'training':
$scope.reservableName = `<strong>${$scope.selectedTraining.name}</strong>`;
@ -931,25 +929,25 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
$scope.reservableName = `<strong>${$scope.selectedSpace.name}</strong>`;
break;
default:
$scope.reservableName = `<span class="warning">${_t("app.admin.calendar.none")}</span>`;
$scope.reservableName = `<span class="warning">${_t('app.admin.calendar.none')}</span>`;
}
const tags = $scope.tags.filter(function (t) {
return $scope.availability.tag_ids.indexOf(t.id) > -1;
})
});
$scope.tagsName = localizedList(tags);
if ($scope.isOnlySubscriptions && $scope.selectedPlans.length > 0) {
$scope.plansName = localizedList($scope.selectedPlans);
}
}
};
const localizedList = function (items) {
if (items.length === 0) return `<span class="text-gray text-italic">${_t("app.admin.calendar.none")}</span>`;
if (items.length === 0) return `<span class="text-gray text-italic">${_t('app.admin.calendar.none')}</span>`;
const names = items.map(function (i) { return $sce.trustAsHtml(`<strong>${i.name}</strong>`); });
if (items.length > 1) return names.slice(0, -1).join(', ') + ` ${_t('app.admin.calendar.and')} ` + names[names.length - 1];
return names[0];
}
};
// !!! MUST BE CALLED AT THE END of the controller
return initialize();
@ -961,7 +959,6 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
*/
Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$scope', '$uibModalInstance', 'Availability', 'availabilityPromise', 'growl', '_t',
function ($scope, $uibModalInstance, Availability, availabilityPromise, growl, _t) {
// is the current slot (to be deleted) recurrent?
$scope.isRecurrent = availabilityPromise.is_recurrent;
@ -981,17 +978,17 @@ Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$s
if (res.deleted > 1) {
growl.success(_t(
'app.admin.calendar.slots_deleted',
{START: moment(start_at).format('LL LT'), COUNT: res.deleted - 1}
{ START: moment(start_at).format('LL LT'), COUNT: res.deleted - 1 }
));
} else {
growl.success(_t(
'app.admin.calendar.slot_successfully_deleted',
{START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT')}
{ START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT') }
));
}
$uibModalInstance.close({
status: 'success',
availabilities: res.details.map(function (d) { return d.availability.id })
availabilities: res.details.map(function (d) { return d.availability.id; })
});
},
function (res) {
@ -1000,32 +997,30 @@ Application.Controllers.controller('DeleteRecurrentAvailabilityController', ['$s
if (data.total > 1) {
growl.warning(_t(
'app.admin.calendar.slots_not_deleted',
{TOTAL: data.total, COUNT: data.total - data.deleted}
{ TOTAL: data.total, COUNT: data.total - data.deleted }
));
} else {
growl.error(_t(
'app.admin.calendar.unable_to_delete_the_slot',
{START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT')}
{ START: moment(start_at).format('LL LT'), END: moment(end_at).format('LT') }
));
}
$uibModalInstance.close({
status: 'failed',
availabilities: data.details.filter(function (d) { return d.status }).map(function (d) { return d.availability.id })
availabilities: data.details.filter(function (d) { return d.status; }).map(function (d) { return d.availability.id; })
});
});
}
};
/**
* Cancellation callback
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
}
};
}
]);
/**
* Controller used in the iCalendar (ICS) imports management page
*/
@ -1060,8 +1055,8 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
// failed
growl.error(_t('app.admin.icalendar.create_error'));
console.error(error);
})
}
});
};
/**
* Return a CSS-like style of the given calendar configuration
@ -1070,11 +1065,11 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
$scope.calendarStyle = function (calendar) {
return {
'border-color': calendar.color,
'color': calendar.text_color,
'width': calendar.text_hidden ? '50px' : 'auto',
'height': calendar.text_hidden ? '21px' : 'auto'
color: calendar.text_color,
width: calendar.text_hidden ? '50px' : 'auto',
height: calendar.text_hidden ? '21px' : 'auto'
};
}
};
/**
* Delete the given calendar from the database
@ -1107,8 +1102,8 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
}
);
}
)
}
);
};
/**
* Asynchronously re-fetches the events from the given calendar
@ -1125,7 +1120,7 @@ Application.Controllers.controller('AdminICalendarController', ['$scope', 'iCale
growl.error(_t('app.admin.icalendar.sync_failed'));
console.error(error);
}
)
}
);
};
}
]);

View File

@ -28,11 +28,15 @@ Application.Services.factory('CalendarConfig', [() =>
prev: 'left-single-arrow',
next: 'right-single-arrow'
},
timeFormat: {
agenda: 'H:mm',
month: 'H(:mm)'
},
slotLabelFormat: 'H:mm',
views: {
agendaWeek: {
timeFormat: 'H:mm'
},
month: {
timeFormat: 'H(:mm)'
}
},
allDaySlot: false,
defaultView: 'agendaWeek',

View File

@ -61,7 +61,7 @@
"angular-translate-interpolation-messageformat": "2.18",
"angular-translate-loader-partial": "2.18",
"angular-ui-bootstrap": "1.2.5",
"angular-ui-calendar": "https://github.com/sophilabs-forks/ui-calendar.git#master",
"angular-ui-calendar": "^1.0.2",
"angular-ui-codemirror": "^0.3.0",
"angular-ui-tour": "https://github.com/Ross-Byrne/angular-ui-tour.git#master",
"angular-unsavedchanges": "0.2",
@ -72,7 +72,7 @@
"codemirror": "^4.8.0",
"d3": "3.5",
"elasticsearch-browser": "3.1",
"fullcalendar": "3.3.1",
"fullcalendar": "3.10.2",
"holderjs": "2.6",
"jasny-bootstrap": "3.1",
"jquery": ">=3.5.0",

View File

@ -1346,11 +1346,10 @@ angular-ui-bootstrap@1.2.5:
resolved "https://registry.yarnpkg.com/angular-ui-bootstrap/-/angular-ui-bootstrap-1.2.5.tgz#b0c1eff0bf3b7a65668984a1b81820a90dc60995"
integrity sha1-sMHv8L87emVmiYShuBggqQ3GCZU=
"angular-ui-calendar@https://github.com/sophilabs-forks/ui-calendar.git#master":
angular-ui-calendar@^1.0.2:
version "1.0.2"
resolved "https://github.com/sophilabs-forks/ui-calendar.git#21c4d834cb82f6f4f4c6417669970ff02e193c27"
dependencies:
fullcalendar "~3.3.x"
resolved "https://registry.yarnpkg.com/angular-ui-calendar/-/angular-ui-calendar-1.0.2.tgz#fa271057425572efaadf6660243cc33cb99f33c1"
integrity sha1-+icQV0JVcu+q32ZgJDzDPLmfM8E=
angular-ui-codemirror@^0.3.0:
version "0.3.0"
@ -3897,13 +3896,10 @@ fstream@^1.0.0, fstream@^1.0.12:
mkdirp ">=0.5 0"
rimraf "2"
fullcalendar@3.3.1, fullcalendar@~3.3.x:
version "3.3.1"
resolved "https://registry.yarnpkg.com/fullcalendar/-/fullcalendar-3.3.1.tgz#e8d458d64b7dcb5ba70ce890dda2488b8911539a"
integrity sha1-6NRY1kt9y1unDOiQ3aJIi4kRU5o=
dependencies:
jquery "2 - 3"
moment "^2.9.0"
fullcalendar@3.10.2:
version "3.10.2"
resolved "https://registry.yarnpkg.com/fullcalendar/-/fullcalendar-3.10.2.tgz#9b1ba84bb02803621b761d1bba91a4f18affafb7"
integrity sha512-YWZaHdp8ZLBqhPz615PoXdA49ymsBTUF+MGDM6H3vyz71Pv/ZW9Pm9/Mj3x6n822k6bs2txFO7muRTSvBhsqKg==
function-bind@^1.1.1:
version "1.1.1"
@ -4908,7 +4904,7 @@ jquery-ujs@^1.2.2:
dependencies:
jquery ">=1.8.0"
"jquery@2 - 3", jquery@>=1.8.0, jquery@>=3.5.0:
jquery@>=1.8.0, jquery@>=3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
@ -5573,11 +5569,6 @@ moment@2.22, "moment@>=2.8.0 <3.0.0":
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
moment@^2.9.0:
version "2.29.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.0.tgz#fcbef955844d91deb55438613ddcec56e86a3425"
integrity sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"