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

617 lines
18 KiB
Plaintext

'use strict'
### 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' ]
##
class MembersController
constructor: ($scope, $state, Group, Training) ->
## Retrieve the profiles groups (eg. students ...)
Group.query (groups) ->
$scope.groups = groups.filter (g) -> g.slug != 'admins' && !g.disabled
## Retrieve the list the available trainings
Training.query().$promise.then (data)->
$scope.trainings = data.map (d) ->
id: d.id
name: d.name
## Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker =
format: Fablab.uibDateFormat
opened: false # default: datePicker is not shown
subscription_date_opened: false
options:
startingDay: Fablab.weekStartingDay
##
# Shows the birth day datepicker
# @param $event {Object} jQuery event object
##
$scope.openDatePicker = ($event) ->
$event.preventDefault()
$event.stopPropagation()
$scope.datePicker.opened = true
##
# Shows the end of subscription datepicker
# @param $event {Object} jQuery event object
##
$scope.openSubscriptionDatePicker = ($event) ->
$event.preventDefault()
$event.stopPropagation()
$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
##
$scope.submited = (content) ->
if !content.id?
$scope.alerts = []
angular.forEach content, (v, k)->
angular.forEach v, (err)->
$scope.alerts.push
msg: k+': '+err,
type: 'danger'
else
$state.go('app.admin.members')
##
# 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)
##
$scope.fileinputClass = (v)->
if v
'fileinput-exists'
else
'fileinput-new'
##
# Controller used in the members/groups management page
##
Application.Controllers.controller "AdminMembersController", ["$scope","$sce", 'membersPromise', 'adminsPromise', 'growl', 'Admin', 'dialogs', '_t', 'Member', 'Export'
, ($scope, $sce, membersPromise, adminsPromise, growl, Admin, dialogs, _t, Member, Export) ->
### PRIVATE STATIC CONSTANTS ###
# number of users loaded each time we click on 'load more...'
USERS_PER_PAGE = 20
### PUBLIC SCOPE ###
## members list
$scope.members = membersPromise
$scope.member =
## Members plain-text filtering. Default: not filtered
searchText: ''
## Members ordering/sorting. Default: not sorted
order: 'id'
## currently displayed page of members
page: 1
## true when all members where loaded
noMore: false
## admins list
$scope.admins = adminsPromise.admins
## Admins ordering/sorting. Default: not sorted
$scope.orderAdmin = null
##
# Change the members ordering criterion to the one provided
# @param orderBy {string} ordering criterion
##
$scope.setOrderMember = (orderBy)->
if $scope.member.order == orderBy
$scope.member.order = '-'+orderBy
else
$scope.member.order = orderBy
resetSearchMember()
memberSearch()
##
# Change the admins ordering criterion to the one provided
# @param orderBy {string} ordering criterion
##
$scope.setOrderAdmin = (orderAdmin)->
if $scope.orderAdmin == orderAdmin
$scope.orderAdmin = '-'+orderAdmin
else
$scope.orderAdmin = orderAdmin
##
# 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:
object: ->
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'))
, -> # cancel confirmed
Admin.delete id: admin.id, ->
admins.splice(findAdminIdxById(admins, admin.id), 1)
growl.success(_t('administrator_successfully_deleted'))
, (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
##
$scope.showNextMembers = ->
$scope.member.page += 1
memberSearch(true)
##
# Callback when the search field content changes: reload the search results
##
$scope.updateTextSearch = ->
resetSearchMember()
memberSearch()
##
# Callback to alert the admin that the export request was acknowledged and is
# processing right now.
##
$scope.alertExport = (type) ->
Export.status({category: 'users', type: type}).then (res) ->
unless (res.data.exists)
growl.success _t('export_is_running_you_ll_be_notified_when_its_ready')
### PRIVATE SCOPE ###
##
# Kind of constructor: these actions will be realized first when the controller is loaded
##
initialize = ->
if (!membersPromise[0] || membersPromise[0].maxMembers <= $scope.members.length)
$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
##
findAdminIdxById = (admins, id)->
(admins.map (admin)->
admin.id
).indexOf(id)
##
# Reinitialize the context of members's search to display new results set
##
resetSearchMember = ->
$scope.member.noMore = false
$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
##
memberSearch = (concat) ->
Member.list { query: { search: $scope.member.searchText, order_by: $scope.member.order, page: $scope.member.page, size: USERS_PER_PAGE } }, (members) ->
if concat
$scope.members = $scope.members.concat(members)
else
$scope.members = members;
if (!members[0] || members[0].maxMembers <= $scope.members.length)
$scope.member.noMore = true
## !!! MUST BE CALLED AT THE END of the controller
initialize()
]
##
# 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'
, ($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
$scope.actionUrl = "/api/members/" + $stateParams.id
## Form action on the above URL
$scope.method = 'patch'
## List of tags associables with user
$scope.tags = tagsPromise
## The user to edit
$scope.user = memberPromise
## Should the passord be modified?
$scope.password =
change: false
## the user subscription
if $scope.user.subscribed_plan? and $scope.user.subscription?
$scope.subscription = $scope.user.subscription
$scope.subscription.expired_at = $scope.subscription.expired_at
else
Plan.query group_id: $scope.user.group_id, (plans)->
$scope.plans = plans
for plan in $scope.plans
plan.nameToDisplay = $filter('humanReadablePlanName')(plan)
## Available trainings list
$scope.trainings = []
## Profiles types (student/standard/...)
$scope.groups = []
## the user wallet
$scope.wallet = walletPromise
## user wallet transactions
$scope.transactions = transactionsPromise
## 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
##
$scope.updateSubscriptionModal = (subscription, free)->
modalInstance = $uibModal.open
animation: true,
templateUrl: '<%= asset_path "admin/subscriptions/expired_at_modal.html" %>'
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', ($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 = (ev)->
ev.preventDefault();
ev.stopPropagation();
$scope.datePicker.opened = true
$scope.ok = ->
Subscription.update { id: subscription.id }, { subscription: { expired_at: $scope.new_expired_at, free: free } }, (_subscription)->
growl.success(_t('you_successfully_changed_the_expiration_date_of_the_user_s_subscription'))
$uibModalInstance.close(_subscription)
, (error)->
growl.error(_t('a_problem_occurred_while_saving_the_date'))
$scope.cancel = ->
$uibModalInstance.dismiss('cancel')
]
# once the form was validated succesfully ...
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
##
$scope.createSubscriptionModal = (user, plans)->
modalInstance = $uibModal.open
animation: true,
templateUrl: '<%= asset_path "admin/subscriptions/create_modal.html" %>'
size: 'lg',
controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', ($scope, $uibModalInstance, Subscription, Group) ->
## selected user
$scope.user = 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}
##
$scope.humanReadablePlanName = (plan, groups, short)->
"#{$filter('humanReadablePlanName')(plan, groups, short)}"
##
# Modal dialog validation callback
##
$scope.ok = ->
$scope.subscription.user_id = user.id
Subscription.save { }, { subscription: $scope.subscription }, (_subscription)->
growl.success(_t('subscription_successfully_purchased'))
$uibModalInstance.close(_subscription)
$state.reload()
, (error)->
growl.error(_t('a_problem_occurred_while_taking_the_subscription'))
##
# Modal dialog cancellation callback
##
$scope.cancel = ->
$uibModalInstance.dismiss('cancel')
]
# once the form was validated succesfully ...
modalInstance.result.then (subscription) ->
$scope.subscription = subscription
$scope.createWalletCreditModal = (user, wallet)->
modalInstance = $uibModal.open
animation: true,
templateUrl: '<%= asset_path "wallet/credit_modal.html" %>'
controller: ['$scope', '$uibModalInstance', 'Wallet', ($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
##
# Callback to open/close the date picker
##
$scope.toggleDatePicker = ($event) ->
$event.preventDefault()
$event.stopPropagation()
$scope.datePicker.opened = !$scope.datePicker.opened
##
# Modal dialog validation callback
##
$scope.ok = ->
Wallet.credit { id: wallet.id },
amount: $scope.amount
avoir: $scope.generate_avoir
avoir_date: $scope.avoir_date
avoir_description: $scope.description
, (_wallet)->
growl.success(_t('wallet_credit_successfully'))
$uibModalInstance.close(_wallet)
, (error)->
growl.error(_t('a_problem_occurred_for_wallet_credit'))
##
# Modal dialog cancellation callback
##
$scope.cancel = ->
$uibModalInstance.dismiss('cancel')
]
# once the form was validated succesfully ...
modalInstance.result.then (wallet) ->
$scope.wallet = wallet
Wallet.transactions {id: wallet.id}, (transactions) ->
$scope.transactions = transactions
##
# 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
##
initialize = ->
CSRF.setMetaTags()
# init the birth date to JS object
$scope.user.profile.birthday = moment($scope.user.profile.birthday).toDate()
## the user subscription
if $scope.user.subscribed_plan? and $scope.user.subscription?
$scope.subscription = $scope.user.subscription
$scope.subscription.expired_at = $scope.subscription.expired_at
else
Plan.query group_id: $scope.user.group_id, (plans)->
$scope.plans = plans
for plan in $scope.plans
plan.nameToDisplay = "#{plan.base_name} - #{plan.interval}"
# Using the MembersController
new MembersController($scope, $state, Group, Training)
## !!! MUST BE CALLED AT THE END of the controller
initialize()
]
##
# Controller used in the member's creation page (admin view)
##
Application.Controllers.controller "NewMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'Group', 'CSRF'
, ($scope, $state, $stateParams, Member, Training, Group, CSRF) ->
CSRF.setMetaTags()
### PUBLIC SCOPE ###
## API URL where the form will be posted
$scope.actionUrl = "/api/members"
## Form action on the above URL
$scope.method = 'post'
## Should the passord be set manually or generated?
$scope.password =
change: false
## 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
$scope.toggleOrganization = ->
if $scope.user.organization
$scope.user.profile = {} unless $scope.user.profile
$scope.user.profile.organization = {}
else
$scope.user.profile.organization = undefined
## Using the MembersController
new MembersController($scope, $state, Group, Training)
]
##
# Controller used in the admin's creation page (admin view)
##
Application.Controllers.controller 'NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', ($state, $scope, Admin, growl, _t)->
## default admin profile
$scope.admin =
profile_attributes:
gender: true
## Default parameters for AngularUI-Bootstrap datepicker
$scope.datePicker =
format: Fablab.uibDateFormat
opened: false
options:
startingDay: Fablab.weekStartingDay
##
# 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
##
$scope.saveAdmin = ->
Admin.save {}, { admin: $scope.admin }, ->
growl.success(_t('administrator_successfully_created_he_will_receive_his_connection_directives_by_email', {GENDER:getGender($scope.admin)}, "messageformat"))
$state.go('app.admin.members')
, (error)->
console.log(error)
### 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'
##
getGender = (user) ->
if user.profile_attributes
if user.profile_attributes.gender then 'male' else 'female'
else 'other'
]