TG-121 : add administration space

This commit is contained in:
mpenchenat 2017-03-29 11:16:41 +02:00
parent fb75b05d1b
commit c7a574c97f
10 changed files with 470 additions and 3 deletions

View File

@ -46,6 +46,8 @@
<script src="bower_components/angular-material/angular-material.js"></script> <script src="bower_components/angular-material/angular-material.js"></script>
<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
<script src="bower_components/angular-material-icons/angular-material-icons.min.js"></script> <script src="bower_components/angular-material-icons/angular-material-icons.min.js"></script>
<script src="bower_components/underscore/underscore.js"></script>
<script src="bower_components/angular-file-upload/dist/angular-file-upload.min.js"></script>
<!-- endbower --> <!-- endbower -->
<!-- endbuild --> <!-- endbuild -->
@ -53,6 +55,10 @@
<script src="scripts/app.js"></script> <script src="scripts/app.js"></script>
<script src="scripts/controllers/login.js"></script> <script src="scripts/controllers/login.js"></script>
<script src="scripts/controllers/studentSpace.js"></script> <script src="scripts/controllers/studentSpace.js"></script>
<script src="scripts/controllers/administrationSpace.js"></script>
<script src="scripts/controllers/administrationDialog.js"></script>
<script src="scripts/services/Filters.js"></script>
<!-- endbuild --> <!-- endbuild -->
</body> </body>
</html> </html>

View File

@ -14,7 +14,8 @@ var app = angular.module('clientApp', [
'ngSanitize', 'ngSanitize',
'ngMaterial', 'ngMaterial',
'ui.router', 'ui.router',
'ngMdIcons' 'ngMdIcons',
'angularFileUpload'
]); ]);
app.config(function ($stateProvider, $urlRouterProvider) { app.config(function ($stateProvider, $urlRouterProvider) {
@ -36,5 +37,11 @@ app.config(function ($stateProvider, $urlRouterProvider) {
url: '/espace-etudiant', url: '/espace-etudiant',
templateUrl: 'views/studentSpace.html', templateUrl: 'views/studentSpace.html',
controller: 'StudentSpaceCtrl' controller: 'StudentSpaceCtrl'
})
.state('administrationSpace', {
url: '/espace-secretariat',
templateUrl: 'views/administrationSpace.html',
controller: 'AdministrationSpaceCtrl'
}); });
}); });

View File

@ -0,0 +1,103 @@
(function () {
'use strict';
/**
* @ngdoc function
* @name frontendApp.controller:AdministrationDialogCtrl
* @description
* # AdministrationDialogCtrl
* Controller of the frontendApp
*/
angular.module('clientApp')
.controller('AdministrationDialogCtrl', function ($scope, $state, FileUploader, $mdDialog, fileNameFilter, illegalFileNamesFilter) {
// Public methods -------------------
$scope.hide = function () {
$mdDialog.hide();
};
$scope.cancel = function () {
$mdDialog.cancel();
};
$scope.answer = function (answer) {
$mdDialog.hide(answer);
};
$scope.allDocumentsAreIllegal = function() {
return (fileNameFilter(uploader.queue, 'absence').length === 0);
};
$scope.areThereIllegalFiles = function() {
return (illegalFileNamesFilter(uploader.queue, 'absence').length !== 0);
};
var uploader = $scope.uploader = new FileUploader({
url: 'upload.php'
});
// Private methods ------------------
// FILTERS
// a sync filter
uploader.filters.push({
name: 'syncFilter',
fn: function (item /*{File|FileLikeObject}*/, options) {
console.log('syncFilter');
return this.queue.length < 10;
}
});
// an async filter
uploader.filters.push({
name: 'asyncFilter',
fn: function (item /*{File|FileLikeObject}*/, options, deferred) {
console.log('asyncFilter');
setTimeout(deferred.resolve, 1e3);
}
});
// CALLBACKS
uploader.onWhenAddingFileFailed = function (item /*{File|FileLikeObject}*/, filter, options) {
console.info('onWhenAddingFileFailed', item, filter, options);
};
uploader.onAfterAddingFile = function (fileItem) {
console.info('onAfterAddingFile', fileItem);
};
uploader.onAfterAddingAll = function (addedFileItems) {
console.info('onAfterAddingAll', addedFileItems);
};
uploader.onBeforeUploadItem = function (item) {
console.info('onBeforeUploadItem', item);
};
uploader.onProgressItem = function (fileItem, progress) {
console.info('onProgressItem', fileItem, progress);
};
uploader.onProgressAll = function (progress) {
console.info('onProgressAll', progress);
};
uploader.onSuccessItem = function (fileItem, response, status, headers) {
console.info('onSuccessItem', fileItem, response, status, headers);
};
uploader.onErrorItem = function (fileItem, response, status, headers) {
console.info('onErrorItem', fileItem, response, status, headers);
};
uploader.onCancelItem = function (fileItem, response, status, headers) {
console.info('onCancelItem', fileItem, response, status, headers);
};
uploader.onCompleteItem = function (fileItem, response, status, headers) {
console.info('onCompleteItem', fileItem, response, status, headers);
};
uploader.onCompleteAll = function () {
console.info('onCompleteAll');
};
console.info('uploader', uploader);
});
})();

