Merge branch 'master' into ue-design

This commit is contained in:
xdrm-brackets 2018-03-07 16:06:54 +01:00
commit fe4db55e06
8 changed files with 494 additions and 57 deletions

View File

@ -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 )

View File

@ -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" }

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 44 43"
height="43px"
id="Layer_1"
version="1.1"
viewBox="0 0 44 43"
width="44px"
xml:space="preserve"
sodipodi:docname="admin.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="5.4883721"
inkscape:cx="22"
inkscape:cy="21.5"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
d="m 14.113324,21.173 c -1.835,-5.235 -1.049,-12.14 3.431,-16.62 6.054,-6.053 15.841,-6.08 21.893,-0.027 6.054,6.053 6.094,15.908 0.041,21.961 -4.615,4.615 -11.168,5.945 -17.026,3.318 l -2.902,2.902 -0.109,0.109 -4.084,-0.714 0.018,4.271 -4.244,0.008 0.532,4.213 -2.1109998,2.111 c -0.006,0.006 -0.006,0.006 -0.01,0.01 l -0.157,-0.156 c -0.741,0.673 -0.504,0.366 -0.615,0.366 l -7.272,0.22 c -0.6,0 -1.126,-0.443 -1.127,-1.044 l 0.283,-7.333 c 0,-0.112 -0.327,0.146 0.346,-0.596 l 0.058,0.059 c 0.003,-0.004 0.003,-0.003 0.006,-0.007 z m 16.773,-7.471 c 1.563,1.563 4.095,1.563 5.657,0 1.562,-1.563 1.563,-4.094 0,-5.657 -1.563,-1.563 -4.095,-1.563 -5.657,0 -1.562,1.563 -1.563,4.095 0,5.657 z"
id="fill-edit"
inkscape:connector-curvature="0"
style="fill:#231f20" /></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -6,40 +6,45 @@
<div class='card container'>
<section data-create=''>
<section class='valid' data-create=''>
<select class='category'>
<option selected='selected' disabled='disabled'>Catégorie d'enseignant</option>
<select class='category' v-model='gstore.create_cat'>
<option selected='selected' disabled='disabled' value='-'>Catégorie d'enseignant</option>
<option v-for='cat in gstore.categories' :value='cat.idCategorie'>{{ cat.labelCategorie }}</option>
</select>
<h1>
<input type='text' placeholder='Prénom Nom' value='Prénom Nom'>
<input type='text' placeholder='Prénom Nom' v-model='gstore.create_name'>
<span data-visible='1'><input type='text' placeholder='identifiant' v-model='gstore.create_cas'></span>
</h1>
<div class='table'>
<div>
<span><input type='text' placeholder='???' value='192'></span>
<span><input type='text' placeholder='???' v-model='gstore.create_h'></span>
<span>heures à faire</span>
</div>
</div>
<div class='sub'>&nbsp;</div>
<div :class="gstore.create_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.create_err_valid?1:0'>{{ gstore.create_err }}</div>
<div class='footer'>
<button class='valid'>Créer l'enseignant</button>
<button class='valid' @click='gstore.ic_handler()'>Créer l'enseignant</button>
</div>
</section>
<section v-if='gstore.professors.length <= 0'>Aucun enseignant trouvé</section>
<section v-for='prof in gstore.professors'
<section v-for='(prof, pi) in gstore.professors'
:data-id='prof.idProfesseur'
:data-category='prof.idCat'
:data-lname='prof.lastName'
:data-fname='prof.firstName'>
<div class='remove' :data-remove='prof.idProfesseur' @click="gstore.ir_handler($event.currentTarget.getAttribute('data-remove'))"></div>
<div class='edit' :data-edit='prof.idProfesseur' @click="gstore.ie_handler($event.currentTarget.getAttribute('data-edit'))"></div>
<div class='admin' :data-admin='prof.idProfesseur' :data-active='prof.admin?1:0' @click="gstore.ia_handler(pi)"></div>
<span class='category'>{{ prof.categorie }}</span>
<h1 :class="prof.hoursToDo > prof.equiTD ? 'warning' : ''">{{ prof.firstName }} {{ prof.lastName }}</h1>
<h1 :class="prof.hoursToDo > prof.equiTD ? 'warning' : ''">{{ prof.firstName }} {{ prof.lastName }} <span :data-visible='prof.casLogin.length'>{{ prof.casLogin }}</span></h1>
<div class='table'>
<div>

