1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-28 09:24:24 +01:00

managers interface

This commit is contained in:
Sylvain 2020-04-21 17:24:22 +02:00
parent f88472eeb3
commit 2ca5c8c50f
10 changed files with 372 additions and 19 deletions

View File

@ -217,6 +217,18 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
}
};
/**
* Change the managers ordering criterion to the one provided
* @param orderManager {string} ordering criterion
*/
$scope.setOrderManager = function (orderManager) {
if ($scope.orderManager === orderManager) {
return $scope.orderManager = `-${orderManager}`;
} else {
return $scope.orderManager = orderManager;
}
};
/**
* Open a modal dialog allowing the admin to create a new partner user
@ -343,6 +355,36 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce',
);
}
/**
* Ask for confirmation then delete the specified manager
* @param managers {Array} full list of managers
* @param manager {Object} manager to delete
*/
$scope.destroyManager = function (managers, manager) {
dialogs.confirm(
{
resolve: {
object () {
return {
title: _t('app.admin.members.confirmation_required'),
msg: $sce.trustAsHtml(_t('app.admin.members.delete_this_manager') + '<br/><br/>' + _t('app.admin.members.this_may_take_a_while_please_wait'))
};
}
}
},
function () { // cancel confirmed
User.delete(
{ id: manager.id },
function () {
managers.splice(findItemIdxById(managers, manager.id), 1);
return growl.success(_t('app.admin.members.manager_successfully_deleted'));
},
function (error) { growl.error(_t('app.admin.members.unable_to_delete_the_manager')); }
);
}
);
}
/**
* Callback for the 'load more' button.
* Will load the next results of the current search, if any
@ -1029,3 +1071,65 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A
}
]);
/**
* Controller used in the manager's creation page (admin view)
*/
Application.Controllers.controller('NewManagerController', ['$state', '$scope', 'User', 'growl', '_t', function ($state, $scope, User, growl, _t) {
// default admin profile
$scope.manager = {
statistic_profile_attributes: {
gender: true
},
profile_attributes: {},
invoicing_profile_attributes: {}
};
// 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 = function ($event) { $scope.datePicker.opened = true; };
/**
* Send the new manager, currently stored in $scope.manager, to the server for database saving
*/
$scope.saveManager = function () {
User.save(
{},
{ manager: $scope.manager },
function () {
growl.success(_t('app.admin.manager_new.manager_successfully_created', { GENDER: getGender($scope.manager) }));
return $state.go('app.admin.members');
}
, function (error) {
growl.error(_t('app.admin.admins_new.failed_to_create_admin') + JSON.stringify(error.data ? error.data : error));
console.error(error);
}
);
};
/* PRIVATE SCOPE */
/**
* Return an enumerable meaningful string for the gender of the provider user
* @param user {Object} Database user record
* @return {string} 'male' or 'female'
*/
const getGender = function (user) {
if (user.statistic_profile_attributes) {
if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
} else { return 'other'; }
};
}
]);

View File

