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

662 lines
22 KiB
Plaintext
Raw Normal View History

/* eslint-disable
handle-callback-err,
no-return-assign,
no-self-assign,
no-undef,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
'use strict'
/* COMMON CODE */
2018-11-19 16:17:49 +01:00
/**
* 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) {
2018-11-19 16:17:49 +01:00
// Retrieve the profiles groups (eg. students ...)
2018-11-20 12:26:06 +01:00
Group.query(function(groups) { $scope.groups = groups.filter(function(g) { return (g.slug !== 'admins') && !g.disabled }) })
2018-11-19 16:17:49 +01:00
// Retrieve the list of available trainings
2018-11-20 12:26:06 +01:00
Training.query().$promise.then(function (data) {
$scope.trainings = data.map(function (d) {
return ({
id: d.id,
name: d.name,
disabled: d.disabled
})
2018-11-20 12:26:06 +01:00
})
})
2018-11-19 16:17:49 +01:00
// Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false, // default: datePicker is not shown
subscription_date_opened: false,
options: {
2016-03-23 18:39:41 +01:00
startingDay: Fablab.weekStartingDay
}
}
2018-11-19 16:17:49 +01:00
/**
* Shows the birth day datepicker
* @param $event {Object} jQuery event object
*/
$scope.openDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = true
}
2018-11-19 16:17:49 +01:00
/**
* 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
}
2018-11-19 16:17:49 +01:00
/**
* 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 = []
2018-11-20 12:26:06 +01:00
return angular.forEach(content, function (v, k) {
angular.forEach(v, function (err) {
$scope.alerts.push({
msg: k + ': ' + err,
2016-03-23 18:39:41 +01:00
type: 'danger'
})
2018-11-20 12:26:06 +01:00
})
})
} else {
return $state.go('app.admin.members')
}
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* Changes the admin's view to the members list page
*/
2018-11-20 12:26:06 +01:00
$scope.cancel = function() { $state.go('app.admin.members') }
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* For use with 'ng-class', returns the CSS class name for the uploads previews.
* The preview may show a placeholder or the content of the file depending on the upload state.
* @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
*/
$scope.fileinputClass = function (v) {
if (v) {
return 'fileinput-exists'
} else {
return 'fileinput-new'
}
}
}
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* 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 */
2016-05-30 15:39:19 +02:00
// number of users loaded each time we click on 'load more...'
const USERS_PER_PAGE = 20
2016-03-23 18:39:41 +01:00
/* PUBLIC SCOPE */
2018-11-19 16:17:49 +01:00
// members list
$scope.members = membersPromise
$scope.member = {
2018-11-19 16:17:49 +01:00
// Members plain-text filtering. Default: not filtered
searchText: '',
2018-11-19 16:17:49 +01:00
// Members ordering/sorting. Default: not sorted
order: 'id',
2018-11-19 16:17:49 +01:00
// currently displayed page of members
page: 1,
2018-11-19 16:17:49 +01:00
// true when all members where loaded
noMore: false
}
2018-11-19 16:17:49 +01:00
// admins list
$scope.admins = adminsPromise.admins
2018-11-19 16:17:49 +01:00
// Admins ordering/sorting. Default: not sorted
$scope.orderAdmin = null
2018-11-19 16:17:49 +01:00
/**
* 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}`
} else {
$scope.member.order = orderBy
}
resetSearchMember()
return memberSearch()
}
2018-11-19 16:17:49 +01:00
/**
* Change the admins ordering criterion to the one provided
2018-11-20 12:26:06 +01:00
* @param orderAdmin {string} ordering criterion
2018-11-19 16:17:49 +01:00
*/
$scope.setOrderAdmin = function (orderAdmin) {
if ($scope.orderAdmin === orderAdmin) {
return $scope.orderAdmin = `-${orderAdmin}`
} else {
return $scope.orderAdmin = orderAdmin
}
}
2018-11-19 16:17:49 +01:00
/**
* Ask for confirmation then delete the specified administrator
* @param admins {Array} full list of administrators
* @param admin {Object} administrator to delete
*/
2018-11-20 12:26:06 +01:00
$scope.destroyAdmin = function (admins, admin) {
dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('confirmation_required'),
msg: $sce.trustAsHtml(_t('do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') + '<br/><br/>' + _t('this_may_take_a_while_please_wait'))
}
}
}
2018-11-20 12:26:06 +01:00
},
function () { // cancel confirmed
Admin.delete(
{ id: admin.id },
function () {
admins.splice(findAdminIdxById(admins, admin.id), 1)
return growl.success(_t('administrator_successfully_deleted'))
},
function (error) { growl.error(_t('unable_to_delete_the_administrator')) }
)
}
)
2018-11-20 12:26:06 +01:00
}
2018-11-19 16:17:49 +01:00
/**
* 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)
}
2018-11-19 16:17:49 +01:00
/**
* Callback when the search field content changes: reload the search results
*/
$scope.updateTextSearch = function () {
resetSearchMember()
return memberSearch()
}
2018-11-19 16:17:49 +01:00
/**
* Callback to alert the admin that the export request was acknowledged and is
* processing right now.
*/
2018-11-20 12:26:06 +01:00
$scope.alertExport = function (type) {
Export.status({ category: 'users', type }).then(function (res) {
if (!res.data.exists) {
return growl.success(_t('export_is_running_you_ll_be_notified_when_its_ready'))
}
})
2018-11-20 12:26:06 +01:00
}
/* PRIVATE SCOPE */
2018-11-19 16:17:49 +01:00
/**
* 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
}
}
2018-11-19 16:17:49 +01:00
/**
* 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
*/
2018-11-20 12:26:06 +01:00
var findAdminIdxById = function (admins, id) {
return (admins.map(function(admin) { return admin.id })).indexOf(id)
}
2018-11-19 16:17:49 +01:00
/**
* Reinitialize the context of members's search to display new results set
*/
var resetSearchMember = function () {
$scope.member.noMore = false
return $scope.member.page = 1
}
2018-11-19 16:17:49 +01:00
/**
* 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
*/
2018-11-20 12:26:06 +01:00
var memberSearch = function (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) {
$scope.members = $scope.members.concat(members)
} else {
$scope.members = members
}
if (!members[0] || (members[0].maxMembers <= $scope.members.length)) {
return $scope.member.noMore = true
}
})
2018-11-20 12:26:06 +01:00
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* 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 */
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// API URL where the form will be posted
$scope.actionUrl = `/api/members/${$stateParams.id}`
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Form action on the above URL
$scope.method = 'patch'
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// List of tags associables with user
$scope.tags = tagsPromise
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// The user to edit
$scope.user = memberPromise
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Should the passord be modified?
$scope.password =
{ change: false }
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// 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
} else {
Plan.query({ group_id: $scope.user.group_id }, function (plans) {
$scope.plans = plans
2018-11-20 12:26:06 +01:00
return Array.from($scope.plans).map( function(plan) {
return (plan.nameToDisplay = $filter('humanReadablePlanName')(plan))
})
})
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Available trainings list
$scope.trainings = []
2018-11-19 16:17:49 +01:00
// Profiles types (student/standard/...)
$scope.groups = []
2018-11-19 16:17:49 +01:00
// the user wallet
$scope.wallet = walletPromise
2018-11-19 16:17:49 +01:00
// user wallet transactions
$scope.transactions = transactionsPromise
2018-11-19 16:17:49 +01:00
// used in wallet partial template to identify parent view
$scope.view = 'member_edit'
// current active authentication provider
$scope.activeProvider = activeProviderPromise
2018-11-19 16:17:49 +01:00
/**
* 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,
templateUrl: '<%= asset_path "admin/subscriptions/expired_at_modal.html" %>',
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', function ($scope, $uibModalInstance, Subscription) {
$scope.new_expired_at = angular.copy(subscription.expired_at)
$scope.free = free
$scope.datePicker = {
opened: false,
format: Fablab.uibDateFormat,
options: {
startingDay: Fablab.weekStartingDay
},
minDate: new Date()
}
$scope.openDatePicker = function (ev) {
ev.preventDefault()
ev.stopPropagation()
return $scope.datePicker.opened = true
}
2018-11-20 12:26:06 +01:00
$scope.ok = function () {
Subscription.update(
{ id: subscription.id },
{ subscription: { expired_at: $scope.new_expired_at, free } },
function (_subscription) {
growl.success(_t('you_successfully_changed_the_expiration_date_of_the_user_s_subscription'))
return $uibModalInstance.close(_subscription)
},
function (error) { growl.error(_t('a_problem_occurred_while_saving_the_date')) }
)
}
2018-11-20 12:26:06 +01:00
$scope.cancel = function () { $uibModalInstance.dismiss('cancel') }
}]
})
// once the form was validated successfully ...
return modalInstance.result.then(function(subscription) { $scope.subscription.expired_at = subscription.expired_at })
}
2018-11-19 16:17:49 +01:00
/**
* 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) {
2018-11-19 16:17:49 +01:00
// selected user
$scope.user = user
2018-11-19 16:17:49 +01:00
// available plans for the selected user
$scope.plans = plans
2018-11-19 16:17:49 +01:00
/**
2018-11-20 12:26:06 +01:00
* Generate a string identifying the given plan by literal human-readable name
2018-11-19 16:17:49 +01:00
* @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}
*/
2018-11-20 12:26:06 +01:00
$scope.humanReadablePlanName = function(plan, groups, short) { return `${$filter('humanReadablePlanName')(plan, groups, short)}` }
2018-11-19 16:17:49 +01:00
/**
* Modal dialog validation callback
*/
$scope.ok = function () {
$scope.subscription.user_id = user.id
return Subscription.save({ }, { subscription: $scope.subscription }, function (_subscription) {
growl.success(_t('subscription_successfully_purchased'))
$uibModalInstance.close(_subscription)
return $state.reload()
}
2018-11-20 12:26:06 +01:00
, function (error) {
growl.error(_t('a_problem_occurred_while_taking_the_subscription'))
console.error(error);
})
}
2018-11-19 16:17:49 +01:00
/**
* Modal dialog cancellation callback
*/
2018-11-20 12:26:06 +01:00
$scope.cancel = function() { $uibModalInstance.dismiss('cancel') }
}]
})
// once the form was validated succesfully ...
2018-11-20 12:26:06 +01:00
return modalInstance.result.then(function(subscription) { $scope.subscription = subscription })
}
$scope.createWalletCreditModal = function (user, wallet) {
const modalInstance = $uibModal.open({
animation: true,
templateUrl: '<%= asset_path "wallet/credit_modal.html" %>',
controller: ['$scope', '$uibModalInstance', 'Wallet', function ($scope, $uibModalInstance, Wallet) {
// default: do not generate a refund invoice
$scope.generate_avoir = false
// date of the generated refund invoice
$scope.avoir_date = null
// optional description shown on the refund invoice
$scope.description = ''
// default configuration for the avoir date selector widget
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false,
options: {
startingDay: Fablab.weekStartingDay
}
}
2018-11-19 16:17:49 +01:00
/**
* Callback to open/close the date picker
*/
$scope.toggleDatePicker = function ($event) {
$event.preventDefault()
$event.stopPropagation()
return $scope.datePicker.opened = !$scope.datePicker.opened
}
2017-10-04 18:56:39 +02:00
2018-11-19 16:17:49 +01:00
/**
* Modal dialog validation callback
*/
2018-11-20 12:26:06 +01:00
$scope.ok = function() {
Wallet.credit(
{ id: wallet.id },
{
amount: $scope.amount,
avoir: $scope.generate_avoir,
avoir_date: $scope.avoir_date,
avoir_description: $scope.description
},
function (_wallet) {
growl.success(_t('wallet_credit_successfully'))
return $uibModalInstance.close(_wallet)
},
function(error) {
growl.error(_t('a_problem_occurred_for_wallet_credit'));
console.error(error)
}
)
}
2018-11-19 16:17:49 +01:00
/**
* Modal dialog cancellation callback
*/
2018-11-20 12:26:06 +01:00
$scope.cancel = function() { $uibModalInstance.dismiss('cancel') }
}
] })
// once the form was validated succesfully ...
return modalInstance.result.then(function (wallet) {
$scope.wallet = wallet
2018-11-20 12:26:06 +01:00
return Wallet.transactions({ id: wallet.id }, function (transactions) { $scope.transactions = transactions })
})
}
2017-10-04 18:56:39 +02:00
2018-11-19 16:17:49 +01:00
/**
* To use as callback in Array.prototype.filter to get only enabled plans
*/
2018-11-20 12:26:06 +01:00
$scope.filterDisabledPlans = function (plan) { return !plan.disabled }
2016-03-23 18:39:41 +01:00
/* PRIVATE SCOPE */
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
CSRF.setMetaTags()
2016-03-23 18:39:41 +01:00
// init the birth date to JS object
$scope.user.profile.birthday = moment($scope.user.profile.birthday).toDate()
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// 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
} else {
Plan.query({ group_id: $scope.user.group_id }, function (plans) {
$scope.plans = plans
2018-11-20 12:26:06 +01:00
return Array.from($scope.plans).map(function (plan) {
return (plan.nameToDisplay = `${plan.base_name} - ${plan.interval}`)
})
})
}
2016-03-23 18:39:41 +01:00
// Using the MembersController
return new MembersController($scope, $state, Group, Training)
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// !!! MUST BE CALLED AT THE END of the controller
return initialize()
}
])
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* 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()
2016-03-23 18:39:41 +01:00
/* PUBLIC SCOPE */
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// API URL where the form will be posted
$scope.actionUrl = '/api/members'
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Form action on the above URL
$scope.method = 'post'
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Should the passord be set manually or generated?
$scope.password =
{ change: false }
2018-11-19 16:17:49 +01:00
// Default member's profile parameters
$scope.user =
{ plan_interval: '' }
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// 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 = {} }
return $scope.user.profile.organization = {}
} else {
return $scope.user.profile.organization = undefined
}
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
// Using the MembersController
return new MembersController($scope, $state, Group, Training)
}
])
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* 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) {
2018-11-19 16:17:49 +01:00
// default admin profile
let getGender
$scope.admin = {
profile_attributes: {
2016-03-23 18:39:41 +01:00
gender: true
}
}
2018-11-19 16:17:49 +01:00
// Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker = {
format: Fablab.uibDateFormat,
opened: false,
options: {
2016-03-23 18:39:41 +01:00
startingDay: Fablab.weekStartingDay
}
}
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* Shows the birth day datepicker
* @param $event {Object} jQuery event object
*/
2018-11-20 12:26:06 +01:00
$scope.openDatePicker = function($event) { $scope.datePicker.opened = true }
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
* Send the new admin, currently stored in $scope.admin, to the server for database saving
*/
2018-11-20 12:26:06 +01:00
$scope.saveAdmin = function() {
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'))
return $state.go('app.admin.members')
}
, function (error) {
console.log(error)
}
)
}
2016-03-23 18:39:41 +01:00
/* PRIVATE SCOPE */
2016-03-23 18:39:41 +01:00
2018-11-19 16:17:49 +01:00
/**
2018-11-20 12:26:06 +01:00
* Return an enumerable meaningful string for the gender of the provider user
2018-11-19 16:17:49 +01:00
* @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' }
} else { return 'other' }
}
}
2016-03-23 18:39:41 +01:00
])