diff --git a/build/database/repo/professor.php b/build/database/repo/professor.php index 1af4a9c..1fcfad0 100644 --- a/build/database/repo/professor.php +++ b/build/database/repo/professor.php @@ -111,13 +111,13 @@ class professor extends Repo_i { $build_rq = []; $bind_param = [ ':idProfesseur' => $id ]; - if( !is_null($lastName) ){ $build_rq[] = '`lastName` = :lastName'; $bind_param[':lastName'] = $lastName; } - if( !is_null($firstName) ){ $build_rq[] = '`firstName` = :firstName'; $bind_param[':firstName'] = $firstName; } - if( !is_null($category) ){ $build_rq[] = '`Categorie_idCategorie` = :category'; $bind_param[':category'] = $category; } - if( !is_null($hoursToDo) ){ $build_rq[] = '`hoursToDo` = :hoursToDo'; $bind_param[':hoursToDo'] = $hoursToDo; } - if( !is_null($initials) ){ $build_rq[] = '`abreviation` = :initials'; $bind_param[':initials'] = $initials; } - if( !is_null($isAdmin) ){ $build_rq[] = '`admin` = :isAdmin'; $bind_param[':isAdmin'] = $isAdmin; } - if( !is_null($casLogin) ){ $build_rq[] = '`casLogin` = :casLogin'; $bind_param[':casLogin'] = $casLogin; } + if( !is_null($lastName) ){ $build_rq[] = '`lastName` = :lastName'; $bind_param[':lastName'] = $lastName; } + if( !is_null($firstName) ){ $build_rq[] = '`firstName` = :firstName'; $bind_param[':firstName'] = $firstName; } + if( !is_null($category) ){ $build_rq[] = '`Categorie_idCategorie` = :category'; $bind_param[':category'] = $category; } + if( !is_null($hoursToDo) ){ $build_rq[] = '`hoursToDo` = :hoursToDo'; $bind_param[':hoursToDo'] = $hoursToDo; } + if( !is_null($initials) ){ $build_rq[] = '`abreviation` = :initials'; $bind_param[':initials'] = $initials; } + if( !is_null($isAdmin) ){ $build_rq[] = '`admin` = :isAdmin'; $bind_param[':isAdmin'] = $isAdmin?1:0; } + if( !is_null($casLogin) ){ $build_rq[] = '`casLogin` = :casLogin'; $bind_param[':casLogin'] = $casLogin; } /* (2) ERROR if no updated field */ if( count($build_rq) <= 0 || count($bind_param) <= 1 ) diff --git a/config/modules.json b/config/modules.json index f6b6e74..c290f4e 100644 --- a/config/modules.json +++ b/config/modules.json @@ -92,9 +92,9 @@ "lastName": { "des": "Professor first name.", "typ": "varchar(2,30,alphanumeric)" }, "category": { "des": "Professor category UID.", "typ": "id" }, "hoursToDo": { "des": "Number of hours professor have to do", "typ": "id" }, - "initials": { "des": "Professor initials", "typ": "varchar(2,2,letters)" }, + "initials": { "des": "Professor initials", "typ": "varchar(2,8,letters)" }, "isAdmin": { "des": "Whether professor is an admin", "typ": "boolean" }, - "casLogin": { "des": "Optional CAS username", "typ": "varchar(6,10,letters)", "opt": true } + "casLogin": { "des": "Optional CAS username", "typ": "varchar(6,16,letters)", "opt": true } }, "out": { "created_uid": { "des": "Created professor UID", "typ": "id" } diff --git a/public_html/asset/svg/admin.svg b/public_html/asset/svg/admin.svg new file mode 100644 index 0000000..2a37dbc --- /dev/null +++ b/public_html/asset/svg/admin.svg @@ -0,0 +1,45 @@ + +image/svg+xml \ No newline at end of file diff --git a/webpack/component/teacher/view.vue b/webpack/component/teacher/view.vue index 090fe66..20d3d1a 100644 --- a/webpack/component/teacher/view.vue +++ b/webpack/component/teacher/view.vue @@ -6,40 +6,45 @@
-
+
- +

- + +

- + heures à faire
-
 
+
{{ gstore.create_err }}
Aucun enseignant trouvé
-
+
+
+
+ {{ prof.categorie }} -

{{ prof.firstName }} {{ prof.lastName }}

+

{{ prof.firstName }} {{ prof.lastName }} {{ prof.casLogin }}

diff --git a/webpack/data/teacher.js b/webpack/data/teacher.js index ab499ee..878bc27 100644 --- a/webpack/data/teacher.js +++ b/webpack/data/teacher.js @@ -204,3 +204,288 @@ gstore.add('is_handler', function(e){ }); + + + +/* (5) Manage instant create +---------------------------------------------------------*/ +/* (1) Initialize inputs */ +gstore.add('create_cat', '-'); +gstore.add('create_name', ''); +gstore.add('create_cas', ''); +gstore.add('create_h', ''); + +/* (2) Initialize error message */ +gstore.add('create_err_valid', false); +gstore.add('create_err', ''); + +/* (3) Define create handler */ +gstore.add('ic_handler', function(prof_id){ + + /* (3.1) Trim text input */ + gstore.get.create_name = gstore.get.create_name.trim(); + gstore.get.create_cas = gstore.get.create_cas.trim().toLowerCase(); + gstore.get.create_h = gstore.get.create_h.trim(); + + /* (3.2) Store values locally */ + var cat = gstore.get.create_cat; + var name = gstore.get.create_name.split(' '); + var cas = gstore.get.create_cas; + var hour = gstore.get.create_h; + + /* (3.3) Init client-side check */ + var errors = []; + + /* (3.3.1) Check category */ + if( isNaN(cat) ) errors.push('La catégorie de l\'enseignant est manquante'); + + /* (3.3.2) Check name */ + if( name.length !== 2 || name[0].length < 2 || name[1].length < 2 ) + errors.push('Le nom doit suivre le format "Prénom Nom"'); + + /* (3.3.3) Check CAS login */ + if( !/^([a-z]{4,16})?$/.test(cas) ) + errors.push('L\'identifiant doit être vide ou comprendre de 4 à 16 lettres'); + + /* (3.3.4) Check hours */ + if( hour === '' || isNaN(hour) || hour < 0 ) + errors.push('Le nombre d\'heures doit être un entier positif.'); + + /* (3.4) Show first error only (for 2s) */ + if( errors.length > 0 ){ + + gstore.get.create_err = errors[0]; + + return setTimeout(() => gstore.add('create_err', ''), 2000); + + } + + + + /* (4.1) Création de la requête */ + var rq = { + firstName: name[0], + lastName: name[1], + category: cat, + initials: name[0].substr(0,2)+name[1].substr(0,2), + hoursToDo: hour, + isAdmin: false + }; + + // optional cas_login + if( cas.length > 0 ) rq.casLogin = cas; + + + /* (4.2) Send request */ + api.call('POST professor', rq, function(rs){ + + console.log(rs); + + /* (4.2.1) Manage 'already exist' error */ + if( rs.error == 29 ){ + gstore.get.create_err = 'Le couple Nom-Prénom est déja utilisé.'; + return setTimeout(() => gstore.add('create_err', ''), 2000); + } + + /* (4.2.2) Manage other errors */ + if( rs.error !== 0 ){ + gstore.get.create_err = 'erreur ('+rs.error+') Impossible de créer l\'enseignant'; + return setTimeout(() => gstore.add('create_err', ''), 2000); + } + + /* (4.2.3) Show that user is created */ + // display all is ok + gstore.add('create_err_valid', true); + gstore.add('create_err', 'L\'enseignant a été créé, il s\'affichera au prochain rechargement'); + + // empty fields + gstore.get.create_cat = '-'; + gstore.get.create_name = ''; + gstore.get.create_cas = ''; + gstore.get.create_h = ''; + + return setTimeout(() => { + gstore.add('create_err', ''); + gstore.add('create_err_valid', false); + }, 2000); + + + }); + + + + +}); + + + + + +/* (6) Manage instant remove +---------------------------------------------------------*/ +/* (1) Define remove handler */ +gstore.add('ir_handler', function(prof_id){ + + /* (1) Abort if wrong prof_id */ + if( prof_id == null || isNaN(prof_id) ) + return; + + /* (2) Show popup */ + (new Promise( (resolve, reject) => { + + popup.ask({ + title: 'Confirmation de suppression', + content: "La suppression de l'enseignant sadkfjsdlkf est irréversible.

Voulez-vous le supprimer définitivement ?", + action: 'Supprimer', + type: 'invalid' + }, (popup_rs) => { popup_rs && resolve() }); + + })).then(function(){ + + + return new Promise( (resolve, reject) => { + + /* (2.1) Delete professor */ + api.call('DELETE professor/'+prof_id, {}, function(rs){ + + /* (2.1.1) Abort on error */ + if( rs.error !== 0 || rs.deleted !== true ) + return reject(rs.error); + + /* (2.1.2) Success */ + resolve(); + + }); + + }); + + /* (3) On success */ + }).then(function(){ + + /* (3.1) Find index in gstore */ + var gi = gstore.get.professors.map( (data, i) => { return ( data.idProfesseur && data.idProfesseur == prof_id ) ? i : ''; }).join(''); + + /* (3.2) Do nothing if not found */ + if( isNaN(gi) ) return; + + /* (3.3) Else -> remove from visible */ + gstore.get.professors.splice(gi, 1); + + + /* (4) On error */ + }).catch(function(err_code){ + + popup.ask({ + title: 'Error ('+err_code+')', + content: 'La suppression a échouée. Veuillez réessayer ultérieurement.', + action: 'OK', + type: 'neutral' + }, () => {}); + + }); + + +}); + + + + + +/* (7) Manage instant edit +---------------------------------------------------------*/ +/* (1) Define edit handler */ +gstore.add('ie_handler', function(prof_id){ + + /* (1) Abort if wrong prof_id */ + if( prof_id == null || isNaN(prof_id) ) + return; + + /* (2) Show popup */ + (new Promise( (resolve, reject) => { + + popup.ask({ + title: 'Confirmation de modification', + content: "La modification de l'enseignant sadkfjsdlkf est irréversible.

Voulez-vous le modifier ?", + action: 'Modifier', + type: 'search' + }, (popup_rs) => { popup_rs && resolve() }); + + })).then(function(){ + + + return new Promise( (resolve, reject) => { + + /* (2.1) Delete professor */ + api.call('PUT professor/'+prof_id, {}, function(rs){ + + /* (2.1.1) Abort on error */ + if( rs.error !== 0 || rs.updated !== true ) + return reject(rs.error); + + /* (2.1.2) Success */ + resolve(); + + }); + + }); + + /* (3) On success */ + }).then(function(){ + + /* (3.1) Find index in gstore */ + var gi = gstore.get.professors.map( (data, i) => { return ( data.idProfesseur && data.idProfesseur == prof_id ) ? i : ''; }).join(''); + + /* (3.2) Do nothing if not found */ + if( isNaN(gi) ) return; + + /* (3.3) Else -> update VueJS element */ + // gstore.get.professors[gi]; + + + + /* (4) On error */ + }).catch(function(err_code){ + + popup.ask({ + title: 'Error ('+err_code+')', + content: 'La modification a échouée. Veuillez réessayer ultérieurement.', + action: 'OK', + type: 'neutral' + }, () => {}); + + }); + + +}); + + + + + +/* (8) Manage instant admin +---------------------------------------------------------*/ +/* (1) Define admin handler */ +gstore.add('ia_handler', function(prof_i){ + + /* (1) Abort if wrong prof_i */ + if( prof_i == null || isNaN(prof_i) || gstore.get.professors[prof_i] == null) + return; + + /* (2) Toggle current value */ + var local = gstore.get.professors[prof_i]; + var is_admin = local.admin == '1' || local.admin === true; + var new_state = !is_admin; + + /* (3.1) Update in database */ + api.call('PUT professor/'+local.idProfesseur, { isAdmin: new_state }, function(rs){ + + /* (3.1.1) Abort on error */ + if( rs.error !== 0 || rs.updated !== true ) + return console.log('Impossible de changer le status \'admin\', erreur '+rs.error); + + /* (3.1.2) Success */ + gstore.get.professors[prof_i].admin = new_state ? 1 : 0; + + }); + +}); \ No newline at end of file diff --git a/webpack/scss/constants.scss b/webpack/scss/constants.scss index 7525ff5..089657b 100644 --- a/webpack/scss/constants.scss +++ b/webpack/scss/constants.scss @@ -10,7 +10,7 @@ $error-color: #cc5857; /* FORMULAIRES */ $form-valid-color: #20d696; $form-neutral-color: #b8c0c8; -$form-search-color: #e5c41d; +$form-search-color: #1d74e5; $form-invalid-color: #ea4b35; $form-grey-color: lighten($secondary-color, 5%); @@ -19,10 +19,10 @@ $form-grey-color: lighten($secondary-color, 5%); // POUR RESOURCE_DISPATCHER -$rd-form-valid-color: '27a560'; -$rd-form-neutral-color: '2193e6'; -$rd-form-search-color: '5630ed'; -$rd-form-invalid-color: 'd52918'; +$rd-form-valid-color: '20d696'; +$rd-form-neutral-color: 'b8c0c8'; +$rd-form-search-color: '1d74e5'; +$rd-form-invalid-color: 'ea4b35'; // Menu $menu-bg: #fff; diff --git a/webpack/scss/container.scss b/webpack/scss/container.scss index e09d3f3..6aeca7d 100644 --- a/webpack/scss/container.scss +++ b/webpack/scss/container.scss @@ -217,6 +217,10 @@ // border: 1px solid darken(#fff, 10%); box-shadow: 0 1px 1px darken(#fff, 20%); + box-shadow: 0 1px 1px darken(#fff, 20%); + &.invalid{ box-shadow: 0; border: 1px solid $form-invalid-color; } + &.valid{ box-shadow: 0; border: 1px solid $form-valid-color; } + background-color: #fff; color: $primary-color; @@ -235,12 +239,62 @@ &.search-hidden, &.filter-hidden{ display: none; } - /* (2) Card generic title */ + + /* (2) REMOVE+EDIT+ADMIN button */ + & > div.remove[data-remove], + & > div.edit[data-edit], + & > div.admin[data-admin]{ + display: inline-block; + position: absolute; + top: 1.3em; + left: calc( 100% - 2em ); + width: 1em; + height: 1em; + + background: url('/asset/svg/remove.svg@bbbbbb') center center no-repeat; + background-size: auto 100%; + + cursor: pointer; + + &:hover{ + background-image: url('/asset/svg/remove.svg@#{$rd-form-invalid-color}'); + } + + } + + /* (2.1) EDIT button */ + & > div.edit[data-edit]{ + left: calc( 100% - 2em - 1.3em ); + + background-image: url('/asset/svg/td.svg@bbbbbb'); + background-size: 80% auto; + + &:hover{ + background-image: url('/asset/svg/td.svg@#{$rd-form-search-color}'); + } + + } + + /* (2.2) ADMIN switch */ + & > div.admin[data-admin]{ + left: calc( 100% - 2em - 1.3em - 1.3em ); + + background-image: url('/asset/svg/admin.svg@bbbbbb'); + background-size: 85% auto; + + &:hover{ background-image: url('/asset/svg/admin.svg@bbbbbb'); } + + &[data-active='1']{ background-image: url('/asset/svg/admin.svg@f4bd18'); } + + } + + /* (4) Card generic title */ & > span.category, & > select.category{ display: block; position: relative; + width: calc( 100% - 5em ); margin-bottom: .5em; @@ -251,9 +305,10 @@ } - /* (2-edot) Card generic title (select) */ + /* (4-edit) Card generic title (select) */ & > select.category{ + width: calc( 100% + 1em ); margin: 0; padding: 0; margin-left: -.4em; // emulate no