@ -931,8 +931,17 @@ angular.module('application.router', ['ui.router'])
}
}
})
.state('app.admin.managers_new', {
url: '/admin/managers/new',
views: {
'main@': {
templateUrl: '<%= asset_path "admin/managers/new.html" %>',
controller: 'NewManagerController'
}
}
})
// authentification providers
// authentication providers
.state('app.admin.authentication_new', {
url: '/admin/authentications/new',
views: {

View File

@ -0,0 +1,34 @@
<section class="heading b-b">
<div class="row no-gutter">
<div class="col-xs-2 col-sm-2 col-md-1">
<section class="heading-btn">
<a href="#" ng-click="backPrevLocation($event)"><i class="fa fa-long-arrow-left "></i></a>
</section>
</div>
<div class="col-xs-10 col-sm-10 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'app.admin.manager_new.add_a_manager' }}</h1>
</section>
</div>
</div>
</section>
<div class="row no-gutter">
<div class=" col-sm-12 col-md-9 b-r nopadding">
<form role="form" name="managerForm" class="form-horizontal" novalidate>
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<ng-include src="'<%= asset_path "shared/_manager_form.html" %>'"></ng-include>
</div>
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'app.shared.buttons.save' | translate}}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-click="saveManager()" ng-disabled="managerForm.$invalid"/>
</div>
</section>
</form>
</div>
</div>

View File

@ -2,34 +2,34 @@
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-filter"></i></span>
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.search_for_an_administrator' | translate }}">
<input type="text" ng-model="searchFilter" class="form-control" placeholder="{{ 'app.admin.members.search_for_a_manager' | translate }}">
</div>
</div>
</div>
<div class="col-md-12">
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.admins_new" translate>{{ 'app.admin.members.add_a_new_administrator' }}</button>
<button type="button" class="btn btn-warning m-t m-b" ui-sref="app.admin.managers_new" translate>{{ 'app.admin.members.add_a_new_manager' }}</button>
<table class="table">
<thead>
<tr>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderManager('profile_attributes.last_name')">{{ 'app.admin.members.surname' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.last_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.last_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('profile_attributes.first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderManager('profile_attributes.first_name')">{{ 'app.admin.members.first_name' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='profile_attributes.first_name', 'fa fa-sort-alpha-desc': orderAdmin =='-profile_attributes.first_name', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderAdmin('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:15%"><a href="" ng-click="setOrderManager('email')">{{ 'app.admin.members.email' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-alpha-asc': orderAdmin =='email', 'fa fa-sort-alpha-desc': orderAdmin =='-email', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderAdmin('profile_attributes.phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"><a href="" ng-click="setOrderManager('profile_attributes.phone')">{{ 'app.admin.members.phone' | translate }} <i class="fa fa-arrows-v" ng-class="{'fa fa-sort-numeric-asc': orderAdmin =='profile_attributes.phone', 'fa fa-sort-numeric-desc': orderAdmin =='-profile_attributes.phone', 'fa fa-arrows-v': orderAdmin }"></i></a></th>
<th style="width:10%"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="admin in admins | filter:searchFilter | orderBy: orderAdmin">
<td class="text-c">{{ admin.profile_attributes.last_name }}</td>
<td class="text-c">{{ admin.profile_attributes.first_name }}</td>
<td>{{ admin.email }}</td>
<td>{{ admin.profile_attributes.phone }}</td>
<tr ng-repeat="manager in managers | filter:searchFilter | orderBy: orderManager">
<td class="text-c">{{ manager.profile_attributes.last_name }}</td>
<td class="text-c">{{ manager.profile_attributes.first_name }}</td>
<td>{{ manager.email }}</td>
<td>{{ manager.profile_attributes.phone }}</td>
<td>
<button class="btn btn-danger" ng-if="admin.id != currentUser.id" ng-click="destroyAdmin(admins, admin)">
<button class="btn btn-danger" ng-if="manager.id != currentUser.id" ng-click="destroyManager(managers, manager)">
<i class="fa fa-trash-o"></i>
</button>
</td>

View File

@ -33,7 +33,7 @@
<td>{{ partner.email }}</td>
<td><a ui-sref="app.admin.plans.edit({id:partner.resource.id})">{{ partner.resource ? partner.resource.base_name : '' }}</a></td>
<td>
<button class="btn btn-danger" ng-if="partner.id != currentUser.id" ng-click="destroyPartner(partners, partner)">
<button class="btn btn-danger" ng-if="!partner.resource" ng-click="destroyPartner(partners, partner)">
<i class="fa fa-trash-o"></i>
</button>
</td>

View File

@ -8,9 +8,9 @@
<ng-include src="'<%= asset_path "admin/members/administrators.html" %>'"></ng-include>
</uib-tab>
<!--<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.managers' | translate }}" class="admins-tab" index="2">
<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.managers' | translate }}" class="admins-tab" index="2">
<ng-include src="'<%= asset_path "admin/members/managers.html" %>'"></ng-include>
</uib-tab>-->
</uib-tab>
<uib-tab classes="level-2-tab" heading="{{ 'app.admin.members.partners' | translate }}" class="admins-tab" index="3">
<ng-include src="'<%= asset_path "admin/members/partners.html" %>'"></ng-include>

View File

@ -0,0 +1,153 @@
<div class="row m-t">
<div class="col-sm-offset-3 col-sm-6">
<div class="form-group" ng-class="{'has-error': managerForm['manager[statistic_profile_attributes][gender]'].$dirty && managerForm['manager[statistic_profile_attributes][gender]'].$invalid}">
<label class="checkbox-inline btn btn-default">
<input type="radio"
name="manager[statistic_profile_attributes][gender]"
ng-model="manager.statistic_profile_attributes.gender"
ng-value="true"
required/>
<i class="fa fa-male m-l-sm"></i> {{ 'app.admin.manager_new.man' | translate }}
</label>
<label class="checkbox-inline btn btn-default">
<input type="radio"
name="manager[statistic_profile_attributes][gender]"
ng-model="manager.statistic_profile_attributes.gender"
ng-value="false"/>
<i class="fa fa-female m-l-sm"></i> {{ 'app.admin.manager_new.woman' | translate }}
</label>
<span class="exponent m-l-xs help-cursor"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[username]'].$dirty && managerForm['manager[username]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input ng-model="admin.username"
type="text" name="manager[username]"
class="form-control"
id="user_username"
placeholder="{{ 'app.admin.manager_new.pseudonym' | translate }}"
required>
</div>
<span class="help-block" ng-show="managerForm['manager[username]'].$dirty && managerForm['manager[username]'].$error.required" translate>{{ 'app.admin.manager_new.pseudonym_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][last_name]'].$dirty && managerForm['manager[profile_attributes][last_name]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input ng-model="manager.profile_attributes.last_name"
type="text"
name="manager[profile_attributes][last_name]"
class="form-control"
id="user_last_name"
placeholder="{{ 'app.admin.manager_new.surname' | translate }}"
required>
</div>
<span class="help-block" ng-show="managerForm['manager[profile_attributes][last_name]'].$dirty && managerForm['manager[profile_attributes][last_name]'].$error.required" translate>{{ 'app.admin.manager_new.surname_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][first_name]'].$dirty && managerForm['manager[profile_attributes][first_name]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input ng-model="manager.profile_attributes.first_name"
type="text"
name="manager[profile_attributes][first_name]"
class="form-control"
id="user_first_name"
placeholder="{{ 'app.admin.manager_new.first_name' | translate }}"
required>
</div>
<span class="help-block" ng-show="managerForm['manager[profile_attributes][first_name]'].$dirty && managerForm['manager[profile_attributes][first_name]'].$error.required" translate>{{ 'app.admin.manager_new.first_name_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[email]'].$dirty && managerForm['manager[email]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-envelope"></i> <span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span></span>
<input ng-model="admin.email"
type="email"
name="manager[email]"
class="form-control"
id="user_email"
placeholder="{{ 'app.admin.manager_new.email_address' | translate }}"
required>
</div>
<span class="help-block" ng-show="managerForm['manager[email]'].$dirty && managerForm['manager[email]'].$error.required" translate>{{ 'app.admin.manager_new.email_is_required' }}</span>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[statistic_profile_attributes][birthday]'].$dirty && managerForm['manager[statistic_profile_attributes][birthday]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar-o"></i> </span>
<input type="text"
id="user_birthday"
class="form-control"
ng-model="manager.statistic_profile_attributes.birthday"
uib-datepicker-popup="{{datePicker.format}}"
datepicker-options="datePicker.options"
is-open="datePicker.opened"
placeholder="{{ 'app.admin.manager_new.birth_date' | translate }}"
ng-click="openDatePicker($event)"
/>
<input type="hidden"
name="manager[statistic_profile_attributes][birthday]"
value="{{manager.statistic_profile_attributes.birthday | toIsoDate}}" />
</div>
</div>
<div class="form-group">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-map-marker"></i> </span>
<input type="hidden"
name="manager[invoicing_profile_attributes][address_attributes][id]"
ng-value="manager.invoicing_profile_attributes.address.id" />
<input ng-model="manager.invoicing_profile_attributes.address_attributes.address"
type="text"
name="manager[invoicing_profile_attributes][address_attributes][address]"
class="form-control"
id="user_address"
placeholder="{{ 'app.admin.manager_new.address' | translate }}">
</div>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[profile_attributes][phone]'].$dirty && managerForm['manager[profile_attributes][phone]'].$invalid}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-phone"></i> </span>
<input ng-model="manager.profile_attributes.phone"
type="text"
name="manager[profile_attributes][phone]"
class="form-control" id="user_phone"
placeholder="{{ 'app.admin.manager_new.phone_number' | translate }}">
</div>
</div>
<div class="form-group" ng-class="{'has-error': managerForm['manager[group_id]'].$dirty && managerForm['manager[group_id]'].$invalid}">
<label for="manager_group_id" class="col-sm-3 control-label">
<span translate>{{ 'app.shared.user_admin.group' }}</span>
<span class="exponent"><i class="fa fa-asterisk" aria-hidden="true"></i></span>
</label>
<div class="col-sm-9">
<select ng-model="manager.group_id" class="form-control" name="manager[group_id]" id="manager_group_id"
ng-options="g.id as g.name for g in groups" required>
</select>
<input type="hidden" name="manager[group_id]" ng-value="manager.group_id" />
<span class="help-block" ng-show="managerForm['manager[group_id]'].$dirty && managerForm['manager[group_id]'].$error.required" translate>{{ 'app.shared.user_admin.group_is_required' }}</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" translate>{{ 'app.shared.user_admin.tags' }}</label>
<div class="col-sm-10">
<input type="hidden" name="manager[tag_ids][]" value="" />
<ui-select multiple ng-model="manager.tag_ids" name="user[tag_ids][]" class="form-control">
<ui-select-match>
<span ng-bind="$item.name"></span>
<input type="hidden" name="manager[tag_ids][]" value="{{$item.id}}" />
</ui-select-match>
<ui-select-choices repeat="t.id as t in (tags | filter: $select.search)">
<span ng-bind-html="t.name | highlight: $select.search"></span>
</ui-select-choices>
</ui-select>
</div>
</div>
</div>
</div>

View File

@ -15,12 +15,18 @@ class API::UsersController < API::ApiController
def create
authorize User
res = UserService.create_partner(partner_params)
res = if params[:user]
UserService.create_partner(partner_params)
elsif params[:manager]
UserService.create_manager(manager_params)
else
nil
end
if res[:saved]
@user = res[:user]
render status: :created
else²
else
render json: res[:user].errors.full_messages, status: :unprocessable_entity
end
end
@ -40,4 +46,13 @@ class API::UsersController < API::ApiController
def partner_params
params.require(:user).permit(:email, :first_name, :last_name)
end
def manager_params
params.require(:manager).permit(
:username, :email, :group_id, :tag_ids,
profile_attributes: %i[first_name last_name phone],
invoicing_profile_attributes: [address_attributes: [:address]],
statistic_profile_attributes: %i[gender birthday]
)
end
end

View File

@ -50,4 +50,19 @@ class UserService
end
{ saved: saved, user: admin }
end
def self.create_manager(params)
generated_password = Devise.friendly_token.first(8)
manager = User.new(params.merge(password: generated_password))
manager.send :set_slug
saved = manager.save
if saved
manager.send_confirmation_instructions
manager.add_role(:admin)
manager.remove_role(:member)
UsersMailer.delay.notify_user_account_created(manager, generated_password)
end
{ saved: saved, user: manager }
end
end

View File

@ -604,6 +604,12 @@ en:
administrators: "Administrators"
search_for_an_administrator: "Search for an administrator"
add_a_new_administrator: "Add a new administrator"
managers: "Managers"
search_for_a_manager: "Search for a manager"
add_a_new_manager: "Add a new manager"
delete_this_manager: "Do you really want to delete this manager? This cannot be undone."
manager_successfully_deleted: "Manager successfully deleted."
unable_to_delete_the_manager: "Unable to delete the manager."
partners: "Partners"
partners_info: "A partner is a special user that can be associated with the «Partner» plans. These users will only receive notifications about subscriptions to these plans."
search_for_a_partner: "Search for a partner"
@ -612,7 +618,6 @@ en:
partner_successfully_deleted: "Partner successfully deleted."
unable_to_delete_the_partner: "Unable to delete the partner."
associated_plan: "Associated plan"
managers: "Managers"
groups: "Groups"
tags: "Tags"
authentication: "Authentication"
@ -775,6 +780,24 @@ en:
birth_date: "Date of birth"
address: "Address"
phone_number: "Phone number"
#add a new manager to the platform
manager_new:
add_a_manager: "Add a manager"
manager_successfully_created: "Manager successfully created. {GENDER, select, female{She} other{He}} receive {GENDER, select, female{her} other{his}} connection directives by e-mail."
failed_to_create_manager: "Unable to create the manager:"
man: "Man"
woman: "Woman"
pseudonym: "Pseudonym"
pseudonym_is_required: "Pseudonym is required."
first_name: "First name"
first_name_is_required: "First name is required."
surname: "Last name"
surname_is_required: "Last name is required."
email_address: "Email address"
email_is_required: "Email address is required."
birth_date: "Date of birth"
address: "Address"
phone_number: "Phone number"
#add a new authentication provider (SSO)
authentication_new:
local_database: "Local Database"