1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-06 01:08:21 +01:00

javascript fixes

This commit is contained in:
Sylvain 2018-11-21 11:32:50 +01:00
parent f528b2021d
commit 6e56ac4286
9 changed files with 518 additions and 522 deletions

View File

@ -2,6 +2,11 @@
"extends": "standard", "extends": "standard",
"rules": { "rules": {
"semi": ["error", "always"] "semi": ["error", "always"]
},
"globals": {
"Application": true,
"angular": true,
"Fablab": true
} }
} }

2
.rubocop.yml Normal file
View File

@ -0,0 +1,2 @@
Metrics/LineLength:
Max: 120

View File

@ -5,6 +5,7 @@
* creating namespaces and moduled for controllers, filters, services, and directives. * creating namespaces and moduled for controllers, filters, services, and directives.
*/ */
// eslint-disable-next-line no-use-before-define
var Application = Application || {}; var Application = Application || {};
Application.Constants = angular.module('application.constants', []); Application.Constants = angular.module('application.constants', []);
@ -13,29 +14,22 @@ Application.Controllers = angular.module('application.controllers', []);
Application.Filters = angular.module('application.filters', []); Application.Filters = angular.module('application.filters', []);
Application.Directives = angular.module('application.directives', []); Application.Directives = angular.module('application.directives', []);
angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ngCookies', 'ui.router', 'ui.bootstrap', angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ngCookies', 'ui.router', 'ui.bootstrap',
'ngUpload', 'duScroll', 'application.filters', 'application.services', 'application.directives', 'ngUpload', 'duScroll', 'application.filters', 'application.services', 'application.directives',
'frapontillo.bootstrap-switch', 'application.constants', 'application.controllers', 'application.router', 'frapontillo.bootstrap-switch', 'application.constants', 'application.controllers', 'application.router',
'ui.select', 'ui.calendar', 'angularMoment', 'Devise', 'DeviseModal', 'angular-growl', 'xeditable', 'ui.select', 'ui.calendar', 'angularMoment', 'Devise', 'DeviseModal', 'angular-growl', 'xeditable',
'checklist-model', 'unsavedChanges', 'angular-loading-bar', 'ngTouch', 'angular-google-analytics', 'checklist-model', 'unsavedChanges', 'angular-loading-bar', 'ngTouch', 'angular-google-analytics',
'angularUtils.directives.dirDisqus', 'summernote', 'elasticsearch', 'angular-medium-editor', 'naif.base64', 'angularUtils.directives.dirDisqus', 'summernote', 'elasticsearch', 'angular-medium-editor', 'naif.base64',
'minicolors', 'pascalprecht.translate', 'ngFitText', 'ngAside', 'ngCapsLock']). 'minicolors', 'pascalprecht.translate', 'ngFitText', 'ngAside', 'ngCapsLock'])
config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfigProvider", "AnalyticsProvider", "uibDatepickerPopupConfig", "$provide", "$translateProvider", .config(['$httpProvider', 'AuthProvider', 'growlProvider', 'unsavedWarningsConfigProvider', 'AnalyticsProvider', 'uibDatepickerPopupConfig', '$provide', '$translateProvider',
function ($httpProvider, AuthProvider, growlProvider, unsavedWarningsConfigProvider, AnalyticsProvider, uibDatepickerPopupConfig, $provide, $translateProvider) { function ($httpProvider, AuthProvider, growlProvider, unsavedWarningsConfigProvider, AnalyticsProvider, uibDatepickerPopupConfig, $provide, $translateProvider) {
// Google analytics // Google analytics
<% #if Rails.env.production? %>
AnalyticsProvider.setAccount(Fablab.gaId); AnalyticsProvider.setAccount(Fablab.gaId);
// track all routes (or not) // track all routes (or not)
AnalyticsProvider.trackPages(true); AnalyticsProvider.trackPages(true);
AnalyticsProvider.setDomainName(Fablab.defaultHost); AnalyticsProvider.setDomainName(Fablab.defaultHost);
AnalyticsProvider.useAnalytics(true); AnalyticsProvider.useAnalytics(true);
AnalyticsProvider.setPageEvent('$stateChangeSuccess'); AnalyticsProvider.setPageEvent('$stateChangeSuccess');
<% #else %>
//AnalyticsProvider.setAccount('DISABLED');
<% #end %>
// Custom messages for the date-picker widget // Custom messages for the date-picker widget
uibDatepickerPopupConfig.closeText = Fablab.translations.app.shared.buttons.close; uibDatepickerPopupConfig.closeText = Fablab.translations.app.shared.buttons.close;
@ -61,10 +55,8 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
$translateProvider.addInterpolation('$translateMessageFormatInterpolation'); $translateProvider.addInterpolation('$translateMessageFormatInterpolation');
// Set the langage of the instance (from ruby configuration) // Set the langage of the instance (from ruby configuration)
$translateProvider.preferredLanguage(Fablab.locale); $translateProvider.preferredLanguage(Fablab.locale);
}]).run(['$rootScope', '$log', 'AuthService', 'Auth', 'amMoment', '$state', 'editableOptions', 'Analytics',
}]).run(["$rootScope", "$log", "AuthService", "Auth", "amMoment", "$state", "editableOptions", 'Analytics',
function ($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions, Analytics) { function ($rootScope, $log, AuthService, Auth, amMoment, $state, editableOptions, Analytics) {
// Angular-moment (date-time manipulations library) // Angular-moment (date-time manipulations library)
amMoment.changeLocale(Fablab.moment_locale); amMoment.changeLocale(Fablab.moment_locale);
@ -88,8 +80,8 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
$rootScope.backPrevLocation = function (event) { $rootScope.backPrevLocation = function (event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if($state.prevState.name == ""){ if ($state.prevState.name === '') {
$state.prevState = "app.public.home"; $state.prevState = 'app.public.home';
} }
$state.go($state.prevState, $state.prevParams); $state.go($state.prevState, $state.prevParams);
}; };
@ -117,7 +109,7 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
// the 'profile completion' page. This is especially useful for user's accounts imported through SSO. // the 'profile completion' page. This is especially useful for user's accounts imported through SSO.
$rootScope.$on('$stateChangeStart', function (event, toState) { $rootScope.$on('$stateChangeStart', function (event, toState) {
Auth.currentUser().then(function (currentUser) { Auth.currentUser().then(function (currentUser) {
if (currentUser.need_completion && toState.name != 'app.logged.profileCompletion') { if (currentUser.need_completion && toState.name !== 'app.logged.profileCompletion') {
$state.go('app.logged.profileCompletion'); $state.go('app.logged.profileCompletion');
} }
}); });
@ -127,7 +119,6 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
// see https://github.com/revolunet/angular-google-analytics#automatic-page-view-tracking // see https://github.com/revolunet/angular-google-analytics#automatic-page-view-tracking
Analytics.pageView(); Analytics.pageView();
/** /**
* This helper method builds and return an array contaning every integers between * This helper method builds and return an array contaning every integers between
* the provided start and end. * the provided start and end.
@ -140,11 +131,10 @@ config(['$httpProvider', 'AuthProvider', "growlProvider", "unsavedWarningsConfig
for (var i = start; i < end; i++) { arr.push(i); } for (var i = start; i < end; i++) { arr.push(i); }
return arr; return arr;
}; };
}]).constant('angularMomentConfig', { }]).constant('angularMomentConfig', {
timezone: Fablab.timezone timezone: Fablab.timezone
}); });
angular.isUndefinedOrNull = function (val) { angular.isUndefinedOrNull = function (val) {
return angular.isUndefined(val) || val === null return angular.isUndefined(val) || val === null;
}; };

View File

@ -14,20 +14,19 @@
Application.Controllers.controller('ApplicationController', ['$rootScope', '$scope', '$window', '$locale', 'Session', 'AuthService', 'Auth', '$uibModal', '$state', 'growl', 'Notification', '$interval', 'Setting', '_t', 'Version', Application.Controllers.controller('ApplicationController', ['$rootScope', '$scope', '$window', '$locale', 'Session', 'AuthService', 'Auth', '$uibModal', '$state', 'growl', 'Notification', '$interval', 'Setting', '_t', 'Version',
function ($rootScope, $scope, $window, $locale, Session, AuthService, Auth, $uibModal, $state, growl, Notification, $interval, Setting, _t, Version) { function ($rootScope, $scope, $window, $locale, Session, AuthService, Auth, $uibModal, $state, growl, Notification, $interval, Setting, _t, Version) {
/* PRIVATE STATIC CONSTANTS */ /* PRIVATE STATIC CONSTANTS */
// User's notifications will get refreshed every 30s // User's notifications will get refreshed every 30s
const NOTIFICATIONS_CHECK_PERIOD = 30000 const NOTIFICATIONS_CHECK_PERIOD = 30000;
/* PUBLIC SCOPE */ /* PUBLIC SCOPE */
// Fab-manager's version // Fab-manager's version
$scope.version = $scope.version =
{ version: '' } { version: '' };
// currency symbol for the current locale (cf. angular-i18n) // currency symbol for the current locale (cf. angular-i18n)
$rootScope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM $rootScope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM;
/** /**
* Set the current user to the provided value and initialize the session * Set the current user to the provided value and initialize the session
@ -35,17 +34,17 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
*/ */
$scope.setCurrentUser = function (user) { $scope.setCurrentUser = function (user) {
if (!angular.isUndefinedOrNull(user)) { if (!angular.isUndefinedOrNull(user)) {
$rootScope.currentUser = user $rootScope.currentUser = user;
Session.create(user) Session.create(user);
getNotifications() getNotifications();
// fab-manager's app-version // fab-manager's app-version
if (user.role === 'admin') { if (user.role === 'admin') {
return $scope.version = Version.get() return $scope.version = Version.get();
} else { } else {
return $scope.version = { version: '' } return $scope.version = { version: '' };
}
} }
} }
};
/** /**
* Login callback * Login callback
@ -53,36 +52,36 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
* @param callback {function} * @param callback {function}
*/ */
$scope.login = function (e, callback) { $scope.login = function (e, callback) {
if (e) { e.preventDefault() } if (e) { e.preventDefault(); }
return openLoginModal(null, null, callback) return openLoginModal(null, null, callback);
} };
/** /**
* Logout callback * Logout callback
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event- * @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.logout = function (e) { $scope.logout = function (e) {
e.preventDefault() e.preventDefault();
return Auth.logout().then(function () { return Auth.logout().then(function () {
Session.destroy() Session.destroy();
$rootScope.currentUser = null $rootScope.currentUser = null;
$rootScope.toCheckNotifications = false $rootScope.toCheckNotifications = false;
$scope.notifications = { $scope.notifications = {
total: 0, total: 0,
unread: 0 unread: 0
} };
return $state.go('app.public.home') return $state.go('app.public.home');
}, function (error) { }, function (error) {
console.error(`An error occurred logging out: ${error}`); console.error(`An error occurred logging out: ${error}`);
}) });
} };
/** /**
* Open the modal window allowing the user to create an account. * Open the modal window allowing the user to create an account.
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event- * @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.signup = function (e) { $scope.signup = function (e) {
if (e) { e.preventDefault() } if (e) { e.preventDefault(); }
return $uibModal.open({ return $uibModal.open({
templateUrl: '<%= asset_path "shared/signupModal.html" %>', templateUrl: '<%= asset_path "shared/signupModal.html" %>',
@ -95,33 +94,33 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
options: { options: {
startingDay: Fablab.weekStartingDay startingDay: Fablab.weekStartingDay
} }
} };
// callback to open the date picker (account creation modal) // callback to open the date picker (account creation modal)
$scope.openDatePicker = function ($event) { $scope.openDatePicker = function ($event) {
$event.preventDefault() $event.preventDefault();
$event.stopPropagation() $event.stopPropagation();
return $scope.datePicker.opened = true return $scope.datePicker.opened = true;
} };
// retrieve the groups (standard, student ...) // retrieve the groups (standard, student ...)
Group.query(function (groups) { Group.query(function (groups) {
$scope.groups = groups; $scope.groups = groups;
}) });
// retrieve the CGU // retrieve the CGU
CustomAsset.get({ name: 'cgu-file' }, function (cgu) { CustomAsset.get({ name: 'cgu-file' }, function (cgu) {
$scope.cgu = cgu.custom_asset; $scope.cgu = cgu.custom_asset;
}) });
// default user's parameters // default user's parameters
$scope.user = { $scope.user = {
is_allow_contact: true, is_allow_contact: true,
is_allow_newsletter: false is_allow_newsletter: false
} };
// Errors display // Errors display
$scope.alerts = [] $scope.alerts = [];
$scope.closeAlert = function (index) { $scope.closeAlert = function (index) {
$scope.alerts.splice(index, 1); $scope.alerts.splice(index, 1);
}; };
@ -136,11 +135,11 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
// register on server // register on server
return Auth.register($scope.user).then(function (user) { return Auth.register($scope.user).then(function (user) {
// creation successful // creation successful
$uibModalInstance.close(user) $uibModalInstance.close(user);
}, function (error) { }, function (error) {
// creation failed... // creation failed...
// restore organization param // restore organization param
$scope.user.organization = orga $scope.user.organization = orga;
// display errors // display errors
angular.forEach(error.data.errors, function (v, k) { angular.forEach(error.data.errors, function (v, k) {
angular.forEach(function (v, err) { angular.forEach(function (v, err) {
@ -150,14 +149,14 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
}); });
}); });
}); });
}) });
}; };
}] }]
}).result['finally'](null).then(function (user) { }).result['finally'](null).then(function (user) {
// when the account was created successfully, set the session to the newly created account // when the account was created successfully, set the session to the newly created account
$scope.setCurrentUser(user) $scope.setCurrentUser(user);
}) });
} };
/** /**
* Open the modal window allowing the user to change his password. * Open the modal window allowing the user to change his password.
@ -171,12 +170,12 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$scope.user = { reset_password_token: token }; $scope.user = { reset_password_token: token };
$scope.alerts = []; $scope.alerts = [];
$scope.closeAlert = function (index) { $scope.closeAlert = function (index) {
$scope.alerts.splice(index, 1) $scope.alerts.splice(index, 1);
}; };
return $scope.changePassword = function () { return $scope.changePassword = function () {
$scope.alerts = [] $scope.alerts = [];
return $http.put('/users/password.json', { user: $scope.user }).success(function () { $uibModalInstance.close() }).error(function (data) { return $http.put('/users/password.json', { user: $scope.user }).success(function () { $uibModalInstance.close(); }).error(function (data) {
angular.forEach(data.errors, function (v, k) { angular.forEach(data.errors, function (v, k) {
angular.forEach(function (v, err) { angular.forEach(function (v, err) {
$scope.alerts.push({ $scope.alerts.push({
@ -185,18 +184,18 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
}); });
}); });
}); });
}) });
} };
}] }]
}).result['finally'](null).then(function () { }).result['finally'](null).then(function () {
growl.success(_t('your_password_was_successfully_changed')) growl.success(_t('your_password_was_successfully_changed'));
return Auth.login().then(function (user) { return Auth.login().then(function (user) {
$scope.setCurrentUser(user) $scope.setCurrentUser(user);
}, function (error) { }, function (error) {
console.error(`Authentication failed: ${error}`); console.error(`Authentication failed: ${error}`);
} }
) );
}) });
}; };
/** /**
@ -204,25 +203,25 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event- * @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.toggleNavSize = function (event) { $scope.toggleNavSize = function (event) {
let $classes, $targets let $classes, $targets;
if (typeof event === 'undefined') { if (typeof event === 'undefined') {
console.error('[ApplicationController::toggleNavSize] Missing event parameter') console.error('[ApplicationController::toggleNavSize] Missing event parameter');
return return;
} }
let toggler = $(event.target) let toggler = $(event.target);
if (!toggler.data('toggle')) { toggler = toggler.closest('[data-toggle^="class"]') } if (!toggler.data('toggle')) { toggler = toggler.closest('[data-toggle^="class"]'); }
const $class = toggler.data()['toggle'] const $class = toggler.data()['toggle'];
const $target = toggler.data('target') || toggler.attr('data-link') const $target = toggler.data('target') || toggler.attr('data-link');
if ($class) { if ($class) {
const $tmp = $class.split(':')[1] const $tmp = $class.split(':')[1];
if ($tmp) { $classes = $tmp.split(',') } if ($tmp) { $classes = $tmp.split(','); }
} }
if ($target) { if ($target) {
$targets = $target.split(',') $targets = $target.split(',');
} }
if ($classes && $classes.length) { if ($classes && $classes.length) {
@ -230,22 +229,22 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
if ($classes[index].indexOf('*') !== -1) { if ($classes[index].indexOf('*') !== -1) {
const patt = new RegExp('\\s', const patt = new RegExp('\\s',
+$classes[index].replace(/\*/g, '[A-Za-z0-9-_]+').split(' ').join('\\s|\\s'), +$classes[index].replace(/\*/g, '[A-Za-z0-9-_]+').split(' ').join('\\s|\\s'),
+'\\s', 'g') +'\\s', 'g');
$(toggler).each(function (i, it) { $(toggler).each(function (i, it) {
let cn = ` ${it.className} ` let cn = ` ${it.className} `;
while (patt.test(cn)) { while (patt.test(cn)) {
cn = cn.replace(patt, ' ') cn = cn.replace(patt, ' ');
} }
return it.className = $.trim(cn) return it.className = $.trim(cn);
}) });
} }
return (($targets[index] !== '#') && $($targets[index]).toggleClass($classes[index])) || toggler.toggleClass($classes[index]) return (($targets[index] !== '#') && $($targets[index]).toggleClass($classes[index])) || toggler.toggleClass($classes[index]);
}) });
} }
toggler.toggleClass('active') toggler.toggleClass('active');
} };
/* PRIVATE SCOPE */ /* PRIVATE SCOPE */
/** /**
@ -254,69 +253,68 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
const initialize = function () { const initialize = function () {
// try to retrieve any currently logged user // try to retrieve any currently logged user
Auth.login().then(function (user) { Auth.login().then(function (user) {
$scope.setCurrentUser(user) $scope.setCurrentUser(user);
// force users to complete their profile if they are not // force users to complete their profile if they are not
if (user.need_completion) { if (user.need_completion) {
return $state.transitionTo('app.logged.profileCompletion') return $state.transitionTo('app.logged.profileCompletion');
} }
}, function (error) { }, function (error) {
console.error(`Authentication failed: ${error}`); console.error(`Authentication failed: ${error}`);
$rootScope.toCheckNotifications = false $rootScope.toCheckNotifications = false;
}) });
// bind to the $stateChangeStart event (AngularJS/UI-Router) // bind to the $stateChangeStart event (AngularJS/UI-Router)
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) { $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
if (!toState.data) { return } if (!toState.data) { return; }
const { authorizedRoles } = toState.data const { authorizedRoles } = toState.data;
if (!AuthService.isAuthorized(authorizedRoles)) { if (!AuthService.isAuthorized(authorizedRoles)) {
event.preventDefault() event.preventDefault();
if (AuthService.isAuthenticated()) { if (AuthService.isAuthenticated()) {
// user is not allowed // user is not allowed
console.error('[ApplicationController::initialize] user is not allowed') console.error('[ApplicationController::initialize] user is not allowed');
} else { } else {
// user is not logged in // user is not logged in
openLoginModal(toState, toParams) openLoginModal(toState, toParams);
} }
} }
}) });
// we stop polling notifications when the page is not in foreground // we stop polling notifications when the page is not in foreground
onPageVisible(function(state) { $rootScope.toCheckNotifications = (state === 'visible') }) onPageVisible(function (state) { $rootScope.toCheckNotifications = (state === 'visible'); });
Setting.get({ name: 'fablab_name' }, function(data) { $scope.fablabName = data.setting.value }) Setting.get({ name: 'fablab_name' }, function (data) { $scope.fablabName = data.setting.value; });
Setting.get({ name: 'name_genre' }, function(data) { $scope.nameGenre = data.setting.value }) Setting.get({ name: 'name_genre' }, function (data) { $scope.nameGenre = data.setting.value; });
// shorthands // shorthands
$scope.isAuthenticated = Auth.isAuthenticated $scope.isAuthenticated = Auth.isAuthenticated;
$scope.isAuthorized = AuthService.isAuthorized $scope.isAuthorized = AuthService.isAuthorized;
return $rootScope.login = $scope.login return $rootScope.login = $scope.login;
} };
/** /**
* Retreive once the notifications from the server and display a message popup for each new one. * Retreive once the notifications from the server and display a message popup for each new one.
* Then, periodically check for new notifications. * Then, periodically check for new notifications.
*/ */
var getNotifications = function () { var getNotifications = function () {
$rootScope.toCheckNotifications = true $rootScope.toCheckNotifications = true;
if (!$rootScope.checkNotificationsIsInit && !!$rootScope.currentUser) { if (!$rootScope.checkNotificationsIsInit && !!$rootScope.currentUser) {
setTimeout(function () { setTimeout(function () {
// we request the most recent notifications // we request the most recent notifications
Notification.last_unread(function (notifications) { Notification.last_unread(function (notifications) {
$rootScope.lastCheck = new Date() $rootScope.lastCheck = new Date();
$scope.notifications = notifications.totals $scope.notifications = notifications.totals;
const toDisplay = []; const toDisplay = [];
angular.forEach(notifications.notifications, function(n) { toDisplay.push(n) }); angular.forEach(notifications.notifications, function (n) { toDisplay.push(n); });
if (toDisplay.length < notifications.totals.unread) { if (toDisplay.length < notifications.totals.unread) {
toDisplay.push({ message: { description: _t('and_NUMBER_other_notifications', { NUMBER: notifications.totals.unread - toDisplay.length }, 'messageformat') } }); toDisplay.push({ message: { description: _t('and_NUMBER_other_notifications', { NUMBER: notifications.totals.unread - toDisplay.length }, 'messageformat') } });
} }
angular.forEach(toDisplay, function(notification) { growl.info(notification.message.description); }) angular.forEach(toDisplay, function (notification) { growl.info(notification.message.description); });
}) });
}, 2000);
}, 2000)
const checkNotifications = function () { const checkNotifications = function () {
if ($rootScope.toCheckNotifications) { if ($rootScope.toCheckNotifications) {
@ -324,15 +322,15 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$rootScope.lastCheck = new Date(); $rootScope.lastCheck = new Date();
$scope.notifications = data.totals; $scope.notifications = data.totals;
angular.forEach(data.notifications, function(notification) { growl.info(notification.message.description) }); angular.forEach(data.notifications, function (notification) { growl.info(notification.message.description); });
}) });
} }
}; };
$interval(checkNotifications, NOTIFICATIONS_CHECK_PERIOD); $interval(checkNotifications, NOTIFICATIONS_CHECK_PERIOD);
$rootScope.checkNotificationsIsInit = true; $rootScope.checkNotificationsIsInit = true;
} }
} };
/** /**
* Open the modal window allowing the user to log in. * Open the modal window allowing the user to log in.
@ -340,19 +338,19 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
var openLoginModal = function (toState, toParams, callback) { var openLoginModal = function (toState, toParams, callback) {
<% active_provider = AuthProvider.active %> <% active_provider = AuthProvider.active %>
<% if active_provider.providable_type != DatabaseProvider.name %> <% if active_provider.providable_type != DatabaseProvider.name %>
$window.location.href = '<%=user_omniauth_authorize_path(AuthProvider.active.strategy_name.to_sym)%>' $window.location.href = '<%=user_omniauth_authorize_path(AuthProvider.active.strategy_name.to_sym)%>';
<% else %> <% else %>
return $uibModal.open({ return $uibModal.open({
templateUrl: '<%= asset_path "shared/deviseModal.html" %>', templateUrl: '<%= asset_path "shared/deviseModal.html" %>',
size: 'sm', size: 'sm',
controller: ['$scope', '$uibModalInstance', '_t', function ($scope, $uibModalInstance, _t) { controller: ['$scope', '$uibModalInstance', '_t', function ($scope, $uibModalInstance, _t) {
const user = ($scope.user = {}) const user = ($scope.user = {});
$scope.login = function () { $scope.login = function () {
Auth.login(user).then(function (user) { Auth.login(user).then(function (user) {
// Authentication succeeded ... // Authentication succeeded ...
$uibModalInstance.close(user) $uibModalInstance.close(user);
if (callback && (typeof callback === 'function')) { if (callback && (typeof callback === 'function')) {
return callback(user) return callback(user);
} }
} }
, function (error) { , function (error) {
@ -361,21 +359,21 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
return $scope.alerts.push({ return $scope.alerts.push({
msg: _t('wrong_email_or_password'), msg: _t('wrong_email_or_password'),
type: 'danger' type: 'danger'
}) });
}) });
} };
// handle modal behaviors. The provided reason will be used to define the following actions // handle modal behaviors. The provided reason will be used to define the following actions
$scope.dismiss = function() { $uibModalInstance.dismiss('cancel') } $scope.dismiss = function () { $uibModalInstance.dismiss('cancel'); };
$scope.openSignup = function (e) { $scope.openSignup = function (e) {
e.preventDefault() e.preventDefault();
return $uibModalInstance.dismiss('signup') return $uibModalInstance.dismiss('signup');
} };
return $scope.openResetPassword = function (e) { return $scope.openResetPassword = function (e) {
e.preventDefault() e.preventDefault();
return $uibModalInstance.dismiss('resetPassword') return $uibModalInstance.dismiss('resetPassword');
} };
}] }]
}).result['finally'](null).then(function (user) { }).result['finally'](null).then(function (user) {
// what to do when the modal is closed // what to do when the modal is closed
@ -384,7 +382,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
$scope.setCurrentUser(user); $scope.setCurrentUser(user);
if ((toState !== null) && (toParams !== null)) { if ((toState !== null) && (toParams !== null)) {
return $state.go(toState, toParams) return $state.go(toState, toParams);
} }
}, function (reason) { }, function (reason) {
// authentication did not ended successfully // authentication did not ended successfully
@ -397,21 +395,21 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
templateUrl: '<%= asset_path "shared/passwordNewModal.html" %>', templateUrl: '<%= asset_path "shared/passwordNewModal.html" %>',
size: 'sm', size: 'sm',
controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) { controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) {
$scope.user = { email: '' } $scope.user = { email: '' };
return $scope.sendReset = function () { return $scope.sendReset = function () {
$scope.alerts = [] $scope.alerts = [];
return $http.post('/users/password.json', { user: $scope.user }).success(function() { $uibModalInstance.close() }).error(function() { return $http.post('/users/password.json', { user: $scope.user }).success(function () { $uibModalInstance.close(); }).error(function () {
$scope.alerts.push({ $scope.alerts.push({
msg: _t('your_email_address_is_unknown'), msg: _t('your_email_address_is_unknown'),
type: 'danger' type: 'danger'
}); });
}) });
} };
}] }]
}).result['finally'](null).then(function() { growl.info(_t('you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')) }) }).result['finally'](null).then(function () { growl.info(_t('you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')); });
}
})
} }
});
};
// otherwise the user just closed the modal // otherwise the user just closed the modal
<% end %> <% end %>
@ -422,11 +420,11 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
* Inspired by http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#answer-1060034 * Inspired by http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#answer-1060034
*/ */
var onPageVisible = function (callback) { var onPageVisible = function (callback) {
let hidden = 'hidden' let hidden = 'hidden';
const onchange = function (evt) { const onchange = function (evt) {
const v = 'visible' const v = 'visible';
const h = 'hidden' const h = 'hidden';
const evtMap = { const evtMap = {
focus: v, focus: v,
focusin: v, focusin: v,
@ -434,38 +432,38 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
blur: h, blur: h,
focusout: h, focusout: h,
pagehide: h pagehide: h
} };
evt = evt || window.event evt = evt || window.event;
if (evt.type in evtMap) { if (evt.type in evtMap) {
if (typeof callback === 'function') { callback(evtMap[evt.type]) } if (typeof callback === 'function') { callback(evtMap[evt.type]); }
} else { } else {
if (typeof callback === 'function') { callback(this[hidden] ? 'hidden' : 'visible') } if (typeof callback === 'function') { callback(this[hidden] ? 'hidden' : 'visible'); }
}
} }
};
// Standards: // Standards:
if (hidden in document) { if (hidden in document) {
document.addEventListener('visibilitychange', onchange) document.addEventListener('visibilitychange', onchange);
} else if ((hidden = 'mozHidden') in document) { } else if ((hidden = 'mozHidden') in document) {
document.addEventListener('mozvisibilitychange', onchange) document.addEventListener('mozvisibilitychange', onchange);
} else if ((hidden = 'webkitHidden') in document) { } else if ((hidden = 'webkitHidden') in document) {
document.addEventListener('webkitvisibilitychange', onchange) document.addEventListener('webkitvisibilitychange', onchange);
} else if ((hidden = 'msHidden') in document) { } else if ((hidden = 'msHidden') in document) {
document.addEventListener('msvisibilitychange', onchange) document.addEventListener('msvisibilitychange', onchange);
// IE 9 and lower // IE 9 and lower
} else if ('onfocusin' in document) { } else if ('onfocusin' in document) {
document.onfocusin = (document.onfocusout = onchange) document.onfocusin = (document.onfocusout = onchange);
// All others // All others
} else { } else {
window.onpageshow = (window.onpagehide = (window.onfocus = (window.onblur = onchange))) window.onpageshow = (window.onpagehide = (window.onfocus = (window.onblur = onchange)));
} }
// set the initial state (but only if browser supports the Page Visibility API) // set the initial state (but only if browser supports the Page Visibility API)
if (document[hidden] !== undefined) { if (document[hidden] !== undefined) {
return onchange({ type: document[hidden] ? 'blur' : 'focus' }) return onchange({ type: document[hidden] ? 'blur' : 'focus' });
}
} }
};
// !!! MUST BE CALLED AT THE END of the controller // !!! MUST BE CALLED AT THE END of the controller
return initialize() return initialize();
} }
]) ]);

