1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-11 22:24:21 +01:00
fab-manager/app/assets/javascripts/controllers/admin/events.js

588 lines
17 KiB
JavaScript

/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
'use strict';
/* COMMON CODE */
//#
// Provides a set of common properties and methods to the $scope parameter. They are used
// in the various events' admin controllers.
//
// Provides :
// - $scope.datePicker = {}
// - $scope.submited(content)
// - $scope.cancel()
// - $scope.addFile()
// - $scope.deleteFile(file)
// - $scope.fileinputClass(v)
// - $scope.toggleStartDatePicker($event)
// - $scope.toggleEndDatePicker($event)
// - $scope.toggleRecurrenceEnd(e)
// - $scope.addPrice()
// - $scope.removePrice(price, $event)
//
// Requires :
// - $scope.event.event_files_attributes = []
// - $state (Ui-Router) [ 'app.public.events_list' ]
//#
class EventsController {
constructor($scope, $state) {
//# default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
startOpened: false, // default: datePicker is not shown
endOpened: false,
recurrenceEndOpened: false,
options: {
startingDay: Fablab.weekStartingDay
}
};
//#
// For use with ngUpload (https://github.com/twilson63/ngUpload).
// Intended to be the callback when an upload is done: any raised error will be stacked in the
// $scope.alerts array. If everything goes fine, the user is redirected to the project page.
// @param content {Object} JSON - The upload's result
//#
$scope.submited = function(content) {
if ((content.id == null)) {
$scope.alerts = [];
return angular.forEach(content, (v, k)=>
angular.forEach(v, err=> $scope.alerts.push({msg: k+': '+err, type: 'danger'}))
);
} else {
return $state.go('app.public.events_list');
}
};
//#
// Changes the user's view to the events list page
//#
$scope.cancel = () => $state.go('app.public.events_list');
//#
// For use with 'ng-class', returns the CSS class name for the uploads previews.
// The preview may show a placeholder or the content of the file depending on the upload state.
// @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
//#
$scope.fileinputClass = function(v){
if (v) {
return 'fileinput-exists';
} else {
return 'fileinput-new';
}
};
//#
// This will create a single new empty entry into the event's attachements list.
//#
$scope.addFile = () => $scope.event.event_files_attributes.push({});
//#
// This will remove the given file from the event's attachements list. If the file was previously uploaded
// to the server, it will be marked for deletion on the server. Otherwise, it will be simply truncated from
// the attachements array.
// @param file {Object} the file to delete
//#
$scope.deleteFile = function(file) {
const index = $scope.event.event_files_attributes.indexOf(file);
if (file.id != null) {
return file._destroy = true;
} else {
return $scope.event.event_files_attributes.splice(index, 1);
}
};
//#
// Show/Hide the "start" datepicker (open the drop down/close it)
//#
$scope.toggleStartDatePicker = function($event) {
$event.preventDefault();
$event.stopPropagation();
return $scope.datePicker.startOpened = !$scope.datePicker.startOpened;
};
//#
// Show/Hide the "end" datepicker (open the drop down/close it)
//#
$scope.toggleEndDatePicker = function($event) {
$event.preventDefault();
$event.stopPropagation();
return $scope.datePicker.endOpened = !$scope.datePicker.endOpened;
};
//#
// Masks/displays the recurrence pane allowing the admin to set the current event as recursive
//#
$scope.toggleRecurrenceEnd = function(e){
e.preventDefault();
e.stopPropagation();
return $scope.datePicker.recurrenceEndOpened = !$scope.datePicker.recurrenceEndOpened;
};
//#
// Initialize a new price item in the additional prices list
//#
$scope.addPrice = () =>
$scope.event.prices.push({
category: null,
amount: null,
})
;
//#
// Remove the price or mark it as 'to delete'
//#
$scope.removePrice = function(price, event) {
event.preventDefault();
event.stopPropagation();
if (price.id) {
return price._destroy = true;
} else {
const index = $scope.event.prices.indexOf(price);
return $scope.event.prices.splice(index, 1);
}
};
}
}
//#
// Controller used in the events listing page (admin view)
//#
Application.Controllers.controller("AdminEventsController", ["$scope", "$state", 'dialogs', '$uibModal', 'growl', 'Event', 'Category', 'EventTheme', 'AgeRange', 'PriceCategory', 'eventsPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t'
, function($scope, $state, dialogs, $uibModal, growl, Event, Category, EventTheme, AgeRange, PriceCategory, eventsPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) {
/* PUBLIC SCOPE */
//# By default, the pagination mode is activated to limit the page size
$scope.paginateActive = true;
//# The events displayed on the page
$scope.events = eventsPromise;
//# Current virtual page
$scope.page = 1;
//# Temporary datastore for creating new elements
$scope.inserted = {
category: null,
theme: null,
age_range: null
};
//# List of categories for the events
$scope.categories = categoriesPromise;
//# List of events themes
$scope.themes = themesPromise;
//# List of age ranges
$scope.ageRanges = ageRangesPromise;
//# List of price categories for the events
$scope.priceCategories = priceCategoriesPromise;
//# Default: we display all events (no restriction)
$scope.eventsScope =
{selected: ''};
//#
// Adds a bucket of events to the bottom of the page, grouped by month
//#
$scope.loadMoreEvents = function() {
$scope.page += 1;
return Event.query({ page: $scope.page, scope: $scope.eventsScope.selected }, function(data){
$scope.events = $scope.events.concat(data);
return paginationCheck(data, $scope.events);
});
};
//#
// Saves a new element / Update an existing one to the server (form validation callback)
// @param model {string} model name
// @param data {Object} element name
// @param [id] {number} element id, in case of update
//#
$scope.saveElement = function(model, data, id) {
if (id != null) {
return getModel(model)[0].update({id}, data);
} else {
return getModel(model)[0].save(data, resp=> getModel(model)[1][getModel(model)[1].length-1].id = resp.id);
}
};
//#
// Deletes the element at the specified index
// @param model {string} model name
// @param index {number} element index in the $scope[model] array
//#
$scope.removeElement = function(model, index) {
if ((model === 'category') && (getModel(model)[1].length === 1)) {
growl.error(_t('at_least_one_category_is_required')+' '+_t('unable_to_delete_the_last_one'));
return false;
}
if (getModel(model)[1][index].related_to > 0) {
growl.error(_t('unable_to_delete_ELEMENT_already_in_use_NUMBER_times', {ELEMENT:model, NUMBER:getModel(model)[1][index].related_to}, "messageformat"));
return false;
}
return dialogs.confirm({
resolve: {
object() {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_ELEMENT', {ELEMENT:model}, "messageformat")
};
}
}
}
, () => // delete confirmed
getModel(model)[0].delete(getModel(model)[1][index], null, () => getModel(model)[1].splice(index, 1)
, () => growl.error(_t('unable_to_delete_an_error_occured')))
);
};
//#
// Creates a new empty entry in the $scope[model] array
// @param model {string} model name
//#
$scope.addElement = function(model) {
$scope.inserted[model] = {
name: '',
related_to: 0
};
return getModel(model)[1].push($scope.inserted[model]);
};
//#
// Removes the newly inserted but not saved element / Cancel the current element modification
// @param model {string} model name
// @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
// @param index {number} element index in the $scope[model] array
//#
$scope.cancelElement = function(model, rowform, index) {
if (getModel(model)[1][index].id != null) {
return rowform.$cancel();
} else {
return getModel(model)[1].splice(index, 1);
}
};
//#
// Open a modal dialog allowing the definition of a new price category.
// Save it once filled and handle the result.
//#
$scope.newPriceCategory = () =>
$uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>',
size: 'md',
resolve: {
category() { return {}; }
},
controller: 'PriceCategoryController'}).result['finally'](null).then(p_cat =>
// save the price category to the API
PriceCategory.save(p_cat, function(cat) {
$scope.priceCategories.push(cat);
return growl.success(_t('price_category_successfully_created'));
}
, function(err){
growl.error(_t('unable_to_add_the_price_category_check_name_already_used'));
return console.error(err);
})
)
;
//#
// Update the given price category with the new properties
// to specify in a modal dialog
// @param index {number} index of the caterory in the $scope.priceCategories array
// @param id {number} price category ID, must match the ID of the category at the index specified above
//#
$scope.editPriceCategory = function(id, index) {
if ($scope.priceCategories[index].id !== id) {
return growl.error(_t('unexpected_error_occurred_please_refresh'));
} else {
return $uibModal.open({
templateUrl: '<%= asset_path "admin/events/price_form.html" %>',
size: 'md',
resolve: {
category() { return $scope.priceCategories[index]; }
},
controller: 'PriceCategoryController'}).result['finally'](null).then(p_cat =>
// update the price category to the API
PriceCategory.update({id}, {price_category: p_cat}, function(cat) {
$scope.priceCategories[index] = cat;
return growl.success(_t('price_category_successfully_updated'));
}
, function(err){
growl.error(_t('unable_to_update_the_price_category'));
return console.error(err);
})
);
}
};
//#
// Delete the given price category from the API
// @param index {number} index of the caterory in the $scope.priceCategories array
// @param id {number} price category ID, must match the ID of the category at the index specified above
//#
$scope.removePriceCategory = function(id, index) {
if ($scope.priceCategories[index].id !== id) {
return growl.error(_t('unexpected_error_occurred_please_refresh'));
} else if ($scope.priceCategories[index].events > 0) {
return growl.error(_t('unable_to_delete_this_price_category_because_it_is_already_used'));
} else {
return dialogs.confirm({
resolve: {
object() {
return {
title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_price_category')
};
}
}
}
, () => // delete confirmed
PriceCategory.remove({id}, function() { // successfully deleted
growl.success(_t('price_category_successfully_deleted'));
return $scope.priceCategories.splice(index, 1);
}
, () => growl.error(_t('price_category_deletion_failed')))
);
}
};
//#
// Triggered when the admin changes the events filter (all, passed, future).
// We request the first page of corresponding events to the API
//#
$scope.changeScope = function() {
Event.query({page: 1, scope: $scope.eventsScope.selected}, function(data){
$scope.events = data;
return paginationCheck(data, $scope.events);
});
return $scope.page = 1;
};
/* PRIVATE SCOPE */
//#
// Kind of constructor: these actions will be realized first when the controller is loaded
//#
const initialize = () => paginationCheck(eventsPromise, $scope.events);
//#
// Check if all events are already displayed OR if the button 'load more events'
// is required
// @param lastEvents {Array} last events loaded onto the diplay (ie. last "page")
// @param events {Array} full list of events displayed on the page (not only the last retrieved)
//#
var paginationCheck = function(lastEvents, events){
if (lastEvents.length > 0) {
if (events.length >= lastEvents[0].nb_total_events) {
return $scope.paginateActive = false;
} else {
return $scope.paginateActive = true;
}
} else {
return $scope.paginateActive = false;
}
};
//#
// Return the model and the datastore matching the given name
// @param name {string} 'category', 'theme' or 'age_range'
// @return {[Object, Array]} model and datastore
//#
var getModel = function(name) {
switch (name) {
case 'category': return [Category, $scope.categories];
case 'theme': return [EventTheme, $scope.themes];
case 'age_range': return [AgeRange, $scope.ageRanges];
default: return [null, []];
}
};
// init the controller (call at the end !)
return initialize();
}
]);
//#
// Controller used in the reservations listing page for a specific event
//#
Application.Controllers.controller("ShowEventReservationsController", ["$scope", 'eventPromise', 'reservationsPromise', function($scope, eventPromise, reservationsPromise) {
//# retrieve the event from the ID provided in the current URL
$scope.event = eventPromise;
//# list of reservations for the current event
return $scope.reservations = reservationsPromise;
}
]);
//#
// Controller used in the event creation page
//#
Application.Controllers.controller("NewEventController", ["$scope", "$state", 'CSRF', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t'
, function($scope, $state, CSRF, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) {
CSRF.setMetaTags();
//# API URL where the form will be posted
$scope.actionUrl = "/api/events/";
//# Form action on the above URL
$scope.method = 'post';
//# List of categories for the events
$scope.categories = categoriesPromise;
//# List of events themes
$scope.themes = themesPromise;
//# List of age ranges
$scope.ageRanges = ageRangesPromise;
//# List of availables price's categories
$scope.priceCategories = priceCategoriesPromise;
//# Default event parameters
$scope.event = {
event_files_attributes: [],
start_date: new Date(),
end_date: new Date(),
start_time: new Date(),
end_time: new Date(),
all_day: 'false',
recurrence: 'none',
category_id: null,
prices: []
};
//# Possible types of recurrences for an event
$scope.recurrenceTypes = [
{label: _t('none'), value: 'none'},
{label: _t('every_days'), value: 'day'},
{label: _t('every_week'), value: 'week'},
{label: _t('every_month'), value: 'month'},
{label: _t('every_year'), value: 'year'}
];
//# Using the EventsController
return new EventsController($scope, $state);
}
]);
//#
// Controller used in the events edition page
//#
Application.Controllers.controller("EditEventController", ["$scope", "$state", "$stateParams", 'CSRF', 'eventPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise'
, function($scope, $state, $stateParams, CSRF, eventPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise) {
/* PUBLIC SCOPE */
//# API URL where the form will be posted
$scope.actionUrl = `/api/events/${$stateParams.id}`;
//# Form action on the above URL
$scope.method = 'put';
//# Retrieve the event details, in case of error the user is redirected to the events listing
$scope.event = eventPromise;
//# List of categories for the events
$scope.categories = categoriesPromise;
//# List of availables price's categories
$scope.priceCategories = priceCategoriesPromise;
//# List of events themes
$scope.themes = themesPromise;
//# List of age ranges
$scope.ageRanges = ageRangesPromise;
/* PRIVATE SCOPE */
//#
// Kind of constructor: these actions will be realized first when the controller is loaded
//#
const initialize = function() {
CSRF.setMetaTags();
// init the dates to JS objects
$scope.event.start_date = moment($scope.event.start_date).toDate();
$scope.event.end_date = moment($scope.event.end_date).toDate();
//# Using the EventsController
return new EventsController($scope, $state);
};
//# !!! MUST BE CALLED AT THE END of the controller
return initialize();
}
]);