Merge branch 'ue-from-teacher'

This commit is contained in:
xdrm-brackets 2018-03-12 11:02:56 +01:00
commit 9b19c42aee
10 changed files with 889 additions and 72 deletions

View File

@ -44,6 +44,10 @@
// On recupere le sous-type si défini // On recupere le sous-type si défini
$flags = isset($match[3]) ? explode(',', substr($match[3], 1)) : null; $flags = isset($match[3]) ? explode(',', substr($match[3], 1)) : null;
// Si numeric -> to String
if( is_numeric($value) )
$value = (string) $value;
// On effectue la verification de taille // On effectue la verification de taille
$lenCheck = $checker && is_string($value) && strlen($value) <= $max && strlen($value) >= $min; $lenCheck = $checker && is_string($value) && strlen($value) <= $max && strlen($value) >= $min;
@ -91,6 +95,11 @@
return $checker && is_numeric($value) && $value <= 2147483647 && $value >= 0; return $checker && is_numeric($value) && $value <= 2147483647 && $value >= 0;
break; break;
// Entier relatif (neg ou pos)
case 'int':
return $checker && is_int($value);
break;
// String quelconque (peut etre vide) // String quelconque (peut etre vide)
case 'text': case 'text':
return $checker && is_string($value); return $checker && is_string($value);
@ -143,7 +152,7 @@
break; break;
case "float": case "float":
return $checker && is_float($value); return $checker && ( is_int($value) || is_float($value) );
break; break;
default: default:

View File

@ -42,4 +42,129 @@ class ueController{
} }
/* (2) Creates a new UE
*
* @code<String> The code of the UE
* @label<String> The UE label (name)
* @required<bool> If the UE is required
* @volumeCours<float> The UE required volume of COURSES
* @volumeTD<float> The UE required volume of TD
* @volumeTP<float> The UE required volume of TP
* @disabled<bool> [OPT] If it is disabled
* @defaultFormation<int> [OPT] If there is a foreign key for a default formation (if only one formation)
*
* @return created_code<int> The created UE code (if no error)
*
---------------------------------------------------------*/
public static function post($args){
$code = "";
$label = "";
$required = false;
$volumeCours = 0;
$volumeTD = 0;
$volumeTP = 0;
$disabled = true;
$defaultFormation = -1;
extract($args);
/* Get the ue repo */
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Check if ue code already exists */
$exists = $ue_repo->get($code);
/* (2) If found -> already exists */
if( count($exists) > 0 )
return ['error' => new Error(Err::AlreadyExists)];
/* (3) Else try to create */
$repo_rtn = $ue_repo->create(
$code,
$label,
$required,
$volumeCours,
$volumeTD,
$volumeTP,
$disabled,
is_int($defaultFormation) && $defaultFormation < 0 ? null : $defaultFormation
);
/* (4) If repo error -> return it */
if( is_null($repo_rtn) )
return ['error' => new Error(Err::RepoError)];
/* (5) Else return UID */
return ['created_code' => $repo_rtn];
}
/* (3) Deletes an existing UE
*
* @code<String> The UE code
*
* @return deleted<bool> Whether it has been removed
*
---------------------------------------------------------*/
public static function delete($args){
$code = '';
extract($args);
/* Get the ue repo */
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Try to delete */
return ['deleted' => $ue_repo->delete($code)];
}
/* (4) Edits an existing UE
*
* @code<String> The code of the UE
* @label<String> [OPT] The UE label (name)
* @required<bool> [OPT] If the UE is required
* @volumeCours<float> [OPT] The UE required volume of COURSES
* @volumeTD<float> [OPT] The UE required volume of TD
* @volumeTP<float> [OPT] The UE required volume of TP
* @disabled<bool> [OPT] If it is disabled
* @defaultFormation<int> [OPT] default formation for this UE (-1 to unset, NULL to ignore)
*
* @return updated<bool> Whether it has been updated
*
---------------------------------------------------------*/
public static function put($args){
$code = "";
$label = "";
$required = false;
$volumeCours = 0;
$volumeTD = 0;
$volumeTP = 0;
$disabled = true;
$defaultFormation = null;
extract($args);
/* Get the ue repo */
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Try to update */
return ['updated' => $ue_repo->update(
$code,
$label,
$required,
$volumeCours,
$volumeTD,
$volumeTP,
$disabled,
$defaultFormation
)];
}
} }

View File

@ -221,21 +221,25 @@ class professor extends Repo_i {
/* (1) Prepare Statement */ /* (1) Prepare Statement */
$st = $this->pdo->prepare("SELECT * FROM `Professeur` WHERE `casLogin` = :cas_login"); $st = $this->pdo->prepare("SELECT * FROM `Professeur` WHERE `casLogin` = :cas_login");
/* (2) Bind params and execute statement */ /* (2) Check if statement error */
if( is_bool($st) )
return NULL;
/* (3) Bind params and execute statement */
$success = $st->execute([ ':cas_login' => $cas_login ]); $success = $st->execute([ ':cas_login' => $cas_login ]);
/* (3) Manage error */ /* (4) Manage error */
if( !$success ) if( !$success )
return NULL; return NULL;
/* (4) Get data */ /* (5) Get data */
$fetched = $st->fetch(); $fetched = $st->fetch();
/* (5) Return NULL on no result */ /* (6) Return NULL on no result */
if( $fetched === false ) if( $fetched === false )
return NULL; return NULL;
/* (6) Return data */ /* (7) Return data */
return $fetched; return $fetched;
} }