View File

@ -18,46 +18,46 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
/* PUBLIC SCOPE */ /* PUBLIC SCOPE */
// The events displayed on the page // The events displayed on the page
$scope.events = [] $scope.events = [];
// The currently displayed page number // The currently displayed page number
$scope.page = 1 $scope.page = 1;
// List of categories for the events // List of categories for the events
$scope.categories = categoriesPromise $scope.categories = categoriesPromise;
// List of events themes // List of events themes
$scope.themes = themesPromise $scope.themes = themesPromise;
// List of age ranges // List of age ranges
$scope.ageRanges = ageRangesPromise $scope.ageRanges = ageRangesPromise;
// Hide or show the 'load more' button // Hide or show the 'load more' button
$scope.noMoreResults = false $scope.noMoreResults = false;
// Active filters for the events list // Active filters for the events list
$scope.filters = { $scope.filters = {
category_id: null, category_id: null,
theme_id: null, theme_id: null,
age_range_id: null age_range_id: null
} };
$scope.monthNames = [<%= t('date.month_names')[1..-1].map { |m| "\"#{m}\"" }.join(', ') %>] $scope.monthNames = [<%= t('date.month_names')[1..-1].map { |m| "\"#{m}\"" }.join(', ') %>];
/** /**
* Adds a resultset of events to the bottom of the page, grouped by month * Adds a resultset of events to the bottom of the page, grouped by month
*/ */
$scope.loadMoreEvents = function () { $scope.loadMoreEvents = function () {
$scope.page += 1 $scope.page += 1;
return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) { return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) {
$scope.events = $scope.events.concat(data) $scope.events = $scope.events.concat(data);
groupEvents($scope.events) groupEvents($scope.events);
if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) { if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) {
return $scope.noMoreResults = true return $scope.noMoreResults = true;
}
})
} }
});
};
/** /**
* Callback to redirect the user to the specified event page * Callback to redirect the user to the specified event page
@ -70,22 +70,22 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
*/ */
$scope.filterEvents = function () { $scope.filterEvents = function () {
// reinitialize results datasets // reinitialize results datasets
$scope.page = 1 $scope.page = 1;
$scope.eventsGroupByMonth = {} $scope.eventsGroupByMonth = {};
$scope.events = [] $scope.events = [];
$scope.monthOrder = [] $scope.monthOrder = [];
$scope.noMoreResults = false $scope.noMoreResults = false;
// run a search query // run a search query
return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) { return Event.query(Object.assign({ page: $scope.page }, $scope.filters), function (data) {
$scope.events = data $scope.events = data;
groupEvents(data) groupEvents(data);
if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) { if (!data[0] || (data[0].nb_total_events <= $scope.events.length)) {
return $scope.noMoreResults = true return $scope.noMoreResults = true;
}
})
} }
});
};
/** /**
* Test if the provided event occurs on a single day or on many days * Test if the provided event occurs on a single day or on many days
@ -115,27 +115,27 @@ Application.Controllers.controller('EventsController', ['$scope', '$state', 'Eve
return _.map(['month_id', 'year'], function (key) { return _.map(['month_id', 'year'], function (key) {
return obj[key]; return obj[key];
}); });
}) });
$scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth) $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth);
return $scope.monthOrder = Object.keys($scope.eventsGroupByMonth) return $scope.monthOrder = Object.keys($scope.eventsGroupByMonth);
}
} }
};
// # !!! MUST BE CALLED AT THE END of the controller // # !!! MUST BE CALLED AT THE END of the controller
initialize() initialize();
} }
]) ]);
Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise', Application.Controllers.controller('ShowEventController', ['$scope', '$state', '$stateParams', 'Event', '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'growl', '_t', 'Wallet', 'helpers', 'dialogs', 'priceCategoriesPromise', 'settingsPromise',
function ($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, growl, _t, Wallet, helpers, dialogs, priceCategoriesPromise, settingsPromise) { function ($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, growl, _t, Wallet, helpers, dialogs, priceCategoriesPromise, settingsPromise) {
/* PUBLIC SCOPE */ /* PUBLIC SCOPE */
// reservations for the currently shown event // reservations for the currently shown event
$scope.reservations = [] $scope.reservations = [];
// user to deal with // user to deal with
$scope.ctrl = $scope.ctrl =
{ member: {} } { member: {} };
// parameters for a new reservation // parameters for a new reservation
$scope.reserve = { $scope.reserve = {
@ -148,26 +148,26 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
amountTotal: 0, amountTotal: 0,
totalNoCoupon: 0, totalNoCoupon: 0,
totalSeats: 0 totalSeats: 0
} };
// Discount coupon to apply to the basket, if any // Discount coupon to apply to the basket, if any
$scope.coupon = $scope.coupon =
{ applied: null } { applied: null };
// Get the details for the current event (event's id is recovered from the current URL) // Get the details for the current event (event's id is recovered from the current URL)
$scope.event = eventPromise $scope.event = eventPromise;
// List of price categories for the events // List of price categories for the events
$scope.priceCategories = priceCategoriesPromise $scope.priceCategories = priceCategoriesPromise;
// Global config: is the user authorized to change his bookings slots? // Global config: is the user authorized to change his bookings slots?
$scope.enableBookingMove = (settingsPromise.booking_move_enable === 'true') $scope.enableBookingMove = (settingsPromise.booking_move_enable === 'true');
// Global config: delay in hours before a booking while changing the booking slot is forbidden // Global config: delay in hours before a booking while changing the booking slot is forbidden
$scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay) $scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay);
// Message displayed to the end user about rules that applies to events reservations // Message displayed to the end user about rules that applies to events reservations
$scope.eventExplicationsAlert = settingsPromise.event_explications_alert $scope.eventExplicationsAlert = settingsPromise.event_explications_alert;
/** /**
* Callback to delete the provided event (admins only) * Callback to delete the provided event (admins only)
@ -180,91 +180,92 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
return { return {
title: _t('confirmation_required'), title: _t('confirmation_required'),
msg: _t('do_you_really_want_to_delete_this_event') msg: _t('do_you_really_want_to_delete_this_event')
} };
} }
} }
}, function () { }, function () {
// the admin has confirmed, delete // the admin has confirmed, delete
event.$delete(function () { event.$delete(function () {
$state.go('app.public.events_list') $state.go('app.public.events_list');
return growl.info(_t('event_successfully_deleted')) return growl.info(_t('event_successfully_deleted'));
}, function (error) { }, function (error) {
console.error(error); console.error(error);
growl.error(_t('unable_to_delete_the_event_because_some_users_alredy_booked_it')); growl.error(_t('unable_to_delete_the_event_because_some_users_alredy_booked_it'));
})} });
)
} }
);
};
/** /**
* Callback to call when the number of tickets to book changes in the current booking * Callback to call when the number of tickets to book changes in the current booking
*/ */
$scope.changeNbPlaces = function () { $scope.changeNbPlaces = function () {
// compute the total remaning places // compute the total remaning places
let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces;
for (let ticket in $scope.reserve.tickets) { for (let ticket in $scope.reserve.tickets) {
remain -= $scope.reserve.tickets[ticket] remain -= $scope.reserve.tickets[ticket];
} }
// we store the total number of seats booked, this is used to know if the 'pay' button must be shown // we store the total number of seats booked, this is used to know if the 'pay' button must be shown
$scope.reserve.totalSeats = $scope.event.nb_free_places - remain $scope.reserve.totalSeats = $scope.event.nb_free_places - remain;
// update the available seats for full price tickets // update the available seats for full price tickets
const fullPriceRemains = $scope.reserve.nbReservePlaces + remain const fullPriceRemains = $scope.reserve.nbReservePlaces + remain;
$scope.reserve.nbPlaces.normal = __range__(0, fullPriceRemains, true) $scope.reserve.nbPlaces.normal = __range__(0, fullPriceRemains, true);
// update the available seats for other prices tickets // update the available seats for other prices tickets
for (let key in $scope.reserve.nbPlaces) { for (let key in $scope.reserve.nbPlaces) {
if (key !== 'normal') { if (key !== 'normal') {
const priceRemain = $scope.reserve.tickets[key] + remain const priceRemain = $scope.reserve.tickets[key] + remain;
$scope.reserve.nbPlaces[key] = __range__(0, priceRemain, true) $scope.reserve.nbPlaces[key] = __range__(0, priceRemain, true);
} }
} }
// recompute the total price // recompute the total price
return $scope.computeEventAmount() return $scope.computeEventAmount();
} };
/** /**
* Callback to reset the current reservation parameters * Callback to reset the current reservation parameters
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event- * @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.cancelReserve = function (e) { $scope.cancelReserve = function (e) {
e.preventDefault() e.preventDefault();
return resetEventReserve() return resetEventReserve();
} };
/** /**
* Callback to allow the user to set the details for his reservation * Callback to allow the user to set the details for his reservation
*/ */
$scope.reserveEvent = function () { $scope.reserveEvent = function () {
if ($scope.event.nb_total_places > 0) { if ($scope.event.nb_total_places > 0) {
$scope.reserveSuccess = false $scope.reserveSuccess = false;
if (!$scope.isAuthenticated()) { if (!$scope.isAuthenticated()) {
return $scope.login(null, function (user) { return $scope.login(null, function (user) {
$scope.reserve.toReserve = !$scope.reserve.toReserve $scope.reserve.toReserve = !$scope.reserve.toReserve;
if (user.role !== 'admin') { if (user.role !== 'admin') {
return $scope.ctrl.member = user return $scope.ctrl.member = user;
} }
}) });
} else { } else {
return $scope.reserve.toReserve = !$scope.reserve.toReserve return $scope.reserve.toReserve = !$scope.reserve.toReserve;
}
} }
} }
};
/** /**
* Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's * Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
* reservations. (admins only) * reservations. (admins only)
*/ */
$scope.updateMember = function () { $scope.updateMember = function () {
resetEventReserve() resetEventReserve();
$scope.reserveSuccess = false $scope.reserveSuccess = false;
if ($scope.ctrl.member) { if ($scope.ctrl.member) {
return Member.get({ id: $scope.ctrl.member.id }, function (member) { return Member.get({ id: $scope.ctrl.member.id }, function (member) {
$scope.ctrl.member = member $scope.ctrl.member = member;
return getReservations($scope.event.id, 'Event', $scope.ctrl.member.id) return getReservations($scope.event.id, 'Event', $scope.ctrl.member.id);
}) });
}
} }
};
/** /**
* Callback to trigger the payment process of the current reservation * Callback to trigger the payment process of the current reservation
@ -272,23 +273,23 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.payEvent = function () { $scope.payEvent = function () {
// first, we check that a user was selected // first, we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) { if (Object.keys($scope.ctrl.member).length > 0) {
const reservation = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) const reservation = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event);
return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) { return Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount) const amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount);
if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) { if (($scope.currentUser.role !== 'admin') && (amountToPay > 0)) {
return payByStripe(reservation) return payByStripe(reservation);
} else { } else {
if (($scope.currentUser.role === 'admin') || (amountToPay === 0)) { if (($scope.currentUser.role === 'admin') || (amountToPay === 0)) {
return payOnSite(reservation) return payOnSite(reservation);
} }
} }
}) });
} 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
return growl.error(_t('please_select_a_member_first')) return growl.error(_t('please_select_a_member_first'));
}
} }
};
/** /**
* Callback to validate the booking of a free event * Callback to validate the booking of a free event
@ -301,40 +302,40 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
slots_attributes: [], slots_attributes: [],
nb_reserve_places: $scope.reserve.nbReservePlaces, nb_reserve_places: $scope.reserve.nbReservePlaces,
tickets_attributes: [] tickets_attributes: []
} };
// a single slot is used for events // a single slot is used for events
reservation.slots_attributes.push({ reservation.slots_attributes.push({
start_at: $scope.event.start_date, start_at: $scope.event.start_date,
end_at: $scope.event.end_date, end_at: $scope.event.end_date,
availability_id: $scope.event.availability.id availability_id: $scope.event.availability.id
}) });
// iterate over reservations per prices // iterate over reservations per prices
for (let price_id in $scope.reserve.tickets) { for (let price_id in $scope.reserve.tickets) {
const seats = $scope.reserve.tickets[price_id] const seats = $scope.reserve.tickets[price_id];
reservation.tickets_attributes.push({ reservation.tickets_attributes.push({
event_price_category_id: price_id, event_price_category_id: price_id,
booked: seats booked: seats
}) });
} }
// set the attempting marker // set the attempting marker
$scope.attempting = true $scope.attempting = true;
// save the reservation to the API // save the reservation to the API
return Reservation.save({ reservation }, function (reservation) { return Reservation.save({ reservation }, function (reservation) {
// reservation successfull // reservation successfull
afterPayment(reservation) afterPayment(reservation);
return $scope.attempting = false return $scope.attempting = false;
} }
, function (response) { , function (response) {
// reservation failed // reservation failed
$scope.alerts = [] $scope.alerts = [];
$scope.alerts.push({ $scope.alerts.push({
msg: response.data.card[0], msg: response.data.card[0],
type: 'danger' type: 'danger'
}) });
// unset the attempting marker // unset the attempting marker
return $scope.attempting = false return $scope.attempting = false;
}) });
} };
/** /**
* Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose * Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose
@ -343,89 +344,89 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event- * @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/ */
$scope.modifyReservation = function (reservation, e) { $scope.modifyReservation = function (reservation, e) {
e.preventDefault() e.preventDefault();
e.stopPropagation() e.stopPropagation();
const index = $scope.reservations.indexOf(reservation) const index = $scope.reservations.indexOf(reservation);
return $uibModal.open({ return $uibModal.open({
templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>', templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>',
resolve: { resolve: {
event () { return $scope.event }, event () { return $scope.event; },
reservation () { return reservation } reservation () { return reservation; }
}, },
controller: ['$scope', '$uibModalInstance', 'event', 'reservation', 'Reservation', function ($scope, $uibModalInstance, event, reservation, Reservation) { controller: ['$scope', '$uibModalInstance', 'event', 'reservation', 'Reservation', function ($scope, $uibModalInstance, event, reservation, Reservation) {
// we copy the controller's resolved parameters into the scope // we copy the controller's resolved parameters into the scope
$scope.event = event $scope.event = event;
$scope.reservation = angular.copy(reservation) $scope.reservation = angular.copy(reservation);
// set the reservable_id to the first available event // set the reservable_id to the first available event
for (e of Array.from(event.recurrence_events)) { for (e of Array.from(event.recurrence_events)) {
if (e.nb_free_places > reservation.total_booked_seats) { if (e.nb_free_places > reservation.total_booked_seats) {
$scope.reservation.reservable_id = e.id $scope.reservation.reservable_id = e.id;
break break;
} }
} }
// Callback to validate the new reservation's date // Callback to validate the new reservation's date
$scope.ok = function () { $scope.ok = function () {
let eventToPlace = null let eventToPlace = null;
angular.forEach(event.recurrence_events, function (e) { angular.forEach(event.recurrence_events, function (e) {
if (e.id === parseInt($scope.reservation.reservable_id, 10)) { if (e.id === parseInt($scope.reservation.reservable_id, 10)) {
return eventToPlace = e return eventToPlace = e;
} }
}) });
$scope.reservation.slots[0].start_at = eventToPlace.start_date $scope.reservation.slots[0].start_at = eventToPlace.start_date;
$scope.reservation.slots[0].end_at = eventToPlace.end_date $scope.reservation.slots[0].end_at = eventToPlace.end_date;
$scope.reservation.slots[0].availability_id = eventToPlace.availability_id $scope.reservation.slots[0].availability_id = eventToPlace.availability_id;
$scope.reservation.slots_attributes = $scope.reservation.slots $scope.reservation.slots_attributes = $scope.reservation.slots;
$scope.attempting = true $scope.attempting = true;
Reservation.update({ id: reservation.id }, { reservation: $scope.reservation }, function (reservation) { Reservation.update({ id: reservation.id }, { reservation: $scope.reservation }, function (reservation) {
$uibModalInstance.close(reservation) $uibModalInstance.close(reservation);
$scope.attempting = true $scope.attempting = true;
} }
, function (response) { , function (response) {
$scope.alerts = [] $scope.alerts = [];
angular.forEach(response, function (v, k) { angular.forEach(response, function (v, k) {
angular.forEach(v, function (err) { angular.forEach(v, function (err) {
$scope.alerts.push({ msg: k + ': ' + err, type: 'danger' }); $scope.alerts.push({ msg: k + ': ' + err, type: 'danger' });
}) });
}) });
$scope.attempting = false; $scope.attempting = false;
}) });
} };
// Callback to cancel the modification // Callback to cancel the modification
$scope.cancel = function() { $uibModalInstance.dismiss('cancel') } $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
} }
] }) ] })
.result['finally'](null).then(function (reservation) { .result['finally'](null).then(function (reservation) {
// remove the reservation from the user's reservations list for this event (occurrence) // remove the reservation from the user's reservations list for this event (occurrence)
$scope.reservations.splice(index, 1) $scope.reservations.splice(index, 1);
// add the number of places transfered (to the new date) to the total of free places for this event // add the number of places transfered (to the new date) to the total of free places for this event
$scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats;
// remove the number of places transfered from the total of free places of the receiving occurrance // remove the number of places transfered from the total of free places of the receiving occurrance
angular.forEach($scope.event.recurrence_events, function (e) { angular.forEach($scope.event.recurrence_events, function (e) {
if (e.id === parseInt(reservation.reservable.id, 10)) { if (e.id === parseInt(reservation.reservable.id, 10)) {
return e.nb_free_places = e.nb_free_places - reservation.total_booked_seats return e.nb_free_places = e.nb_free_places - reservation.total_booked_seats;
}
})
})
} }
});
});
};
/** /**
* Checks if the provided reservation is able to be moved (date change) * Checks if the provided reservation is able to be moved (date change)
* @param reservation {{total_booked_seats:number}} * @param reservation {{total_booked_seats:number}}
*/ */
$scope.reservationCanModify = function (reservation) { $scope.reservationCanModify = function (reservation) {
const slotStart = moment(reservation.slots[0].start_at) const slotStart = moment(reservation.slots[0].start_at);
const now = moment() const now = moment();
let isAble = false let isAble = false;
angular.forEach($scope.event.recurrence_events, function (e) { angular.forEach($scope.event.recurrence_events, function (e) {
if (e.nb_free_places >= reservation.total_booked_seats) { return isAble = true } if (e.nb_free_places >= reservation.total_booked_seats) { return isAble = true; }
}) });
return (isAble && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay)) return (isAble && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay));
} };
/** /**
* Compute the total amount for the current reservation according to the previously set parameters * Compute the total amount for the current reservation according to the previously set parameters
@ -434,25 +435,25 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.computeEventAmount = function () { $scope.computeEventAmount = function () {
// first we check that a user was selected // first we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) { if (Object.keys($scope.ctrl.member).length > 0) {
const r = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event) const r = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event);
return Price.compute(mkRequestParams(r, $scope.coupon.applied), function (res) { return Price.compute(mkRequestParams(r, $scope.coupon.applied), function (res) {
$scope.reserve.amountTotal = res.price $scope.reserve.amountTotal = res.price;
return $scope.reserve.totalNoCoupon = res.price_without_coupon return $scope.reserve.totalNoCoupon = res.price_without_coupon;
}) });
} else { } else {
return $scope.reserve.amountTotal = null return $scope.reserve.amountTotal = null;
}
} }
};
/** /**
* Return the URL allowing to share the current project on the Facebook social network * Return the URL allowing to share the current project on the Facebook social network
*/ */
$scope.shareOnFacebook = function () { return `https://www.facebook.com/share.php?u=${$state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }).replace('#', '%23')}`; } $scope.shareOnFacebook = function () { return `https://www.facebook.com/share.php?u=${$state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }).replace('#', '%23')}`; };
/** /**
* Return the URL allowing to share the current project on the Twitter social network * Return the URL allowing to share the current project on the Twitter social network
*/ */
$scope.shareOnTwitter = function () { return `https://twitter.com/intent/tweet?url=${encodeURIComponent($state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }))}&text=${encodeURIComponent($scope.event.title)}`; } $scope.shareOnTwitter = function () { return `https://twitter.com/intent/tweet?url=${encodeURIComponent($state.href('app.public.events_show', { id: $scope.event.id }, { absolute: true }))}&text=${encodeURIComponent($scope.event.title)}`; };
/** /**
* Return the textual description of the conditions applyable to the given price's category * Return the textual description of the conditions applyable to the given price's category
@ -461,10 +462,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
$scope.getPriceCategoryConditions = function (category_id) { $scope.getPriceCategoryConditions = function (category_id) {
for (let cat of Array.from($scope.priceCategories)) { for (let cat of Array.from($scope.priceCategories)) {
if (cat.id === category_id) { if (cat.id === category_id) {
return cat.conditions return cat.conditions;
}
} }
} }
};
/* PRIVATE SCOPE */ /* PRIVATE SCOPE */
@ -475,25 +476,25 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// set the controlled user as the current user if the current user is not an admin // set the controlled user as the current user if the current user is not an admin
if ($scope.currentUser) { if ($scope.currentUser) {
if ($scope.currentUser.role !== 'admin') { if ($scope.currentUser.role !== 'admin') {
$scope.ctrl.member = $scope.currentUser $scope.ctrl.member = $scope.currentUser;
} }
} }
// initialize the "reserve" object with the event's data // initialize the "reserve" object with the event's data
resetEventReserve() resetEventReserve();
// if non-admin, get the current user's reservations into $scope.reservations // if non-admin, get the current user's reservations into $scope.reservations
if ($scope.currentUser) { if ($scope.currentUser) {
getReservations($scope.event.id, 'Event', $scope.currentUser.id) getReservations($scope.event.id, 'Event', $scope.currentUser.id);
} }
// watch when a coupon is applied to re-compute the total price // watch when a coupon is applied to re-compute the total price
return $scope.$watch('coupon.applied', function (newValue, oldValue) { return $scope.$watch('coupon.applied', function (newValue, oldValue) {
if ((newValue !== null) || (oldValue !== null)) { if ((newValue !== null) || (oldValue !== null)) {
return $scope.computeEventAmount() return $scope.computeEventAmount();
}
})
} }
});
};
/** /**
* Retrieve the reservations for the couple event / user * Retrieve the reservations for the couple event / user
@ -506,7 +507,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
reservable_id, reservable_id,
reservable_type, reservable_type,
user_id user_id
}).$promise.then(function (reservations) { $scope.reservations = reservations }) }).$promise.then(function (reservations) { $scope.reservations = reservations; });
}; };
/** /**
@ -524,27 +525,27 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
slots_attributes: [], slots_attributes: [],
nb_reserve_places: reserve.nbReservePlaces, nb_reserve_places: reserve.nbReservePlaces,
tickets_attributes: [] tickets_attributes: []
} };
reservation.slots_attributes.push({ reservation.slots_attributes.push({
start_at: event.start_date, start_at: event.start_date,
end_at: event.end_date, end_at: event.end_date,
availability_id: event.availability.id, availability_id: event.availability.id,
offered: event.offered || false offered: event.offered || false
}) });
for (let evt_px_cat of Array.from(event.prices)) { for (let evt_px_cat of Array.from(event.prices)) {
const booked = reserve.tickets[evt_px_cat.id] const booked = reserve.tickets[evt_px_cat.id];
if (booked > 0) { if (booked > 0) {
reservation.tickets_attributes.push({ reservation.tickets_attributes.push({
event_price_category_id: evt_px_cat.id, event_price_category_id: evt_px_cat.id,
booked booked
}) });
} }
} }
return reservation return reservation;
} };
/** /**
* Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object * Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
@ -556,10 +557,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
const params = { const params = {
reservation, reservation,
coupon_code: ((coupon ? coupon.code : undefined)) coupon_code: ((coupon ? coupon.code : undefined))
} };
return params return params;
} };
/** /**
* Set the current reservation to the default values. This implies to reservation form to be hidden. * Set the current reservation to the default values. This implies to reservation form to be hidden.
@ -575,16 +576,16 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
toReserve: false, toReserve: false,
amountTotal: 0, amountTotal: 0,
totalSeats: 0 totalSeats: 0
} };
for (let evt_px_cat of Array.from($scope.event.prices)) { for (let evt_px_cat of Array.from($scope.event.prices)) {
$scope.reserve.nbPlaces[evt_px_cat.id] = __range__(0, $scope.event.nb_free_places, true) $scope.reserve.nbPlaces[evt_px_cat.id] = __range__(0, $scope.event.nb_free_places, true);
$scope.reserve.tickets[evt_px_cat.id] = 0 $scope.reserve.tickets[evt_px_cat.id] = 0;
} }
return $scope.event.offered = false return $scope.event.offered = false;
}
} }
};
/** /**
* Open a modal window which trigger the stripe payment process * Open a modal window which trigger the stripe payment process
@ -596,66 +597,66 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
size: 'md', size: 'md',
resolve: { resolve: {
reservation () { reservation () {
return reservation return reservation;
}, },
price () { price () {
return Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise return Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise;
}, },
wallet () { wallet () {
return Wallet.getWalletByUser({ user_id: reservation.user_id }).$promise return Wallet.getWalletByUser({ user_id: reservation.user_id }).$promise;
}, },
cgv () { cgv () {
return CustomAsset.get({ name: 'cgv-file' }).$promise return CustomAsset.get({ name: 'cgv-file' }).$promise;
}, },
objectToPay () { objectToPay () {
return { return {
eventToReserve: $scope.event, eventToReserve: $scope.event,
reserve: $scope.reserve, reserve: $scope.reserve,
member: $scope.ctrl.member member: $scope.ctrl.member
} };
}, },
coupon () { coupon () {
return $scope.coupon.applied return $scope.coupon.applied;
} }
}, },
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon', controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon',
function ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon) { function ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon) {
// User's wallet amount // User's wallet amount
$scope.walletAmount = wallet.amount $scope.walletAmount = wallet.amount;
// Price // Price
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount) $scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
// CGV // CGV
$scope.cgv = cgv.custom_asset $scope.cgv = cgv.custom_asset;
// Reservation // Reservation
$scope.reservation = reservation $scope.reservation = reservation;
// Used in wallet info template to interpolate some translations // Used in wallet info template to interpolate some translations
$scope.numberFilter = $filter('number') $scope.numberFilter = $filter('number');
// Callback for the stripe payment authorization // Callback for the stripe payment authorization
return $scope.payment = function (status, response) { return $scope.payment = function (status, response) {
if (response.error) { if (response.error) {
return growl.error(response.error.message) return growl.error(response.error.message);
} else { } else {
$scope.attempting = true $scope.attempting = true;
$scope.reservation.card_token = response.id $scope.reservation.card_token = response.id;
Reservation.save(mkRequestParams($scope.reservation, coupon), function (reservation) { $uibModalInstance.close(reservation); } Reservation.save(mkRequestParams($scope.reservation, coupon), function (reservation) { $uibModalInstance.close(reservation); }
, function (response) { , function (response) {
$scope.alerts = [] $scope.alerts = [];
$scope.alerts.push({ $scope.alerts.push({
msg: response.data.card[0], msg: response.data.card[0],
type: 'danger' type: 'danger'
}) });
return $scope.attempting = false return $scope.attempting = false;
}) });
}
} }
};
} }
] ]
}).result['finally'](null).then(function(reservation) { afterPayment(reservation); }) }).result['finally'](null).then(function (reservation) { afterPayment(reservation); });
}; };
/** /**
@ -668,100 +669,100 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
size: 'sm', size: 'sm',
resolve: { resolve: {
reservation () { reservation () {
return reservation return reservation;
}, },
price () { price () {
return Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise return Price.compute(mkRequestParams(reservation, $scope.coupon.applied)).$promise;
}, },
wallet () { wallet () {
return Wallet.getWalletByUser({ user_id: reservation.user_id }).$promise return Wallet.getWalletByUser({ user_id: reservation.user_id }).$promise;
}, },
coupon () { coupon () {
return $scope.coupon.applied return $scope.coupon.applied;
} }
}, },
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon',
function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) { function ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) {
// User's wallet amount // User's wallet amount
$scope.walletAmount = wallet.amount $scope.walletAmount = wallet.amount;
// Price // Price
$scope.price = price.price $scope.price = price.price;
// price to pay // price to pay
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount) $scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
// Reservation // Reservation
$scope.reservation = reservation $scope.reservation = reservation;
// Used in wallet info template to interpolate some translations // Used in wallet info template to interpolate some translations
$scope.numberFilter = $filter('number') $scope.numberFilter = $filter('number');
// Button label // Button label
if ($scope.amount > 0) { if ($scope.amount > 0) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }, 'messageformat') $scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')($scope.amount) }, 'messageformat');
} else { } else {
if ((price.price > 0) && ($scope.walletAmount === 0)) { if ((price.price > 0) && ($scope.walletAmount === 0)) {
$scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) }, 'messageformat') $scope.validButtonName = _t('confirm_payment_of_html', { ROLE: $scope.currentUser.role, AMOUNT: $filter('currency')(price.price) }, 'messageformat');
} else { } else {
$scope.validButtonName = _t('confirm') $scope.validButtonName = _t('confirm');
} }
} }
// Callback to validate the payment // Callback to validate the payment
$scope.ok = function () { $scope.ok = function () {
$scope.attempting = true $scope.attempting = true;
return Reservation.save(mkRequestParams($scope.reservation, coupon), function (reservation) { return Reservation.save(mkRequestParams($scope.reservation, coupon), function (reservation) {
$uibModalInstance.close(reservation) $uibModalInstance.close(reservation);
return $scope.attempting = true return $scope.attempting = true;
} }
, function (response) { , function (response) {
$scope.alerts = [] $scope.alerts = [];
angular.forEach(response, function (v, k) { angular.forEach(response, function (v, k) {
angular.forEach(v, function (err) { angular.forEach(v, function (err) {
$scope.alerts.push({ $scope.alerts.push({
msg: k + ': ' + err, msg: k + ': ' + err,
type: 'danger' type: 'danger'
}) });
}) });
}) });
return $scope.attempting = false return $scope.attempting = false;
}) });
} };
// Callback to cancel the payment // Callback to cancel the payment
return $scope.cancel = function() { $uibModalInstance.dismiss('cancel'); } return $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
} }
] }) ] })
.result['finally'](null).then(function(reservation) { afterPayment(reservation) }) .result['finally'](null).then(function (reservation) { afterPayment(reservation); });
} };
/** /**
* What to do after the payment was successful * What to do after the payment was successful
* @param resveration {Object} booked reservation * @param resveration {Object} booked reservation
*/ */
var afterPayment = function (reservation) { var afterPayment = function (reservation) {
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats $scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats;
resetEventReserve() resetEventReserve();
$scope.reserveSuccess = true $scope.reserveSuccess = true;
$scope.coupon.applied = null $scope.coupon.applied = null;
$scope.reservations.push(reservation) $scope.reservations.push(reservation);
if ($scope.currentUser.role === 'admin') { if ($scope.currentUser.role === 'admin') {
return $scope.ctrl.member = null return $scope.ctrl.member = null;
}
} }
};
// !!! MUST BE CALLED AT THE END of the controller // !!! MUST BE CALLED AT THE END of the controller
return initialize() return initialize();
} }
]) ]);
function __range__ (left, right, inclusive) { function __range__ (left, right, inclusive) {
let range = [] let range = [];
let ascending = left < right let ascending = left < right;
let end = !inclusive ? right : ascending ? right + 1 : right - 1 let end = !inclusive ? right : ascending ? right + 1 : right - 1;
for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
range.push(i) range.push(i);
} }
return range return range;
} }