View File

@ -0,0 +1,106 @@
(function () {
'use strict';
/**
* @ngdoc function
* @name frontendApp.controller:AdministrationSpaceCtrl
* @description
* # AdministrationSpaceCtrl
* Controller of the frontendApp
*/
angular.module('clientApp')
.controller('AdministrationSpaceCtrl', function ($scope, $state, $mdDialog, FileUploader) {
angular.extend($scope, {
logout,
deleteAbsence,
deleteTrackingSheet,
importAbsences
})
init();
// Public methods -------------------
function logout() {
$state.go('login');
}
function deleteAbsence(groupIndex, periodIndex, absenceIndex) {
$scope.formationGroups[groupIndex].formattedAbsences[periodIndex].absences.splice(absenceIndex, 1);
}
function deleteTrackingSheet(groupIndex, trackingSheetIndex) {
$scope.formationGroups[groupIndex].trackingSheets.splice(trackingSheetIndex, 1);
}
function importAbsences(ev, type) {
$mdDialog.show({
controller: 'AdministrationDialogCtrl',
templateUrl: 'import-fiches-absences',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: 'false'
})
.then(function (answer) {
$scope.status = 'You said the information was "' + answer + '".';
}, function () {
$scope.status = 'You cancelled the dialog.';
});
}
// Private methods ------------------
function init() {
var formationGroups = [{
label: "Master2 ICE",
absences: [
{ id: 1, title: "Absence_Matthieu_Penchenat_P1" },
{ id: 2, title: "Absence_Renan_Husson_P1" },
{ id: 3, title: "Absence_Renan_Husson_P2" },
{ id: 1, title: "Absence_Renan_Husson_P3" },
{ id: 2, title: "Absence_Matthieu_Penchenat_P2" },
{ id: 3, title: "Absence_Matthieu_Penchenat_P3" },
{ id: 1, title: "Absence_Quentin_Rouland_P1" },
{ id: 2, title: "Absence_Quentin_Rouland_P2" },
{ id: 3, title: "Absence_Quentin_Rouland_P3" },
{ id: 1, title: "Absence_Sitan_Coulibaly_P1" },
{ id: 2, title: "Absence_Sitan_Coulibaly_P2" },
{ id: 3, title: "Absence_Sitan_Coulibaly_P3" }
],
trackingSheets: [
{ id: 3, fileName: "FicheVisite_Sitan_Coulibaly_1" },
{ id: 2, fileName: "FicheVisite_Sitan_Coulibaly_2" },
{ id: 1, fileName: "FicheVisite_Sitan_Coulibaly_3" }
]
}, {
label: "Master1 ISMAG",
absences: [
{ id: 1, title: "Absence_Matthieu_Penchenat_P1" },
{ id: 2, title: "Absence_Matthieu_Penchenat_P2" },
{ id: 3, title: "Absence_Matthieu_Penchenat_P3" }
],
trackingSheets: [
{ id: 3, fileName: "FicheVisite_Renan_Husson_1" },
{ id: 2, fileName: "FicheVisite_Renan_Husson_2" },
{ id: 1, fileName: "FicheVisite_Renan_Husson_3" }
]
}];
$scope.formationGroups = formationGroups.map(function (formationGroup) {
formationGroup.formattedAbsences = reformatAbsences(formationGroup.absences);
return formationGroup;
});
}
function reformatAbsences(absences) {
var myObj = _.groupBy(absences, function (absence) { return absence.title.split('_').pop(); });
return _.map(myObj, function (value, index) {
return { period: index, absences: value };
});
}
});
})();

View File

