1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

[ongoing] import users from csv file: admin inteface

This commit is contained in:
Sylvain 2019-09-24 12:21:19 +02:00
parent f75633e64e
commit e7bb41f38d
12 changed files with 337 additions and 15 deletions

View File

@ -609,6 +609,32 @@ Application.Controllers.controller('NewMemberController', ['$scope', '$state', '
}
]);
/**
* Controller used in the member's import page: import from CSV (admin view)
*/
Application.Controllers.controller('ImportMembersController', ['$scope', '$state', 'Group', 'Training', 'CSRF', 'plans', 'tags',
function($scope, $state, Group, Training, CSRF, plans, tags) {
CSRF.setMetaTags();
/* PUBLIC SCOPE */
// API URL where the form will be posted
$scope.actionUrl = '/api/members/import';
// Form action on the above URL
$scope.method = 'post';
// List of all plans
$scope.plans = plans;
// List of all tags
$scope.tags = tags
// Using the MembersController
return new MembersController($scope, $state, Group, Training);
}
]);
/**
* Controller used in the admin's creation page (admin view)
*/

View File

@ -959,6 +959,20 @@ angular.module('application.router', ['ui.router'])
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_new', 'app.shared.user', 'app.shared.user_admin']).$promise; }]
}
})
.state('app.admin.members_import', {
url: '/admin/members/import',
views: {
'main@': {
templateUrl: '<%= asset_path "admin/members/import.html" %>',
controller: 'ImportMembersController'
}
},
resolve: {
translations: ['Translations', function (Translations) { return Translations.query(['app.admin.members_import', 'app.shared.user', 'app.shared.user_admin']).$promise; }],
plans: ['Plan', function(Plan) { return Plan.query().$promise }],
tags: ['Tag', function(Tag) { return Tag.query().$promise }]
}
})
.state('app.admin.members_edit', {
url: '/admin/members/:id/edit',
views: {

View File

@ -180,6 +180,11 @@ p, .widget p {
.p-lg { padding: 30px; }
.p-l { padding: 16px; }
.p-h-xs { padding-left: 5px; padding-right: 5px; }
.p-h-s { padding-left: 10px; padding-right: 10px; }
.p-h-l { padding-left: 16px; padding-right: 16px; }
.p-h-lg { padding-left: 30px; padding-right: 30px; }
.m-xxs{margin: 2px 4px}
.m-xs{margin: 5px;}
.m-sm{margin: 10px;}
@ -256,6 +261,14 @@ p, .widget p {
.m-b-n-lg{margin-bottom: -30px}
.m-b-n-xl{margin-bottom: -40px}
.m-h-none{margin-left: 0; margin-right: 0;}
.m-h-xs{margin-left: 5px; margin-right: 5px;}
.m-h-sm{margin-left: 10px; margin-right: 10px;}
.m-h{margin-left: 15px; margin-right: 15px;}
.m-h-md{margin-left: 20px; margin-right: 20px;}
.m-h-lg{margin-left: 30px; margin-right: 30px;}
.m-h-xl{margin-left: 40px; margin-right: 40px;}
.media-xs{min-width: 50px}
.media-sm{min-width: 80px}
.media-md{min-width: 90px}

View File

@ -0,0 +1,165 @@
<div>
<section class="heading b-b">
<div class="row no-gutter">
<div class="col-md-1 hidden-xs">
<section class="heading-btn">
<a ng-click="cancel()"><i class="fa fa-long-arrow-left"></i></a>
</section>
</div>
<div class="col-md-8 b-l b-r">
<section class="heading-title">
<h1 translate>{{ 'members_import.import_members' }}</h1>
</section>
</div>
<div class="col-md-3">
<section class="heading-actions wrapper">
<a class="btn btn-lg btn-block btn-default m-t-xs" target="_blank" href="example.csv" translate>
{{ 'members_import.download_example' }}
</a>
</section>
</div>
</div>
</section>
<div class="row p-sm">
<div class="col-md-12">
<p class="alert alert-info" translate>
{{ 'members_import.info' }}
</p>
<p class="alert alert-warning" translate>
{{ 'members_import.required_fields' }}
</p>
</div>
</div>
<div class="row m-h-sm">
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.groups' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.group_name' }}</th>
<th translate>{{ 'members_import.group_identifier' }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="group in groups">
<td>
{{ group.name }}
</td>
<td>
{{ group.slug }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.trainings' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.training_name' }}</th>
<th translate>{{ 'members_import.training_identifier' }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="training in trainings | filterDisabled">
<td>
{{ training.name }}
</td>
<td>
{{ training.id }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row m-h-sm">
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.plans' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.plan_name' }}</th>
<th translate>{{ 'members_import.plan_identifier' }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="plan in plans">
<td>
{{ plan.name }}
</td>
<td>
{{ plan.slug }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-6 p-h-s">
<h3 translate>{{ 'members_import.tags' }}</h3>
<table class="table">
<thead>
<tr>
<th translate>{{ 'members_import.tag_name' }}</th>
<th translate>{{ 'members_import.tag_identifier' }}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="tag in tags">
<td>
{{ tag.name }}
</td>
<td>
{{ tag.id }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row no-gutter">
<div class="col-sm-12 col-md-12 b-r nopadding">
<form role="form" name="importForm" class="form-horizontal" novalidate action="{{ actionUrl }}" ng-upload="submited(content)" upload-options-enable-rails-csrf="true">
<section class="panel panel-default bg-light m-lg">
<div class="panel-body m-r">
<div class="row m-t">
<div class="col-sm-6 col-sm-offset-5">
<div class="form-group">
<span>Lorem ipsum dolor sit amet consectetur adipiscing elit</span>
</div>
</div>
</div>
</div> <!-- ./panel-body -->
<div class="panel-footer no-padder">
<input type="submit" value="{{ 'save' | translate }}" class="r-b btn-valid btn btn-warning btn-block p-lg btn-lg text-u-c" ng-disabled="importForm.$invalid"/>
</div>
</section>
</form>
</div>
</div>
</div>

View File

@ -5,11 +5,18 @@
<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">
<div class="col-xs-8 col-sm-8 col-md-8 b-l">
<section class="heading-title">
<h1 translate>{{ 'users_management' }}</h1>
</section>
</div>
<div class="col-xs-1 col-xs-offset-1 col-md-offset-2 b-l">
<section class="heading-actions wrapper">
<a role="button" class="btn btn-default b-2x rounded m-t-sm m-r-sm pull-right" ui-sref="app.admin.members_import">
<i class="fa fa-cloud-upload"></i>
</a>
</section>
</div>
</div>
</section>

View File

@ -1,13 +1,20 @@
json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :is_rolling, :description, :type, :ui_weight, :disabled
json.amount (plan.amount / 100.00)
json.prices plan.prices, partial: 'api/prices/price', as: :price
json.plan_file_attributes do
json.id plan.plan_file.id
json.attachment_identifier plan.plan_file.attachment_identifier
end if plan.plan_file
# frozen_string_literal: true
json.partners plan.partners do |partner|
json.first_name partner.first_name
json.last_name partner.last_name
json.email partner.email
end if plan.respond_to?(:partners)
json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :is_rolling, :description, :type,
:ui_weight, :disabled
json.amount plan.amount / 100.00
json.prices plan.prices, partial: 'api/prices/price', as: :price
if plan.plan_file
json.plan_file_attributes do
json.id plan.plan_file.id
json.attachment_identifier plan.plan_file.attachment_identifier
end
end
if plan.respond_to?(:partners)
json.partners plan.partners do |partner|
json.first_name partner.first_name
json.last_name partner.last_name
json.email partner.email
end
end

View File

@ -1,5 +1,8 @@
# frozen_string_literal: true
json.array!(@plans) do |plan|
json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :description, :type, :ui_weight, :disabled
json.amount (plan.amount / 100.00)
json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :description, :type, :ui_weight,
:slug, :disabled
json.amount plan.amount / 100.00
json.plan_file_url plan.plan_file.attachment_url if plan.plan_file
end

View File

@ -569,6 +569,27 @@ en:
add_a_member: "Add a member"
user_is_an_organization: "User is an organization"
members_import:
# members bulk import
members_import:
import_members: "Import members"
info: "you can upload a CSV file to create new members or update existing ones. Your file must user the identifiers below to specify the group, trainings, suscription and tags of the members."
required_fields: "Your file must contain, at least, the following information for each user to create: email, name, first name and group."
groups: "Groups"
group_name: "Group name"
group_identifier: "Identifier to use"
trainings: "Trainings"
training_name: "Training name"
training_identifier: "Identifier to use"
plans: "Plans"
plan_name: "Plan name"
plan_identifier: "Identifier to use"
tags: "Tags"
tag_name: "Tag name"
tag_identifier: "Identifier to use"
download_example: "Download the exemple file"
select_file: "Choose a file"
members_edit:
# edit a member
duration: "Duration:"

View File

@ -569,6 +569,27 @@ es:
add_a_member: "Agregar un miembro"
user_is_an_organization: "El usuario es una organización"
members_import:
# members bulk import
members_import:
import_members: "Import members" # translation_missing
info: "you can upload a CSV file to create new members or update existing ones. Your file must user the identifiers below to specify the group, trainings, suscription and tags of the members." # translation_missing
required_fields: "Your file must contain, at least, the following information for each user to create: email, name, first name and group." # translation_missing
groups: "Groups" # translation_missing
group_name: "Group name" # translation_missing
group_identifier: "Identifier to use" # translation_missing
trainings: "Trainings" # translation_missing
training_name: "Training name" # translation_missing
training_identifier: "Identifier to use" # translation_missing
plans: "Plans" # translation_missing
plan_name: "Plan name" # translation_missing
plan_identifier: "Identifier to use" # translation_missing
tags: "Tags" # translation_missing
tag_name: "Tag name" # translation_missing
tag_identifier: "Identifier to use" # translation_missing
download_example: "Download the exemple file" # translation_missing
select_file: "Choose a file" # translation_missing
members_edit:
# edit a member
duration: "Duración:"

View File

@ -569,6 +569,27 @@ fr:
add_a_member: "Ajouter un membre"
user_is_an_organization: "L'utilisateur est une structure"
members_import:
# import massif de members
members_import:
import_members: "Importer des membres"
info: "Vous pouvez téléverser un fichier CVS afin de créer des nouveaux membres ou de mettre à jour les existants. Votre fichier doit utiliser les identifiants ci-dessous pour spécifier les groupe, formations, abonnement et étiquettes des membres."
required_fields: "Votre fichier doit obligatoirement comporter, au minimum, les informations suivantes pour chaque utilisateur à créer : courriel, nom, prénom et groupe."
groups: "Groupes"
group_name: "Nom du groupe"
group_identifier: "Identifiant à utiliser"
trainings: "Formations"
training_name: "Nom de la formation"
training_identifier: "Identifiant à utiliser"
plans: "Abonnements"
plan_name: "Nom de l'abonnement"
plan_identifier: "Identifiant à utiliser"
tags: "Étiquettes"
tag_name: "Nom de l'étiquette"
tag_identifier: "Identifiant à utiliser"
download_example: "Télécharger le fichier d'exemple"
select_file: "Choisissez un fichier"
members_edit:
# modifier un membre
duration: "Durée :"

View File

@ -569,6 +569,27 @@ pt:
add_a_member: "Adicionar membro"
user_is_an_organization: "Usuário é uma organização"
members_import:
# members bulk import
members_import:
import_members: "Import members" # translation_missing
info: "you can upload a CSV file to create new members or update existing ones. Your file must user the identifiers below to specify the group, trainings, suscription and tags of the members." # translation_missing
required_fields: "Your file must contain, at least, the following information for each user to create: email, name, first name and group." # translation_missing
groups: "Groups" # translation_missing
group_name: "Group name" # translation_missing
group_identifier: "Identifier to use" # translation_missing
trainings: "Trainings" # translation_missing
training_name: "Training name" # translation_missing
training_identifier: "Identifier to use" # translation_missing
plans: "Plans" # translation_missing
plan_name: "Plan name" # translation_missing
plan_identifier: "Identifier to use" # translation_missing
tags: "Tags" # translation_missing
tag_name: "Tag name" # translation_missing
tag_identifier: "Identifier to use" # translation_missing
download_example: "Download the exemple file" # translation_missing
select_file: "Choose a file" # translation_missing
members_edit:
# edit a member
duration: "Duração:"

3
public/example.csv Normal file
View File

@ -0,0 +1,3 @@
id;gender;first_name;last_name;username;email;password;birthdate;address;phone;group;subscription;tags;trainings;website;job;interests;softwares;allow_contact;allow_newsletter;facebook;twitter;googleplus;viadeo;linkedin;instagram;youtube;vimeo;dailymotion;github;echosciences;pinterest;lastfm;flickr
;male;jean;dupont;jdupont;jean.dupont@gmail.com;;1970-01-01;12 bvd Libération - 75000 Paris;0123456789;standard;;1,2;1;http://www.example.com;Charpentier;Ping-pong;AutoCAD;yes;no;http://www.facebook.com/jdupont;;;;;;;;;http://github.com/example;;;;
43;;;;;;newpassword
1 id;gender;first_name;last_name;username;email;password;birthdate;address;phone;group;subscription;tags;trainings;website;job;interests;softwares;allow_contact;allow_newsletter;facebook;twitter;googleplus;viadeo;linkedin;instagram;youtube;vimeo;dailymotion;github;echosciences;pinterest;lastfm;flickr
2 ;male;jean;dupont;jdupont;jean.dupont@gmail.com;;1970-01-01;12 bvd Libération - 75000 Paris;0123456789;standard;;1,2;1;http://www.example.com;Charpentier;Ping-pong;AutoCAD;yes;no;http://www.facebook.com/jdupont;;;;;;;;;http://github.com/example;;;;
3 43;;;;;;newpassword