View File

@ -18,16 +18,17 @@ class ue extends Repo_i {
* *
* @code<String> The code of the UE * @code<String> The code of the UE
* @label<String> The UE label (name) * @label<String> The UE label (name)
* @required<bool> If the UE is required
* @volumeCours<float> The UE required volume of COURSES * @volumeCours<float> The UE required volume of COURSES
* @volumeTD<float> The UE required volume of TD * @volumeTD<float> The UE required volume of TD
* @volumeTP<float> The UE required volume of TP * @volumeTP<float> The UE required volume of TP
* @disabled<bool> If it is disabled * @disabled<bool> [OPT] If it is disabled
* @defaultFormation<bool> If there is a foreign key for a default formation (if only one formation) * @defaultFormation<int> [OPT] If there is a foreign key for a default formation (if only one formation)
* *
* @return created_code<String> Code of the created UE (NULL on error) * @return created_code<String> Code of the created UE (NULL on error)
* *
---------------------------------------------------------*/ ---------------------------------------------------------*/
public function create(string $code, string $label, bool $required, float $volumeCours, float $volumeTD, float $volumeTP, bool $disabled = false, ?int $defaultFormation = null) : ?int { public function create(string $code, string $label, bool $required, float $volumeCours, float $volumeTD, float $volumeTP, bool $disabled = false, ?int $defaultFormation = null) : ?string {
/* (1) Prepare request */ /* (1) Prepare request */
$st = $this->pdo->prepare("INSERT INTO UE(`code`, `label`, `required`, `volumeCours`, `volumeTD`, `volumeTP`, `disabled`, `Formation_idFormation`) $st = $this->pdo->prepare("INSERT INTO UE(`code`, `label`, `required`, `volumeCours`, `volumeTD`, `volumeTP`, `disabled`, `Formation_idFormation`)
@ -39,14 +40,14 @@ class ue extends Repo_i {
/* (3) Bind params and execute request */ /* (3) Bind params and execute request */
$success = $st->execute([ $success = $st->execute([
"code" => $code, ':code' => $code,
"label" => $label, ':label' => $label,
"required" => $required ? 1 : 0, ':required' => $required ? 1 : 0,
"volCours" => $volumeCours, ':volCours' => $volumeCours,
"volTD" => $volumeTD, ':volTD' => $volumeTD,
"volTP" => $volumeTP, ':volTP' => $volumeTP,
"disabled" => $disabled ? 1 : 0, ':disabled' => $disabled ? 1 : 0,
"idFormation" => $defaultFormation ':idFormation' => $defaultFormation
]); ]);
/* (4) Manage execution error */ /* (4) Manage execution error */
@ -54,7 +55,7 @@ class ue extends Repo_i {
return null; return null;
/* (5) Return insert id */ /* (5) Return insert id */
return $this->pdo->lastInsertId(); return $code;
} }
@ -68,7 +69,7 @@ class ue extends Repo_i {
* @volumeTD<float> [OPT] The UE's new volume of TD * @volumeTD<float> [OPT] The UE's new volume of TD
* @volumeTP<float> [OPT] The UE's new volume of TP * @volumeTP<float> [OPT] The UE's new volume of TP
* @disabled<bool> [OPT] Whether the UE is disabled * @disabled<bool> [OPT] Whether the UE is disabled
* @defaultFormation<int> [OPT] The default formation foreign key * @defaultFormation<int> [OPT] The default formation foreign key (-1 to unset)
* *
* @return updated<bool> Whether the update have been successful * @return updated<bool> Whether the update have been successful
* *
@ -80,15 +81,25 @@ class ue extends Repo_i {
$bind_param = [ ':code' => $code ]; $bind_param = [ ':code' => $code ];
if( !is_null($label) ){ $build_rq[] = '`label` = :label'; $bind_param[':label'] = $label; } if( !is_null($label) ){ $build_rq[] = '`label` = :label'; $bind_param[':label'] = $label; }
if( !is_null($required) ){ $build_rq[] = '`required` = :required'; $bind_param[':required'] = $required; } if( !is_null($required) ){ $build_rq[] = '`required` = :required'; $bind_param[':required'] = $required?1:0; }
if( !is_null($volumeCours) ){ $build_rq[] = '`volumeCours` = :volumeCours'; $bind_param[':volumeCours'] = $volumeCours; } if( !is_null($volumeCours) ){ $build_rq[] = '`volumeCours` = :volumeCours'; $bind_param[':volumeCours'] = $volumeCours; }
if( !is_null($volumeTD) ){ $build_rq[] = '`volumeTD` = :volumeTD'; $bind_param[':volumeTD'] = $volumeTD; } if( !is_null($volumeTD) ){ $build_rq[] = '`volumeTD` = :volumeTD'; $bind_param[':volumeTD'] = $volumeTD; }
if( !is_null($volumeTP) ){ $build_rq[] = '`volumeTP` = :volumeTP'; $bind_param[':volumeTP'] = $volumeTP; } if( !is_null($volumeTP) ){ $build_rq[] = '`volumeTP` = :volumeTP'; $bind_param[':volumeTP'] = $volumeTP; }
if( !is_null($disabled) ){ $build_rq[] = '`disabled` = :disabled'; $bind_param[':disabled'] = $disabled; } if( !is_null($disabled) ){ $build_rq[] = '`disabled` = :disabled'; $bind_param[':disabled'] = $disabled?1:0; }
if( !is_null($defaultFormation) ){ $build_rq[] = '`Formation_idFormation` = :defaultFormation'; $bind_param[':defaultFormation'] = $defaultFormation; }
// not null @defaultFormation -> set it
if( !is_null($defaultFormation) ){
// if @defaultFormation is (-1) -> unset
if( $defaultFormation < 0 ){ $build_rq[] = '`Formation_idFormation` = NULL'; }
// else -> set to new value
else{ $build_rq[] = '`Formation_idFormation` = :defaultFormation'; $bind_param[':defaultFormation'] = $defaultFormation; }
}
/* (2) ERROR if no updated field */ /* (2) ERROR if no updated field */
if( count($build_rq) <= 0 || count($bind_param) <= 1 ) if( count($build_rq) <= 0 )
return FALSE; return FALSE;
/* (3) Build request */ /* (3) Build request */
@ -160,14 +171,19 @@ class ue extends Repo_i {
* @return ues<array> The UEs matching code (NULL on error) * @return ues<array> The UEs matching code (NULL on error)
* *
---------------------------------------------------------*/ ---------------------------------------------------------*/
public function get(?String $code=null) : ?array{ public function get(?String $code=null) : array{
/* (1) Manage if no id given */ /* (1) Manage if no id given */
$cond = is_null($code) ? '' : ' WHERE `code` = :code'; $cond = is_null($code) ? '' : 'WHERE `code` = :code';
$parm = is_null($code) ? [] : [':code' => $code]; $parm = is_null($code) ? [] : [':code' => $code];
/* (2) Prepare Statement */ /* (2) Prepare Statement */
$st = $this->pdo->prepare("SELECT * FROM `UE`$cond GROUP BY `label` ASC"); $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$st = $this->pdo->prepare("SELECT ue.code, ue.label, ue.disabled, ue.required, ue.volumeCours, ue.volumeTD, ue.volumeTP, IFNULL(ue.Formation_idFormation, -1) idForm, f.labelFormation labelForm
FROM `UE` ue
LEFT JOIN Formation f ON ue.Formation_idFormation = f.idFormation
$cond
ORDER BY `ue`.`label` ASC");
/* (3) Bind params and execute statement */ /* (3) Bind params and execute statement */
if( is_bool($st) ) return []; if( is_bool($st) ) return [];

View File

@ -185,17 +185,17 @@
"des": "Creates a new UE", "des": "Creates a new UE",
"per": [], "per": [],
"par": { "par": {
"code": { "des": "UE code.", "typ": "varchar(2,30,alphanumeric)" }, "code": { "des": "UE code.", "typ": "varchar(4,20,alphanumeric)" },
"label": { "des": "UE label", "typ": "varchar(2,30,alphanumeric)" }, "label": { "des": "UE label", "typ": "varchar(4,30,alphanumeric)" },
"required": { "des": "If UE is required", "typ": "bool" }, "required": { "des": "If UE is required", "typ": "bool" },
"volumeCours": { "des": "Number of course hours for UE", "typ": "float" }, "volumeCours": { "des": "Number of course hours for UE", "typ": "float" },
"volumeTD": { "des": "Number of TD hours for UE", "typ": "float" }, "volumeTD": { "des": "Number of TD hours for UE", "typ": "float" },
"volumeTP": { "des": "Number of TP hours for UE", "typ": "float" }, "volumeTP": { "des": "Number of TP hours for UE", "typ": "float" },
"disabled": { "des": "Whether UE is disabled", "typ": "boolean" }, "disabled": { "des": "Whether UE is disabled", "typ": "boolean" },
"defaultFormation": { "des": "UID for optional default formation", "typ": "id", "opt": true } "defaultFormation": { "des": "UID for optional default formation (-1 if none)", "typ": "int", "opt": true, "def": -1 }
}, },
"out": { "out": {
"created_code": { "des": "Created UE code", "typ": "varchar(2,30,alphanumeric)" } "created_code": { "des": "Created UE code", "typ": "varchar(4,20,alphanumeric)" }
} }
}, },
@ -203,11 +203,41 @@
"des": "Get one or all UE", "des": "Get one or all UE",
"per": [], "per": [],
"par": { "par": {
"URL0": { "des": "Optional UE code.", "typ": "varchar(2,30,alphanumeric)", "ren": "code", "opt": true } "URL0": { "des": "Optional UE code.", "typ": "varchar(4,20,alphanumeric)", "ren": "code", "opt": true }
}, },
"out": { "out": {
"ues": { "des": "UE list", "typ": "array" } "ues": { "des": "UE list", "typ": "array" }
} }
},
"DELETE": {
"des": "Deletes an existing UE",
"per": [],
"par": {
"URL0": { "des": "UE code.", "typ": "varchar(4,20,alphanumeric)", "ren": "code" }
},
"out": {
"deleted": { "des": "Whether it has been deleted", "typ": "boolean" }
}
},
"PUT": {
"des": "Edits an existing UE",
"per": [],
"par": {
"URL0": { "des": "UE code.", "typ": "varchar(4,20,alphanumeric)", "ren": "code" },
"label": { "des": "UE label", "typ": "varchar(4,30,alphanumeric)", "opt": true },
"required": { "des": "If UE is required", "typ": "bool", "opt": true },
"volumeCours": { "des": "Number of course hours for UE", "typ": "float", "opt": true },
"volumeTD": { "des": "Number of TD hours for UE", "typ": "float", "opt": true },
"volumeTP": { "des": "Number of TP hours for UE", "typ": "float", "opt": true },
"disabled": { "des": "Whether UE is disabled", "typ": "boolean", "opt": true },
"defaultFormation": { "des": "UID for optional default formation (-1 for none)", "typ": "int", "opt": true }
},
"out": {
"updated": { "des": "Whether the UE has been updated", "typ": "boolean" }
}
} }

View File

@ -0,0 +1,44 @@
<?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 141.732 141.732"
height="141.732px"
id="Livello_1"
version="1.1"
viewBox="0 0 141.732 141.732"
width="141.732px"
xml:space="preserve"
sodipodi:docname="bell.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata11"><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="defs9" /><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="namedview7"
showgrid="false"
inkscape:zoom="1.6651145"
inkscape:cx="70.865997"
inkscape:cy="70.865997"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Livello_1" /><path
inkscape:connector-curvature="0"
id="fill-edit"
d="m 135.055,115.131 c 0,-3.528 -2.861,-6.396 -6.395,-6.396 -8.83,0 -15.988,-7.158 -15.988,-15.99 V 54.367 C 112.672,34.361 98.463,17.658 79.533,13.688 80.121,12.446 80.461,11.06 80.461,9.593 80.462,4.296 76.165,0 70.866,0 c -5.299,0 -9.594,4.296 -9.594,9.598 0,1.467 0.339,2.853 0.927,4.095 -18.928,3.97 -33.137,20.673 -33.137,40.679 v 38.377 c 0,8.832 -7.159,15.99 -15.99,15.99 -3.533,0 -6.396,2.864 -6.396,6.396 0,3.531 2.862,6.396 6.396,6.396 h 15.99 29.011 c 0,9.716 5.729,17.591 12.793,17.591 7.064,0 12.792,-7.875 12.792,-17.591 h 29.014 15.988 v 0 c 3.534,-0.005 6.395,-2.867 6.395,-6.4" /></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -59,7 +59,7 @@
<!-- if VIEW MODE --> <!-- if VIEW MODE -->
<div class='goo-menu' v-show='gstore.edit_i!=pi'> <div class='goo-menu' v-show='gstore.edit_i!=pi'>
<div class='admin' :data-admin='prof.idProfesseur' :data-active='prof.admin?1:0' @click="gstore.ia_handler(pi)"></div> <div class='admin' :data-admin='prof.idProfesseur' :data-active='prof.admin?1:0' @click="gstore.ia_handler(pi)"></div>
<div class='remove' :data-remove='prof.idProfesseur' @click="gstore.ir_handler($event.currentTarget.getAttribute('data-remove'))"></div> <div class='remove' :data-remove='prof.idProfesseur' @click="gstore.ir_handler(prof.idProfesseur)"></div>
<div class='edit' :data-edit='prof.idProfesseur' @click="gstore.ie_toggle(pi)"></div> <div class='edit' :data-edit='prof.idProfesseur' @click="gstore.ie_toggle(pi)"></div>
</div> </div>

View File

@ -1,38 +1,110 @@
<template> <template>
<div id='CONTAINER' class='card' style="top: 0; height: 100%"> <div id='CONTAINER' class='card'>
<div class="card container" style="width: 100%"> <div class='card container'>
<section v-if='gstore.ues.length <= 0'>Aucun enseignant trouvé</section>
<section v-for='ue in gstore.ues' :data-id='ue.code'> <input class='card instant-search neutral' type='text' @keyup='gstore.is_handler($event)' placeholder='Recherche instantannée' id='ue_view_instant_search'>
<span class='category'>{{ ue.code }}</span> <button class='card toggle valid' :data-active='gstore.create_card?1:0' @click='gstore.create_card=!gstore.create_card'>+</button>
<h1>{{ ue.label }}</h1>
<div class='table'> <section class='valid' data-create='' v-show='gstore.create_card'>
<div>
<span>{{ue.volumeCours}}</span>
<span>heures de cours</span>
</div>
<div>
<span>{{ue.volumeTD}}</span>
<span>heures de TD</span>
</div>
<div>
<span>{{ue.volumeTP}}</span>
<span>heures de TP</span>
</div>
</div>
<div class='sub'><strong>ZOU</strong> équivalents TD</div> <select v-model='gstore.create_form' class='category'>
<option selected='selected' disabled='disabled' value='-'>Formation par défaut</option>
<option value='-1'>Pas de formation par défaut</option>
<option v-for='form in gstore.formations' :value='form.idForm'>{{ form.labelForm }}</option>
</select>
<div class='footer'> <h1>
<span :class="(ue.volumeCours == 0) ? 'course' : 'course active'">{{ ue.volumeCours }}</span><hr> <input type='text' placeholder='Libellé' v-model='gstore.create_label'>
<span :class="(ue.volumeTD == 0) ? 'td' : 'td active'">{{ ue.volumeTD }}</span><hr> <span data-visible='1'>(<input type='text' placeholder='code' v-model='gstore.create_code'>)</span>
<span :class="(ue.volumeTP == 0) ? 'tp' : 'tp active'">{{ ue.volumeTP }}</span> </h1>
</div>
</section> <div :class="gstore.create_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.create_err_valid?1:0'>{{ gstore.create_err }}</div>
</div>
<div class='footer'>
<span class='course'><input type='text' placeholder='???' v-model='gstore.create_vol.c'><span>Cours</span></span>
<hr>
<span class='td'><input type='text' placeholder='???' v-model='gstore.create_vol.td'><span>TD</span></span>
<hr>
<span class='tp'><input type='text' placeholder='???' v-model='gstore.create_vol.tp'><span>TP</span></span>
</div>
<div class='footer'>
<button class='valid' @click='gstore.ic_handler()'>Créer l'UE</button>
</div>
</section>
<section v-if='gstore.ues.length <= 0'>Aucune UE trouvée</section>
<section v-for='(ue, pi) in gstore.ues'
:class="gstore.edit_i==pi ? 'search' : ''"
:data-id='ue.code'
:data-label='ue.label'>
<!-- if VIEW MODE -->
<div class='goo-menu' v-show='gstore.edit_i!=pi'>
<div class='enabled' :data-enabled='ue.code' :data-active='ue.disabled?0:1' @click="gstore.ia_handler(pi)"></div>
<div class='remove' :data-remove='ue.code' @click="gstore.ir_handler(ue.code)"></div>
<div class='edit' :data-edit='ue.code' @click="gstore.ie_toggle(pi)"></div>
</div>
<!-- if VIEW MODE -->
<span v-show='gstore.edit_i!=pi' class='category'>{{ ue.labelForm || 'Pas de formation par défaut' }}</span>
<!-- if EDIT MODE -->
<select v-show='gstore.edit_i==pi' v-model='gstore.edit_form' class='category'>
<option selected='selected' disabled='disabled' value='-'>Formation par défaut</option>
<option value='-1'>Pas de formation par défaut</option>
<option v-for='form in gstore.formations' :value='form.idForm'>{{ form.labelForm }}</option>
</select>
<!-- endif -->
<!-- if VIEW MODE -->
<h1 v-show='gstore.edit_i!=pi' :class="ue.required ? 'warning' : ''">{{ ue.label }}<span :data-visible='1'>({{ ue.code }})</span></h1>
<!-- if EDIT MODE -->
<h1 v-show='gstore.edit_i==pi' :class="ue.required ? 'warning' : ''">
<input type='text' placeholder='Libellé' v-model='gstore.edit_label'>
<span :data-visible='1'>({{ ue.code }})</span>
<!-- <span data-visible='1'>(<input type='text' placeholder='code' v-model='gstore.edit_code'>)</span> -->
</h1>
<!-- endif -->
<!-- if VIEW MODE -->
<div v-show='gstore.edit_i!=pi' class='sub'><strong>{{ ue.volumeCours + ue.volumeTD + ue.volumeTP }}h</strong> totales</div>
<!-- if EDIT MODE -->
<div v-show='gstore.edit_i==pi' :class="gstore.edit_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.edit_err_valid?1:0'>{{ gstore.edit_err }}</div>
<!-- endif -->
<div class='footer'>
<!-- if VIEW MODE -->
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeCours == 0) ? 'course' : 'course active'">{{ ue.volumeCours }}h <span>Cours</span></span>
<hr v-show='gstore.edit_i!=pi'>
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeTD == 0) ? 'td' : 'td active'">{{ ue.volumeTD }}h <span>TD</span></span>
<hr v-show='gstore.edit_i!=pi'>
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeTP == 0) ? 'tp' : 'tp active'">{{ ue.volumeTP }}h <span>TP</span></span>
<!-- if EDIT MODE -->
<span v-show='gstore.edit_i==pi' class='course'><input type='text' placeholder='???' v-model='gstore.edit_vol.c'><span>Cours</span></span>
<hr v-show='gstore.edit_i==pi'>
<span v-show='gstore.edit_i==pi' class='td'><input type='text' placeholder='???' v-model='gstore.edit_vol.td'><span>TD</span></span>
<hr v-show='gstore.edit_i==pi'>
<span v-show='gstore.edit_i==pi' class='tp'><input type='text' placeholder='???' v-model='gstore.edit_vol.tp'><span>TP</span></span>
<!-- endif -->
</div>
<!-- if EDIT MODE -->
<div class='footer' v-show='gstore.edit_i==pi'>
<button v-show='gstore.edit_i==pi' class='search' @click='gstore.ie_handler(pi)'>Modifier l'UE</button>
<button v-show='gstore.edit_i==pi' class='grey' @click='gstore.ie_toggle(-1)'>Annuler</button>
</div>
</section>
</div>
</div> </div>

View File

@ -1,16 +1,511 @@
/* (1) Load UEs /* (1) Load ues
---------------------------------------------------------*/ ---------------------------------------------------------*/
/* (1) Initialize list */ /* (1) Initialize list */
gstore.add('ues', []); gstore.add('ues', []);
/* (2) Get UEs */ /* (2) Get ues */
api.call('GET ue', { vh: true }, function(rs) { api.call('GET ue/', { vh: true }, function(rs){
// {1} If error -> abort // // {1} If error -> abort //
if(rs.error !== 0) if( rs.error !== 0 )return console.log('No UE found, error: '+rs.error);
return console.log('No UE found, error: ' + rs.error);
// {2} Store UEs //
console.log(rs); console.log(rs);
// {2} Store ues //
gstore.get.ues = rs.ues; gstore.get.ues = rs.ues;
}); });
/* (2) Load formations
---------------------------------------------------------*/
/* (1) Initialize list */
gstore.add('formations', []);
/* (2) Get Formations */
api.call('GET formation', {}, function(rs){
// {1} If error -> abort //
if( rs.error !== 0 ) return console.log('No formation found, error: '+rs.error);
console.log(rs);
// {2} Format UE filters //
for( var i = 0 ; i < rs.formations.length ; i++ )
gstore.get.formations.push( rs.formations[i] );
});
/* (2) Manage Instant Search (IS)
---------------------------------------------------------*/
/* (1) Define global timeout index */
gstore.add('is_to', null);
/* (2) Define search value buffer */
gstore.add('is_buf', null);
/* (3) Define instant search function */
gstore.add('is_handler', function(e){
/* (1) Remove last timeout */
if( gstore.get.is_to != null )
clearTimeout(gstore.get.is_to);
/* (2) Store value in buffer */
gstore.get.is_buf = e.target.value.trim().toLowerCase();
/* (3) Launch timeout (wait 1s) before filtering */
gstore.get.is_to = setTimeout(function(){
// 1. Fetch elements
var local_ptr = gstore.get.ues;
var l = gstore.get.ues.length;
// 2. For each element
for( var e = 0 ; e < l ; e++ ){
// 2.1. Show by default
var element = document.querySelector('section[data-id=\''+local_ptr[e].code+'\']');
if( !element ) continue;
element.remClass('search-hidden');
// 2.2. Extract name components
var code = local_ptr[e].code.trim().toLowerCase();
var label = local_ptr[e].label.trim().toLowerCase();
// 2.3. Hide if does not match
var match_offset = gstore.get.is_buf.length == 0 || code.search(gstore.get.is_buf) + label.search(gstore.get.is_buf);
if( match_offset <= -2 )
element.addClass('search-hidden');
}
}, 250);
});
/* (5) Manage instant create
---------------------------------------------------------*/
/* (1) Initialize toggle show */
gstore.add('create_card', false);
/* (2) Initialize inputs */
gstore.add('create_form', '-');
gstore.add('create_label', '');
gstore.add('create_code', '');
gstore.add('create_vol', { c: '', td: '', tp: ''});
/* (3) Initialize error message */
gstore.add('create_err_valid', false);
gstore.add('create_err', '');
/* (4) Define create handler */
gstore.add('ic_handler', function(){
/* (4.1) Trim text input */
gstore.get.create_label = gstore.get.create_label.trim();
gstore.get.create_code = gstore.get.create_code.trim().toUpperCase();
gstore.get.create_form = gstore.get.create_form.toString().trim();
gstore.get.create_vol.c = gstore.get.create_vol.c.toString().trim();
gstore.get.create_vol.td = gstore.get.create_vol.td.toString().trim();
gstore.get.create_vol.tp = gstore.get.create_vol.tp.toString().trim();
/* (5.4) Store values locally */
var form = gstore.get.create_form;
var label = gstore.get.create_label;
var code = gstore.get.create_code;
var vco = gstore.get.create_vol.c;
var vtd = gstore.get.create_vol.td;
var vtp = gstore.get.create_vol.tp;
/* (5.5) Init client-side check */
var errors = [];
/* (5.5.1) Check formation */
if( isNaN(form) ) errors.push('La formation de l\'UE est manquante');
/* (5.5.2) Check label */
if( typeof label !== 'string' || label.length < 4 )
errors.push('Le label doit comprendre faire au moins 4 caractères');
/* (5.5.3) Check code */
if( !/^[A-Z0-9]{4,20}$/.test(code) )
errors.push('Le code doit comprendre de 4 à 20 lettres/chiffres');
/* (5.5.4) Check volumes */
if( vco === '' || isNaN(vco) || vco < 0 )
errors.push('Le volume horaire de cours doit être un entier positif.');
/* (5.5.5) Check TD */
if( vtd === '' || isNaN(vtd) || vtd < 0 )
errors.push('Le volume horaire de TD doit être un entier positif.');
/* (5.5.6) Check TP */
if( vtp === '' || isNaN(vtp) || vtp < 0 )
errors.push('Le volume horaire de TP doit être un entier positif.');
/* (4.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.5.1) Création de la requête */
var rq = {
label: label,
code: code,
volumeCours: parseInt(vco), /*TODO*/
volumeTD: parseInt(vtd), /*TODO*/
volumeTP: parseInt(vtp), /*TODO*/
required: false, /*TODO*/
disabled: false /*TODO*/
};
// optional param
if( form != -1 )
rq.defaultFormation = form;
/* (4.5.2) Send request */
api.call('POST ue', rq, function(rs){
console.log(rs);
/* (4.5.2.1) Manage 'already exist' error */
if( rs.error == 29 ){
gstore.get.create_err = 'Le code est déja utilisé.';
return setTimeout(() => gstore.add('create_err', ''), 2000);
}
/* (4.5.2.2) Manage other errors */
if( rs.error !== 0 ){
gstore.get.create_err = 'erreur ('+rs.error+') Impossible de créer l\'UE';
return setTimeout(() => gstore.add('create_err', ''), 2000);
}
/* (4.5.2.3) Show that user is created */
// display all is ok
gstore.add('create_err_valid', true);
gstore.add('create_err', 'L\'UE a été créé, elle s\'affichera au prochain rechargement');
// empty fields
gstore.get.create_form = '-';
gstore.get.create_label = '';
gstore.get.create_code = '';
// 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(ue_id){
/* (1) Abort if wrong ue_id */
if( ue_id == null )
return;
/* (2.1) Find index in gstore */
var gi = gstore.get.ues.map( (data, i) => { return ( data.code && data.code == ue_id ) ? i : ''; }).join('');
/* (2.2) Exit if not found */
if( isNaN(gi) ) return;
var local = gstore.get.ues[gi];
/* (3) Show popup */
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de suppression',
content: "La suppression de l'UE <b>"+local.label+" ("+local.code+")</b> est irréversible.<br><br>Voulez-vous lea supprimer définitivement ?",
action: 'Supprimer',
type: 'invalid'
}, (popup_rs) => { popup_rs && resolve() });
/* (4) On pop-up confirmation */
})).then(function(){
return new Promise( (resolve, reject) => {
/* (4.1) Delete UE */
api.call('DELETE ue/'+ue_id, {}, function(rs){
/* (4.1.1) Abort on error */
if( rs.error !== 0 || rs.deleted !== true )
return reject(rs.error);
/* (4.1.2) Success */
resolve();
});
});
/* (5) On success */
}).then(function(){
/* remove from visible */
gstore.get.ues.splice(gi, 1);
/* (6) 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) Init edit_mode */
gstore.add('edit_i', -1);
/* (2) Initialize inputs */
gstore.add('edit_form', '-');
gstore.add('edit_label', '');
// gstore.add('edit_code', '');
gstore.add('edit_vol', { c: '', td: '', tp: ''});
/* (3) Initialize error message */
gstore.add('edit_err_valid', false);
gstore.add('edit_err', '');
/* (4) Define toggle view */
gstore.add('ie_toggle', function(ue_i){
/* (4.1) Abort if wrong ue_i */
if( ue_i == null || isNaN(ue_i) || gstore.get.ues[ue_i] == null)
return gstore.add('edit_i', -1);
/* (4.2) Toggle current value */
var ue = gstore.get.ues[ue_i];
/* (4.3) Pre-fill edit values */
gstore.get.edit_form = ue.idForm;
gstore.get.edit_label = ue.label;
gstore.get.edit_code = ue.code;
gstore.get.edit_vol.c = ue.volumeCours;
gstore.get.edit_vol.td = ue.volumeTD;
gstore.get.edit_vol.tp = ue.volumeTP;
/* (4.4) Set card to edit mode */
gstore.get.edit_i = ue_i;
});
/* (5) Confirm update */
gstore.add('ie_handler', function(ue_i){
/* (5.1) Abort if wrong ue_i */
if( ue_i == null || isNaN(ue_i) || gstore.get.ues[ue_i] == null)
return;
/* (5.2) Toggle current value */
var ue = gstore.get.ues[ue_i];
/* (5.3) Trim text input */
gstore.get.edit_label = gstore.get.edit_label.trim();
// gstore.get.edit_code = gstore.get.edit_code.toString().trim().toUpperCase();
gstore.get.edit_vol.c = gstore.get.edit_vol.c.toString().trim();
gstore.get.edit_vol.td = gstore.get.edit_vol.td.toString().trim();
gstore.get.edit_vol.tp = gstore.get.edit_vol.tp.toString().trim();
/* (5.4) Store values locally */
var form = gstore.get.edit_form;
var label = gstore.get.edit_label;
// var code = gstore.get.edit_code;
var vco = gstore.get.edit_vol.c;
var vtd = gstore.get.edit_vol.td;
var vtp = gstore.get.edit_vol.tp;
/* (5.5) Init client-side check */
var errors = [];
/* (5.5.1) Check formation */
if( isNaN(form) ) errors.push('La formation de l\'UE est manquante');
/* (5.5.2) Check label */
if( typeof label !== 'string' || label.length < 4 )
errors.push('Le label doit comprendre faire au moins 4 caractères');
/* (5.5.3) Check code */
// if( !/^[A-Z0-9]{4,20}$/.test(code) )
// errors.push('Le code doit comprendre de 4 à 20 lettres/chiffres');
/* (5.5.4) Check volumes */
if( vco === '' || isNaN(vco) || vco < 0 )
errors.push('Le volume horaire de cours doit être un entier positif.');
/* (5.5.5) Check TD */
if( vtd === '' || isNaN(vtd) || vtd < 0 )
errors.push('Le volume horaire de TD doit être un entier positif.');
/* (5.5.6) Check TP */
if( vtp === '' || isNaN(vtp) || vtp < 0 )
errors.push('Le volume horaire de TP doit être un entier positif.');
/* (5.6) Show first error only (for 2s) */
if( errors.length > 0 ){
gstore.get.edit_err = errors[0];
return setTimeout(() => gstore.add('edit_err', ''), 2000);
}
/* (5.7) Création de la requête */
var rq = {};
( label != ue.label ) && ( rq.label = label );
// ( code != ue.code ) && ( rq.code = code );
( vco != ue.volumeCours ) && ( rq.volumeCours = parseInt(vco) );
( vtd != ue.volumeTD ) && ( rq.volumeTD = parseInt(vtd) );
( vtp != ue.volumeTP ) && ( rq.volumeTP = parseInt(vtp) );
( form != ue.idForm ) && ( rq.defaultFormation = parseInt(form) );
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de modification',
content: `La modification de l'UE <b>${ue.label} (${ue.code})</b> est irréversible.<br><br>Voulez-vous la modifier ?`,
action: 'Modifier',
type: 'search'
}, (popup_rs) => { popup_rs && resolve() });
/* (5.8) On pop-up confirmation */
})).then(function(){
return new Promise( (resolve, reject) => {
/* (5.8.1) Update UE */
api.call(`PUT ue/${ue.code}`, rq, function(rs){
/* (5.8.1.1) Abort on error */
console.log(rq, rs.error != 0, rs.updated != true);
if( rs.error != 0 || rs.updated != true )
return reject(rs.error);
/* (5.8.1.2) Success */
resolve();
});
});
/* (5.9) On success */
}).then(function(){
/* (5.9.1) update VueJS element */
gstore.get.ues[ue_i].label = label;
// gstore.get.ues[ue_i].code = code;
gstore.get.ues[ue_i].idForm = parseInt(form);
gstore.get.ues[ue_i].volumeCours = parseInt(vco);
gstore.get.ues[ue_i].volumeTD = parseInt(vtd);
gstore.get.ues[ue_i].volumeTP = parseInt(vtp);
/* (5.9.2) Try to set the formation label */
var fi = gstore.get.formations.map( (data, i) => { return ( data.idForm && data.idForm == form ) ? i : ''; }).join('');
/* (5.9.3) Exit if not found */
if( isNaN(fi) )
return gstore.add('edit_i', -1);
/* (5.9.4) If not found -> set default text */
if( !gstore.get.formations.hasOwnProperty(fi) )
gstore.get.ues[ue_i].labelForm = '';
/* (5.9.5) If found -> set formation label */
else
gstore.get.ues[ue_i].labelForm = gstore.get.formations[fi].labelForm;
/* (5.9.5) Remove edit mode */
gstore.add('edit_i', -1);
/* (5.10) 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(ue_i){
/* (1) Abort if wrong ue_i */
if( ue_i == null || isNaN(ue_i) || gstore.get.ues[ue_i] == null)
return;
/* (2) Toggle current value */
var local = gstore.get.ues[ue_i];
var is_disabled = local.disabled == '1' || local.disabled === true;
var new_state = !is_disabled;
/* (3.1) Update in database */
api.call('PUT ue/'+local.code, { disabled: 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 \'activée\', erreur '+rs.error);
/* (3.1.2) Success */
gstore.get.ues[ue_i].disabled = new_state ? 1 : 0;
});
});