View File

@ -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 <b>sadkfjsdlkf</b> est irréversible.<br><br>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 <b>sadkfjsdlkf</b> est irréversible.<br><br>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;
});
});

View File

@ -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;

View File

@ -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 <select>
@ -274,7 +329,7 @@
}
/* (3) Card title */
/* (5) Card title */
& > h1{
display: flex;
@ -304,8 +359,8 @@
text-anchor: center;
}
/* (3-edit) Card title -> edit inputs */
input{
/* (5-edit) Card title -> edit inputs */
& input{
display: inline-block;
position: relative;
@ -327,9 +382,32 @@
background: transparent;
}
/* (5.1) Sub-content */
& > span[data-visible]{
display: inline-block;
color: $secondary-color;
margin: 0;
margin-left: .5em;
transform: scale(.9);
&:before{ content: '('; }
&:after{ content: ')'; }
&[data-visible='0']{ display: none; }
/* (5.1-edit) input*/
& > input{
text-align: center;
}
}
}
/* (4) Card 2-column array */
/* (6) Card 2-column array */
& > div.table{
display: flex;
@ -347,7 +425,7 @@
align-items: center;
flex-wrap: nowrap;
/* (5) Column */
/* (6.1) Column */
& > div{
display: flex;
@ -368,7 +446,7 @@
align-items: center;
flex-wrap: nowrap;
/* (5.1) Column Emphasis */
/* (6.1.1) Column Emphasis */
& > span,
& > span:first-child{
display: block;
@ -379,7 +457,7 @@
font-size: 1.5em;
letter-spacing: .05em;
/* (5.1-edit) Editable text entry */
/* (6.1.1-edit) Editable text entry */
& > input{
display: inline-block;
width: 2em;
@ -395,7 +473,7 @@
}
}
/* (5.2) Column Emphasis */
/* (6.2) Column Emphasis */
& > span:last-child{
display: block;
position: relative;
@ -415,7 +493,7 @@
}
/* (6) Card footer */
/* (7) Card footer */
& > div.footer{
display: flex;
@ -442,7 +520,7 @@
flex-wrap: nowrap;
/* (6.1) Card footer element */
/* (7.1) Card footer element */
& > span{
display: flex;
@ -494,7 +572,12 @@
}
/* (6.2) Card footer separator */
/* (7.1-edit) Card button submit */
& > button{
margin: auto;
}
/* (7.2) Card footer separator */
& > hr{
display: block;
position: relative;
@ -504,17 +587,9 @@
border: 0;
background-color: darken(#fff, 10%);
}
/* (6-edit) Card button submit */
& > button{
margin: auto;
}
}
/* (6) Card sub */
/* (8) Card sub */
& > div.sub{
display: inline-block;
@ -522,6 +597,36 @@
color: lighten($primary-color, 20%);
&:before{
content: '';
display: inline-block;
position: relative;
width: 0em;
height: 1em;
background: center bottom -.1em no-repeat;
background-size: auto .9em;
text-anchor: center;
}
&.warning{
color: $form-invalid-color;
&:before{
width: 1em;
margin-right: .3em;
background-image: url('/asset/svg/warning_radio.svg@#{$rd-form-invalid-color}');
}
&[data-valid='1']{
color: $form-valid-color;
}
}
}
}

View File

@ -19,9 +19,7 @@
border-radius: 5px;
background: $bg-color;
color: $secondary-color;
box-shadow: -10px 10px 0 rgba(26, 33, 40, .8);
color: $primary-color;
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
@ -36,9 +34,9 @@
& > .header{
display: block;
position: relative;
width: calc( 100% - 2*1em );
width: calc( 100% - 2*2em );
padding: 1em;
padding: 1em 2em;
border-radius: 5px 5px 0 0;
border-bottom: 2px solid darken($bg-color, 5%);
@ -52,21 +50,20 @@
& > .body{
display: block;
position: relative;
width: calc( 100% - 2*1em );
width: calc( 100% - 2*2em );
padding: 1em;
padding: 2em;
b, strong{ color: darken($primary-color, 10%); }
b, strong{
color: $primary-color;
}
}
& > .footer{
display: flex;
position: relative;
width: calc( 100% - 2*1em );
width: calc( 100% - 2*2em );
padding: 1em;
padding: 1em 2em;
background-color: darken($bg-color, 5%);