@ -0,0 +1,28 @@
(function () {
'use strict';
angular.module('clientApp')
.filter('fileName', function () {
return function (queue, type) {
var reg = (type === 'absence') ?/^Absence_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/ : /^Visite_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/;
return queue.filter(function (item) {
return reg.test(item.file.name);
});
};
})
.filter('illegalFileNames', function () {
return function (queue, type) {
var reg = (type === 'absence') ?/^Absence_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/ : /^Visite_[A-Z][a-z]*_[A-Z][a-z]*_P\d*.pdf$/;
return queue.filter(function (item) {
return !reg.test(item.file.name);
}).map(function(item) {
return item.file.name;
});
};
});
})();

View File

@ -34,4 +34,26 @@ body > ui-view > div > div.bg-booklet.layout-align-center-center.layout-row.flex
.p-home { .p-home {
font-size: 0.6em; font-size: 0.6em;
} }
.well {
padding: 15px;
margin: 5px;
background-color: #e0e0e0;
border: dotted 3px lightgray;
}
.note {
padding: 15px;
margin: 5px;
background-color: #a5d6a7;
}
.error {
padding: 15px;
margin: 5px;
background-color: #ef9a9a;
}

View File

@ -0,0 +1,189 @@
<div layout="column">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 md-truncate flex>Bienvenue Isabelle Michu</h2>
<md-button ng-click="logout()">
Se déconnecter
</md-button>
</div>
</md-toolbar>
</div>
<div layout="row" layout-margin>
<div ng-cloak flex="100">
<md-content>
<md-tabs md-dynamic-height md-border-bottom>
<md-tab ng-repeat="formationGroup in formationGroups" label="{{formationGroup.label}}">
<div layout="row" layout-margin>
<md-card flex="50">
<md-card-title>
<md-card-title-text>
<span class="md-headline">Fiches d'absence</span>
</md-card-title-text>
</md-card-title>
<md-card-content>
<md-content flex layout-padding>
<div style="height: 350px; overflow: auto">
<div ng-repeat="formattedAbsence in formationGroup.formattedAbsences">
<md-subheader class="md-no-sticky">Période {{formattedAbsence.period}}</md-subheader>
<md-list-item ng-repeat="absence in formattedAbsence.absences">
<p>{{absence.title}}</p>
<ng-md-icon icon="delete" style="fill: grey" size="24" ng-click="deleteAbsence($parent.$parent.$index, $parent.$index, $index)"></ng-md-icon>
</md-list-item>
</div>
</div>
<br />
<center>
<md-button class="md-raised md-primary" ng-click="importAbsences($event)">Importer des fiches d'absences</md-button>
</center>
</md-content>
</md-card-content>
</md-card>
<md-card flex="50">
<md-card-title>
<md-card-title-text>
<span class="md-headline">Fiches de visite</span>
</md-card-title-text>
</md-card-title>
<md-card-content>
<md-content flex layout-padding>
<div style="height: 350px; overflow: auto">
<md-list-item ng-repeat="sheet in formationGroup.trackingSheets">
<p>{{sheet.fileName}}</p>
<ng-md-icon icon="delete" style="fill: grey" size="24" ng-click="deleteTrackingSheet($parent.$index, $index)"></ng-md-icon>
</md-list-item>
</div>
<br />
<center>
<md-button class="md-raised md-primary">Importer des fiches de visite</md-button>
</center>
</md-content>
</md-card-content>
</md-card>
</div>
</md-tab>
</md-tabs>
</md-content>
</div>
</div>
<script type="text/ng-template" id="import-fiches-absences">
<md-dialog aria-label="Importer des fiches d'absences">
<form ng-cloak>
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Importer des fiches d'absences</h2>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content">
<div layout="row">
<div flex>
<h3>Importer des fiches d'absences</h3>
<input type="file" nv-file-select="" accept=".pdf" uploader="uploader" multiple /><br/><br/>
<p class="note">
<ng-md-icon icon="warning" size="20"></ng-md-icon>
Note d'utilisation : chaque fiche d'absence doit respecter une règle de nommage.<br/> Nom
de fichier : Absence_Prenom_Nom_PN.pdf<br/>
<i>Le N de PN étant un numéro - exemple : (P1, P2, P3, etc.)</i>
</p>
<div ng-if="uploader.queue.length !== 0">
<h3>Liste des documents : </h3>
<p>Nombre de documents : {{ uploader.queue.length }}</p>
<div ng-if="areThereIllegalFiles()" style="background-color: #ef9a9a; padding: 2px 10px 2px 10px;margin: 10px 0 10px 0">
<p>
Les documents suivants ne respecte pas la règle de nommage :
</p>
<ul>
<li ng-repeat="fileName in uploader.queue | illegalFileNames:'absence'">{{fileName}}</li>
</ul>
</div>
<div ng-if="!allDocumentsAreIllegal()">
<table class="bordered">
<thead>
<tr>
<th width="50%">Nom</th>
<th ng-show="uploader.isHTML5">Taille</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in uploader.queue | fileName:'absence'">
<td><strong>{{ item.file.name }}</strong></td>
<td ng-show="uploader.isHTML5" nowrap>{{ item.file.size/1024/1024|number:2 }} MB</td>
<td class="text-center">
<span ng-show="item.isSuccess">
<ng-md-icon icon="done" size="24"></ng-md-icon>
</span>
<span ng-show="item.isCancel">
<ng-md-icon icon="cancel" size="24"></ng-md-icon>
</span>
<span ng-show="item.isError">
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon>
</span>
</td>
<td nowrap>
<md-button type="button" class="md-raised md-primary" ng-click="item.upload()" ng-disabled="item.isReady || item.isUploading || item.isSuccess">
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Charger
</md-button>
<md-button type="button" class="md-raised" ng-click="item.cancel()" ng-disabled="!item.isUploading">
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Annuler
</md-button>
<md-button type="button" class="md-raised md-warn" ng-click="item.remove()">
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Supprimer
</md-button>
</td>
</tr>
</tbody>
</table>
<div style='margin-top : 40px'>
<div>
Niveau de progression :
<md-progress-linear md-mode="determinate" value="{{uploader.progress}}"></md-progress-linear>
</div>
<md-button type="button" class="md-raised md-primary" ng-click="uploader.uploadAll()" ng-disabled="!uploader.getNotUploadedItems().length">
<ng-md-icon icon="file_upload" size="24"></ng-md-icon> Tout charger
</md-button>
<md-button type="button" class="md-raised" ng-click="uploader.cancelAll()" ng-disabled="!uploader.isUploading">
<ng-md-icon icon="cancel" size="24"></ng-md-icon> Tout annuler
</md-button>
<md-button type="button" class="md-raised md-warn" ng-click="uploader.clearQueue()" ng-disabled="!uploader.queue.length">
<ng-md-icon icon="remove_circle_outline" size="24"></ng-md-icon> Tout supprimer
</md-button>
</div>
</div>
</div>
</div>
</div>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-click="answer('useful')">
Fermer
</md-button>
</md-dialog-actions>
</form>
</md-dialog>
</script>