View File

@ -218,6 +218,7 @@
/* (2.1.1) lighter color on hover */ /* (2.1.1) lighter color on hover */
& > div.remove[data-remove], & > div.remove[data-remove],
& > div.edit[data-edit], & > div.edit[data-edit],
& > div.enabled[data-enabled],
& > div.admin[data-admin]{ & > div.admin[data-admin]{
background-color: darken(#fff, 12%); background-color: darken(#fff, 12%);
@ -232,6 +233,7 @@
/* (2.2) Design elements */ /* (2.2) Design elements */
& > div.remove[data-remove], & > div.remove[data-remove],
& > div.edit[data-edit], & > div.edit[data-edit],
& > div.enabled[data-enabled],
& > div.admin[data-admin]{ & > div.admin[data-admin]{
display: inline-block; display: inline-block;
position: absolute; position: absolute;
@ -275,6 +277,15 @@
&[data-active='1']:hover{ background-image: url('/asset/svg/admin.svg@f4a118'); } &[data-active='1']:hover{ background-image: url('/asset/svg/admin.svg@f4a118'); }
} }
/* (2.3) DISABLED switch */
&.enabled[data-enabled]{
background-image: url('/asset/svg/bell.svg@aaaaaa');
z-index: 105;
&:hover{ background-image: url('/asset/svg/bell.svg@555555'); }
&[data-active='1']{ background-image: url('/asset/svg/bell.svg@f4bd18'); }
&[data-active='1']:hover{ background-image: url('/asset/svg/bell.svg@f4a118'); }
}
} }
@ -574,6 +585,17 @@
text-transform: uppercase; text-transform: uppercase;
} }
/* (7.1-edit) Card footer input */
& > input{
width: 2.5em;
margin: 0;
padding: 0;
border: 0;
}
} }
/* (7.1-edit) Card button submit */ /* (7.1-edit) Card button submit */