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

Merge branch 'erb' into es6

This commit is contained in:
Sylvain 2018-11-19 16:20:46 +01:00
commit 185ea30db3
21 changed files with 2037 additions and 2046 deletions

View File

@ -15,27 +15,27 @@
/* COMMON CODE */
// # list of supported authentication methods
// list of supported authentication methods
const METHODS = {
'DatabaseProvider': 'local_database',
'OAuth2Provider': 'o_auth2'
}
// #
// Iterate through the provided array and return the index of the requested element
// @param elements {Array<{id:*}>}
// @param id {*} id of the element to retrieve in the list
// @returns {number} index of the requested element, in the provided array
// #
/**
* Iterate through the provided array and return the index of the requested element
* @param elements {Array<{id:*}>}
* @param id {*} id of the element to retrieve in the list
* @returns {number} index of the requested element, in the provided array
*/
const findIdxById = (elements, id) =>
(elements.map(elem => elem.id)).indexOf(id)
// #
// For OAuth2 ententications, mapping the user's ID is mendatory. This function will check that this mapping
// is effective and will return false otherwise
// @param mappings {Array<Object>} expected: $scope.provider.providable_attributes.o_auth2_mappings_attributes
// @returns {Boolean} true if the mapping is declared
// #
/**
* For OAuth2 ententications, mapping the user's ID is mendatory. This function will check that this mapping
* is effective and will return false otherwise
* @param mappings {Array<Object>} expected: $scope.provider.providable_attributes.o_auth2_mappings_attributes
* @returns {Boolean} true if the mapping is declared
*/
const check_oauth2_id_is_mapped = function (mappings) {
for (let mapping of Array.from(mappings)) {
if ((mapping.local_model === 'user') && (mapping.local_field === 'uid') && !mapping._destroy) {
@ -45,36 +45,36 @@ const check_oauth2_id_is_mapped = function (mappings) {
return false
}
// #
// Provides a set of common callback methods and data to the $scope parameter. These methods are used
// in the various authentication providers' controllers.
//
// Provides :
// - $scope.authMethods
// - $scope.mappingFields
// - $scope.cancel()
// - $scope.defineDataMapping(mapping)
//
// Requires :
// - mappingFieldsPromise: retrieved by AuthProvider.mapping_fields()
// - $state (Ui-Router) [ 'app.admin.members' ]
// #
/**
* Provides a set of common callback methods and data to the $scope parameter. These methods are used
* in the various authentication providers' controllers.
*
* Provides :
* - $scope.authMethods
* - $scope.mappingFields
* - $scope.cancel()
* - $scope.defineDataMapping(mapping)
*
* Requires :
* - mappingFieldsPromise: retrieved by AuthProvider.mapping_fields()
* - $state (Ui-Router) [ 'app.admin.members' ]
*/
class AuthenticationController {
constructor ($scope, $state, $uibModal, mappingFieldsPromise) {
// # list of supported authentication methods
// list of supported authentication methods
$scope.authMethods = METHODS
// # list of fields that can be mapped through the SSO
// list of fields that can be mapped through the SSO
$scope.mappingFields = mappingFieldsPromise
// #
// Changes the admin's view to the members list page
// #
/**
* Changes the admin's view to the members list page
*/
$scope.cancel = () => $state.go('app.admin.members')
// #
// Open a modal allowing to specify the data mapping for the given field
// #
/**
* Open a modal allowing to specify the data mapping for the given field
*/
$scope.defineDataMapping = mapping =>
$uibModal.open({
templateUrl: '<%= asset_path "admin/authentications/_data_mapping.html" %>',
@ -91,14 +91,14 @@ class AuthenticationController {
},
controller: ['$scope', '$uibModalInstance', 'field', 'datatype', function ($scope, $uibModalInstance, field, datatype) {
// # parent field
// parent field
$scope.field = field
// # expected data type
// expected data type
$scope.datatype = datatype
// # data transformation rules
// data transformation rules
$scope.transformation =
{ rules: field.transformation || { type: datatype } }
// # available transformation formats
// available transformation formats
$scope.formats = {
date: [
{
@ -124,7 +124,7 @@ class AuthenticationController {
]
}
// # Create a new mapping between anything and an expected integer
// Create a new mapping between anything and an expected integer
$scope.addIntegerMapping = function () {
if (!angular.isArray($scope.transformation.rules.mapping)) {
$scope.transformation.rules.mapping = []
@ -132,10 +132,10 @@ class AuthenticationController {
return $scope.transformation.rules.mapping.push({ from: '', to: 0 })
}
// # close and save the modifications
// close and save the modifications
$scope.ok = () => $uibModalInstance.close($scope.transformation.rules)
// # do not save the modifications
// do not save the modifications
return $scope.cancel = () => $uibModalInstance.dismiss()
}
] })
@ -143,21 +143,21 @@ class AuthenticationController {
}
}
// #
// Page listing all authentication providers
// #
/**
* Page listing all authentication providers
*/
Application.Controllers.controller('AuthentificationController', ['$scope', '$state', '$rootScope', 'dialogs', 'growl', 'authProvidersPromise', 'AuthProvider', '_t',
function ($scope, $state, $rootScope, dialogs, growl, authProvidersPromise, AuthProvider, _t) {
/* PUBLIC SCOPE */
// # full list of authentication providers
// full list of authentication providers
$scope.providers = authProvidersPromise
// #
// Translate the classname into an explicit textual message
// @param type {string} Ruby polymorphic model classname
// @returns {string}
// #
/**
* Translate the classname into an explicit textual message
* @param type {string} Ruby polymorphic model classname
* @returns {string}
*/
$scope.getType = function (type) {
const text = METHODS[type]
if (typeof text !== 'undefined') {
@ -167,11 +167,11 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
}
}
// #
// Translate the status string into an explicit textual message
// @param status {string} active | pending | previous
// @returns {string}
// #
/**
* Translate the status string into an explicit textual message
* @param status {string} active | pending | previous
* @returns {string}
*/
$scope.getState = function (status) {
switch (status) {
case 'active': return _t('active')
@ -181,11 +181,11 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
}
}
// #
// Ask for confirmation then delete the specified provider
// @param providers {Array} full list of authentication providers
// @param provider {Object} provider to delete
// #
/**
* Ask for confirmation then delete the specified provider
* @param providers {Array} full list of authentication providers
* @param provider {Object} provider to delete
*/
return $scope.destroyProvider = (providers, provider) =>
dialogs.confirm({
resolve: {
@ -210,23 +210,23 @@ Application.Controllers.controller('AuthentificationController', ['$scope', '$st
])
// #
// Page to add a new authentication provider
// #
/**
* Page to add a new authentication provider
*/
Application.Controllers.controller('NewAuthenticationController', ['$scope', '$state', '$rootScope', '$uibModal', 'dialogs', 'growl', 'mappingFieldsPromise', 'authProvidersPromise', 'AuthProvider', '_t',
function ($scope, $state, $rootScope, $uibModal, dialogs, growl, mappingFieldsPromise, authProvidersPromise, AuthProvider, _t) {
$scope.mode = 'creation'
// # default parameters for the new authentication provider
// default parameters for the new authentication provider
$scope.provider = {
name: '',
providable_type: '',
providable_attributes: {}
}
// #
// Initialize some provider's specific properties when selecting the provider type
// #
/**
* Initialize some provider's specific properties when selecting the provider type
*/
$scope.updateProvidable = function () {
// === OAuth2Provider ===
if ($scope.provider.providable_type === 'OAuth2Provider') {
@ -237,9 +237,9 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
}
// Add others providers initializers here if needed ...
// #
// Validate and save the provider parameters in database
// #
/**
* Validate and save the provider parameters in database
*/
$scope.registerProvider = function () {
// === DatabaseProvider ===
let provider
@ -292,24 +292,24 @@ Application.Controllers.controller('NewAuthenticationController', ['$scope', '$s
}
}
// # Using the AuthenticationController
// Using the AuthenticationController
return new AuthenticationController($scope, $state, $uibModal, mappingFieldsPromise)
}
])
// #
// Page to edit an already added authentication provider
// #
/**
* Page to edit an already added authentication provider
*/
Application.Controllers.controller('EditAuthenticationController', ['$scope', '$state', '$stateParams', '$rootScope', '$uibModal', 'dialogs', 'growl', 'providerPromise', 'mappingFieldsPromise', 'AuthProvider', '_t',
function ($scope, $state, $stateParams, $rootScope, $uibModal, dialogs, growl, providerPromise, mappingFieldsPromise, AuthProvider, _t) {
// # parameters of the currently edited authentication provider
// parameters of the currently edited authentication provider
$scope.provider = providerPromise
$scope.mode = 'edition'
// #
// Update the current provider with the new inputs
// #
/**
* Update the current provider with the new inputs
*/
$scope.updateProvider = function () {
// check the ID mapping
if (!check_oauth2_id_is_mapped($scope.provider.providable_attributes.o_auth2_mappings_attributes)) {
@ -323,7 +323,7 @@ Application.Controllers.controller('EditAuthenticationController', ['$scope', '$
, () => growl.error(_t('an_error_occurred_unable_to_update_the_provider')))
}
// # Using the AuthenticationController
// Using the AuthenticationController
return new AuthenticationController($scope, $state, $uibModal, mappingFieldsPromise)
}
])

View File

@ -14,9 +14,9 @@
*/
'use strict'
// #
// Controller used in the calendar management page
// #
/**
* Controller used in the calendar management page
*/
Application.Controllers.controller('AdminCalendarController', ['$scope', '$state', '$uibModal', 'moment', 'Availability', 'Slot', 'Setting', 'Export', 'growl', 'dialogs', 'bookingWindowStart', 'bookingWindowEnd', 'machinesPromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $state, $uibModal, moment, Availability, Slot, Setting, Export, growl, dialogs, bookingWindowStart, bookingWindowEnd, machinesPromise, _t, uiCalendarConfig, CalendarConfig) {
@ -34,20 +34,20 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
/* PUBLIC SCOPE */
// # list of the FabLab machines
// list of the FabLab machines
$scope.machines = machinesPromise
// # currently selected availability
// currently selected availability
$scope.availability = null
// # bind the availabilities slots with full-Calendar events
// bind the availabilities slots with full-Calendar events
$scope.eventSources = []
$scope.eventSources.push({
url: '/api/availabilities',
textColor: 'black'
})
// # fullCalendar (v2) configuration
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
slotDuration: BASE_SLOT,
snapDuration: BOOKING_SNAP,
@ -69,10 +69,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
})
// #
// Open a confirmation modal to cancel the booking of a user for the currently selected event.
// @param slot {Object} reservation slot of a user, inherited from $resource
// #
/**
* Open a confirmation modal to cancel the booking of a user for the currently selected event.
* @param slot {Object} reservation slot of a user, inherited from $resource
*/
$scope.cancelBooking = slot =>
// open a confirmation dialog
dialogs.confirm({
@ -106,11 +106,11 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
)
)
// #
// Open a confirmation modal to remove a machine for the currently selected availability,
// except if it is the last machine of the reservation.
// @param machine {Object} must contain the machine ID and name
// #
/**
* Open a confirmation modal to remove a machine for the currently selected availability,
* except if it is the last machine of the reservation.
* @param machine {Object} must contain the machine ID and name
*/
$scope.removeMachine = function (machine) {
if ($scope.availability.machine_ids.length === 1) {
return growl.error(_t('admin_calendar.unable_to_remove_the_last_machine_of_the_slot_delete_the_slot_rather'))
@ -154,10 +154,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
}
// #
// Callback to alert the admin that the export request was acknowledged and is
// processing right now.
// #
/**
* Callback to alert the admin that the export request was acknowledged and is
* processing right now.
*/
$scope.alertExport = type =>
Export.status({ category: 'availabilities', type }).then(function (res) {
if (!res.data.exists) {
@ -165,9 +165,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
})
// #
// Mark the selected slot as unavailable for new reservations or allow reservations again on it
// #
/**
* Mark the selected slot as unavailable for new reservations or allow reservations again on it
*/
$scope.toggleLockReservations = function () {
// first, define a shortcut to the lock property
const locked = $scope.availability.lock
@ -210,9 +210,9 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
}
// #
// Confirm and destroy the slot in $scope.availability
// #
/**
* Confirm and destroy the slot in $scope.availability
*/
$scope.removeSlot = () =>
// open a confirmation dialog
dialogs.confirm({
@ -238,11 +238,11 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
/* PRIVATE SCOPE */
// #
// Return an enumerable meaninful string for the gender of the provider user
// @param user {Object} Database user record
// @return {string} 'male' or 'female'
// #
/**
* Return an enumerable meaninful string for the gender of the provider user
* @param user {Object} Database user record
* @return {string} 'male' or 'female'
*/
var getGender = function (user) {
if (user.profile) {
if (user.profile.gender === 'true') { return 'male' } else { return 'female' }
@ -251,7 +251,7 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// Triggered when the admin drag on the agenda to create a new reservable slot.
// @see http://fullcalendar.io/docs/selection/select_callback/
// #
//
var calendarSelectCb = function (start, end, jsEvent, view) {
start = moment.tz(start.toISOString(), Fablab.timezone)
end = moment.tz(end.toISOString(), Fablab.timezone)
@ -296,10 +296,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
return uiCalendarConfig.calendars.calendar.fullCalendar('unselect')
}
// #
// Triggered when the admin clicks on a availability slot in the agenda.
// @see http://fullcalendar.io/docs/mouse/eventClick/
// #
/**
* Triggered when the admin clicks on a availability slot in the agenda.
* @see http://fullcalendar.io/docs/mouse/eventClick/
*/
var calendarEventClickCb = function (event, jsEvent, view) {
$scope.availability = event
@ -312,11 +312,11 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
}
}
// #
// Triggered when fullCalendar tries to graphicaly render an event block.
// Append the event tag into the block, just after the event title.
// @see http://fullcalendar.io/docs/event_rendering/eventRender/
// #
/**
* Triggered when fullCalendar tries to graphicaly render an event block.
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element) {
element.find('.fc-content').prepend('<span class="remove-event">x&nbsp;</span>')
if (event.tags.length > 0) {
@ -329,10 +329,10 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
// force return to prevent coffee-script auto-return to return random value (possiblity falsy)
}
// #
// Triggered when resource fetching starts/stops.
// @see https://fullcalendar.io/docs/resource_data/loading/
// #
/**
* Triggered when resource fetching starts/stops.
* @see https://fullcalendar.io/docs/resource_data/loading/
*/
return loadingCb = function (isLoading, view) {
if (isLoading) {
// we remove existing events when fetching starts to prevent duplicates
@ -343,42 +343,42 @@ Application.Controllers.controller('AdminCalendarController', ['$scope', '$state
])
// #
// Controller used in the slot creation modal window
// #
/**
* Controller used in the slot creation modal window
*/
Application.Controllers.controller('CreateEventModalController', ['$scope', '$uibModalInstance', 'moment', 'start', 'end', 'machinesPromise', 'Availability', 'trainingsPromise', 'spacesPromise', 'Tag', 'growl', '_t',
function ($scope, $uibModalInstance, moment, start, end, machinesPromise, Availability, trainingsPromise, spacesPromise, Tag, growl, _t) {
// # $uibModal parameter
// $uibModal parameter
$scope.start = start
// # $uibModal parameter
// $uibModal parameter
$scope.end = end
// # machines list
// machines list
$scope.machines = machinesPromise.filter(m => !m.disabled)
// # trainings list
// trainings list
$scope.trainings = trainingsPromise.filter(t => !t.disabled)
// # spaces list
// spaces list
$scope.spaces = spacesPromise.filter(s => !s.disabled)
// # machines associated with the created slot
// machines associated with the created slot
$scope.selectedMachines = []
// # training associated with the created slot
// training associated with the created slot
$scope.selectedTraining = null
// # space associated with the created slot
// space associated with the created slot
$scope.selectedSpace = null
// # UI step
// UI step
$scope.step = 1
// # the user is not able to edit the ending time of the availability, unless he set the type to 'training'
// the user is not able to edit the ending time of the availability, unless he set the type to 'training'
$scope.endDateReadOnly = true
// # timepickers configuration
// timepickers configuration
$scope.timepickers = {
start: {
hstep: 1,
@ -390,17 +390,17 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
}
}
// # slot details
// slot details
$scope.availability = {
start_at: start,
end_at: end,
available_type: 'machines' // default
}
// #
// Adds or removes the provided machine from the current slot
// @param machine {Object}
// #
/**
* Adds or removes the provided machine from the current slot
* @param machine {Object}
*/
$scope.toggleSelection = function (machine) {
const index = $scope.selectedMachines.indexOf(machine)
if (index > -1) {
@ -410,9 +410,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
}
}
// #
// Callback for the modal window validation: save the slot and closes the modal
// #
/**
* Callback for the modal window validation: save the slot and closes the modal
*/
$scope.ok = function () {
if ($scope.availability.available_type === 'machines') {
if ($scope.selectedMachines.length > 0) {
@ -431,27 +431,27 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
, availability => $uibModalInstance.close(availability))
}
// #
// Move the modal UI to the next step
// #
/**
* Move the modal UI to the next step
*/
$scope.next = function () {
if ($scope.step === 1) { $scope.setNbTotalPlaces() }
return $scope.step++
}
// #
// Move the modal UI to the next step
// #
/**
* Move the modal UI to the next step
*/
$scope.previous = () => $scope.step--
// #
// Callback to cancel the slot creation
// #
/**
* Callback to cancel the slot creation
*/
$scope.cancel = () => $uibModalInstance.dismiss('cancel')
// #
// For training avaiabilities, set the maximum number of people allowed to register on this slot
// #
/**
* For training avaiabilities, set the maximum number of people allowed to register on this slot
*/
$scope.setNbTotalPlaces = function () {
if ($scope.availability.available_type === 'training') {
return $scope.availability.nb_total_places = $scope.selectedTraining.nb_total_places
@ -462,9 +462,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if ($scope.trainings.length > 0) {
$scope.selectedTraining = $scope.trainings[0]
@ -475,9 +475,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
Tag.query().$promise.then(data => $scope.tags = data)
// # When we configure a machine availability, do not let the user change the end time, as the total
// # time must be dividable by 60 minutes (base slot duration). For training availabilities, the user
// # can configure any duration as it does not matters.
// When we configure a machine availability, do not let the user change the end time, as the total
// time must be dividable by 60 minutes (base slot duration). For training availabilities, the user
// can configure any duration as it does not matters.
$scope.$watch('availability.available_type', function (newValue, oldValue, scope) {
if ((newValue === 'machines') || (newValue === 'space')) {
$scope.endDateReadOnly = true
@ -489,8 +489,8 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
}
})
// # When the start date is changed, if we are configuring a machine availability,
// # maintain the relative length of the slot (ie. change the end time accordingly)
// When the start date is changed, if we are configuring a machine availability,
// maintain the relative length of the slot (ie. change the end time accordingly)
$scope.$watch('start', function (newValue, oldValue, scope) {
// for machine or space availabilities, adjust the end time
if (($scope.availability.available_type === 'machines') || ($scope.availability.available_type === 'space')) {
@ -507,9 +507,9 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
return $scope.availability.start_at = $scope.start
})
// # Maintain consistency between the end time and the date object in the availability object
// Maintain consistency between the end time and the date object in the availability object
return $scope.$watch('end', function (newValue, oldValue, scope) {
// # we prevent the admin from setting the end of the availability before its begining
// we prevent the admin from setting the end of the availability before its begining
if (moment($scope.start).add(1, 'hour').isAfter(newValue)) {
$scope.end = oldValue
}
@ -518,7 +518,7 @@ Application.Controllers.controller('CreateEventModalController', ['$scope', '$ui
})
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -15,30 +15,30 @@
/* 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' ]
// #
/**
* 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
// default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
startOpened: false, // default: datePicker is not shown
@ -49,12 +49,12 @@ class EventsController {
}
}
// #
// 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
// #
/**
* 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 = []
@ -66,16 +66,16 @@ class EventsController {
}
}
// #
// Changes the user's view to the events list page
// #
/**
* 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)
// #
/**
* 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'
@ -84,17 +84,17 @@ class EventsController {
}
}
// #
// This will create a single new empty entry into the event's attachements list.
// #
/**
* 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
// #
/**
* 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) {
@ -104,45 +104,45 @@ class EventsController {
}
}
// #
// Show/Hide the "start" datepicker (open the drop down/close it)
// #
/**
* 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)
// #
/**
* 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
// #
/**
* 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
// #
/**
* 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'
// #
/**
* Remove the price or mark it as 'to delete'
*/
$scope.removePrice = function (price, event) {
event.preventDefault()
event.stopPropagation()
@ -156,48 +156,48 @@ class EventsController {
}
}
// #
// Controller used in the events listing page (admin view)
// #
/**
* 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
// By default, the pagination mode is activated to limit the page size
$scope.paginateActive = true
// # The events displayed on the page
// The events displayed on the page
$scope.events = eventsPromise
// # Current virtual page
// Current virtual page
$scope.page = 1
// # Temporary datastore for creating new elements
// Temporary datastore for creating new elements
$scope.inserted = {
category: null,
theme: null,
age_range: null
}
// # List of categories for the events
// List of categories for the events
$scope.categories = categoriesPromise
// # List of events themes
// List of events themes
$scope.themes = themesPromise
// # List of age ranges
// List of age ranges
$scope.ageRanges = ageRangesPromise
// # List of price categories for the events
// List of price categories for the events
$scope.priceCategories = priceCategoriesPromise
// # Default: we display all events (no restriction)
// Default: we display all events (no restriction)
$scope.eventsScope =
{ selected: '' }
// #
// Adds a bucket of events to the bottom of the page, grouped by month
// #
/**
* 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) {
@ -206,12 +206,12 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
})
}
// #
// 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
// #
/**
* 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)
@ -220,11 +220,11 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
}
}
// #
// Deletes the element at the specified index
// @param model {string} model name
// @param index {number} element index in the $scope[model] array
// #
/**
* 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'))
@ -250,10 +250,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
)
}
// #
// Creates a new empty entry in the $scope[model] array
// @param model {string} model name
// #
/**
* Creates a new empty entry in the $scope[model] array
* @param model {string} model name
*/
$scope.addElement = function (model) {
$scope.inserted[model] = {
name: '',
@ -262,12 +262,12 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
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
// #
/**
* 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()
@ -276,10 +276,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
}
}
// #
// Open a modal dialog allowing the definition of a new price category.
// Save it once filled and handle the result.
// #
/**
* 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" %>',
@ -299,12 +299,12 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
})
)
// #
// 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
// #
/**
* 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'))
@ -329,11 +329,11 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
}
}
// #
// 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
// #
/**
* 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'))
@ -360,10 +360,10 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
}
}
// #
// Triggered when the admin changes the events filter (all, passed, future).
// We request the first page of corresponding events to the API
// #
/**
* 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
@ -374,17 +374,17 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* 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)
// #
/**
* 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) {
@ -397,11 +397,11 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
}
}
// #
// Return the model and the datastore matching the given name
// @param name {string} 'category', 'theme' or 'age_range'
// @return {[Object, Array]} model and datastore
// #
/**
* 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]
@ -417,44 +417,44 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
])
// #
// Controller used in the reservations listing page for a specific event
// #
/**
* 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
// retrieve the event from the ID provided in the current URL
$scope.event = eventPromise
// # list of reservations for the current event
// list of reservations for the current event
return $scope.reservations = reservationsPromise
}
])
// #
// Controller used in the event creation page
// #
/**
* 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
// API URL where the form will be posted
$scope.actionUrl = '/api/events/'
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # List of categories for the events
// List of categories for the events
$scope.categories = categoriesPromise
// # List of events themes
// List of events themes
$scope.themes = themesPromise
// # List of age ranges
// List of age ranges
$scope.ageRanges = ageRangesPromise
// # List of availables price's categories
// List of availables price's categories
$scope.priceCategories = priceCategoriesPromise
// # Default event parameters
// Default event parameters
$scope.event = {
event_files_attributes: [],
start_date: new Date(),
@ -467,7 +467,7 @@ Application.Controllers.controller('NewEventController', ['$scope', '$state', 'C
prices: []
}
// # Possible types of recurrences for an event
// Possible types of recurrences for an event
$scope.recurrenceTypes = [
{ label: _t('none'), value: 'none' },
{ label: _t('every_days'), value: 'day' },
@ -476,44 +476,44 @@ Application.Controllers.controller('NewEventController', ['$scope', '$state', 'C
{ label: _t('every_year'), value: 'year' }
]
// # Using the EventsController
// Using the EventsController
return new EventsController($scope, $state)
}
])
// #
// Controller used in the events edition page
// #
/**
* 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
// API URL where the form will be posted
$scope.actionUrl = `/api/events/${$stateParams.id}`
// # Form action on the above URL
// 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
// 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
// List of categories for the events
$scope.categories = categoriesPromise
// # List of availables price's categories
// List of availables price's categories
$scope.priceCategories = priceCategoriesPromise
// # List of events themes
// List of events themes
$scope.themes = themesPromise
// # List of age ranges
// List of age ranges
$scope.ageRanges = ageRangesPromise
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
@ -521,11 +521,11 @@ Application.Controllers.controller('EditEventController', ['$scope', '$state', '
$scope.event.start_date = moment($scope.event.start_date).toDate()
$scope.event.end_date = moment($scope.event.end_date).toDate()
// # Using the EventsController
// Using the EventsController
return new EventsController($scope, $state)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -14,9 +14,9 @@
*/
'use strict'
// #
// Controller used in the admin invoices listing page
// #
/**
* Controller used in the admin invoices listing page
*/
Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'Invoice', 'invoices', '$uibModal', 'growl', '$filter', 'Setting', 'settings', '_t',
function ($scope, $state, Invoice, invoices, $uibModal, growl, $filter, Setting, settings, _t) {
/* PRIVATE STATIC CONSTANTS */
@ -26,7 +26,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
/* PUBLIC SCOPE */
// # List of all users invoices
// List of all users invoices
$scope.invoices = invoices
// Invoices filters
@ -42,10 +42,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
// true when all invoices are loaded
$scope.noMoreResults = false
// # Default invoices ordering/sorting
// Default invoices ordering/sorting
$scope.orderInvoice = '-reference'
// # Invoices parameters
// Invoices parameters
$scope.invoice = {
logo: null,
reference: {
@ -76,19 +76,19 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
}
// # Placeholding date for the invoice creation
// Placeholding date for the invoice creation
$scope.today = moment()
// # Placeholding date for the reservation begin
// Placeholding date for the reservation begin
$scope.inOneWeek = moment().add(1, 'week').startOf('hour')
// # Placeholding date for the reservation end
// Placeholding date for the reservation end
$scope.inOneWeekAndOneHour = moment().add(1, 'week').add(1, 'hour').startOf('hour')
// #
// Change the invoices ordering criterion to the one provided
// @param orderBy {string} ordering criterion
// #
/**
* Change the invoices ordering criterion to the one provided
* @param orderBy {string} ordering criterion
*/
$scope.setOrderInvoice = function (orderBy) {
if ($scope.orderInvoice === orderBy) {
$scope.orderInvoice = `-${orderBy}`
@ -100,10 +100,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return invoiceSearch()
}
// #
// Open a modal window asking the admin the details to refund the user about the provided invoice
// @param invoice {Object} invoice inherited from angular's $resource
// #
/**
* Open a modal window asking the admin the details to refund the user about the provided invoice
* @param invoice {Object} invoice inherited from angular's $resource
*/
$scope.generateAvoirForInvoice = function (invoice) {
// open modal
const modalInstance = $uibModal.open({
@ -124,10 +124,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Generate an invoice reference sample from the parametrized model
// @returns {string} invoice reference sample
// #
/**
* Generate an invoice reference sample from the parametrized model
* @returns {string} invoice reference sample
*/
$scope.mkReference = function () {
let sample = $scope.invoice.reference.model
if (sample) {
@ -149,10 +149,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return sample
}
// #
// Generate an order nmuber sample from the parametrized model
// @returns {string} invoice reference sample
// #
/**
* Generate an order nmuber sample from the parametrized model
* @returns {string} invoice reference sample
*/
$scope.mkNumber = function () {
let sample = $scope.invoice.number.model
if (sample) {
@ -170,9 +170,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
return sample
}
// #
// Open a modal dialog allowing the user to edit the invoice reference generation template
// #
/**
* Open a modal dialog allowing the user to edit the invoice reference generation template
*/
$scope.openEditReference = function () {
const modalInstance = $uibModal.open({
animation: true,
@ -202,9 +202,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
)
}
// #
// Open a modal dialog allowing the user to edit the invoice code
// #
/**
* Open a modal dialog allowing the user to edit the invoice code
*/
$scope.openEditCode = function () {
const modalInstance = $uibModal.open({
animation: true,
@ -254,9 +254,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Open a modal dialog allowing the user to edit the invoice number
// #
/**
* Open a modal dialog allowing the user to edit the invoice number
*/
$scope.openEditInvoiceNb = function () {
const modalInstance = $uibModal.open({
animation: true,
@ -286,10 +286,10 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
)
}
// #
// Open a modal dialog allowing the user to edit the VAT parameters for the invoices
// The VAT can be disabled and its rate can be configured
// #
/**
* Open a modal dialog allowing the user to edit the VAT parameters for the invoices
* The VAT can be disabled and its rate can be configured
*/
$scope.openEditVAT = function () {
const modalInstance = $uibModal.open({
animation: true,
@ -339,9 +339,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Callback to save the value of the text zone when editing is done
// #
/**
* Callback to save the value of the text zone when editing is done
*/
$scope.textEditEnd = function (event) {
const parsed = parseHtml($scope.invoice.text.content)
return Setting.update({ name: 'invoice_text' }, { value: parsed }, function (data) {
@ -354,9 +354,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Callback to save the value of the legal information zone when editing is done
// #
/**
* Callback to save the value of the legal information zone when editing is done
*/
$scope.legalsEditEnd = function (event) {
const parsed = parseHtml($scope.invoice.legals.content)
return Setting.update({ name: 'invoice_legals' }, { value: parsed }, function (data) {
@ -369,19 +369,19 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Callback when any of the filters changes.
// Full reload the results list
// #
/**
* Callback when any of the filters changes.
* Full reload the results list
*/
$scope.handleFilterChange = function () {
resetSearchInvoice()
return invoiceSearch()
}
// #
// Callback for the 'load more' button.
// Will load the next results of the current search, if any
// #
/**
* Callback for the 'load more' button.
* Will load the next results of the current search, if any
*/
$scope.showNextInvoices = function () {
$scope.page += 1
return invoiceSearch(true)
@ -389,9 +389,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if (!invoices[0] || (invoices[0].maxInvoices <= $scope.invoices.length)) {
$scope.noMoreResults = true
@ -424,20 +424,20 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
})
}
// #
// Output the given integer with leading zeros. If the given value is longer than the given
// length, it will be truncated.
// @param value {number} the integer to pad
// @param length {number} the length of the resulting string.
// #
/**
* Output the given integer with leading zeros. If the given value is longer than the given
* length, it will be truncated.
* @param value {number} the integer to pad
* @param length {number} the length of the resulting string.
*/
var padWithZeros = (value, length) => (1e15 + value + '').slice(-length)
// #
// Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
// The supported tags are <b>, <u>, <i> and <br>.
// @param html {string} single line html text
// @return {string} multi line simplified html text
// #
/**
* Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
* The supported tags are <b>, <u>, <i> and <br>.
* @param html {string} single line html text
* @return {string} multi line simplified html text
*/
var parseHtml = html =>
html = html.replace(/<\/?(\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g, function (match, p1, offset, string) {
if (['b', 'u', 'i', 'br'].includes(p1)) {
@ -447,19 +447,19 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
})
// #
// Reinitialize the context of invoices' search to display new results set
// #
/**
* Reinitialize the context of invoices' search to display new results set
*/
var resetSearchInvoice = function () {
$scope.page = 1
return $scope.noMoreResults = false
}
// #
// Run a search query with the current parameters set concerning invoices, then affect or concat the results
// to $scope.invoices
// @param concat {boolean} if true, the result will be append to $scope.invoices instead of being affected
// #
/**
* Run a search query with the current parameters set concerning invoices, then affect or concat the results
* to $scope.invoices
* @param concat {boolean} if true, the result will be append to $scope.invoices instead of being affected
*/
var invoiceSearch = concat =>
Invoice.list({
query: {
@ -482,32 +482,32 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
}
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the invoice refunding modal window
// #
/**
* Controller used in the invoice refunding modal window
*/
Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModalInstance', 'invoice', 'Invoice', 'growl', '_t',
function ($scope, $uibModalInstance, invoice, Invoice, growl, _t) {
/* PUBLIC SCOPE */
// # invoice linked to the current refund
// invoice linked to the current refund
$scope.invoice = invoice
// # Associative array containing invoice_item ids associated with boolean values
// Associative array containing invoice_item ids associated with boolean values
$scope.partial = {}
// # Default refund parameters
// Default refund parameters
$scope.avoir = {
invoice_id: invoice.id,
subscription_to_expire: false,
invoice_items_ids: []
}
// # Possible refunding methods
// Possible refunding methods
$scope.avoirModes = [
{ name: _t('none'), value: 'none' },
{ name: _t('by_cash'), value: 'cash' },
@ -516,12 +516,12 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
{ name: _t('by_wallet'), value: 'wallet' }
]
// # If a subscription was took with the current invoice, should it be canceled or not
// If a subscription was took with the current invoice, should it be canceled or not
$scope.subscriptionExpireOptions = {}
$scope.subscriptionExpireOptions[_t('yes')] = true
$scope.subscriptionExpireOptions[_t('no')] = false
// # AngularUI-Bootstrap datepicker parameters to define when to refund
// AngularUI-Bootstrap datepicker parameters to define when to refund
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -530,18 +530,18 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
}
}
// #
// Callback to open the datepicker
// #
/**
* Callback to open the datepicker
*/
$scope.openDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = true
}
// #
// Validate the refunding and generate a refund invoice
// #
/**
* Validate the refunding and generate a refund invoice
*/
$scope.ok = function () {
// check that at least 1 element of the invoice is refunded
$scope.avoir.invoice_items_ids = []
@ -564,18 +564,18 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
}
}
// #
// Cancel the refund, dismiss the modal window
// #
/**
* Cancel the refund, dismiss the modal window
*/
$scope.cancel = () => $uibModalInstance.dismiss('cancel')
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
// # if the invoice was payed with stripe, allow to refund through stripe
// if the invoice was payed with stripe, allow to refund through stripe
Invoice.get({ id: invoice.id }, function (data) {
$scope.invoice = data
// default : all elements of the invoice are refund
@ -588,7 +588,7 @@ Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModal
}
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -17,30 +17,30 @@
/* COMMON CODE */
// #
// Provides a set of common properties and methods to the $scope parameter. They are used
// in the various members' admin controllers.
//
// Provides :
// - $scope.groups = [{Group}]
// - $scope.trainings = [{Training}]
// - $scope.plans = []
// - $scope.datePicker = {}
// - $scope.submited(content)
// - $scope.cancel()
// - $scope.fileinputClass(v)
// - $scope.openDatePicker($event)
// - $scope.openSubscriptionDatePicker($event)
//
// Requires :
// - $state (Ui-Router) [ 'app.admin.members' ]
// #
/**
* Provides a set of common properties and methods to the $scope parameter. They are used
* in the various members' admin controllers.
*
* Provides :
* - $scope.groups = [{Group}]
* - $scope.trainings = [{Training}]
* - $scope.plans = []
* - $scope.datePicker = {}
* - $scope.submited(content)
* - $scope.cancel()
* - $scope.fileinputClass(v)
* - $scope.openDatePicker($event)
* - $scope.openSubscriptionDatePicker($event)
*
* Requires :
* - $state (Ui-Router) [ 'app.admin.members' ]
*/
class MembersController {
constructor ($scope, $state, Group, Training) {
// # Retrieve the profiles groups (eg. students ...)
// Retrieve the profiles groups (eg. students ...)
Group.query(groups => $scope.groups = groups.filter(g => (g.slug !== 'admins') && !g.disabled))
// # Retrieve the list of available trainings
// Retrieve the list of available trainings
Training.query().$promise.then(data =>
$scope.trainings = data.map(d =>
({
@ -51,7 +51,7 @@ class MembersController {
)
)
// # Default parameters for AngularUI-Bootstrap datepicker
// Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -61,32 +61,32 @@ class MembersController {
}
}
// #
// Shows the birth day datepicker
// @param $event {Object} jQuery event object
// #
/**
* Shows the birth day datepicker
* @param $event {Object} jQuery event object
*/
$scope.openDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = true
}
// #
// Shows the end of subscription datepicker
// @param $event {Object} jQuery event object
// #
/**
* Shows the end of subscription datepicker
* @param $event {Object} jQuery event object
*/
$scope.openSubscriptionDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.subscription_date_opened = true
}
// #
// 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 members listing page.
// @param content {Object} JSON - The upload's result
// #
/**
* 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 members listing page.
* @param content {Object} JSON - The upload's result
*/
$scope.submited = function (content) {
if ((content.id == null)) {
$scope.alerts = []
@ -103,16 +103,16 @@ class MembersController {
}
}
// #
// Changes the admin's view to the members list page
// #
/**
* Changes the admin's view to the members list page
*/
$scope.cancel = () => $state.go('app.admin.members')
// #
// 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)
// #
/**
* 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'
@ -123,9 +123,9 @@ class MembersController {
}
}
// #
// Controller used in the members/groups management page
// #
/**
* Controller used in the members/groups management page
*/
Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', 'membersPromise', 'adminsPromise', 'growl', 'Admin', 'dialogs', '_t', 'Member', 'Export',
function ($scope, $sce, membersPromise, adminsPromise, growl, Admin, dialogs, _t, Member, Export) {
/* PRIVATE STATIC CONSTANTS */
@ -135,30 +135,30 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
/* PUBLIC SCOPE */
// # members list
// members list
$scope.members = membersPromise
$scope.member = {
// # Members plain-text filtering. Default: not filtered
// Members plain-text filtering. Default: not filtered
searchText: '',
// # Members ordering/sorting. Default: not sorted
// Members ordering/sorting. Default: not sorted
order: 'id',
// # currently displayed page of members
// currently displayed page of members
page: 1,
// # true when all members where loaded
// true when all members where loaded
noMore: false
}
// # admins list
// admins list
$scope.admins = adminsPromise.admins
// # Admins ordering/sorting. Default: not sorted
// Admins ordering/sorting. Default: not sorted
$scope.orderAdmin = null
// #
// Change the members ordering criterion to the one provided
// @param orderBy {string} ordering criterion
// #
/**
* Change the members ordering criterion to the one provided
* @param orderBy {string} ordering criterion
*/
$scope.setOrderMember = function (orderBy) {
if ($scope.member.order === orderBy) {
$scope.member.order = `-${orderBy}`
@ -170,10 +170,10 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
return memberSearch()
}
// #
// Change the admins ordering criterion to the one provided
// @param orderBy {string} ordering criterion
// #
/**
* Change the admins ordering criterion to the one provided
* @param orderBy {string} ordering criterion
*/
$scope.setOrderAdmin = function (orderAdmin) {
if ($scope.orderAdmin === orderAdmin) {
return $scope.orderAdmin = `-${orderAdmin}`
@ -182,11 +182,11 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
}
}
// #
// Ask for confirmation then delete the specified administrator
// @param admins {Array} full list of administrators
// @param admin {Object} administrator to delete
// #
/**
* Ask for confirmation then delete the specified administrator
* @param admins {Array} full list of administrators
* @param admin {Object} administrator to delete
*/
$scope.destroyAdmin = (admins, admin) =>
dialogs.confirm({
resolve: {
@ -206,27 +206,27 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
, error => growl.error(_t('unable_to_delete_the_administrator')))
)
// #
// Callback for the 'load more' button.
// Will load the next results of the current search, if any
// #
/**
* Callback for the 'load more' button.
* Will load the next results of the current search, if any
*/
$scope.showNextMembers = function () {
$scope.member.page += 1
return memberSearch(true)
}
// #
// Callback when the search field content changes: reload the search results
// #
/**
* Callback when the search field content changes: reload the search results
*/
$scope.updateTextSearch = function () {
resetSearchMember()
return memberSearch()
}
// #
// Callback to alert the admin that the export request was acknowledged and is
// processing right now.
// #
/**
* Callback to alert the admin that the export request was acknowledged and is
* processing right now.
*/
$scope.alertExport = type =>
Export.status({ category: 'users', type }).then(function (res) {
if (!res.data.exists) {
@ -236,37 +236,37 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if (!membersPromise[0] || (membersPromise[0].maxMembers <= $scope.members.length)) {
return $scope.member.noMore = true
}
}
// #
// Iterate through the provided array and return the index of the requested admin
// @param admins {Array} full list of users with role 'admin'
// @param id {Number} user id of the admin to retrieve in the list
// @returns {Number} index of the requested admin, in the provided array
// #
/**
* Iterate through the provided array and return the index of the requested admin
* @param admins {Array} full list of users with role 'admin'
* @param id {Number} user id of the admin to retrieve in the list
* @returns {Number} index of the requested admin, in the provided array
*/
var findAdminIdxById = (admins, id) =>
(admins.map(admin => admin.id)).indexOf(id)
// #
// Reinitialize the context of members's search to display new results set
// #
/**
* Reinitialize the context of members's search to display new results set
*/
var resetSearchMember = function () {
$scope.member.noMore = false
return $scope.member.page = 1
}
// #
// Run a search query with the current parameters set ($scope.member[searchText,order,page])
// and affect or append the result in $scope.members, depending on the concat parameter
// @param concat {boolean} if true, the result will be append to $scope.members instead of being affected
// #
/**
* Run a search query with the current parameters set ($scope.member[searchText,order,page])
* and affect or append the result in $scope.members, depending on the concat parameter
* @param concat {boolean} if true, the result will be append to $scope.members instead of being affected
*/
var memberSearch = concat =>
Member.list({ query: { search: $scope.member.searchText, order_by: $scope.member.order, page: $scope.member.page, size: USERS_PER_PAGE } }, function (members) {
if (concat) {
@ -280,35 +280,35 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
}
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the member edition page
// #
/**
* Controller used in the member edition page
*/
Application.Controllers.controller('EditMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise', 'activeProviderPromise', 'Wallet',
function ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise, activeProviderPromise, Wallet) {
/* PUBLIC SCOPE */
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/members/${$stateParams.id}`
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'patch'
// # List of tags associables with user
// List of tags associables with user
$scope.tags = tagsPromise
// # The user to edit
// The user to edit
$scope.user = memberPromise
// # Should the passord be modified?
// Should the passord be modified?
$scope.password =
{ change: false }
// # the user subscription
// the user subscription
if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
$scope.subscription = $scope.user.subscription
$scope.subscription.expired_at = $scope.subscription.expired_at
@ -320,29 +320,29 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
})
}
// # Available trainings list
// Available trainings list
$scope.trainings = []
// # Profiles types (student/standard/...)
// Profiles types (student/standard/...)
$scope.groups = []
// # the user wallet
// the user wallet
$scope.wallet = walletPromise
// # user wallet transactions
// user wallet transactions
$scope.transactions = transactionsPromise
// # used in wallet partial template to identify parent view
// used in wallet partial template to identify parent view
$scope.view = 'member_edit'
// current active authentication provider
$scope.activeProvider = activeProviderPromise
// #
// Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not)
// @param subscription {Object} User's subscription object
// @param free {boolean} True if the extent is offered, false otherwise
// #
/**
* Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not)
* @param subscription {Object} User's subscription object
* @param free {boolean} True if the extent is offered, false otherwise
*/
$scope.updateSubscriptionModal = function (subscription, free) {
const modalInstance = $uibModal.open({
animation: true,
@ -380,36 +380,36 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
return modalInstance.result.then(subscription => $scope.subscription.expired_at = subscription.expired_at)
}
// #
// Open a modal dialog allowing the admin to set a subscription for the given user.
// @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id
// @param plans {Array} List of plans, availables for the currently reviewed user, as recovered from GET /api/plans
// #
/**
* Open a modal dialog allowing the admin to set a subscription for the given user.
* @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id
* @param plans {Array} List of plans, availables for the currently reviewed user, as recovered from GET /api/plans
*/
$scope.createSubscriptionModal = function (user, plans) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "admin/subscriptions/create_modal.html" %>',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', function ($scope, $uibModalInstance, Subscription, Group) {
// # selected user
// selected user
$scope.user = user
// # available plans for the selected user
// available plans for the selected user
$scope.plans = plans
// #
// Generate a string identifying the given plan by literal humain-readable name
// @param plan {Object} Plan object, as recovered from GET /api/plan/:id
// @param groups {Array} List of Groups objects, as recovered from GET /api/groups
// @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
// will be included.
// @returns {String}
// #
/**
* Generate a string identifying the given plan by literal humain-readable name
* @param plan {Object} Plan object, as recovered from GET /api/plan/:id
* @param groups {Array} List of Groups objects, as recovered from GET /api/groups
* @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
* will be included.
* @returns {String}
*/
$scope.humanReadablePlanName = (plan, groups, short) => `${$filter('humanReadablePlanName')(plan, groups, short)}`
// #
// Modal dialog validation callback
// #
/**
* Modal dialog validation callback
*/
$scope.ok = function () {
$scope.subscription.user_id = user.id
return Subscription.save({ }, { subscription: $scope.subscription }, function (_subscription) {
@ -420,9 +420,9 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
, error => growl.error(_t('a_problem_occurred_while_taking_the_subscription')))
}
// #
// Modal dialog cancellation callback
// #
/**
* Modal dialog cancellation callback
*/
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
] })
@ -453,18 +453,18 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
}
}
// #
// Callback to open/close the date picker
// #
/**
* Callback to open/close the date picker
*/
$scope.toggleDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = !$scope.datePicker.opened
}
// #
// Modal dialog validation callback
// #
/**
* Modal dialog validation callback
*/
$scope.ok = () =>
Wallet.credit({ id: wallet.id }, {
amount: $scope.amount,
@ -478,9 +478,9 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
}
, error => growl.error(_t('a_problem_occurred_for_wallet_credit')))
// #
// Modal dialog cancellation callback
// #
/**
* Modal dialog cancellation callback
*/
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
] })
@ -491,23 +491,23 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
})
}
// #
// To use as callback in Array.prototype.filter to get only enabled plans
// #
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
$scope.filterDisabledPlans = plan => !plan.disabled
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
// init the birth date to JS object
$scope.user.profile.birthday = moment($scope.user.profile.birthday).toDate()
// # the user subscription
// the user subscription
if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
$scope.subscription = $scope.user.subscription
$scope.subscription.expired_at = $scope.subscription.expired_at
@ -523,36 +523,36 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state',
return new MembersController($scope, $state, Group, Training)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the member's creation page (admin view)
// #
/**
* Controller used in the member's creation page (admin view)
*/
Application.Controllers.controller('NewMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'Group', 'CSRF',
function ($scope, $state, $stateParams, Member, Training, Group, CSRF) {
CSRF.setMetaTags()
/* PUBLIC SCOPE */
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/members'
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # Should the passord be set manually or generated?
// Should the passord be set manually or generated?
$scope.password =
{ change: false }
// # Default member's profile parameters
// Default member's profile parameters
$scope.user =
{ plan_interval: '' }
// # Callback when the admin check/unckeck the box telling that the new user is an organization.
// # Disable or enable the organization fields in the form, accordingly
// Callback when the admin check/unckeck the box telling that the new user is an organization.
// Disable or enable the organization fields in the form, accordingly
$scope.toggleOrganization = function () {
if ($scope.user.organization) {
if (!$scope.user.profile) { $scope.user.profile = {} }
@ -562,16 +562,16 @@ Application.Controllers.controller('NewMemberController', ['$scope', '$state', '
}
}
// # Using the MembersController
// Using the MembersController
return new MembersController($scope, $state, Group, Training)
}
])
// #
// Controller used in the admin's creation page (admin view)
// #
/**
* Controller used in the admin's creation page (admin view)
*/
Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', function ($state, $scope, Admin, growl, _t) {
// # default admin profile
// default admin profile
let getGender
$scope.admin = {
profile_attributes: {
@ -579,7 +579,7 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
}
}
// # Default parameters for AngularUI-Bootstrap datepicker
// Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false,
@ -588,15 +588,15 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
}
}
// #
// Shows the birth day datepicker
// @param $event {Object} jQuery event object
// #
/**
* Shows the birth day datepicker
* @param $event {Object} jQuery event object
*/
$scope.openDatePicker = $event => $scope.datePicker.opened = true
// #
// Send the new admin, currently stored in $scope.admin, to the server for database saving
// #
/**
* Send the new admin, currently stored in $scope.admin, to the server for database saving
*/
$scope.saveAdmin = () =>
Admin.save({}, { admin: $scope.admin }, function () {
growl.success(_t('administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }, 'messageformat'))
@ -606,11 +606,11 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
/* PRIVATE SCOPE */
// #
// Return an enumerable meaninful string for the gender of the provider user
// @param user {Object} Database user record
// @return {string} 'male' or 'female'
// #
/**
* Return an enumerable meaninful string for the gender of the provider user
* @param user {Object} Database user record
* @return {string} 'male' or 'female'
*/
return getGender = function (user) {
if (user.profile_attributes) {
if (user.profile_attributes.gender) { return 'male' } else { return 'female' }

View File

@ -25,20 +25,20 @@ class PlanController {
// protection against request forgery
CSRF.setMetaTags()
// # groups list
// groups list
$scope.groups = groups.filter(g => (g.slug !== 'admins') && !g.disabled)
// # users with role 'partner', notifiables for a partner plan
// users with role 'partner', notifiables for a partner plan
$scope.partners = partners.users
// # Subscriptions prices, machines prices and training prices, per groups
// Subscriptions prices, machines prices and training prices, per groups
$scope.group_pricing = prices
// #
// 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)
// #
/**
* 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'
@ -47,10 +47,10 @@ class PlanController {
}
}
// #
// Mark the provided file for deletion
// @param file {Object}
// #
/**
* Mark the provided file for deletion
* @param file {Object}
*/
$scope.deleteFile = function (file) {
if ((file != null) && (file.id != null)) {
return file._destroy = true
@ -59,30 +59,30 @@ class PlanController {
}
}
// #
// Controller used in the plan creation form
// #
/**
* Controller used in the plan creation form
*/
Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal', 'groups', 'prices', 'partners', 'CSRF', '$state', 'growl', '_t',
function ($scope, $uibModal, groups, prices, partners, CSRF, $state, growl, _t) {
({
/* PRIVATE STATIC CONSTANTS */
// # when creating a new contact for a partner plan, this ID will be sent to the server
// when creating a new contact for a partner plan, this ID will be sent to the server
NEW_PARTNER_ID: null
/* PUBLIC SCOPE */
})
// # current form is used to create a new plan
// current form is used to create a new plan
$scope.mode = 'creation'
// # prices bindings
// prices bindings
$scope.prices = {
training: {},
machine: {}
}
// # form inputs bindings
// form inputs bindings
$scope.plan = {
type: null,
group_id: null,
@ -95,21 +95,21 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
ui_weight: 0
}
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/plans/'
// # HTTP method for the rest API
// HTTP method for the rest API
$scope.method = 'POST'
// #
// Checks if the partner contact is a valid data. Used in the form validation process
// @returns {boolean}
// #
/**
* Checks if the partner contact is a valid data. Used in the form validation process
* @returns {boolean}
*/
$scope.partnerIsValid = () => ($scope.plan.type === 'Plan') || ($scope.plan.partnerId || ($scope.plan.partnerContact && $scope.plan.partnerContact.email))
// #
// Open a modal dialog allowing the admin to create a new partner user
// #
/**
* Open a modal dialog allowing the admin to create a new partner user
*/
$scope.openPartnerNewModal = function (subscription) {
const modalInstance = $uibModal.open({
animation: true,
@ -136,11 +136,11 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
})
}
// #
// Display some messages and redirect the user, once the form was submitted, depending on the result status
// (failed/succeeded).
// @param content {Object}
// #
/**
* Display some messages and redirect the user, once the form was submitted, depending on the result status
* (failed/succeeded).
* @param content {Object}
*/
$scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) {
return growl.error(_t('new_plan.unable_to_create_the_subscription_please_try_again'))
@ -160,43 +160,43 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
}
])
// #
// Controller used in the plan edition form
// #
/**
* Controller used in the plan edition form
*/
Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'spaces', 'prices', 'partners', 'CSRF', '$state', '$stateParams', 'growl', '$filter', '_t', 'Plan',
function ($scope, groups, plans, planPromise, machines, spaces, prices, partners, CSRF, $state, $stateParams, growl, $filter, _t, Plan) {
/* PUBLIC SCOPE */
// # List of spaces
// List of spaces
$scope.spaces = spaces
// # List of plans
// List of plans
$scope.plans = plans
// # List of machines
// List of machines
$scope.machines = machines
// # List of groups
// List of groups
$scope.groups = groups
// # current form is used for edition mode
// current form is used for edition mode
$scope.mode = 'edition'
// # edited plan data
// edited plan data
$scope.plan = planPromise
if ($scope.plan.type === null) { $scope.plan.type = 'Plan' }
if ($scope.plan.disabled) { $scope.plan.disabled = 'true' }
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/plans/${$stateParams.id}`
// # HTTP method for the rest API
// HTTP method for the rest API
$scope.method = 'PATCH'
// #
// If a parent plan was set ($scope.plan.parent), the prices will be copied from this parent plan into
// the current plan prices list. Otherwise, the current plan prices will be erased.
// #
/**
* If a parent plan was set ($scope.plan.parent), the prices will be copied from this parent plan into
* the current plan prices list. Otherwise, the current plan prices will be erased.
*/
$scope.copyPricesFromPlan = function () {
if ($scope.plan.parent) {
return Plan.get({ id: $scope.plan.parent }, parentPlan =>
@ -229,10 +229,10 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
}
}
// #
// Display some messages once the form was submitted, depending on the result status (failed/succeeded)
// @param content {Object}
// #
/**
* Display some messages once the form was submitted, depending on the result status (failed/succeeded)
* @param content {Object}
*/
$scope.afterSubmit = function (content) {
if ((content.id == null) && (content.plan_ids == null)) {
return growl.error(_t('edit_plan.unable_to_save_subscription_changes_please_try_again'))
@ -242,21 +242,21 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
}
}
// #
// Generate a string identifying the given plan by literal humain-readable name
// @param plan {Object} Plan object, as recovered from GET /api/plan/:id
// @param groups {Array} List of Groups objects, as recovered from GET /api/groups
// @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
// will be included.
// @returns {String}
// #
/**
* Generate a string identifying the given plan by literal humain-readable name
* @param plan {Object} Plan object, as recovered from GET /api/plan/:id
* @param groups {Array} List of Groups objects, as recovered from GET /api/groups
* @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
* will be included.
* @returns {String}
*/
$scope.humanReadablePlanName = (plan, groups, short) => `${$filter('humanReadablePlanName')(plan, groups, short)}`
// #
// Retrieve the machine from its ID
// @param machine_id {number} machine identifier
// @returns {Object} Machine
// #
/**
* Retrieve the machine from its ID
* @param machine_id {number} machine identifier
* @returns {Object} Machine
*/
$scope.getMachine = function (machine_id) {
for (let machine of Array.from($scope.machines)) {
if (machine.id === machine_id) {
@ -265,11 +265,11 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
}
}
// #
// Retrieve the space from its ID
// @param space_id {number} space identifier
// @returns {Object} Space
// #
/**
* Retrieve the space from its ID
* @param space_id {number} space identifier
* @returns {Object} Space
*/
$scope.getSpace = function (space_id) {
for (let space of Array.from($scope.spaces)) {
if (space.id === space_id) {
@ -280,14 +280,14 @@ Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'p
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = () =>
// Using the PlansController
new PlanController($scope, groups, prices, partners, CSRF)
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -15,66 +15,66 @@
*/
'use strict'
// #
// Controller used in the prices edition page
// #
/**
* Controller used in the prices edition page
*/
Application.Controllers.controller('EditPricingController', ['$scope', '$state', '$uibModal', '$filter', 'TrainingsPricing', 'Credit', 'Pricing', 'Plan', 'Coupon', 'plans', 'groups', 'growl', 'machinesPricesPromise', 'Price', 'dialogs', 'trainingsPricingsPromise', 'trainingsPromise', 'machineCreditsPromise', 'machinesPromise', 'trainingCreditsPromise', 'couponsPromise', 'spacesPromise', 'spacesPricesPromise', 'spacesCreditsPromise', '_t',
function ($scope, $state, $uibModal, $filter, TrainingsPricing, Credit, Pricing, Plan, Coupon, plans, groups, growl, machinesPricesPromise, Price, dialogs, trainingsPricingsPromise, trainingsPromise, machineCreditsPromise, machinesPromise, trainingCreditsPromise, couponsPromise, spacesPromise, spacesPricesPromise, spacesCreditsPromise, _t) {
/* PUBLIC SCOPE */
// # List of machines prices (not considering any plan)
// List of machines prices (not considering any plan)
$scope.machinesPrices = machinesPricesPromise
// # List of trainings pricing
// List of trainings pricing
$scope.trainingsPricings = trainingsPricingsPromise
// # List of available subscriptions plans (eg. student/month, PME/year ...)
// List of available subscriptions plans (eg. student/month, PME/year ...)
$scope.plans = plans
$scope.enabledPlans = plans.filter(p => !p.disabled)
// # List of groups (eg. normal, student ...)
// List of groups (eg. normal, student ...)
$scope.groups = groups.filter(g => g.slug !== 'admins')
$scope.enabledGroups = groups.filter(g => (g.slug !== 'admins') && !g.disabled)
// # Associate free machine hours with subscriptions
// Associate free machine hours with subscriptions
$scope.machineCredits = machineCreditsPromise
// # Array of associations (plan <-> training)
// Array of associations (plan <-> training)
$scope.trainingCredits = trainingCreditsPromise
// # Associate a plan with all its trainings ids
// Associate a plan with all its trainings ids
$scope.trainingCreditsGroups = {}
// # List of trainings
// List of trainings
$scope.trainings = trainingsPromise.filter(t => !t.disabled)
// # List of machines
// List of machines
$scope.machines = machinesPromise
$scope.enabledMachines = machinesPromise.filter(m => !m.disabled)
// # List of coupons
// List of coupons
$scope.coupons = couponsPromise
// # List of spaces
// List of spaces
$scope.spaces = spacesPromise
$scope.enabledSpaces = spacesPromise.filter(s => !s.disabled)
// # Associate free space hours with subscriptions
// Associate free space hours with subscriptions
$scope.spaceCredits = spacesCreditsPromise
// # List of spaces prices (not considering any plan)
// List of spaces prices (not considering any plan)
$scope.spacesPrices = spacesPricesPromise
// # The plans list ordering. Default: by group
// The plans list ordering. Default: by group
$scope.orderPlans = 'group_id'
// # Status of the drop-down menu in Credits tab
// Status of the drop-down menu in Credits tab
$scope.status =
{ isopen: false }
// # Default: we show only enabled plans
// Default: we show only enabled plans
$scope.planFiltering = 'enabled'
// # Available options for filtering plans by status
// Available options for filtering plans by status
$scope.filterDisabled = [
'enabled',
'disabled',
@ -97,11 +97,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Retrieve a plan from its given identifier and returns it
// @param id {number} plan ID
// @returns {Object} Plan, inherits from $resource
// #
/**
* Retrieve a plan from its given identifier and returns it
* @param id {number} plan ID
* @returns {Object} Plan, inherits from $resource
*/
$scope.getPlanFromId = function (id) {
for (let plan of Array.from($scope.plans)) {
if (plan.id === parseInt(id)) {
@ -110,11 +110,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Retrieve a group from its given identifier and returns it
// @param id {number} group ID
// @returns {Object} Group, inherits from $resource
// #
/**
* Retrieve a group from its given identifier and returns it
* @param id {number} group ID
* @returns {Object} Group, inherits from $resource
*/
$scope.getGroupFromId = function (groups, id) {
for (let group of Array.from(groups)) {
if (group.id === parseInt(id)) {
@ -123,12 +123,12 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Returns a human readable string of named trainings, according to the provided array.
// $scope.trainings may contains the full list of training. The returned string will only contains the trainings
// whom ID are given in the provided parameter
// @param trainings {Array<number>} trainings IDs array
// #
/**
* Returns a human readable string of named trainings, according to the provided array.
* $scope.trainings may contains the full list of training. The returned string will only contains the trainings
* whom ID are given in the provided parameter
* @param trainings {Array<number>} trainings IDs array
*/
$scope.showTrainings = function (trainings) {
if (!angular.isArray(trainings) || !(trainings.length > 0)) {
return _t('pricing.none')
@ -143,11 +143,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
if (selected.length) { return selected.join(' | ') } else { return _t('pricing.none') }
}
// #
// Validation callback when editing training's credits. Save the changes.
// @param newdata {Object} training and associated plans
// @param planId {number|string} plan id
// #
/**
* Validation callback when editing training's credits. Save the changes.
* @param newdata {Object} training and associated plans
* @param planId {number|string} plan id
*/
$scope.saveTrainingCredits = function (newdata, planId) {
// save the number of credits
Plan.update({ id: planId },
@ -202,16 +202,16 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
})
}
// #
// Cancel the current training credit modification
// @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
// #
/**
* Cancel the current training credit modification
* @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
*/
$scope.cancelTrainingCredit = rowform => rowform.$cancel()
// #
// Create a new empty entry in the $scope.machineCredits array
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Create a new empty entry in the $scope.machineCredits array
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.addMachineCredit = function (e) {
e.preventDefault()
e.stopPropagation()
@ -221,11 +221,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return $scope.status.isopen = !$scope.status.isopen
}
// #
// In the Credits tab, return the name of the machine/space associated with the given credit
// @param credit {Object} credit object, inherited from $resource
// @returns {String}
// #
/**
* In the Credits tab, return the name of the machine/space associated with the given credit
* @param credit {Object} credit object, inherited from $resource
* @returns {String}
*/
$scope.showCreditableName = function (credit) {
let selected = _t('pricing.not_set')
if (credit && credit.creditable_id) {
@ -238,11 +238,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return selected
}
// #
// In the Credits tab, return the machine/space associated with the given credit
// @param credit {Object} credit object, inherited from $resource
// @returns {Object}
// #
/**
* In the Credits tab, return the machine/space associated with the given credit
* @param credit {Object} credit object, inherited from $resource
* @returns {Object}
*/
$scope.getCreditable = function (credit) {
let selected
if (credit && credit.creditable_id) {
@ -263,12 +263,12 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return selected
}
// #
// Validation callback when editing machine's credits. Save the changes.
// This will prevent the creation of two credits associating the same machine and plan.
// @param data {Object} machine, associated plan and number of credit hours.
// @param [id] {number} credit id for edition, create a new credit object if not provided
// #
/**
* Validation callback when editing machine's credits. Save the changes.
* This will prevent the creation of two credits associating the same machine and plan.
* @param data {Object} machine, associated plan and number of credit hours.
* @param [id] {number} credit id for edition, create a new credit object if not provided
*/
$scope.saveMachineCredit = function (data, id) {
for (let mc of Array.from($scope.machineCredits)) {
if ((mc.plan_id === data.plan_id) && (mc.creditable_id === data.creditable_id) && ((id === null) || (mc.id !== id))) {
@ -297,11 +297,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Removes the newly inserted but not saved machine credit / Cancel the current machine credit modification
// @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
// @param index {number} credit index in the $scope.machineCredits array
// #
/**
* Removes the newly inserted but not saved machine credit / Cancel the current machine credit modification
* @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
* @param index {number} credit index in the $scope.machineCredits array
*/
$scope.cancelMachineCredit = function (rowform, index) {
if ($scope.machineCredits[index].id != null) {
return rowform.$cancel()
@ -310,19 +310,19 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Deletes the machine credit at the specified index
// @param index {number} machine credit index in the $scope.machineCredits array
// #
/**
* Deletes the machine credit at the specified index
* @param index {number} machine credit index in the $scope.machineCredits array
*/
$scope.removeMachineCredit = function (index) {
Credit.delete($scope.machineCredits[index])
return $scope.machineCredits.splice(index, 1)
}
// #
// Create a new empty entry in the $scope.spaceCredits array
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Create a new empty entry in the $scope.spaceCredits array
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.addSpaceCredit = function (e) {
e.preventDefault()
e.stopPropagation()
@ -332,12 +332,12 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return $scope.status.isopen = !$scope.status.isopen
}
// #
// Validation callback when editing space's credits. Save the changes.
// This will prevent the creation of two credits associated with the same space and plan.
// @param data {Object} space, associated plan and number of credit hours.
// @param [id] {number} credit id for edition, create a new credit object if not provided
// #
/**
* Validation callback when editing space's credits. Save the changes.
* This will prevent the creation of two credits associated with the same space and plan.
* @param data {Object} space, associated plan and number of credit hours.
* @param [id] {number} credit id for edition, create a new credit object if not provided
*/
$scope.saveSpaceCredit = function (data, id) {
for (let sc of Array.from($scope.spaceCredits)) {
if ((sc.plan_id === data.plan_id) && (sc.creditable_id === data.creditable_id) && ((id === null) || (sc.id !== id))) {
@ -366,11 +366,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Removes the newly inserted but not saved space credit / Cancel the current space credit modification
// @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
// @param index {number} credit index in the $scope.spaceCredits array
// #
/**
* Removes the newly inserted but not saved space credit / Cancel the current space credit modification
* @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
* @param index {number} credit index in the $scope.spaceCredits array
*/
$scope.cancelSpaceCredit = function (rowform, index) {
if ($scope.spaceCredits[index].id != null) {
return rowform.$cancel()
@ -379,30 +379,30 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Deletes the space credit at the specified index
// @param index {number} space credit index in the $scope.spaceCredits array
// #
/**
* Deletes the space credit at the specified index
* @param index {number} space credit index in the $scope.spaceCredits array
*/
$scope.removeSpaceCredit = function (index) {
Credit.delete($scope.spaceCredits[index])
return $scope.spaceCredits.splice(index, 1)
}
// #
// If the plan does not have a type, return a default value for display purposes
// @param type {string|undefined|null} plan's type (eg. 'partner')
// @returns {string}
// #
/**
* If the plan does not have a type, return a default value for display purposes
* @param type {string|undefined|null} plan's type (eg. 'partner')
* @returns {string}
*/
$scope.getPlanType = function (type) {
if (type === 'PartnerPlan') {
return _t('pricing.partner')
} else { return _t('pricing.standard') }
}
// #
// Change the plans ordering criterion to the one provided
// @param orderBy {string} ordering criterion
// #
/**
* Change the plans ordering criterion to the one provided
* @param orderBy {string} ordering criterion
*/
$scope.setOrderPlans = function (orderBy) {
if ($scope.orderPlans === orderBy) {
return $scope.orderPlans = `-${orderBy}`
@ -411,9 +411,9 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Retrieve a price from prices array by a machineId and a groupId
// #
/**
* Retrieve a price from prices array by a machineId and a groupId
*/
$scope.findPriceBy = function (prices, machineId, groupId) {
for (let price of Array.from(prices)) {
if ((price.priceable_id === machineId) && (price.group_id === groupId)) {
@ -422,9 +422,9 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// update a price for a machine and a group, not considering any plan
// #
/**
* update a price for a machine and a group, not considering any plan
*/
$scope.updatePrice = function (data, price) {
if (data != null) {
return Price.update({ id: price.id }, { price: { amount: data } }).$promise
@ -433,10 +433,10 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Delete the specified subcription plan
// @param id {number} plan id
// #
/**
* Delete the specified subcription plan
* @param id {number} plan id
*/
$scope.deletePlan = function (plans, id) {
if (typeof id !== 'number') {
return console.error('[EditPricingController::deletePlan] Error: invalid id parameter')
@ -467,21 +467,21 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Generate a string identifying the given plan by literal humain-readable name
// @param plan {Object} Plan object, as recovered from GET /api/plan/:id
// @param groups {Array} List of Groups objects, as recovered from GET /api/groups
// @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
// will be included.
// @returns {String}
// #
/**
* Generate a string identifying the given plan by literal humain-readable name
* @param plan {Object} Plan object, as recovered from GET /api/plan/:id
* @param groups {Array} List of Groups objects, as recovered from GET /api/groups
* @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
* will be included.
* @returns {String}
*/
$scope.humanReadablePlanName = (plan, groups, short) => `${$filter('humanReadablePlanName')(plan, groups, short)}`
// #
// Delete a coupon from the server's database and, in case of success, from the list in memory
// @param coupons {Array<Object>} should be called with $scope.coupons
// @param id {number} ID of the coupon to delete
// #
/**
* Delete a coupon from the server's database and, in case of success, from the list in memory
* @param coupons {Array<Object>} should be called with $scope.coupons
* @param id {number} ID of the coupon to delete
*/
$scope.deleteCoupon = function (coupons, id) {
if (typeof id !== 'number') {
return console.error('[EditPricingController::deleteCoupon] Error: invalid id parameter')
@ -516,10 +516,10 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Open a modal allowing to select an user and send him the details of the provided coupon
// @param coupon {Object} The coupon to send
// #
/**
* Open a modal allowing to select an user and send him the details of the provided coupon
* @param coupon {Object} The coupon to send
*/
$scope.sendCouponToUser = coupon =>
$uibModal.open({
templateUrl: '<%= asset_path "admin/pricing/sendCoupon.html" %>',
@ -528,14 +528,14 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
},
size: 'md',
controller: ['$scope', '$uibModalInstance', 'Coupon', 'coupon', '_t', function ($scope, $uibModalInstance, Coupon, coupon, _t) {
// # Member, receiver of the coupon
// Member, receiver of the coupon
$scope.ctrl =
{ member: null }
// # Details of the coupon to send
// Details of the coupon to send
$scope.coupon = coupon
// # Callback to validate sending of the coupon
// Callback to validate sending of the coupon
$scope.ok = () =>
Coupon.send({ coupon_code: coupon.code, user_id: $scope.ctrl.member.id }, function (res) {
growl.success(_t('pricing.coupon_successfully_sent_to_USER', { USER: $scope.ctrl.member.name }))
@ -543,20 +543,20 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
, err => growl.error(_t('pricing.an_error_occurred_unable_to_send_the_coupon')))
// # Callback to close the modal and cancel the sending process
// Callback to close the modal and cancel the sending process
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
] })
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
$scope.trainingCreditsGroups = groupCreditsByPlan($scope.trainingCredits)
// # adds empty array for plan which hasn't any credits yet
// adds empty array for plan which hasn't any credits yet
return (() => {
const result = []
for (let plan of Array.from($scope.plans)) {
@ -570,19 +570,19 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
})()
}
// #
// Retrieve an item index by its ID from the given array of objects
// @param items {Array<{id:number}>}
// @param id {number}
// @returns {number} item index in the provided array
// #
/**
* Retrieve an item index by its ID from the given array of objects
* @param items {Array<{id:number}>}
* @param id {number}
* @returns {number} item index in the provided array
*/
var findItemIdxById = (items, id) =>
(items.map(item => item.id)).indexOf(id)
// #
// Group the given credits array into a map associating the plan ID with its associated trainings/machines
// @return {Object} the association map
// #
/**
* Group the given credits array into a map associating the plan ID with its associated trainings/machines
* @return {Object} the association map
*/
var groupCreditsByPlan = function (credits) {
const creditsMap = {}
angular.forEach(credits, function (c) {
@ -594,11 +594,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
return creditsMap
}
// #
// Iterate through $scope.traininfCredits to find the credit matching the given criterion
// @param trainingId {number|string} training ID
// @param planId {number|string} plan ID
// #
/**
* Iterate through $scope.traininfCredits to find the credit matching the given criterion
* @param trainingId {number|string} training ID
* @param planId {number|string} plan ID
*/
var findTrainingCredit = function (trainingId, planId) {
trainingId = parseInt(trainingId)
planId = parseInt(planId)
@ -610,11 +610,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// #
// Retrieve a training from its given identifier and returns it
// @param id {number} training ID
// @returns {Object} Training inherited from $resource
// #
/**
* Retrieve a training from its given identifier and returns it
* @param id {number} training ID
* @returns {Object} Training inherited from $resource
*/
var getTrainingFromId = function (id) {
for (let training of Array.from($scope.trainings)) {
if (training.id === parseInt(id)) {
@ -623,7 +623,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state',
}
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -19,70 +19,70 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
function ($scope, $state, $rootScope, $uibModal, es, Member, _t, membersPromise, statisticsPromise) {
/* PRIVATE STATIC CONSTANTS */
// # search window size
// search window size
const RESULTS_PER_PAGE = 20
// # keep search context for (delay in minutes) ...
// keep search context for (delay in minutes) ...
const ES_SCROLL_TIME = 1
/* PUBLIC SCOPE */
// # ui-view transitions optimization: if true, the stats will never be refreshed
// ui-view transitions optimization: if true, the stats will never be refreshed
$scope.preventRefresh = false
// # statistics structure in elasticSearch
// statistics structure in elasticSearch
$scope.statistics = statisticsPromise
// # fablab users list
// fablab users list
$scope.members = membersPromise
// # statistics data recovered from elasticSearch
// statistics data recovered from elasticSearch
$scope.data = null
// # when did the search was triggered
// when did the search was triggered
$scope.searchDate = null
// # id of the elastic search context
// id of the elastic search context
$scope.scrollId = null
// # total number of results for the current query
// total number of results for the current query
$scope.totalHits = null
// # configuration of the widget allowing to pick the ages range
// configuration of the widget allowing to pick the ages range
$scope.agePicker = {
show: false,
start: null,
end: null
}
// # total CA for the current view
// total CA for the current view
$scope.sumCA = 0
// # average users' age for the current view
// average users' age for the current view
$scope.averageAge = 0
// # total of the stat field for non simple types
// total of the stat field for non simple types
$scope.sumStat = 0
// # Results of custom aggregations for the current type
// Results of custom aggregations for the current type
$scope.customAggs = {}
// # default: results are not sorted
// default: results are not sorted
$scope.sorting = {
ca: 'none',
date: 'desc'
}
// # active tab will be set here
// active tab will be set here
$scope.selectedIndex = null
// # type filter binding
// type filter binding
$scope.type = {
selected: null,
active: null
}
// # selected custom filter
// selected custom filter
$scope.customFilter = {
show: false,
criterion: {},
@ -99,14 +99,14 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
} // France: the week starts on monday
}
// # available custom filters
// available custom filters
$scope.filters = []
// # default: we do not open the datepicker menu
// default: we do not open the datepicker menu
$scope.datePicker =
{ show: false }
// # datePicker parameters for interval beginning
// datePicker parameters for interval beginning
$scope.datePickerStart = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -118,7 +118,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// # datePicker parameters for interval ending
// datePicker parameters for interval ending
$scope.datePickerEnd = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -130,29 +130,29 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Callback to open the datepicker (interval start)
// @param $event {Object} jQuery event object
// #
/**
* Callback to open the datepicker (interval start)
* @param $event {Object} jQuery event object
*/
$scope.toggleStartDatePicker = $event => toggleDatePicker($event, $scope.datePickerStart)
// #
// Callback to open the datepicker (interval end)
// @param $event {Object} jQuery event object
// #
/**
* Callback to open the datepicker (interval end)
* @param $event {Object} jQuery event object
*/
$scope.toggleEndDatePicker = $event => toggleDatePicker($event, $scope.datePickerEnd)
// #
// Callback to open the datepicker (custom filter)
// @param $event {Object} jQuery event object
// #
/**
* Callback to open the datepicker (custom filter)
* @param $event {Object} jQuery event object
*/
$scope.toggleCustomDatePicker = $event => toggleDatePicker($event, $scope.customFilter.datePicker)
// #
// Callback called when the active tab is changed.
// recover the current tab and store its value in $scope.selectedIndex
// @param tab {Object} elasticsearch statistic structure (from statistic_indices table)
// #
/**
* Callback called when the active tab is changed.
* recover the current tab and store its value in $scope.selectedIndex
* @param tab {Object} elasticsearch statistic structure (from statistic_indices table)
*/
$scope.setActiveTab = function (tab) {
$scope.selectedIndex = tab
$scope.type.selected = tab.types[0]
@ -166,10 +166,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return refreshStats()
}
// #
// Returns true if the provided tab must be hidden due to some global or local configuration
// @param tab {Object} elasticsearch statistic structure (from statistic_indices table)
// #
/**
* Returns true if the provided tab must be hidden due to some global or local configuration
* @param tab {Object} elasticsearch statistic structure (from statistic_indices table)
*/
$scope.hiddenTab = function (tab) {
if (tab.table) {
if ((tab.es_type_key === 'subscription') && $rootScope.fablabWithoutPlans) {
@ -184,9 +184,9 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Callback to validate the filters and send a new request to elastic
// #
/**
* Callback to validate the filters and send a new request to elastic
*/
$scope.validateFilterChange = function () {
$scope.agePicker.show = false
$scope.customFilter.show = false
@ -195,24 +195,24 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return refreshStats()
}
// #
// Callback to validate the dates range and refresh the data from elastic
// #
/**
* Callback to validate the dates range and refresh the data from elastic
*/
$scope.validateDateChange = function () {
$scope.datePicker.show = false
return refreshStats()
}
// #
// Parse the given date and return a user-friendly string
// @param date {Date} JS date or ant moment.js compatible date string
// #
/**
* Parse the given date and return a user-friendly string
* @param date {Date} JS date or ant moment.js compatible date string
*/
$scope.formatDate = date => moment(date).format('LL')
// #
// Parse the sex and return a user-friendly string
// @param sex {string} 'male' | 'female'
// #
/**
* Parse the sex and return a user-friendly string
* @param sex {string} 'male' | 'female'
*/
$scope.formatSex = function (sex) {
if (sex === 'male') {
return _t('man')
@ -222,10 +222,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Retrieve the label for the given subtype in the current type
// @param key {string} statistic subtype key
// #
/**
* Retrieve the label for the given subtype in the current type
* @param key {string} statistic subtype key
*/
$scope.formatSubtype = function (key) {
let label = ''
angular.forEach($scope.type.active.subtypes, function (subtype) {
@ -236,10 +236,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return label
}
// #
// Helper usable in ng-switch to determine the input type to display for custom filter value
// @param filter {Object} custom filter criterion
// #
/**
* Helper usable in ng-switch to determine the input type to display for custom filter value
* @param filter {Object} custom filter criterion
*/
$scope.getCustomValueInputType = function (filter) {
if (filter && filter.values) {
if (typeof (filter.values[0]) === 'string') {
@ -252,10 +252,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Change the sorting order and refresh the results to match the new order
// @param filter {Object} any filter
// #
/**
* Change the sorting order and refresh the results to match the new order
* @param filter {Object} any filter
*/
$scope.toggleSorting = function (filter) {
switch ($scope.sorting[filter]) {
case 'none': $scope.sorting[filter] = 'asc'; break
@ -265,19 +265,19 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return refreshStats()
}
// #
// Return the user's name from his given ID
// @param id {number} user ID
// #
/**
* Return the user's name from his given ID
* @param id {number} user ID
*/
$scope.getUserNameFromId = function (id) {
const name = $scope.members[id]
return (name || `ID ${id}`)
}
// #
// Run a scroll query to elasticsearch to append the next packet of results to those displayed.
// If the ES search context has expired when the user ask for more results, we re-run the whole query.
// #
/**
* Run a scroll query to elasticsearch to append the next packet of results to those displayed.
* If the ES search context has expired when the user ask for more results, we re-run the whole query.
*/
$scope.showMoreResults = function () {
// if all results were retrieved, do nothing
if ($scope.data.length >= $scope.totalHits) {
@ -303,9 +303,9 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Open a modal dialog asking the user for details about exporting the statistics tables to an excel file
// #
/**
* Open a modal dialog asking the user for details about exporting the statistics tables to an excel file
*/
$scope.exportToExcel = function () {
const options = {
templateUrl: '<%= asset_path "admin/statistics/export.html" %>',
@ -337,9 +337,9 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = () =>
// workaround for angular-bootstrap::tabs behavior: on tab deletion, another tab will be selected
// which will cause every tabs to reload, one by one, when the view is closed
@ -349,20 +349,20 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
})
// #
// Generic function to toggle a bootstrap datePicker
// @param $event {Object} jQuery event object
// @param datePicker {Object} settings object of the concerned datepicker. Must have an 'opened' property
// #
/**
* Generic function to toggle a bootstrap datePicker
* @param $event {Object} jQuery event object
* @param datePicker {Object} settings object of the concerned datepicker. Must have an 'opened' property
*/
var toggleDatePicker = function ($event, datePicker) {
$event.preventDefault()
$event.stopPropagation()
return datePicker.opened = !datePicker.opened
}
// #
// Force update the statistics table, querying elasticSearch according to the current config values
// #
/**
* Force update the statistics table, querying elasticSearch according to the current config values
*/
var refreshStats = function () {
if ($scope.selectedIndex && !$scope.preventRefresh) {
$scope.data = []
@ -395,14 +395,14 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Run the elasticSearch query to retreive the /stats/type aggregations
// @param index {String} elasticSearch document type (account|event|machine|project|subscription|training)
// @param type {String} statistics type (month|year|booking|hour|user|project)
// @param custom {{key:{string}, value:{string}}|null} custom filter property or null to disable this filter
// @param callback {function} function be to run after results were retrieved, it will receive
// two parameters : results {Object}, error {String} (if any)
// #
/**
* Run the elasticSearch query to retreive the /stats/type aggregations
* @param index {String} elasticSearch document type (account|event|machine|project|subscription|training)
* @param type {String} statistics type (month|year|booking|hour|user|project)
* @param custom {{key:{string}, value:{string}}|null} custom filter property or null to disable this filter
* @param callback {function} function be to run after results were retrieved, it will receive
* two parameters : results {Object}, error {String} (if any)
*/
var queryElasticStats = function (index, type, custom, callback) {
// handle invalid callback
if (typeof (callback) !== 'function') {
@ -431,17 +431,17 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
})
}
// #
// Build an object representing the content of the REST-JSON query to elasticSearch,
// based on the provided parameters for row data recovering.
// @param type {String} statistics type (month|year|booking|hour|user|project)
// @param custom {{key:{string}, value:{string}}|null} custom filter property or null to disable this filter
// @param ageMin {Number|null} filter by age: range lower value OR null to do not filter
// @param ageMax {Number|null} filter by age: range higher value OR null to do not filter
// @param intervalBegin {moment} statitics interval beginning (moment.js type)
// @param intervalEnd {moment} statitics interval ending (moment.js type)
// @param sortings {Array|null} elasticSearch criteria for sorting the results
// #
/**
* Build an object representing the content of the REST-JSON query to elasticSearch,
* based on the provided parameters for row data recovering.
* @param type {String} statistics type (month|year|booking|hour|user|project)
* @param custom {{key:{string}, value:{string}}|null} custom filter property or null to disable this filter
* @param ageMin {Number|null} filter by age: range lower value OR null to do not filter
* @param ageMax {Number|null} filter by age: range higher value OR null to do not filter
* @param intervalBegin {moment} statitics interval beginning (moment.js type)
* @param intervalEnd {moment} statitics interval ending (moment.js type)
* @param sortings {Array|null} elasticSearch criteria for sorting the results
*/
var buildElasticDataQuery = function (type, custom, ageMin, ageMax, intervalBegin, intervalEnd, sortings) {
const q = {
'query': {
@ -512,11 +512,11 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return q
}
// #
// Build the elasticSearch query DSL to match the selected cutom filter
// @param custom {Object} if custom is empty or undefined, an empty string will be returned
// @returns {{match:{*}}|string}
// #
/**
* Build the elasticSearch query DSL to match the selected cutom filter
* @param custom {Object} if custom is empty or undefined, an empty string will be returned
* @returns {{match:{*}}|string}
*/
var buildElasticCustomCriterion = function (custom) {
if (custom) {
const criterion = {
@ -534,10 +534,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
}
}
// #
// Parse the provided criteria array and return the corresponding elasticSearch syntax
// @param criteria {Array} array of {key_to_sort:order}
// #
/**
* Parse the provided criteria array and return the corresponding elasticSearch syntax
* @param criteria {Array} array of {key_to_sort:order}
*/
var buildElasticSortCriteria = function (criteria) {
const crits = []
angular.forEach(criteria, function (value, key) {
@ -550,10 +550,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
return crits
}
// #
// Fullfil the list of available options in the custom filter panel. The list will be based on common
// properties and on index-specific properties (additional_fields)
// #
/**
* Fullfil the list of available options in the custom filter panel. The list will be based on common
* properties and on index-specific properties (additional_fields)
*/
var buildCustomFiltersList = function () {
$scope.filters = []
@ -587,10 +587,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
})
}
// #
// Build and return an object according to the custom filter set by the user, used to request elasticsearch
// @return {Object|null}
// #
/**
* Build and return an object according to the custom filter set by the user, used to request elasticsearch
* @return {Object|null}
*/
var buildCustomFilterQuery = function () {
let custom = null
if (!angular.isUndefinedOrNull($scope.customFilter.criterion) &&
@ -612,32 +612,32 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state',
Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$uibModalInstance', 'Export', 'dates', 'query', 'index', 'type', 'CSRF', 'growl', '_t',
function ($scope, $uibModalInstance, Export, dates, query, index, type, CSRF, growl, _t) {
// # Retrieve Anti-CSRF tokens from cookies
// Retrieve Anti-CSRF tokens from cookies
CSRF.setMetaTags()
// # Bindings for date range
// Bindings for date range
$scope.dates = dates
// # Body of the query to export
// Body of the query to export
$scope.query = JSON.stringify(query)
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/stats/${index.key}/export`
// # Key of the current search' statistic type
// Key of the current search' statistic type
$scope.typeKey = type.key
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # Anti-CSRF token to inject into the download form
// Anti-CSRF token to inject into the download form
$scope.csrfToken = angular.element('meta[name="csrf-token"]')[0].content
// # Binding of the export type (global / current)
// Binding of the export type (global / current)
$scope.export =
{ type: 'current' }
// # datePicker parameters for interval beginning
// datePicker parameters for interval beginning
$scope.exportStart = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -648,7 +648,7 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
}
}
// # datePicker parameters for interval ending
// datePicker parameters for interval ending
$scope.exportEnd = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -659,22 +659,22 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
}
}
// #
// Callback to open the datepicker (interval start)
// @param $event {Object} jQuery event object
// #
/**
* Callback to open the datepicker (interval start)
* @param $event {Object} jQuery event object
*/
$scope.toggleStartDatePicker = $event => $scope.exportStart.opened = !$scope.exportStart.opened
// #
// Callback to open the datepicker (interval end)
// @param $event {Object} jQuery event object
// #
/**
* Callback to open the datepicker (interval end)
* @param $event {Object} jQuery event object
*/
$scope.toggleEndDatePicker = $event => $scope.exportEnd.opened = !$scope.exportEnd.opened
// #
// Callback when exchanging the export type between 'global' and 'current view'
// Adjust the query and the requesting url according to this type.
// #
/**
* Callback when exchanging the export type between 'global' and 'current view'
* Adjust the query and the requesting url according to this type.
*/
$scope.setRequest = function () {
if ($scope.export.type === 'global') {
$scope.actionUrl = '/stats/global/export'
@ -700,9 +700,9 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
}
}
// #
// Callback to close the modal, telling the caller what is exported
// #
/**
* Callback to close the modal, telling the caller what is exported
*/
$scope.exportData = function () {
const statusQry = { category: 'statistics', type: $scope.export.type, query: $scope.query }
if ($scope.export.type !== 'global') {
@ -719,9 +719,9 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u
return $uibModalInstance.close(statusQry)
}
// #
// Callback to cancel the export and close the modal
// #
/**
* Callback to cancel the export and close the modal
*/
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
])

View File

@ -16,27 +16,27 @@
/* COMMON CODE */
// #
// Provides a set of common callback methods to the $scope parameter. These methods are used
// in the various trainings' admin controllers.
//
// Provides :
// - $scope.submited(content)
// - $scope.fileinputClass(v)
// - $scope.onDisableToggled
//
// Requires :
// - $state (Ui-Router) [ 'app.admin.trainings' ]
// - $scope.training
// #
/**
* Provides a set of common callback methods to the $scope parameter. These methods are used
* in the various trainings' admin controllers.
*
* Provides :
* - $scope.submited(content)
* - $scope.fileinputClass(v)
* - $scope.onDisableToggled
*
* Requires :
* - $state (Ui-Router) [ 'app.admin.trainings' ]
* - $scope.training
/*
class TrainingsController {
constructor ($scope, $state) {
// #
// For use with ngUpload (https://github.com/twilson63/ngUpload).
// Intended to be the callback when the upload is done: any raised error will be stacked in the
// $scope.alerts array. If everything goes fine, the user is redirected to the trainings list.
// @param content {Object} JSON - The upload's result
// #
/*
* For use with ngUpload (https://github.com/twilson63/ngUpload).
* Intended to be the callback when the upload is done: any raised error will be stacked in the
* $scope.alerts array. If everything goes fine, the user is redirected to the trainings list.
* @param content {Object} JSON - The upload's result
*/
$scope.submited = function (content) {
if ((content.id == null)) {
$scope.alerts = []
@ -53,21 +53,21 @@ class TrainingsController {
}
}
// #
// Changes the current user's view, redirecting him to the machines list
// #
/**
* Changes the current user's view, redirecting him to the machines list
*/
$scope.cancel = () => $state.go('app.admin.trainings')
// #
// Force the 'public_page' attribute to false when the current training is disabled
// #
/**
* Force the 'public_page' attribute to false when the current training is disabled
*/
$scope.onDisableToggled = () => $scope.training.public_page = !$scope.training.disabled
// #
// 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)
// #
/**
* 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'
@ -78,117 +78,117 @@ class TrainingsController {
}
}
// #
// Controller used in the training creation page (admin)
// #
/**
* Controller used in the training creation page (admin)
*/
Application.Controllers.controller('NewTrainingController', [ '$scope', '$state', 'machinesPromise', 'CSRF',
function ($scope, $state, machinesPromise, CSRF) {
/* PUBLIC SCOPE */
// # Form action on the following URL
// Form action on the following URL
$scope.method = 'post'
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/trainings/'
// # list of machines
// list of machines
$scope.machines = machinesPromise
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
// # Using the TrainingsController
// Using the TrainingsController
return new TrainingsController($scope, $state)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the training edition page (admin)
// #
/**
* Controller used in the training edition page (admin)
*/
Application.Controllers.controller('EditTrainingController', [ '$scope', '$state', '$stateParams', 'trainingPromise', 'machinesPromise', 'CSRF',
function ($scope, $state, $stateParams, trainingPromise, machinesPromise, CSRF) {
/* PUBLIC SCOPE */
// # Form action on the following URL
// Form action on the following URL
$scope.method = 'patch'
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/trainings/${$stateParams.id}`
// # Details of the training to edit (id in URL)
// Details of the training to edit (id in URL)
$scope.training = trainingPromise
// # list of machines
// list of machines
$scope.machines = machinesPromise
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
// # Using the TrainingsController
// Using the TrainingsController
return new TrainingsController($scope, $state)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the trainings management page, allowing admins users to see and manage the list of trainings and reservations.
// #
/**
* Controller used in the trainings management page, allowing admins users to see and manage the list of trainings and reservations.
*/
Application.Controllers.controller('TrainingsAdminController', ['$scope', '$state', '$uibModal', 'Training', 'trainingsPromise', 'machinesPromise', '_t', 'growl', 'dialogs',
function ($scope, $state, $uibModal, Training, trainingsPromise, machinesPromise, _t, growl, dialogs) {
/* PUBLIC SCOPE */
// # list of trainings
// list of trainings
let groupAvailabilities
$scope.trainings = trainingsPromise
// # simplified list of machines
// simplified list of machines
$scope.machines = machinesPromise
// # Training to monitor, binded with drop-down selection
// Training to monitor, binded with drop-down selection
$scope.monitoring =
{ training: null }
// # list of training availabilies, grouped by date
// list of training availabilies, grouped by date
$scope.groupedAvailabilities = {}
// # default: accordions are not open
// default: accordions are not open
$scope.accordions = {}
// # Binding for the parseInt function
// Binding for the parseInt function
$scope.parseInt = parseInt
// # Default: we show only enabled trainings
// Default: we show only enabled trainings
$scope.trainingFiltering = 'enabled'
// # Available options for filtering trainings by status
// Available options for filtering trainings by status
$scope.filterDisabled = [
'enabled',
'disabled',
'all'
]
// #
// In the trainings listing tab, return the stringified list of machines associated with the provided training
// @param training {Object} Training object, inherited from $resource
// @returns {string}
// #
/**
* In the trainings listing tab, return the stringified list of machines associated with the provided training
* @param training {Object} Training object, inherited from $resource
* @returns {string}
*/
$scope.showMachines = function (training) {
const selected = []
angular.forEach($scope.machines, function (m) {
@ -199,11 +199,11 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
if (selected.length) { return selected.join(', ') } else { return _t('none') }
}
// #
// Removes the newly inserted but not saved training / Cancel the current training modification
// @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
// @param index {number} training index in the $scope.trainings array
// #
/**
* Removes the newly inserted but not saved training / Cancel the current training modification
* @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
* @param index {number} training index in the $scope.trainings array
*/
$scope.cancelTraining = function (rowform, index) {
if ($scope.trainings[index].id != null) {
return rowform.$cancel()
@ -212,13 +212,13 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
}
}
// #
// In the trainings monitoring tab, callback to open a modal window displaying the current bookings for the
// provided training slot. The admin will be then able to validate the training for the users that followed
// the training.
// @param training {Object} Training object, inherited from $resource
// @param availability {Object} time slot when the training occurs
// #
/**
* In the trainings monitoring tab, callback to open a modal window displaying the current bookings for the
* provided training slot. The admin will be then able to validate the training for the users that followed
* the training.
* @param training {Object} Training object, inherited from $resource
* @param availability {Object} time slot when the training occurs
*/
$scope.showReservations = (training, availability) =>
$uibModal.open({
templateUrl: '<%= asset_path "admin/trainings/validTrainingModal.html" %>',
@ -227,10 +227,10 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
$scope.usersToValid = []
// #
// Mark/unmark the provided user for training validation
// @param user {Object} from the availability.reservation_users list
// #
/**
* Mark/unmark the provided user for training validation
* @param user {Object} from the availability.reservation_users list
*/
$scope.toggleSelection = function (user) {
const index = $scope.usersToValid.indexOf(user)
if (index > -1) {
@ -240,9 +240,9 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
}
}
// #
// Validates the modifications (training validations) and save them to the server
// #
/**
* Validates the modifications (training validations) and save them to the server
*/
$scope.ok = function () {
const users = $scope.usersToValid.map(u => u.id)
return Training.update({ id: training.id }, {
@ -257,18 +257,18 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
})
}
// #
// Cancel the modifications and close the modal window
// #
/**
* Cancel the modifications and close the modal window
*/
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
] })
// #
// Delete the provided training and, in case of sucess, remove it from the trainings list afterwards
// @param index {number} index of the provided training in $scope.trainings
// @param training {Object} training to delete
// #
/**
* Delete the provided training and, in case of sucess, remove it from the trainings list afterwards
* @param index {number} index of the provided training in $scope.trainings
* @param training {Object} training to delete
*/
$scope.removeTraining = (index, training) =>
dialogs.confirm({
resolve: {
@ -288,23 +288,23 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
, error => growl.warning(_t('unable_to_delete_the_training_because_some_users_alredy_booked_it')))
)
// #
// Takes a month number and return its localized literal name
// @param {Number} from 0 to 11
// @returns {String} eg. 'janvier'
// #
/**
* Takes a month number and return its localized literal name
* @param {Number} from 0 to 11
* @returns {String} eg. 'janvier'
*/
$scope.formatMonth = function (number) {
number = parseInt(number)
return moment().month(number).format('MMMM')
}
// #
// Given a day, month and year, return a localized literal name for the day
// @param day {Number} from 1 to 31
// @param month {Number} from 0 to 11
// @param year {Number} Gregorian's year number
// @returns {String} eg. 'mercredi 12'
// #
/**
* Given a day, month and year, return a localized literal name for the day
* @param day {Number} from 1 to 31
* @param month {Number} from 0 to 11
* @param year {Number} Gregorian's year number
* @returns {String} eg. 'mercredi 12'
*/
$scope.formatDay = function (day, month, year) {
day = parseInt(day)
month = parseInt(month)
@ -313,10 +313,10 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
return moment({ year, month, day }).format('dddd D')
}
// #
// Callback when the drop-down selection is changed.
// The selected training details will be loaded from the API and rendered into the accordions.
// #
/**
* Callback when the drop-down selection is changed.
* The selected training details will be loaded from the API and rendered into the accordions.
*/
$scope.selectTrainingToMonitor = () =>
Training.availabilities({ id: $scope.monitoring.training.id }, function (training) {
$scope.groupedAvailabilities = groupAvailabilities([training])
@ -331,11 +331,11 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
/* PRIVATE SCOPE */
// #
// Group the trainings availabilites by trainings and by dates and return the resulting tree
// @param trainings {Array} $scope.trainings is expected here
// @returns {Object} Tree constructed as /training_name/year/month/day/[availabilities]
// #
/**
* Group the trainings availabilites by trainings and by dates and return the resulting tree
* @param trainings {Array} $scope.trainings is expected here
* @returns {Object} Tree constructed as /training_name/year/month/day/[availabilities]
*/
return groupAvailabilities = function (trainings) {
const tree = {}
for (let training of Array.from(trainings)) {

View File

@ -465,7 +465,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
}
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -166,11 +166,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
// Message displayed to the end user about rules that applies to events reservations
$scope.eventExplicationsAlert = settingsPromise.event_explications_alert
// #
// Callback to delete the provided event (admins only)
// @param event {$resource} angular's Event $resource
// #
$scope.deleteEvent = function(event) {
/**
* Callback to delete the provided event (admins only)
* @param event {$resource} angular's Event $resource
*/
$scope.deleteEvent = event =>
dialogs.confirm({
resolve: {
object () {
@ -192,9 +192,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
)
}
// #
// 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 () {
// compute the total remaning places
let remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces
@ -220,18 +220,18 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
return $scope.computeEventAmount()
}
// #
// Callback to reset the current reservation parameters
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Callback to reset the current reservation parameters
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.cancelReserve = function (e) {
e.preventDefault()
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 () {
if ($scope.event.nb_total_places > 0) {
$scope.reserveSuccess = false
@ -248,10 +248,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// #
// Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
// reservations. (admins only)
// #
/**
* Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
* reservations. (admins only)
*/
$scope.updateMember = function () {
resetEventReserve()
$scope.reserveSuccess = false
@ -263,9 +263,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// #
// Callback to trigger the payment process of the current reservation
// #
/**
* Callback to trigger the payment process of the current reservation
*/
$scope.payEvent = function () {
// first, we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) {
@ -287,9 +287,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// #
// Callback to validate the booking of a free event
// #
/**
* Callback to validate the booking of a free event
*/
$scope.validReserveEvent = function () {
const reservation = {
user_id: $scope.ctrl.member.id,
@ -333,12 +333,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
})
}
// #
// Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose
// a new date for his reservation (if any available)
// @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}}
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose
* a new date for his reservation (if any available)
* @param reservation {{id:number, reservable_id:number, nb_reserve_places:number}}
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.modifyReservation = function (reservation, e) {
e.preventDefault()
e.stopPropagation()
@ -409,10 +409,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
})
}
// #
// Checks if the provided reservation is able to be moved (date change)
// @param reservation {{total_booked_seats:number}}
// #
/**
* Checks if the provided reservation is able to be moved (date change)
* @param reservation {{total_booked_seats:number}}
*/
$scope.reservationCanModify = function (reservation) {
const slotStart = moment(reservation.slots[0].start_at)
const now = moment()
@ -424,10 +424,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
return (isAble && $scope.enableBookingMove && (slotStart.diff(now, 'hours') >= $scope.moveBookingDelay))
}
// #
// Compute the total amount for the current reservation according to the previously set parameters
// and assign the result in $scope.reserve.amountTotal
// #
/**
* Compute the total amount for the current reservation according to the previously set parameters
* and assign the result in $scope.reserve.amountTotal
*/
$scope.computeEventAmount = function () {
// first we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) {
@ -441,24 +441,20 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// #
// 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')}`;
}
/**
* Return the URL allowing to share the current project on the Facebook social network
*/
$scope.shareOnFacebook = () => `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
// #
$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 URL allowing to share the current project on the Twitter social network
*/
$scope.shareOnTwitter = () => `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
// @param category_id {number} ID of the price's category
// #
/**
* Return the textual description of the conditions applyable to the given price's category
* @param category_id {number} ID of the price's category
*/
$scope.getPriceCategoryConditions = function (category_id) {
for (let cat of Array.from($scope.priceCategories)) {
if (cat.id === category_id) {
@ -469,9 +465,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
// set the controlled user as the current user if the current user is not an admin
if ($scope.currentUser) {
@ -496,27 +492,22 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
})
}
// #
// Retrieve the reservations for the couple event / user
// @param reservable_id {number} the current event id
// @param reservable_type {string} 'Event'
// @param user_id {number} the user's id (current or managed)
// #
const getReservations = function(reservable_id, reservable_type, user_id) {
Reservation.query({
reservable_id,
reservable_type,
user_id
}).$promise.then(function(reservations) { $scope.reservations = reservations; })
}
/**
* Retrieve the reservations for the couple event / user
* @param reservable_id {number} the current event id
* @param reservable_type {string} 'Event'
* @param user_id {number} the user's id (current or managed)
*/
var getReservations = (reservable_id, reservable_type, user_id) =>
Reservation.query({ reservable_id, reservable_type, user_id }).$promise.then(reservations => $scope.reservations = reservations)
// #
// Create an hash map implementing the Reservation specs
// @param member {Object} User as retrieved from the API: current user / selected user if current is admin
// @param reserve {Object} Reservation parameters (places...)
// @param event {Object} Current event
// @return {{user_id:number, reservable_id:number, reservable_type:string, slots_attributes:Array<Object>, nb_reserve_places:number}}
// #
/**
* Create an hash map implementing the Reservation specs
* @param member {Object} User as retreived from the API: current user / selected user if current is admin
* @param reserve {Object} Reservation parameters (places...)
* @param event {Object} Current event
* @return {{user_id:number, reservable_id:number, reservable_type:string, slots_attributes:Array<Object>, nb_reserve_places:number}}
*/
var mkReservation = function (member, reserve, event) {
const reservation = {
user_id: member.id,
@ -547,12 +538,12 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
return reservation
}
// #
// Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
// @param reservation {Object} as returned by mkReservation()
// @param coupon {Object} Coupon as returned from the API
// @return {{reservation:Object, coupon_code:string}}
// #
/**
* Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
* @param reservation {Object} as returned by mkReservation()
* @param coupon {Object} Coupon as returned from the API
* @return {{reservation:Object, coupon_code:string}}
*/
var mkRequestParams = function (reservation, coupon) {
const params = {
reservation,
@ -562,9 +553,9 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
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.
*/
var resetEventReserve = function () {
if ($scope.event) {
$scope.reserve = {
@ -587,11 +578,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// #
// Open a modal window which trigger the stripe payment process
// @param reservation {Object} to book
// #
const payByStripe = function(reservation) {
/**
* Open a modal window which trigger the stripe payment process
* @param reservation {Object} to book
*/
var payByStripe = reservation =>
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
size: 'md',
@ -659,11 +650,11 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}).result['finally'](null).then(function(reservation) { afterPayment(reservation); })
};
// #
// Open a modal window which trigger the local payment process
// @param reservation {Object} to book
// #
const payOnSite = function(reservation) {
/**
* Open a modal window which trigger the local payment process
* @param reservation {Object} to book
*/
var payOnSite = reservation =>
$uibModal.open({
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
size: 'sm',
@ -737,10 +728,10 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
.result['finally'](null).then(function(reservation) { afterPayment(reservation) })
}
// #
// What to do after the payment was successful
// @param reservation {Object} booked reservation
// #
/**
* What to do after the payment was successful
* @param resveration {Object} booked reservation
*/
var afterPayment = function (reservation) {
$scope.event.nb_free_places = $scope.event.nb_free_places - reservation.total_booked_seats
resetEventReserve()
@ -752,7 +743,7 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
}
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -16,29 +16,29 @@
/* COMMON CODE */
// #
// Provides a set of common callback methods to the $scope parameter. These methods are used
// in the various machines' admin controllers.
//
// Provides :
// - $scope.submited(content)
// - $scope.cancel()
// - $scope.fileinputClass(v)
// - $scope.addFile()
// - $scope.deleteFile(file)
//
// Requires :
// - $scope.machine.machine_files_attributes = []
// - $state (Ui-Router) [ 'app.public.machines_list' ]
// #
/**
* Provides a set of common callback methods to the $scope parameter. These methods are used
* in the various machines' admin controllers.
*
* Provides :
* - $scope.submited(content)
* - $scope.cancel()
* - $scope.fileinputClass(v)
* - $scope.addFile()
* - $scope.deleteFile(file)
*
* Requires :
* - $scope.machine.machine_files_attributes = []
* - $state (Ui-Router) [ 'app.public.machines_list' ]
/*
class MachinesController {
constructor ($scope, $state) {
// #
// For use with ngUpload (https://github.com/twilson63/ngUpload).
// Intended to be the callback when the upload is done: any raised error will be stacked in the
// $scope.alerts array. If everything goes fine, the user is redirected to the machines list.
// @param content {Object} JSON - The upload's result
// #
/*
* For use with ngUpload (https://github.com/twilson63/ngUpload).
* Intended to be the callback when the upload is done: any raised error will be stacked in the
* $scope.alerts array. If everything goes fine, the user is redirected to the machines list.
* @param content {Object} JSON - The upload's result
*/
$scope.submited = function (content) {
if ((content.id == null)) {
$scope.alerts = []
@ -55,16 +55,16 @@ class MachinesController {
}
}
// #
// Changes the current user's view, redirecting him to the machines list
// #
/**
* Changes the current user's view, redirecting him to the machines list
*/
$scope.cancel = () => $state.go('app.public.machines_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)
// #
/**
* 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'
@ -73,17 +73,17 @@ class MachinesController {
}
}
// #
// This will create a single new empty entry into the machine attachements list.
// #
/**
* This will create a single new empty entry into the machine attachements list.
*/
$scope.addFile = () => $scope.machine.machine_files_attributes.push({})
// #
// This will remove the given file from the machine 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
// #
/**
* This will remove the given file from the machine 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.machine.machine_files_attributes.indexOf(file)
if (file.id != null) {
@ -95,14 +95,14 @@ class MachinesController {
}
}
// #
// Manages the transition when a user clicks on the reservation button.
// According to the status of user currently logged into the system, redirect him to the reservation page,
// or display a modal window asking him to complete a training before he can book a machine reservation.
// @param machine {{id:number}} An object containg the id of the machine to book,
// the object will be completed before the fonction returns.
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Manages the transition when a user clicks on the reservation button.
* According to the status of user currently logged into the system, redirect him to the reservation page,
* or display a modal window asking him to complete a training before he can book a machine reservation.
* @param machine {{id:number}} An object containg the id of the machine to book,
* the object will be completed before the fonction returns.
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
const _reserveMachine = function (machine, e) {
const _this = this
e.preventDefault()
@ -177,21 +177,21 @@ const _reserveMachine = function (machine, e) {
})
}
// #
// Controller used in the public listing page, allowing everyone to see the list of machines
// #
/**
* Controller used in the public listing page, allowing everyone to see the list of machines
*/
Application.Controllers.controller('MachinesController', ['$scope', '$state', '_t', 'Machine', '$uibModal', 'machinesPromise', function ($scope, $state, _t, Machine, $uibModal, machinesPromise) {
// # Retrieve the list of machines
// Retrieve the list of machines
$scope.machines = machinesPromise
// #
// Redirect the user to the machine details page
// #
/**
* Redirect the user to the machine details page
*/
$scope.showMachine = machine => $state.go('app.public.machines_show', { id: machine.slug })
// #
// Callback to book a reservation for the current machine
// #
/**
* Callback to book a reservation for the current machine
*/
$scope.reserveMachine = _reserveMachine.bind({
$scope,
$state,
@ -200,10 +200,10 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
Machine
})
// # Default: we show only enabled machines
// Default: we show only enabled machines
$scope.machineFiltering = 'enabled'
// # Available options for filtering machines by status
// Available options for filtering machines by status
return $scope.filterDisabled = [
'enabled',
'disabled',
@ -212,70 +212,70 @@ Application.Controllers.controller('MachinesController', ['$scope', '$state', '_
}
])
// #
// Controller used in the machine creation page (admin)
// #
/**
* Controller used in the machine creation page (admin)
*/
Application.Controllers.controller('NewMachineController', ['$scope', '$state', 'CSRF', function ($scope, $state, CSRF) {
CSRF.setMetaTags()
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/machines/'
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # default machine parameters
// default machine parameters
$scope.machine =
{ machine_files_attributes: [] }
// # Using the MachinesController
// Using the MachinesController
return new MachinesController($scope, $state)
}
])
// #
// Controller used in the machine edition page (admin)
// #
/**
* Controller used in the machine edition page (admin)
*/
Application.Controllers.controller('EditMachineController', ['$scope', '$state', '$stateParams', 'machinePromise', 'CSRF', function ($scope, $state, $stateParams, machinePromise, CSRF) {
/* PUBLIC SCOPE */
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/machines/${$stateParams.id}`
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'put'
// # Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
// Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
$scope.machine = machinePromise
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
// # Using the MachinesController
// Using the MachinesController
return new MachinesController($scope, $state)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the machine details page (public)
// #
/**
* Controller used in the machine details page (public)
*/
Application.Controllers.controller('ShowMachineController', ['$scope', '$state', '$uibModal', '$stateParams', '_t', 'Machine', 'growl', 'machinePromise', 'dialogs',
function ($scope, $state, $uibModal, $stateParams, _t, Machine, growl, machinePromise, dialogs) {
// # Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
// Retrieve the details for the machine id in the URL, if an error occurs redirect the user to the machines list
$scope.machine = machinePromise
// #
// Callback to delete the current machine (admins only)
// #
/**
* Callback to delete the current machine (admins only)
*/
$scope.delete = function (machine) {
// check the permissions
if ($scope.currentUser.role !== 'admin') {
@ -299,9 +299,9 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
}
}
// #
// Callback to book a reservation for the current machine
// #
/**
* Callback to book a reservation for the current machine
*/
return $scope.reserveMachine = _reserveMachine.bind({
$scope,
$state,
@ -312,10 +312,10 @@ Application.Controllers.controller('ShowMachineController', ['$scope', '$state',
}
])
// #
// Controller used in the machine reservation page (for logged users who have completed the training and admins).
// This controller workflow is pretty similar to the trainings reservation controller.
// #
/**
* Controller used in the machine reservation page (for logged users who have completed the training and admins).
* This controller workflow is pretty similar to the trainings reservation controller.
*/
Application.Controllers.controller('ReserveMachineController', ['$scope', '$stateParams', '_t', 'moment', 'Auth', '$timeout', 'Member', 'Availability', 'plansPromise', 'groupsPromise', 'machinePromise', 'settingsPromise', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, _t, moment, Auth, $timeout, Member, Availability, plansPromise, groupsPromise, machinePromise, settingsPromise, uiCalendarConfig, CalendarConfig) {
@ -332,19 +332,19 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
/* PUBLIC SCOPE */
// # bind the machine availabilities with full-Calendar events
// bind the machine availabilities with full-Calendar events
$scope.eventSources = []
// # indicates the state of the current view : calendar or plans information
// indicates the state of the current view : calendar or plans information
$scope.plansAreShown = false
// # will store the user's plan if he choosed to buy one
// will store the user's plan if he choosed to buy one
$scope.selectedPlan = null
// # the moment when the plan selection changed for the last time, used to trigger changes in the cart
// the moment when the plan selection changed for the last time, used to trigger changes in the cart
$scope.planSelectionTime = null
// # mapping of fullCalendar events.
// mapping of fullCalendar events.
$scope.events = {
reserved: [], // Slots that the user wants to book
modifiable: null, // Slot that the user wants to change
@ -353,16 +353,16 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
moved: null // Slots that were just moved by the user (change done) -> {newSlot:* oldSlot: *}
}
// # the moment when the slot selection changed for the last time, used to trigger changes in the cart
// the moment when the slot selection changed for the last time, used to trigger changes in the cart
$scope.selectionTime = null
// # the last clicked event in the calender
// the last clicked event in the calender
$scope.selectedEvent = null
// # the application global settings
// the application global settings
$scope.settings = settingsPromise
// # list of plans, classified by group
// list of plans, classified by group
$scope.plansClassifiedByGroup = []
for (let group of Array.from(groupsPromise)) {
const groupObj = { id: group.id, name: group.name, plans: [] }
@ -372,14 +372,14 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
$scope.plansClassifiedByGroup.push(groupObj)
}
// # the user to deal with, ie. the current user for non-admins
// the user to deal with, ie. the current user for non-admins
$scope.ctrl =
{ member: {} }
// # current machine to reserve
// current machine to reserve
$scope.machine = machinePromise
// # fullCalendar (v2) configuration
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
@ -391,24 +391,24 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
}
})
// # Global config: message to the end user concerning the subscriptions rules
// Global config: message to the end user concerning the subscriptions rules
$scope.subscriptionExplicationsAlert = settingsPromise.subscription_explications_alert
// # Global config: message to the end user concerning the machine bookings
// Global config: message to the end user concerning the machine bookings
$scope.machineExplicationsAlert = settingsPromise.machine_explications_alert
// #
// Change the last selected slot's appearence to looks like 'added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'added to cart'
*/
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = FREE_SLOT_BORDER_COLOR
$scope.selectedEvent.title = _t('i_reserve')
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'never added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'never added to cart'
*/
$scope.markSlotAsRemoved = function (slot) {
slot.backgroundColor = 'white'
slot.borderColor = FREE_SLOT_BORDER_COLOR
@ -421,23 +421,23 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return updateCalendar()
}
// #
// Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
// #
/**
* Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
*/
$scope.slotCancelled = () => $scope.markSlotAsRemoved($scope.selectedEvent)
// #
// Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
// #
/**
* Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'
$scope.selectedEvent.title = _t('i_change')
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
// #
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyMachineSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -450,9 +450,9 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return updateCalendar()
}
// #
// When modifying an already booked reservation, callback when the modification was successfully done.
// #
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyMachineSlot = function () {
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? _t('i_ve_reserved') : _t('not_available')
$scope.events.placable.backgroundColor = 'white'
@ -471,9 +471,9 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return updateCalendar()
}
// #
// Cancel the current booking modification, reseting the whole process
// #
/**
* Cancel the current booking modification, reseting the whole process
*/
$scope.cancelModifyMachineSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -485,20 +485,20 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return updateCalendar()
}
// #
// Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
// reservations. (admins only)
// #
/**
* Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
* reservations. (admins only)
*/
$scope.updateMember = function () {
$scope.plansAreShown = false
$scope.selectedPlan = null
return Member.get({ id: $scope.ctrl.member.id }, member => $scope.ctrl.member = member)
}
// #
// Changes the user current view from the plan subsription screen to the machine reservation agenda
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Changes the user current view from the plan subsription screen to the machine reservation agenda
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.doNotSubscribePlan = function (e) {
e.preventDefault()
$scope.plansAreShown = false
@ -506,15 +506,15 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return $scope.planSelectionTime = new Date()
}
// #
// Switch the user's view from the reservation agenda to the plan subscription
// #
/**
* Switch the user's view from the reservation agenda to the plan subscription
*/
$scope.showPlans = () => $scope.plansAreShown = true
// #
// Add the provided plan to the current shopping cart
// @param plan {Object} the plan to subscribe
// #
/**
* Add the provided plan to the current shopping cart
* @param plan {Object} the plan to subscribe
*/
$scope.selectPlan = function (plan) {
// toggle selected plan
if ($scope.selectedPlan !== plan) {
@ -525,11 +525,11 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return $scope.planSelectionTime = new Date()
}
// #
// Once the reservation is booked (payment process successfully completed), change the event style
// in fullCalendar, update the user's subscription and free-credits if needed
// @param reservation {Object}
// #
/**
* Once the reservation is booked (payment process successfully completed), change the event style
* in fullCalendar, update the user's subscription and free-credits if needed
* @param reservation {Object}
*/
$scope.afterPayment = function (reservation) {
angular.forEach($scope.events.reserved, function (machineSlot, key) {
machineSlot.is_reserved = true
@ -556,16 +556,16 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
return refetchCalendar()
}
// #
// To use as callback in Array.prototype.filter to get only enabled plans
// #
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
$scope.filterDisabledPlans = plan => !plan.disabled
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
Availability.machine({ machineId: $stateParams.id }, availabilities =>
$scope.eventSources.push({
@ -579,22 +579,22 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
}
}
// #
// Triggered when the user click on a reservation slot in the agenda.
// Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
// the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
// if it's too late).
// #
/**
* Triggered when the user click on a reservation slot in the agenda.
* Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
* if it's too late).
*/
var calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event
return $scope.selectionTime = new Date()
}
// #
// Triggered when fullCalendar tries to graphicaly render an event block.
// Append the event tag into the block, just after the event title.
// @see http://fullcalendar.io/docs/event_rendering/eventRender/
// #
/**
* Triggered when fullCalendar tries to graphicaly render an event block.
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = ''
@ -605,14 +605,14 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
}
}
// #
// After payment, update the id of the newly reserved slot with the id returned by the server.
// This will allow the user to modify the reservation he just booked. The associated user will also be registered
// with the slot.
// @param slot {Object}
// @param reservation {Object}
// @param user {Object} user associated with the slot
// #
/**
* After payment, update the id of the newly reserved slot with the id returned by the server.
* This will allow the user to modify the reservation he just booked. The associated user will also be registered
* with the slot.
* @param slot {Object}
* @param reservation {Object}
* @param user {Object} user associated with the slot
*/
var updateMachineSlot = (slot, reservation, user) =>
angular.forEach(reservation.slots, function (s) {
if (slot.start.isSame(s.start_at)) {
@ -621,21 +621,21 @@ Application.Controllers.controller('ReserveMachineController', ['$scope', '$stat
}
})
// #
// Update the calendar's display to render the new attributes of the events
// #
/**
* Update the calendar's display to render the new attributes of the events
*/
var updateCalendar = () => uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
// #
// Asynchronously fetch the events from the API and refresh the calendar's view with these new events
// #
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = () =>
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents')
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -16,18 +16,18 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
function ($scope, $rootScope, $state, $uibModal, Auth, dialogs, growl, plansPromise, groupsPromise, Subscription, Member, subscriptionExplicationsPromise, _t, Wallet, helpers) {
/* PUBLIC SCOPE */
// # list of groups
// list of groups
let plan
$scope.groups = groupsPromise.filter(g => (g.slug !== 'admins') & !g.disabled)
// # default : do not show the group changing form
// # group ID of the current/selected user
// default : do not show the group changing form
// group ID of the current/selected user
$scope.group = {
change: false,
id: null
}
// # list of plans, classified by group
// list of plans, classified by group
$scope.plansClassifiedByGroup = []
for (var group of Array.from($scope.groups)) {
const groupObj = { id: group.id, name: group.name, plans: [] }
@ -37,34 +37,34 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
$scope.plansClassifiedByGroup.push(groupObj)
}
// # user to deal with
// user to deal with
$scope.ctrl = {
member: null,
member_id: null
}
// # already subscribed plan of the current user
// already subscribed plan of the current user
$scope.paid =
{ plan: null }
// # plan to subscribe (shopping cart)
// plan to subscribe (shopping cart)
$scope.selectedPlan = null
// # Discount coupon to apply to the basket, if any
// Discount coupon to apply to the basket, if any
$scope.coupon =
{ applied: null }
// # Storage for the total price (plan price + coupon, if any)
// Storage for the total price (plan price + coupon, if any)
$scope.cart =
{ total: null }
// # text that appears in the bottom-right box of the page (subscriptions rules details)
// text that appears in the bottom-right box of the page (subscriptions rules details)
$scope.subscriptionExplicationsAlert = subscriptionExplicationsPromise.setting.value
// #
// Callback to deal with the subscription of the user selected in the dropdown list instead of the current user's
// subscription. (admins only)
// #
/**
* Callback to deal with the subscription of the user selected in the dropdown list instead of the current user's
* subscription. (admins only)
*/
$scope.updateMember = function () {
$scope.selectedPlan = null
$scope.paid.plan = null
@ -75,10 +75,10 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
})
}
// #
// Add the provided plan to the shopping basket
// @param plan {Object} The plan to subscribe to
// #
/**
* Add the provided plan to the shopping basket
* @param plan {Object} The plan to subscribe to
*/
$scope.selectPlan = function (plan) {
if ($scope.isAuthenticated()) {
if ($scope.selectedPlan !== plan) {
@ -92,9 +92,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
// #
// Callback to trigger the payment process of the subscription
// #
/**
* Callback to trigger the payment process of the subscription
*/
$scope.openSubscribePlanModal = () =>
Wallet.getWalletByUser({ user_id: $scope.ctrl.member.id }, function (wallet) {
const amountToPay = helpers.getAmountToPay($scope.cart.total, wallet.amount)
@ -107,9 +107,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
})
// #
// Return the group object, identified by the ID set in $scope.group.id
// #
/**
* Return the group object, identified by the ID set in $scope.group.id
*/
$scope.getUserGroup = function () {
for (group of Array.from($scope.groups)) {
if (group.id === $scope.group.id) {
@ -118,9 +118,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
// #
// Change the group of the current/selected user to the one set in $scope.group.id
// #
/**
* Change the group of the current/selected user to the one set in $scope.group.id
*/
$scope.selectGroup = () =>
Member.update({ id: $scope.ctrl.member.id }, { user: { group_id: $scope.group.id } }, function (user) {
$scope.ctrl.member = user
@ -142,22 +142,22 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
return console.error(err)
})
// #
// Return an enumerable meaninful string for the gender of the provider user
// @param user {Object} Database user record
// @return {string} 'male' or 'female'
// #
/**
* Return an enumerable meaninful string for the gender of the provider user
* @param user {Object} Database user record
* @return {string} 'male' or 'female'
*/
$scope.getGender = function (user) {
if (user && user.profile) {
if (user.profile.gender === 'true') { return 'male' } else { return 'female' }
} else { return 'other' }
}
// #
// Test if the provided date is in the future
// @param dateTime {Date}
// @return {boolean}
// #
/**
* Test if the provided date is in the future
* @param dateTime {Date}
* @return {boolean}
*/
$scope.isInFuture = function (dateTime) {
if (moment().diff(moment(dateTime)) < 0) {
return true
@ -166,16 +166,16 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
// #
// To use as callback in Array.prototype.filter to get only enabled plans
// #
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
$scope.filterDisabledPlans = plan => !plan.disabled
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if ($scope.currentUser) {
if ($scope.currentUser.role !== 'admin') {
@ -195,10 +195,10 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
})
}
// #
// Compute the total amount for the current reservation according to the previously set parameters
// and assign the result in $scope.reserve.amountTotal
// #
/**
* Compute the total amount for the current reservation according to the previously set parameters
* and assign the result in $scope.reserve.amountTotal
*/
var updateCartPrice = function () {
// first we check that a user was selected
if (Object.keys($scope.ctrl.member).length > 0) {
@ -218,9 +218,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
// #
// Open a modal window which trigger the stripe payment process
// #
/**
* Open a modal window which trigger the stripe payment process
*/
var payByStripe = () =>
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
@ -251,11 +251,11 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// retrieve the CGV
CustomAsset.get({ name: 'cgv-file' }, cgv => $scope.cgv = cgv.custom_asset)
// #
// Callback for click on the 'proceed' button.
// Handle the stripe's card tokenization process response and save the subscription to the API with the
// card token just created.
// #
/**
* Callback for click on the 'proceed' button.
* Handle the stripe's card tokenization process response and save the subscription to the API with the
* card token just created.
*/
return $scope.payment = function (status, response) {
if (response.error) {
return growl.error(response.error.message)
@ -289,9 +289,9 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
return $scope.coupon.applied = null
})
// #
// Open a modal window which trigger the local payment process
// #
/**
* Open a modal window which trigger the local payment process
*/
var payOnSite = () =>
$uibModal.open({
templateUrl: '<%= asset_path "plans/payment_modal.html" %>',
@ -336,10 +336,10 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
}
}
// #
// Callback for the 'proceed' button.
// Save the subscription to the API
// #
/**
* Callback for the 'proceed' button.
* Save the subscription to the API
*/
$scope.ok = function () {
$scope.attempting = true
return Subscription.save({
@ -359,10 +359,10 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
})
}
// #
// Callback for the 'cancel' button.
// Close the modal box.
// #
/**
* Callback for the 'cancel' button.
* Close the modal box.
*/
return $scope.cancel = () => $uibModalInstance.dismiss('cancel')
}
] })
@ -375,7 +375,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
return $scope.coupon.applied = null
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])

View File

@ -17,37 +17,37 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
function ($scope, $rootScope, $state, $window, _t, growl, CSRF, Auth, Member, settingsPromise, activeProviderPromise, groupsPromise, cguFile, memberPromise, Session, dialogs, AuthProvider) {
/* PUBLIC SCOPE */
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/members/${memberPromise.id}`
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'patch'
// # genre of the application name (eg. "_le_ Fablab" or "_la_ Fabrique")
// genre of the application name (eg. "_le_ Fablab" or "_la_ Fabrique")
$scope.nameGenre = settingsPromise.name_genre
// # name of the current fablab application (eg. "Fablab de la Casemate")
// name of the current fablab application (eg. "Fablab de la Casemate")
$scope.fablabName = settingsPromise.fablab_name
// # information from the current SSO provider
// information from the current SSO provider
$scope.activeProvider = activeProviderPromise
// # list of user's groups (student/standard/...)
// list of user's groups (student/standard/...)
$scope.groups = groupsPromise
// # current user, contains information retrieved from the SSO
// current user, contains information retrieved from the SSO
$scope.user = memberPromise
// # disallow the user to change his password as he connect from SSO
// disallow the user to change his password as he connect from SSO
$scope.preventPassword = true
// # mapping of fields to disable
// mapping of fields to disable
$scope.preventField = {}
// # CGU
// CGU
$scope.cgu = cguFile.custom_asset
// # Angular-Bootstrap datepicker configuration for birthday
// Angular-Bootstrap datepicker configuration for birthday
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
@ -56,23 +56,23 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
}
}
// #
// Callback to diplay the datepicker as a dropdown when clicking on the input field
// @param $event {Object} jQuery event object
// #
/**
* Callback to diplay the datepicker as a dropdown when clicking on the input field
* @param $event {Object} jQuery event object
*/
$scope.openDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = true
}
// #
// For use with ngUpload (https://github.com/twilson63/ngUpload).
// Intended to be the callback when the upload is done: any raised error will be stacked in the
// $scope.alerts array. If everything goes fine, the user's profile is updated and the user is
// redirected to the home page
// @param content {Object} JSON - The upload's result
// #
/**
* For use with ngUpload (https://github.com/twilson63/ngUpload).
* Intended to be the callback when the upload is done: any raised error will be stacked in the
* $scope.alerts array. If everything goes fine, the user's profile is updated and the user is
* redirected to the home page
* @param content {Object} JSON - The upload's result
*/
$scope.submited = function (content) {
if ((content.id == null)) {
$scope.alerts = []
@ -96,11 +96,11 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
}
}
// #
// 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)
// #
/**
* 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'
@ -109,9 +109,9 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
}
}
// #
// Merge the current user into the account with the given auth_token
// #
/**
* Merge the current user into the account with the given auth_token
*/
$scope.registerAuthToken = () =>
Member.merge({ id: $rootScope.currentUser.id }, { user: { auth_token: $scope.user.auth_token } }, function (user) {
$scope.user = user
@ -128,10 +128,10 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
}
})
// #
// Return the email given by the SSO provider, parsed if needed
// @return {String} E-mail of the current user
// #
/**
* Return the email given by the SSO provider, parsed if needed
* @return {String} E-mail of the current user
*/
$scope.ssoEmail = function () {
const { email } = memberPromise
if (email) {
@ -143,10 +143,10 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
return email
}
// #
// Test if the user's mail is marked as duplicate
// @return {boolean}
// #
/**
* Test if the user's mail is marked as duplicate
* @return {boolean}
*/
$scope.hasDuplicate = function () {
const { email } = memberPromise
if (email) {
@ -154,10 +154,10 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
}
}
// #
// Ask for email confirmation and send the SSO merging token again
// @param $event {Object} jQuery event object
// #
/**
* Ask for email confirmation and send the SSO merging token again
* @param $event {Object} jQuery event object
*/
$scope.resendCode = function (event) {
event.preventDefault()
event.stopPropagation()
@ -176,9 +176,9 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
)
}
// #
// Disconnect and re-connect the user to the SSO to force the synchronisation of the profile's data
// #
/**
* Disconnect and re-connect the user to the SSO to force the synchronisation of the profile's data
*/
$scope.syncProfile = () =>
Auth.logout().then(function (oldUser) {
Session.destroy()
@ -193,9 +193,9 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
@ -206,7 +206,7 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo
return angular.forEach(activeProviderPromise.mapping, map => $scope.preventField[map] = true)
}
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}

View File

@ -17,33 +17,33 @@
/* COMMON CODE */
// #
// Provides a set of common properties and methods to the $scope parameter. They are used
// in the various projects' admin controllers.
//
// Provides :
// - $scope.totalSteps
// - $scope.machines = [{Machine}]
// - $scope.components = [{Component}]
// - $scope.themes = [{Theme}]
// - $scope.licences = [{Licence}]
// - $scope.allowedExtensions = [{String}]
// - $scope.submited(content)
// - $scope.cancel()
// - $scope.addFile()
// - $scope.deleteFile(file)
// - $scope.addStep()
// - $scope.deleteStep(step)
// - $scope.changeStepIndex(step, newIdx)
//
// Requires :
// - $scope.project.project_caos_attributes = []
// - $scope.project.project_steps_attributes = []
// - $state (Ui-Router) [ 'app.public.projects_show', 'app.public.projects_list' ]
// #
/**
* Provides a set of common properties and methods to the $scope parameter. They are used
* in the various projects' admin controllers.
*
* Provides :
* - $scope.totalSteps
* - $scope.machines = [{Machine}]
* - $scope.components = [{Component}]
* - $scope.themes = [{Theme}]
* - $scope.licences = [{Licence}]
* - $scope.allowedExtensions = [{String}]
* - $scope.submited(content)
* - $scope.cancel()
* - $scope.addFile()
* - $scope.deleteFile(file)
* - $scope.addStep()
* - $scope.deleteStep(step)
* - $scope.changeStepIndex(step, newIdx)
*
* Requires :
* - $scope.project.project_caos_attributes = []
* - $scope.project.project_steps_attributes = []
* - $state (Ui-Router) [ 'app.public.projects_show', 'app.public.projects_list' ]
*/
class ProjectsController {
constructor ($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document, Diacritics, dialogs, allowedExtensions, _t) {
// # Retrieve the list of machines from the server
// Retrieve the list of machines from the server
Machine.query().$promise.then(data =>
$scope.machines = data.map(d =>
({
@ -53,7 +53,7 @@ class ProjectsController {
)
)
// # Retrieve the list of components from the server
// Retrieve the list of components from the server
Component.query().$promise.then(data =>
$scope.components = data.map(d =>
({
@ -63,7 +63,7 @@ class ProjectsController {
)
)
// # Retrieve the list of themes from the server
// Retrieve the list of themes from the server
Theme.query().$promise.then(data =>
$scope.themes = data.map(d =>
({
@ -73,7 +73,7 @@ class ProjectsController {
)
)
// # Retrieve the list of licences from the server
// Retrieve the list of licences from the server
Licence.query().$promise.then(data =>
$scope.licences = data.map(d =>
({
@ -83,18 +83,18 @@ class ProjectsController {
)
)
// # Total number of documentation steps for the current project
// Total number of documentation steps for the current project
$scope.totalSteps = $scope.project.project_steps_attributes.length
// # List of extensions allowed for CAD attachements upload
// List of extensions allowed for CAD attachements upload
$scope.allowedExtensions = allowedExtensions
// #
// 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
// #
/**
* 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 = []
@ -113,11 +113,11 @@ class ProjectsController {
}
}
// #
// 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)
// #
/**
* 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'
@ -126,17 +126,17 @@ class ProjectsController {
}
}
// #
// This will create a single new empty entry into the project's CAO attachements list.
// #
/**
* This will create a single new empty entry into the project's CAO attachements list.
*/
$scope.addFile = () => $scope.project.project_caos_attributes.push({})
// #
// This will remove the given file from the project's CAO 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 CAO attachements array.
// @param file {Object} the file to delete
// #
/**
* This will remove the given file from the project's CAO 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 CAO attachements array.
* @param file {Object} the file to delete
*/
$scope.deleteFile = function (file) {
const index = $scope.project.project_caos_attributes.indexOf(file)
if (file.id != null) {
@ -146,20 +146,20 @@ class ProjectsController {
}
}
// #
// This will create a single new empty entry into the project's steps list.
// #
/**
* This will create a single new empty entry into the project's steps list.
*/
$scope.addStep = function () {
$scope.totalSteps += 1
return $scope.project.project_steps_attributes.push({ step_nb: $scope.totalSteps, project_step_images_attributes: [] })
}
// #
// This will remove the given step from the project's steps list. If the step was previously saved
// on the server, it will be marked for deletion for the next saving. Otherwise, it will be simply truncated from
// the steps array.
// @param file {Object} the file to delete
// #
/**
* This will remove the given step from the project's steps list. If the step was previously saved
* on the server, it will be marked for deletion for the next saving. Otherwise, it will be simply truncated from
* the steps array.
* @param file {Object} the file to delete
*/
$scope.deleteStep = step =>
dialogs.confirm({
resolve: {
@ -195,13 +195,13 @@ class ProjectsController {
})()
})
// #
// Change the step_nb property of the given step to the new value provided. The step that was previously at this
// index will be assigned to the old position of the provided step.
// @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
// @param step {Object} the project's step to reindex
// @param newIdx {number} the new index to assign to the step
// #
/**
* Change the step_nb property of the given step to the new value provided. The step that was previously at this
* index will be assigned to the old position of the provided step.
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
* @param step {Object} the project's step to reindex
* @param newIdx {number} the new index to assign to the step
*/
$scope.changeStepIndex = function (event, step, newIdx) {
if (event) { event.preventDefault() }
for (let s of Array.from($scope.project.project_steps_attributes)) {
@ -224,16 +224,16 @@ class ProjectsController {
, error => console.error(error))
}
// #
// This will create a single new empty entry into the project's step image list.
// #
/**
* This will create a single new empty entry into the project's step image list.
*/
$scope.addProjectStepImage = step => step.project_step_images_attributes.push({})
// #
// This will remove the given image from the project's step image list.
// @param step {Object} the project step has images
// @param image {Object} the image to delete
// #
/**
* This will remove the given image from the project's step image list.
* @param step {Object} the project step has images
* @param image {Object} the image to delete
*/
$scope.deleteProjectStepImage = function (step, image) {
const index = step.project_step_images_attributes.indexOf(image)
if (image.id != null) {
@ -245,9 +245,9 @@ class ProjectsController {
}
}
// #
// Controller used on projects listing page
// #
/**
* Controller used on projects listing page
*/
Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout',
function ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout) {
/* PRIVATE STATIC CONSTANTS */
@ -257,16 +257,16 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
/* PUBLIC SCOPE */
// # Fab-manager's instance ID in the openLab network
// Fab-manager's instance ID in the openLab network
$scope.openlabAppId = Fablab.openlabAppId
// # Is openLab enabled on the instance?
// Is openLab enabled on the instance?
$scope.openlab = {
projectsActive: Fablab.openlabProjectsActive,
searchOverWholeNetwork: false
}
// # default search parameters
// default search parameters
$scope.search = {
q: ($location.$$search.q || ''),
from: ($location.$$search.from || undefined),
@ -275,16 +275,16 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
theme_id: (parseInt($location.$$search.theme_id) || undefined)
}
// # list of projects to display
// list of projects to display
$scope.projects = []
// # list of machines / used for filtering
// list of machines / used for filtering
$scope.machines = machinesPromise
// # list of themes / used for filtering
// list of themes / used for filtering
$scope.themes = themesPromise
// # list of components / used for filtering
// list of components / used for filtering
$scope.components = componentsPromise
$scope.searchOverWholeNetworkChanged = () =>
@ -334,10 +334,10 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
}
}
// #
// Callback to switch the user's view to the detailled project page
// @param project {{slug:string}} The project to display
// #
/**
* Callback to switch the user's view to the detailled project page
* @param project {{slug:string}} The project to display
*/
$scope.showProject = function (project) {
if (($scope.openlab.searchOverWholeNetwork === true) && (project.app_id !== Fablab.openlabAppId)) {
$window.open(project.project_url, '_blank')
@ -347,9 +347,9 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
}
}
// #
// function to set all url query search parameters from search object
// #
/**
* function to set all url query search parameters from search object
*/
$scope.setUrlQueryParams = function (search) {
updateUrlParam('page', 1)
updateUrlParam('q', search.q)
@ -361,9 +361,9 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if ($location.$$search.whole_network === 'f') {
$scope.openlab.searchOverWholeNetwork = false
@ -373,10 +373,10 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
return $scope.triggerSearch()
}
// #
// function to update url query param, little hack to turn off reloadOnSearch and re-enable it after setting the params
// params example: 'q' , 'presse-purée'
// #
/**
* function to update url query param, little hack to turn off reloadOnSearch and re-enable it after setting the params
* params example: 'q' , 'presse-purée'
*/
var updateUrlParam = function (name, value) {
$state.current.reloadOnSearch = false
$location.search(name, value)
@ -399,25 +399,25 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P
return project
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
// #
// Controller used in the project creation page
// #
/**
* Controller used in the project creation page
*/
Application.Controllers.controller('NewProjectController', ['$scope', '$state', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', '$document', 'CSRF', 'Diacritics', 'dialogs', 'allowedExtensions', '_t',
function ($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document, CSRF, Diacritics, dialogs, allowedExtensions, _t) {
CSRF.setMetaTags()
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/projects/'
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # Default project parameters
// Default project parameters
$scope.project = {
project_steps_attributes: [],
project_caos_attributes: []
@ -425,25 +425,25 @@ Application.Controllers.controller('NewProjectController', ['$scope', '$state',
$scope.matchingMembers = []
// # Using the ProjectsController
// Using the ProjectsController
return new ProjectsController($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document, Diacritics, dialogs, allowedExtensions, _t)
}
])
// #
// Controller used in the project edition page
// #
/**
* Controller used in the project edition page
*/
Application.Controllers.controller('EditProjectController', ['$scope', '$state', '$stateParams', 'Project', 'Machine', 'Member', 'Component', 'Theme', 'Licence', '$document', 'CSRF', 'projectPromise', 'Diacritics', 'dialogs', 'allowedExtensions', '_t',
function ($scope, $state, $stateParams, Project, Machine, Member, Component, Theme, Licence, $document, CSRF, projectPromise, Diacritics, dialogs, allowedExtensions, _t) {
CSRF.setMetaTags()
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/projects/${$stateParams.id}`
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'put'
// # Retrieve the project's details, if an error occured, redirect the user to the projects list page
// Retrieve the project's details, if an error occured, redirect the user to the projects list page
$scope.project = projectPromise
$scope.matchingMembers = $scope.project.project_users.map(u =>
@ -453,28 +453,28 @@ Application.Controllers.controller('EditProjectController', ['$scope', '$state',
})
)
// # Using the ProjectsController
// Using the ProjectsController
return new ProjectsController($scope, $state, Project, Machine, Member, Component, Theme, Licence, $document, Diacritics, dialogs, allowedExtensions, _t)
}
])
// #
// Controller used in the public project's details page
// #
/**
* Controller used in the public project's details page
*/
Application.Controllers.controller('ShowProjectController', ['$scope', '$state', 'projectPromise', '$location', '$uibModal', 'dialogs', '_t',
function ($scope, $state, projectPromise, $location, $uibModal, dialogs, _t) {
/* PUBLIC SCOPE */
// # Store the project's details
// Store the project's details
$scope.project = projectPromise
$scope.projectUrl = $location.absUrl()
$scope.disqusShortname = Fablab.disqusShortname
// #
// Test if the provided user has the edition rights on the current project
// @param [user] {{id:number}} (optional) the user to check rights
// @returns boolean
// #
/**
* Test if the provided user has the edition rights on the current project
* @param [user] {{id:number}} (optional) the user to check rights
* @returns boolean
*/
$scope.projectEditableBy = function (user) {
if ((user == null)) { return false }
if ($scope.project.author_id === user.id) { return true }
@ -485,20 +485,20 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
return canEdit
}
// #
// Test if the provided user has the deletion rights on the current project
// @param [user] {{id:number}} (optional) the user to check rights
// @returns boolean
// #
/**
* Test if the provided user has the deletion rights on the current project
* @param [user] {{id:number}} (optional) the user to check rights
* @returns boolean
*/
$scope.projectDeletableBy = function (user) {
if ((user == null)) { return false }
if ($scope.project.author_id === user.id) { return true }
}
// #
// Callback to delete the current project. Then, the user is redirected to the projects list page,
// which is refreshed. Admins and project owner only are allowed to delete a project
// #
/**
* Callback to delete the current project. Then, the user is redirected to the projects list page,
* which is refreshed. Admins and project owner only are allowed to delete a project
*/
$scope.deleteProject = function () {
// check the permissions
if (($scope.currentUser.role === 'admin') || $scope.projectDeletableBy($scope.currentUser)) {
@ -521,10 +521,10 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
}
}
// #
// Open a modal box containg a form that allow the end-user to signal an abusive content
// @param e {Object} jQuery event
// #
/**
* Open a modal box containg a form that allow the end-user to signal an abusive content
* @param e {Object} jQuery event
*/
$scope.signalAbuse = function (e) {
if (e) { e.preventDefault() }
@ -559,14 +559,14 @@ Application.Controllers.controller('ShowProjectController', ['$scope', '$state',
] })
}
// #
// 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 = () => `https://www.facebook.com/share.php?u=${$state.href('app.public.projects_show', { id: $scope.project.slug }, { 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
*/
return $scope.shareOnTwitter = () => `https://twitter.com/intent/tweet?url=${encodeURIComponent($state.href('app.public.projects_show', { id: $scope.project.slug }, { absolute: true }))}&text=${encodeURIComponent($scope.project.name)}`
}
])

View File

@ -16,29 +16,29 @@
/* COMMON CODE */
// #
// Provides a set of common callback methods to the $scope parameter. These methods are used
// in the various spaces' admin controllers.
//
// Provides :
// - $scope.submited(content)
// - $scope.cancel()
// - $scope.fileinputClass(v)
// - $scope.addFile()
// - $scope.deleteFile(file)
//
// Requires :
// - $scope.space.space_files_attributes = []
// - $state (Ui-Router) [ 'app.public.spaces_list' ]
// #
/**
* Provides a set of common callback methods to the $scope parameter. These methods are used
* in the various spaces' admin controllers.
*
* Provides :
* - $scope.submited(content)
* - $scope.cancel()
* - $scope.fileinputClass(v)
* - $scope.addFile()
* - $scope.deleteFile(file)
*
* Requires :
* - $scope.space.space_files_attributes = []
* - $state (Ui-Router) [ 'app.public.spaces_list' ]
/*
class SpacesController {
constructor ($scope, $state) {
// #
// For use with ngUpload (https://github.com/twilson63/ngUpload).
// Intended to be the callback when the upload is done: any raised error will be stacked in the
// $scope.alerts array. If everything goes fine, the user is redirected to the spaces list.
// @param content {Object} JSON - The upload's result
// #
/*
* For use with ngUpload (https://github.com/twilson63/ngUpload).
* Intended to be the callback when the upload is done: any raised error will be stacked in the
* $scope.alerts array. If everything goes fine, the user is redirected to the spaces list.
* @param content {Object} JSON - The upload's result
*/
$scope.submited = function (content) {
if ((content.id == null)) {
$scope.alerts = []
@ -55,16 +55,16 @@ class SpacesController {
}
}
// #
// Changes the current user's view, redirecting him to the spaces list
// #
/**
* Changes the current user's view, redirecting him to the spaces list
*/
$scope.cancel = () => $state.go('app.public.spaces_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)
// #
/**
* 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'
@ -73,17 +73,17 @@ class SpacesController {
}
}
// #
// This will create a single new empty entry into the space attachements list.
// #
/**
* This will create a single new empty entry into the space attachements list.
*/
$scope.addFile = () => $scope.space.space_files_attributes.push({})
// #
// This will remove the given file from the space 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
// #
/**
* This will remove the given file from the space 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.space.space_files_attributes.indexOf(file)
if (file.id != null) {
@ -95,27 +95,27 @@ class SpacesController {
}
}
// #
// Controller used in the public listing page, allowing everyone to see the list of spaces
// #
/**
* Controller used in the public listing page, allowing everyone to see the list of spaces
*/
Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spacesPromise', function ($scope, $state, spacesPromise) {
// # Retrieve the list of spaces
// Retrieve the list of spaces
$scope.spaces = spacesPromise
// #
// Redirect the user to the space details page
// #
/**
* Redirect the user to the space details page
*/
$scope.showSpace = space => $state.go('app.public.space_show', { id: space.slug })
// #
// Callback to book a reservation for the current space
// #
/**
* Callback to book a reservation for the current space
*/
$scope.reserveSpace = space => $state.go('app.logged.space_reserve', { id: space.slug })
// # Default: we show only enabled spaces
// Default: we show only enabled spaces
$scope.spaceFiltering = 'enabled'
// # Available options for filtering spaces by status
// Available options for filtering spaces by status
return $scope.filterDisabled = [
'enabled',
'disabled',
@ -124,64 +124,64 @@ Application.Controllers.controller('SpacesController', ['$scope', '$state', 'spa
}
])
// #
// Controller used in the space creation page (admin)
// #
/**
* Controller used in the space creation page (admin)
*/
Application.Controllers.controller('NewSpaceController', ['$scope', '$state', 'CSRF', function ($scope, $state, CSRF) {
CSRF.setMetaTags()
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = '/api/spaces/'
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'post'
// # default space parameters
// default space parameters
$scope.space =
{ space_files_attributes: [] }
// # Using the SpacesController
// Using the SpacesController
return new SpacesController($scope, $state)
}
])
// #
// Controller used in the space edition page (admin)
// #
/**
* Controller used in the space edition page (admin)
*/
Application.Controllers.controller('EditSpaceController', ['$scope', '$state', '$stateParams', 'spacePromise', 'CSRF', function ($scope, $state, $stateParams, spacePromise, CSRF) {
CSRF.setMetaTags()
// # API URL where the form will be posted
// API URL where the form will be posted
$scope.actionUrl = `/api/spaces/${$stateParams.id}`
// # Form action on the above URL
// Form action on the above URL
$scope.method = 'put'
// # space to modify
// space to modify
$scope.space = spacePromise
// # Using the SpacesController
// Using the SpacesController
return new SpacesController($scope, $state)
}
])
Application.Controllers.controller('ShowSpaceController', ['$scope', '$state', 'spacePromise', '_t', 'dialogs', 'growl', function ($scope, $state, spacePromise, _t, dialogs, growl) {
// # Details of the space witch id/slug is provided in the URL
// Details of the space witch id/slug is provided in the URL
$scope.space = spacePromise
// #
// Callback to book a reservation for the current space
// @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Callback to book a reservation for the current space
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.reserveSpace = function (event) {
event.preventDefault()
return $state.go('app.logged.space_reserve', { id: $scope.space.slug })
}
// #
// Callback to book a reservation for the current space
// @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Callback to book a reservation for the current space
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
return $scope.deleteSpace = function (event) {
event.preventDefault()
// check the permissions
@ -208,11 +208,11 @@ Application.Controllers.controller('ShowSpaceController', ['$scope', '$state', '
}
])
// #
// Controller used in the spaces reservation agenda page.
// This controller is very similar to the machine reservation controller with one major difference: here, there is many places
// per slots.
// #
/**
* Controller used in the spaces reservation agenda page.
* This controller is very similar to the machine reservation controller with one major difference: here, there is many places
* per slots.
*/
Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateParams', 'Auth', '$timeout', 'Availability', 'Member', 'availabilitySpacesPromise', 'plansPromise', 'groupsPromise', 'settingsPromise', 'spacePromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, $timeout, Availability, Member, availabilitySpacesPromise, plansPromise, groupsPromise, settingsPromise, spacePromise, _t, uiCalendarConfig, CalendarConfig) {
@ -229,14 +229,14 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
/* PUBLIC SCOPE */
// # bind the spaces availabilities with full-Calendar events
// bind the spaces availabilities with full-Calendar events
$scope.eventSources = [ { events: availabilitySpacesPromise, textColor: 'black' } ]
// # the user to deal with, ie. the current user for non-admins
// the user to deal with, ie. the current user for non-admins
$scope.ctrl =
{ member: {} }
// # list of plans, classified by group
// list of plans, classified by group
$scope.plansClassifiedByGroup = []
for (let group of Array.from(groupsPromise)) {
const groupObj = { id: group.id, name: group.name, plans: [] }
@ -246,7 +246,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
$scope.plansClassifiedByGroup.push(groupObj)
}
// # mapping of fullCalendar events.
// mapping of fullCalendar events.
$scope.events = {
reserved: [], // Slots that the user wants to book
modifiable: null, // Slot that the user wants to change
@ -255,25 +255,25 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
moved: null // Slots that were just moved by the user (change done) -> {newSlot:* oldSlot: *}
}
// # the moment when the slot selection changed for the last time, used to trigger changes in the cart
// the moment when the slot selection changed for the last time, used to trigger changes in the cart
$scope.selectionTime = null
// # the last clicked event in the calender
// the last clicked event in the calender
$scope.selectedEvent = null
// # indicates the state of the current view : calendar or plans information
// indicates the state of the current view : calendar or plans information
$scope.plansAreShown = false
// # will store the user's plan if he choosed to buy one
// will store the user's plan if he choosed to buy one
$scope.selectedPlan = null
// # the moment when the plan selection changed for the last time, used to trigger changes in the cart
// the moment when the plan selection changed for the last time, used to trigger changes in the cart
$scope.planSelectionTime = null
// # Selected space
// Selected space
$scope.space = spacePromise
// # fullCalendar (v2) configuration
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
@ -285,26 +285,26 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
}
})
// # Application global settings
// Application global settings
$scope.settings = settingsPromise
// # Global config: message to the end user concerning the subscriptions rules
// Global config: message to the end user concerning the subscriptions rules
$scope.subscriptionExplicationsAlert = settingsPromise.subscription_explications_alert
// # Global config: message to the end user concerning the space reservation
// Global config: message to the end user concerning the space reservation
$scope.spaceExplicationsAlert = settingsPromise.space_explications_alert
// #
// Change the last selected slot's appearence to looks like 'added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'added to cart'
*/
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = SELECTED_EVENT_BG_COLOR
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'never added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'never added to cart'
*/
$scope.markSlotAsRemoved = function (slot) {
slot.backgroundColor = 'white'
slot.title = ''
@ -318,23 +318,23 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return updateCalendar()
}
// #
// Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
// #
/**
* Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
*/
$scope.slotCancelled = () => $scope.markSlotAsRemoved($scope.selectedEvent)
// #
// Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
// #
/**
* Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'
$scope.selectedEvent.title = _t('space_reserve.i_change')
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
// #
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyTrainingSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -347,9 +347,9 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return updateCalendar()
}
// #
// When modifying an already booked reservation, callback when the modification was successfully done.
// #
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyTrainingSlot = function () {
$scope.events.placable.title = _t('space_reserve.i_ve_reserved')
$scope.events.placable.backgroundColor = 'white'
@ -369,9 +369,9 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return updateCalendar()
}
// #
// Cancel the current booking modification, reseting the whole process
// #
/**
* Cancel the current booking modification, reseting the whole process
*/
$scope.cancelModifyTrainingSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -383,10 +383,10 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return updateCalendar()
}
// #
// Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
// reservations. (admins only)
// #
/**
* Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
* reservations. (admins only)
*/
$scope.updateMember = function () {
if ($scope.ctrl.member) {
Member.get({ id: $scope.ctrl.member.id }, function (member) {
@ -407,10 +407,10 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return $scope.plansAreShown = false
}
// #
// Add the provided plan to the current shopping cart
// @param plan {Object} the plan to subscribe
// #
/**
* Add the provided plan to the current shopping cart
* @param plan {Object} the plan to subscribe
*/
$scope.selectPlan = function (plan) {
// toggle selected plan
if ($scope.selectedPlan !== plan) {
@ -421,10 +421,10 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return $scope.planSelectionTime = new Date()
}
// #
// Changes the user current view from the plan subsription screen to the machine reservation agenda
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Changes the user current view from the plan subsription screen to the machine reservation agenda
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.doNotSubscribePlan = function (e) {
e.preventDefault()
$scope.plansAreShown = false
@ -432,16 +432,16 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return $scope.planSelectionTime = new Date()
}
// #
// Switch the user's view from the reservation agenda to the plan subscription
// #
/**
* Switch the user's view from the reservation agenda to the plan subscription
*/
$scope.showPlans = () => $scope.plansAreShown = true
// #
// Once the reservation is booked (payment process successfully completed), change the event style
// in fullCalendar, update the user's subscription and free-credits if needed
// @param reservation {Object}
// #
/**
* Once the reservation is booked (payment process successfully completed), change the event style
* in fullCalendar, update the user's subscription and free-credits if needed
* @param reservation {Object}
*/
$scope.afterPayment = function (reservation) {
angular.forEach($scope.events.paid, function (spaceSlot, key) {
spaceSlot.is_reserved = true
@ -466,29 +466,29 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return refetchCalendar()
}
// #
// To use as callback in Array.prototype.filter to get only enabled plans
// #
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
$scope.filterDisabledPlans = plan => !plan.disabled
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if ($scope.currentUser.role !== 'admin') {
return Member.get({ id: $scope.currentUser.id }, member => $scope.ctrl.member = member)
}
}
// #
// Triggered when the user clicks on a reservation slot in the agenda.
// Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
// the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
// if it's too late).
// @see http://fullcalendar.io/docs/mouse/eventClick/
// #
/**
* Triggered when the user clicks on a reservation slot in the agenda.
* Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
* if it's too late).
* @see http://fullcalendar.io/docs/mouse/eventClick/
*/
var calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event
if ($stateParams.id === 'all') {
@ -497,11 +497,11 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
return $scope.selectionTime = new Date()
}
// #
// Triggered when fullCalendar tries to graphicaly render an event block.
// Append the event tag into the block, just after the event title.
// @see http://fullcalendar.io/docs/event_rendering/eventRender/
// #
/**
* Triggered when fullCalendar tries to graphicaly render an event block.
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element, view) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = ''
@ -512,12 +512,12 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
}
}
// #
// After payment, update the id of the newly reserved slot with the id returned by the server.
// This will allow the user to modify the reservation he just booked.
// @param slot {Object}
// @param reservation {Object}
// #
/**
* After payment, update the id of the newly reserved slot with the id returned by the server.
* This will allow the user to modify the reservation he just booked.
* @param slot {Object}
* @param reservation {Object}
*/
var updateSpaceSlotId = (slot, reservation) =>
angular.forEach(reservation.slots, function (s) {
if (slot.start_at === slot.start_at) {
@ -525,21 +525,21 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
}
})
// #
// Update the calendar's display to render the new attributes of the events
// #
/**
* Update the calendar's display to render the new attributes of the events
*/
var updateCalendar = () => uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
// #
// Asynchronously fetch the events from the API and refresh the calendar's view with these new events
// #
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = () =>
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents')
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}

View File

@ -14,35 +14,35 @@
*/
'use strict'
// #
// Public listing of the trainings
// #
/**
* Public listing of the trainings
*/
Application.Controllers.controller('TrainingsController', ['$scope', '$state', 'trainingsPromise', function ($scope, $state, trainingsPromise) {
// # List of trainings
// List of trainings
$scope.trainings = trainingsPromise
// #
// Callback for the 'reserve' button
// #
/**
* Callback for the 'reserve' button
*/
$scope.reserveTraining = (training, event) => $state.go('app.logged.trainings_reserve', { id: training.slug })
// #
// Callback for the 'show' button
// #
/**
* Callback for the 'show' button
*/
return $scope.showTraining = training => $state.go('app.public.training_show', { id: training.slug })
}
])
// #
// Public view of a specific training
// #
/**
* Public view of a specific training
*/
Application.Controllers.controller('ShowTrainingController', ['$scope', '$state', 'trainingPromise', 'growl', '_t', 'dialogs', function ($scope, $state, trainingPromise, growl, _t, dialogs) {
// # Current training
// Current training
$scope.training = trainingPromise
// #
// Callback to delete the current training (admins only)
// #
/**
* Callback to delete the current training (admins only)
*/
$scope.delete = function (training) {
// check the permissions
if ($scope.currentUser.role !== 'admin') {
@ -66,23 +66,23 @@ Application.Controllers.controller('ShowTrainingController', ['$scope', '$state'
}
}
// #
// Callback for the 'reserve' button
// #
/**
* Callback for the 'reserve' button
*/
$scope.reserveTraining = (training, event) => $state.go('app.logged.trainings_reserve', { id: training.id })
// #
// Revert view to the full list of trainings ("<-" button)
// #
/**
* Revert view to the full list of trainings ("<-" button)
*/
return $scope.cancel = event => $state.go('app.public.trainings_list')
}
])
// #
// Controller used in the training reservation agenda page.
// This controller is very similar to the machine reservation controller with one major difference: here, ONLY ONE
// training can be reserved during the reservation process (the shopping cart may contains only one training and a subscription).
// #
/**
* Controller used in the training reservation agenda page.
* This controller is very similar to the machine reservation controller with one major difference: here, ONLY ONE
* training can be reserved during the reservation process (the shopping cart may contains only one training and a subscription).
*/
Application.Controllers.controller('ReserveTrainingController', ['$scope', '$stateParams', 'Auth', '$timeout', 'Availability', 'Member', 'availabilityTrainingsPromise', 'plansPromise', 'groupsPromise', 'settingsPromise', 'trainingPromise', '_t', 'uiCalendarConfig', 'CalendarConfig',
function ($scope, $stateParams, Auth, $timeout, Availability, Member, availabilityTrainingsPromise, plansPromise, groupsPromise, settingsPromise, trainingPromise, _t, uiCalendarConfig, CalendarConfig) {
@ -96,14 +96,14 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
/* PUBLIC SCOPE */
// # bind the trainings availabilities with full-Calendar events
// bind the trainings availabilities with full-Calendar events
$scope.eventSources = [ { events: availabilityTrainingsPromise, textColor: 'black' } ]
// # the user to deal with, ie. the current user for non-admins
// the user to deal with, ie. the current user for non-admins
$scope.ctrl =
{ member: {} }
// # list of plans, classified by group
// list of plans, classified by group
$scope.plansClassifiedByGroup = []
for (let group of Array.from(groupsPromise)) {
const groupObj = { id: group.id, name: group.name, plans: [] }
@ -113,7 +113,7 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
$scope.plansClassifiedByGroup.push(groupObj)
}
// # mapping of fullCalendar events.
// mapping of fullCalendar events.
$scope.events = {
reserved: [], // Slots that the user wants to book
modifiable: null, // Slot that the user wants to change
@ -122,28 +122,28 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
moved: null // Slots that were just moved by the user (change done) -> {newSlot:* oldSlot: *}
}
// # the moment when the slot selection changed for the last time, used to trigger changes in the cart
// the moment when the slot selection changed for the last time, used to trigger changes in the cart
$scope.selectionTime = null
// # the last clicked event in the calender
// the last clicked event in the calender
$scope.selectedEvent = null
// # indicates the state of the current view : calendar or plans information
// indicates the state of the current view : calendar or plans information
$scope.plansAreShown = false
// # will store the user's plan if he choosed to buy one
// will store the user's plan if he choosed to buy one
$scope.selectedPlan = null
// # the moment when the plan selection changed for the last time, used to trigger changes in the cart
// the moment when the plan selection changed for the last time, used to trigger changes in the cart
$scope.planSelectionTime = null
// # Selected training
// Selected training
$scope.training = trainingPromise
// # 'all' OR training's slug
// 'all' OR training's slug
$scope.mode = $stateParams.id
// # fullCalendar (v2) configuration
// fullCalendar (v2) configuration
$scope.calendarConfig = CalendarConfig({
minTime: moment.duration(moment(settingsPromise.booking_window_start).format('HH:mm:ss')),
maxTime: moment.duration(moment(settingsPromise.booking_window_end).format('HH:mm:ss')),
@ -155,29 +155,29 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
}
})
// # Application global settings
// Application global settings
$scope.settings = settingsPromise
// # Global config: message to the end user concerning the subscriptions rules
// Global config: message to the end user concerning the subscriptions rules
$scope.subscriptionExplicationsAlert = settingsPromise.subscription_explications_alert
// # Global config: message to the end user concerning the training reservation
// Global config: message to the end user concerning the training reservation
$scope.trainingExplicationsAlert = settingsPromise.training_explications_alert
// # Global config: message to the end user giving advice about the training reservation
// Global config: message to the end user giving advice about the training reservation
$scope.trainingInformationMessage = settingsPromise.training_information_message
// #
// Change the last selected slot's appearence to looks like 'added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'added to cart'
*/
$scope.markSlotAsAdded = function () {
$scope.selectedEvent.backgroundColor = SELECTED_EVENT_BG_COLOR
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'never added to cart'
// #
/**
* Change the last selected slot's appearence to looks like 'never added to cart'
*/
$scope.markSlotAsRemoved = function (slot) {
slot.backgroundColor = 'white'
slot.title = slot.training.name
@ -191,23 +191,23 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return updateCalendar()
}
// #
// Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
// #
/**
* Callback when a slot was successfully cancelled. Reset the slot style as 'ready to book'
*/
$scope.slotCancelled = () => $scope.markSlotAsRemoved($scope.selectedEvent)
// #
// Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
// #
/**
* Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
*/
$scope.markSlotAsModifying = function () {
$scope.selectedEvent.backgroundColor = '#eee'
$scope.selectedEvent.title = $scope.selectedEvent.training.name + ' - ' + _t('i_change')
return updateCalendar()
}
// #
// Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
// #
/**
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
*/
$scope.changeModifyTrainingSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -220,9 +220,9 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return updateCalendar()
}
// #
// When modifying an already booked reservation, callback when the modification was successfully done.
// #
/**
* When modifying an already booked reservation, callback when the modification was successfully done.
*/
$scope.modifyTrainingSlot = function () {
$scope.events.placable.title = $scope.currentUser.role !== 'admin' ? $scope.events.placable.training.name + ' - ' + _t('i_ve_reserved') : $scope.events.placable.training.name
$scope.events.placable.backgroundColor = 'white'
@ -242,9 +242,9 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return updateCalendar()
}
// #
// Cancel the current booking modification, reseting the whole process
// #
/**
* Cancel the current booking modification, reseting the whole process
*/
$scope.cancelModifyTrainingSlot = function () {
if ($scope.events.placable) {
$scope.events.placable.backgroundColor = 'white'
@ -256,10 +256,10 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return updateCalendar()
}
// #
// Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
// reservations. (admins only)
// #
/**
* Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
* reservations. (admins only)
*/
$scope.updateMember = function () {
if ($scope.ctrl.member) {
Member.get({ id: $scope.ctrl.member.id }, function (member) {
@ -281,10 +281,10 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return $scope.plansAreShown = false
}
// #
// Add the provided plan to the current shopping cart
// @param plan {Object} the plan to subscribe
// #
/**
* Add the provided plan to the current shopping cart
* @param plan {Object} the plan to subscribe
*/
$scope.selectPlan = function (plan) {
// toggle selected plan
if ($scope.selectedPlan !== plan) {
@ -295,10 +295,10 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return $scope.planSelectionTime = new Date()
}
// #
// Changes the user current view from the plan subsription screen to the machine reservation agenda
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Changes the user current view from the plan subsription screen to the machine reservation agenda
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.doNotSubscribePlan = function (e) {
e.preventDefault()
$scope.plansAreShown = false
@ -306,16 +306,16 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return $scope.planSelectionTime = new Date()
}
// #
// Switch the user's view from the reservation agenda to the plan subscription
// #
/**
* Switch the user's view from the reservation agenda to the plan subscription
*/
$scope.showPlans = () => $scope.plansAreShown = true
// #
// Once the reservation is booked (payment process successfully completed), change the event style
// in fullCalendar, update the user's subscription and free-credits if needed
// @param reservation {Object}
// #
/**
* Once the reservation is booked (payment process successfully completed), change the event style
* in fullCalendar, update the user's subscription and free-credits if needed
* @param reservation {Object}
*/
$scope.afterPayment = function (reservation) {
$scope.events.paid[0].backgroundColor = 'white'
$scope.events.paid[0].is_reserved = true
@ -338,29 +338,29 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return refetchCalendar()
}
// #
// To use as callback in Array.prototype.filter to get only enabled plans
// #
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
$scope.filterDisabledPlans = plan => !plan.disabled
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the controller is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
if ($scope.currentUser.role !== 'admin') {
return Member.get({ id: $scope.currentUser.id }, member => $scope.ctrl.member = member)
}
}
// #
// Triggered when the user clicks on a reservation slot in the agenda.
// Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
// the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
// if it's too late).
// @see http://fullcalendar.io/docs/mouse/eventClick/
// #
/**
* Triggered when the user clicks on a reservation slot in the agenda.
* Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
* if it's too late).
* @see http://fullcalendar.io/docs/mouse/eventClick/
*/
var calendarEventClickCb = function (event, jsEvent, view) {
$scope.selectedEvent = event
if ($stateParams.id === 'all') {
@ -369,11 +369,11 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
return $scope.selectionTime = new Date()
}
// #
// Triggered when fullCalendar tries to graphicaly render an event block.
// Append the event tag into the block, just after the event title.
// @see http://fullcalendar.io/docs/event_rendering/eventRender/
// #
/**
* Triggered when fullCalendar tries to graphicaly render an event block.
* Append the event tag into the block, just after the event title.
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
*/
var eventRenderCb = function (event, element, view) {
if (($scope.currentUser.role === 'admin') && (event.tags.length > 0)) {
let html = ''
@ -384,12 +384,12 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
}
}
// #
// After payment, update the id of the newly reserved slot with the id returned by the server.
// This will allow the user to modify the reservation he just booked.
// @param slot {Object}
// @param reservation {Object}
// #
/**
* After payment, update the id of the newly reserved slot with the id returned by the server.
* This will allow the user to modify the reservation he just booked.
* @param slot {Object}
* @param reservation {Object}
*/
var updateTrainingSlotId = (slot, reservation) =>
angular.forEach(reservation.slots, function (s) {
if (slot.start_at === slot.start_at) {
@ -397,21 +397,21 @@ Application.Controllers.controller('ReserveTrainingController', ['$scope', '$sta
}
})
// #
// Update the calendar's display to render the new attributes of the events
// #
/**
* Update the calendar's display to render the new attributes of the events
*/
var updateCalendar = () => uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
// #
// Asynchronously fetch the events from the API and refresh the calendar's view with these new events
// #
/**
* Asynchronously fetch the events from the API and refresh the calendar's view with these new events
*/
var refetchCalendar = () =>
$timeout(function () {
uiCalendarConfig.calendars.calendar.fullCalendar('refetchEvents')
return uiCalendarConfig.calendars.calendar.fullCalendar('rerenderEvents')
})
// # !!! MUST BE CALLED AT THE END of the controller
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}

View File

@ -39,48 +39,48 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
},
templateUrl: '<%= asset_path "shared/_cart.html" %>',
link ($scope, element, attributes) {
// # will store the user's plan if he choosed to buy one
// will store the user's plan if he choosed to buy one
$scope.selectedPlan = null
// # total amount of the bill to pay
// total amount of the bill to pay
$scope.amountTotal = 0
// # total amount of the elements in the cart, without considering any coupon
// total amount of the elements in the cart, without considering any coupon
$scope.totalNoCoupon = 0
// # Discount coupon to apply to the basket, if any
// Discount coupon to apply to the basket, if any
$scope.coupon =
{ applied: null }
// # Global config: is the user authorized to change his bookings slots?
// Global config: is the user authorized to change his bookings slots?
$scope.enableBookingMove = ($scope.settings.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($scope.settings.booking_move_delay)
// # Global config: is the user authorized to cancel his bookings?
// Global config: is the user authorized to cancel his bookings?
$scope.enableBookingCancel = ($scope.settings.booking_cancel_enable === 'true')
// # Global config: delay in hours before a booking while the cancellation is forbidden
// Global config: delay in hours before a booking while the cancellation is forbidden
$scope.cancelBookingDelay = parseInt($scope.settings.booking_cancel_delay)
// #
// Add the provided slot to the shopping cart (state transition from free to 'about to be reserved')
// and increment the total amount of the cart if needed.
// @param slot {Object} fullCalendar event object
// #
/**
* Add the provided slot to the shopping cart (state transition from free to 'about to be reserved')
* and increment the total amount of the cart if needed.
* @param slot {Object} fullCalendar event object
*/
$scope.validateSlot = function (slot) {
slot.isValid = true
return updateCartPrice()
}
// #
// Remove the provided slot from the shopping cart (state transition from 'about to be reserved' to free)
// and decrement the total amount of the cart if needed.
// @param slot {Object} fullCalendar event object
// @param index {number} index of the slot in the reservation array
// @param [event] {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Remove the provided slot from the shopping cart (state transition from 'about to be reserved' to free)
* and decrement the total amount of the cart if needed.
* @param slot {Object} fullCalendar event object
* @param index {number} index of the slot in the reservation array
* @param [event] {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.removeSlot = function (slot, index, event) {
if (event) { event.preventDefault() }
$scope.events.reserved.splice(index, 1)
@ -94,10 +94,10 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return updateCartPrice()
}
// #
// Checks that every selected slots were added to the shopping cart. Ie. will return false if
// any checked slot was not validated by the user.
// #
/**
* Checks that every selected slots were added to the shopping cart. Ie. will return false if
* any checked slot was not validated by the user.
*/
$scope.isSlotsValid = function () {
let isValid = true
angular.forEach($scope.events.reserved, function (m) {
@ -106,9 +106,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return isValid
}
// #
// Switch the user's view from the reservation agenda to the plan subscription
// #
/**
* Switch the user's view from the reservation agenda to the plan subscription
*/
$scope.showPlans = function () {
// first, we ensure that a user was selected (admin) or logged (member)
if (Object.keys($scope.user).length > 0) {
@ -119,9 +119,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Validates the shopping chart and redirect the user to the payment step
// #
/**
* Validates the shopping chart and redirect the user to the payment step
*/
$scope.payCart = function () {
// first, we check that a user was selected
if (Object.keys($scope.user).length > 0) {
@ -143,9 +143,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// When modifying an already booked reservation, confirm the modification.
// #
/**
* When modifying an already booked reservation, confirm the modification.
*/
$scope.modifySlot = () =>
Slot.update({ id: $scope.events.modifiable.id }, {
slot: {
@ -171,10 +171,10 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return console.error(err)
})
// #
// Cancel the current booking modification, reseting the whole process
// @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* Cancel the current booking modification, reseting the whole process
* @param event {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.cancelModifySlot = function (event) {
if (event) { event.preventDefault() }
if (typeof $scope.onSlotModifyCancel === 'function') { $scope.onSlotModifyCancel() }
@ -182,20 +182,20 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return $scope.events.modifiable = null
}
// #
// When modifying an already booked reservation, cancel the choice of the new slot
// @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
// #
/**
* When modifying an already booked reservation, cancel the choice of the new slot
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
*/
$scope.removeSlotToPlace = function (e) {
e.preventDefault()
if (typeof $scope.onSlotModifyUnselect === 'function') { $scope.onSlotModifyUnselect() }
return $scope.events.placable = null
}
// #
// Checks if $scope.events.modifiable and $scope.events.placable have tag incompatibilities
// @returns {boolean} true in case of incompatibility
// #
/**
* Checks if $scope.events.modifiable and $scope.events.placable have tag incompatibilities
* @returns {boolean} true in case of incompatibility
*/
$scope.tagMissmatch = function () {
if ($scope.events.placable.tag_ids.length === 0) { return false }
for (let tag of Array.from($scope.events.modifiable.tags)) {
@ -206,17 +206,17 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return false
}
// #
// Check if the currently logged user has teh 'admin' role?
// @returns {boolean}
// #
/**
* Check if the currently logged user has teh 'admin' role?
* @returns {boolean}
*/
$scope.isAdmin = () => $rootScope.currentUser && ($rootScope.currentUser.role === 'admin')
/* PRIVATE SCOPE */
// #
// Kind of constructor: these actions will be realized first when the directive is loaded
// #
/**
* Kind of constructor: these actions will be realized first when the directive is loaded
*/
const initialize = function () {
// What the binded slot
$scope.$watch('slotSelectionTime', function (newValue, oldValue) {
@ -243,9 +243,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
})
}
// #
// Callback triggered when the selected slot changed
// #
/**
* Callback triggered when the selected slot changed
*/
var slotSelectionChanged = function () {
if ($scope.slot) {
if (!$scope.slot.is_reserved && !$scope.events.modifiable && !$scope.slot.is_completed) {
@ -328,9 +328,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Reset the parameters that may lead to a wrong price but leave the content (events added to cart)
// #
/**
* Reset the parameters that may lead to a wrong price but leave the content (events added to cart)
*/
var resetCartState = function () {
$scope.selectedPlan = null
$scope.coupon.applied = null
@ -340,10 +340,10 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return $scope.events.placable = null
}
// #
// Determines if the provided booked slot is able to be modified by the user.
// @param slot {Object} fullCalendar event object
// #
/**
* Determines if the provided booked slot is able to be modified by the user.
* @param slot {Object} fullCalendar event object
*/
var slotCanBeModified = function (slot) {
if ($scope.isAdmin()) { return true }
const slotStart = moment(slot.start)
@ -355,10 +355,10 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Determines if the provided booked slot is able to be canceled by the user.
// @param slot {Object} fullCalendar event object
// #
/**
* Determines if the provided booked slot is able to be canceled by the user.
* @param slot {Object} fullCalendar event object
*/
var slotCanBeCanceled = function (slot) {
if ($scope.isAdmin()) { return true }
const slotStart = moment(slot.start)
@ -370,9 +370,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Callback triggered when the selected slot changed
// #
/**
* Callback triggered when the selected slot changed
*/
var planSelectionChanged = function () {
if (Auth.isAuthenticated()) {
if ($scope.selectedPlan !== $scope.plan) {
@ -389,9 +389,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Update the total price of the current selection/reservation
// #
/**
* Update the total price of the current selection/reservation
*/
var updateCartPrice = function () {
if (Object.keys($scope.user).length > 0) {
const r = mkReservation($scope.user, $scope.events.reserved, $scope.selectedPlan)
@ -417,12 +417,12 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
})
)
// #
// Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
// @param reservation {Object} as returned by mkReservation()
// @param coupon {Object} Coupon as returned from the API
// @return {{reservation:Object, coupon_code:string}}
// #
/**
* Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
* @param reservation {Object} as returned by mkReservation()
* @param coupon {Object} Coupon as returned from the API
* @return {{reservation:Object, coupon_code:string}}
*/
var mkRequestParams = function (reservation, coupon) {
const params = {
reservation,
@ -432,13 +432,13 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return params
}
// #
// Create an hash map implementing the Reservation specs
// @param member {Object} User as retreived from the API: current user / selected user if current is admin
// @param slots {Array<Object>} Array of fullCalendar events: slots selected on the calendar
// @param [plan] {Object} Plan as retrived from the API: plan to buy with the current reservation
// @return {{user_id:Number, reservable_id:Number, reservable_type:String, slots_attributes:Array<Object>, plan_id:Number|null}}
// #
/**
* Create an hash map implementing the Reservation specs
* @param member {Object} User as retreived from the API: current user / selected user if current is admin
* @param slots {Array<Object>} Array of fullCalendar events: slots selected on the calendar
* @param [plan] {Object} Plan as retrived from the API: plan to buy with the current reservation
* @return {{user_id:Number, reservable_id:Number, reservable_type:String, slots_attributes:Array<Object>, plan_id:Number|null}}
*/
var mkReservation = function (member, slots, plan = null) {
const reservation = {
user_id: member.id,
@ -459,9 +459,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return reservation
}
// #
// Open a modal window that allows the user to process a credit card payment for his current shopping cart.
// #
/**
* Open a modal window that allows the user to process a credit card payment for his current shopping cart.
*/
var payByStripe = reservation =>
$uibModal.open({
templateUrl: '<%= asset_path "stripe/payment_modal.html" %>',
@ -500,9 +500,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
// Used in wallet info template to interpolate some translations
$scope.numberFilter = $filter('number')
// #
// Callback to process the payment with Stripe, triggered on button click
// #
/**
* Callback to process the payment with Stripe, triggered on button click
*/
return $scope.payment = function (status, response) {
if (response.error) {
return growl.error(response.error.message)
@ -538,9 +538,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
] })
.result['finally'](null).then(reservation => afterPayment(reservation))
// #
// Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
// #
/**
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
*/
var payOnSite = reservation =>
$uibModal.open({
templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>',
@ -587,9 +587,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
}
}
// #
// Callback to process the local payment, triggered on button click
// #
/**
* Callback to process the local payment, triggered on button click
*/
$scope.ok = function () {
$scope.attempting = true
return Reservation.save(mkRequestParams($scope.reservation, coupon), function (reservation) {
@ -607,9 +607,9 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
] })
.result['finally'](null).then(reservation => afterPayment(reservation))
// #
// Actions to run after the payment was successfull
// #
/**
* Actions to run after the payment was successfull
*/
var afterPayment = function (reservation) {
// we set the cart content as 'paid' to display a summary of the transaction
$scope.events.paid = $scope.events.reserved
@ -622,7 +622,7 @@ Application.Directives.directive('cart', [ '$rootScope', '$uibModal', 'dialogs',
return $scope.selectedPlan = null
}
// # !!! MUST BE CALLED AT THE END of the directive
// !!! MUST BE CALLED AT THE END of the directive
return initialize()
}
})

View File

@ -40,9 +40,9 @@ Application.Directives.directive('coupon', [ '$rootScope', 'Coupon', '_t', ($roo
}
})
// #
// Callback to validate the code
// #
/**
* Callback to validate the code
*/
$scope.validateCode = function () {
$scope.messages = []
if ($scope.couponCode === '') {
@ -66,9 +66,9 @@ Application.Directives.directive('coupon', [ '$rootScope', 'Coupon', '_t', ($roo
}
}
// #
// Callback to remove the message at provided index from the displayed list
// #
/**
* Callback to remove the message at provided index from the displayed list
*/
return $scope.closeMessage = index => $scope.messages.splice(index, 1)
}
})

View File

@ -11,14 +11,14 @@
*/
'use strict'
// #
// This directive will allow to select a member.
// Please surround it with a ng-if directive to prevent it from being used by a non-admin user.
// The resulting member will be set into the parent $scope (=> $scope.ctrl.member).
// The directive takes an optional parameter "subscription" as a "boolean string" that will filter the user
// which have a valid running subscription or not.
// Usage: <select-member [subscription="false|true"]></select-member>
// #
/**
* This directive will allow to select a member.
* Please surround it with a ng-if directive to prevent it from being used by a non-admin user.
* The resulting member will be set into the parent $scope (=> $scope.ctrl.member).
* The directive takes an optional parameter "subscription" as a "boolean string" that will filter the user
* which have a valid running subscription or not.
* Usage: <select-member [subscription="false|true"]></select-member>
*/
Application.Directives.directive('selectMember', [ 'Diacritics', 'Member', (Diacritics, Member) =>
({
restrict: 'E',

View File

@ -38,7 +38,7 @@ angular.module('application.router', ['ui.router'])
]
},
onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', function ($rootScope, logoFile, logoBlackFile) {
// # Application logo
// Application logo
$rootScope.logo = logoFile.custom_asset
return $rootScope.logoBlack = logoBlackFile.custom_asset
}