View File

@ -8,6 +8,8 @@
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-card-content> <md-card-content>
<md-button ui-sref="administrationSpace">Log to administration</md-button>
<p class="p-home"> <p class="p-home">
The titles of Washed Out's breakthrough song and the first single from Paracosm share the two most important words in Ernest The titles of Washed Out's breakthrough song and the first single from Paracosm share the two most important words in Ernest
Greene's musical language: feel it. It's a simple request, as well... Greene's musical language: feel it. It's a simple request, as well...

View File

@ -8,7 +8,9 @@
"angular-sanitize": "^1.4.0", "angular-sanitize": "^1.4.0",
"angular-material": "^1.1.3", "angular-material": "^1.1.3",
"angular-ui-router": "^0.4.2", "angular-ui-router": "^0.4.2",
"angular-material-icons": "^0.7.1" "angular-material-icons": "^0.7.1",
"underscore": "^1.8.3",
"angular-file-upload": "^2.5.0"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "^1.4.0" "angular-mocks": "^1.4.0"

View File

@ -29,6 +29,8 @@ module.exports = function(config) {
'bower_components/angular-material/angular-material.js', 'bower_components/angular-material/angular-material.js',
'bower_components/angular-ui-router/release/angular-ui-router.js', 'bower_components/angular-ui-router/release/angular-ui-router.js',
'bower_components/angular-material-icons/angular-material-icons.min.js', 'bower_components/angular-material-icons/angular-material-icons.min.js',
'bower_components/underscore/underscore.js',
'bower_components/angular-file-upload/dist/angular-file-upload.min.js',
'bower_components/angular-mocks/angular-mocks.js', 'bower_components/angular-mocks/angular-mocks.js',
// endbower // endbower
'app/scripts/**/*.js', 'app/scripts/**/*.js',