View File

@ -17,11 +17,6 @@
* inherits $scope.$parent.notifications (global notifications state) from ApplicationController * inherits $scope.$parent.notifications (global notifications state) from ApplicationController
*/ */
Application.Controllers.controller('NotificationsController', ['$scope', 'Notification', function ($scope, Notification) { Application.Controllers.controller('NotificationsController', ['$scope', 'Notification', function ($scope, Notification) {
/* PRIVATE STATIC CONSTANTS */
// Number of notifications added to the page when the user clicks on 'load next notifications'
const NOTIFICATIONS_PER_PAGE = 15;
/* PUBLIC SCOPE */ /* PUBLIC SCOPE */
// Array containg the archived notifications (already read) // Array containg the archived notifications (already read)

View File

@ -111,6 +111,7 @@ Application.Services.service('Diacritics', [
} }
} }
// eslint-disable-next-line no-control-regex
return str.replace(/[^\u0000-\u007E]/g, function (a) { return str.replace(/[^\u0000-\u007E]/g, function (a) {
return diacriticsMap[a] || a; return diacriticsMap[a] || a;
}); });

View File

@ -2,9 +2,12 @@ class API::NotificationsController < API::ApiController
include NotifyWith::NotificationsApi include NotifyWith::NotificationsApi
before_action :authenticate_user! before_action :authenticate_user!
# Number of notifications added to the page when the user clicks on 'load next notifications'
NOTIFICATIONS_PER_PAGE = 15
def index def index
loop do loop do
@notifications = current_user.notifications.page(params[:page]).per(15).order('created_at DESC') @notifications = current_user.notifications.page(params[:page]).per(NOTIFICATIONS_PER_PAGE).order('created_at DESC')
# we delete obsolete notifications on first access # we delete obsolete notifications on first access
break unless delete_obsoletes(@notifications) break unless delete_obsoletes(@notifications)
end end
@ -38,6 +41,7 @@ class API::NotificationsController < API::ApiController
end end
private private
def delete_obsoletes(notifications) def delete_obsoletes(notifications)
cleaned = false cleaned = false
notifications.each do |n| notifications.each do |n|