Added module historyDefault/getById to check current user in history.details.view + Refactor css + added info in svg tags + javascript began management (only launch event, implementation TODO)

This commit is contained in:
xdrm-brackets 2017-11-12 13:01:29 +01:00
parent b0f9cfae1e
commit 43e626d32d
6 changed files with 296 additions and 80 deletions

View File

@ -199,6 +199,39 @@
}
/* (x) Return the entry data for an history id
*
* @id_entry<id> UID of the history entry
*
* @return data<array> Entry data
*
---------------------------------------------------------*/
public function getById($params){
extract($params);
/* (1) Get history entry data
---------------------------------------------------------*/
/* (1) Request */
$entry = new Repo('history/getById', [$id_entry]);
/* (2) Get response */
$entry = $entry->answer();
/* (3) Manage error */
if( !is_array($entry) )
return ['error' => new Error(Err::RepoError)];
/* (2) Return data
---------------------------------------------------------*/
return [ 'entry' => $entry ];
}
}

View File

@ -11,6 +11,7 @@
public $id_history;
private $timeline = [];
private $entry = [];
/* (1) Constructor
@ -23,7 +24,21 @@
---------------------------------------------------------*/
$this->id_history = $id_history;
/* (2) Get machine timeline
/* (3) Get entry data
---------------------------------------------------------*/
/* (1) Request */
$entry_req = new Request('historyDefault/getById', [ 'id_entry' => $this->id_history ]);
/* (2) Get response */
$entry_res = $entry_req->dispatch();
/* (3) On success, store entry data */
if( $entry_res->error->get() == Err::Success )
$this->entry = $entry_res->get('entry');
/* (3) Get machine timeline
---------------------------------------------------------*/
/* (1) Request */
$mac_req = new Request('historyDefault/get_timeline', [ 'id_entry' => $this->id_history ]);
@ -61,68 +76,95 @@
$cl = count($this->timeline);
/* (4) Create ranges of size 15 */
$r_size = 10;
$rl = round( $cl / $r_size );
$r_size = 10; // set the number of actions by line
$rl = floor( $cl / $r_size ); // calculate the number of whole lines
$lrl = $cl % $r_size; // calculate the number of actions in the last line
$y_range_diff = 100;
$total_height = $y_range_diff*$rl;
$total_height = ($lrl == 0 ) ? $y_range_diff*$rl : $y_range_diff*($rl+1); // total height (number of entries + last if not empty)
/* (1) Svg tag */
$RAW .= "<svg width='1000' height='$total_height' viewBox='0 0 1000 $total_height'>";
/* (5) Svg tag */
$RAW .= "<svg width='1000' height='$total_height' viewBox='0 0 1000 $total_height' class='timeline'>";
/* (2) Start CIRCLE */
$RAW .= "<circle cx='50' cy='50' r='6' fill='#edf0f5'/>";
$RAW .= "<circle cx='50' cy='50' r='4' fill='#555'/>";
/* (6) Start CIRCLE */
$RAW .= "<circle cx='50' cy='50' r='6' fill='#edf0f5' class='tstart'/>";
$RAW .= "<circle cx='50' cy='50' r='4' fill='#555' class='tstart'/>";
for( $r = 0 ; $r < $rl ; $r++ ){
for( $r = 0 ; $r <= $rl ; $r++ ){
// if last line is empty -> stop here
if( $r == $rl && $lrl == 0 )
break;
$y = $y_range_diff*$r + 50;
/* (2) Build barebone
---------------------------------------------------------*/
/* (1) Timeline LINE */
$RAW .= "<path d='m50 $y L900 $y' style='stroke-dasharray: 3px;' stroke='#444'/>";
/* (1) Default TIMELINE */
if( $r < $rl ){
$RAW .= "<path d='m50 $y L900 $y' style='stroke-dasharray: 3px;' stroke='#444' class='timeline line'/>";
/* (2) Last TIMELINE (can be shorter) */
}else{ // if last line -> trace timeline until last element +1
$len = 100 + $lrl*800/$r_size;
$RAW .= "<path d='m50 $y L$len $y' style='stroke-dasharray: 3px;' stroke='#444' class='timeline line'/>";
}
/* (3) Build each action
---------------------------------------------------------*/
for( $c = 0 ; $c < $r_size ; $c++ ){
$el_len = ( $r == $rl ) ? $lrl : $r_size ;// number of elements on the line
for( $c = 0 ; $c < $el_len ; $c++ ){
// exit if no more entry
/* (1) Display nothing if last entry and is empty */
if( !isset($this->timeline[$r*$r_size+$c]) )
break;
/* (2) Get entry data */
$entry = $this->timeline[$r*$r_size+$c];
/* (3) Get useful data */
$action_class = strtolower($entry['action_name']);
$icon_uri = '/src/static/timeline/'.$action_class.'@'.$this->get_action_color($action_class).'.svg';
$x_offset = 100 + $c*800/$r_size;
$x_img_offset = $x_offset - 5.5;
$data_user = "data-user='".$entry['id_user']."'";
$data_machine = " data-machine='".$entry['id_machine']."'";
$data_action = " data-action='".$entry['id_action']."'";
$data_time = " data-time='".date('H:i:s d/m/Y', $entry['timestamp'])."'";
$data_tags = $data_user.$data_machine.$data_action.$data_time;
// circle
$RAW .= "<circle cx='$x_offset' cy='$y' r='15' class='svg_$action_class op_svg'/>";
$RAW .= "<circle cx='$x_offset' cy='$y' r='12' class='svg_$action_class'/>";
/* (4) Draw entry circles */
$RAW .= "<circle cx='$x_offset' cy='$y' r='15' class='timeline around $action_class' $data_tags />";
$RAW .= "<circle cx='$x_offset' cy='$y' r='12' class='timeline center $action_class' />";
// inside icon
/* (5) Draw circle around if not current user */
if( $this->entry['id_user'] != $entry['id_user'] )
$RAW .= "<circle cx='$x_offset' cy='$y' r='18' class='timeline user' />";
/* (6) Draw entry icon (action) */
$y_decal = $y - 5.5;
$RAW .= "<image x='$x_img_offset' y='$y_decal' width='12' height='12' xlink:href='$icon_uri'/>";
$RAW .= "\t<image x='$x_img_offset' y='$y_decal' width='12' height='12' xlink:href='$icon_uri' class='icon' />";
}
// exit if no more entry
if( !isset($this->timeline[$r*$r_size]) )
break;
}
/* (4) Stop CIRCLE */
/* (4) Close SVG
---------------------------------------------------------*/
/* (1) Stop CIRCLE */
$x_offset = 100 + $c*800/$r_size;
$RAW .= "<circle cx='$x_offset' cy='$y' r='6' fill='#edf0f5'/>";
$RAW .= "<circle cx='$x_offset' cy='$y' r='4' fill='#555'/>";
$RAW .= "<circle cx='$x_offset' cy='$y' r='6' fill='#edf0f5' class='tstop' />";
$RAW .= "<circle cx='$x_offset' cy='$y' r='4' fill='#555' class='tstop' />";
/* (5) Close SVG tag */
/* (2) Close SVG tag */
$RAW .= "</svg>";

View File

@ -616,6 +616,17 @@
"output": {
"timeline": { "description": "Données de la timeline.", "type": "array" }
}
},
"getById": {
"description": "Retourne les données associées à une entrée historique.",
"permissions": [["admin"]],
"parameters": {
"id_entry": { "description": "UID de l'entrée historique", "type": "id" }
},
"output": {
"entry": { "description": "Données de l'entrée.", "type": "array" }
}
}
},

View File

@ -540,43 +540,83 @@ article.check-table{
/* [4] Timeline SVG
=========================================================*/
svg.timeline{
/* (1) svg circles -> set right transform-origin */
svg > circle[class^=svg_]{
& circle.timeline{
-webkit-transform-origin: 50% 50% 0;
transform-origin: 50% 50% 0;
transition: transform .2s ease-in-out;
}
/* (2) Set cursor to pointer */
svg > circle[class^=svg_]:not(.op_svg){
&.around{
cursor: pointer;
}
/* (3) Middle circles -> scale+ on hover */
svg > circle[class^=svg_]:not(.op_svg):hover{
/* (3) Dispatch event to next nodes */
&:hover + .center{ // center node
-webkit-transform: scale(1.2);
transform: scale(1.2);
}
/* (4) Avoid icons inside middle circles to block :hover */
svg > circle[class^=svg_]:not(.op_svg) + image{
-webkit-pionter-events: none;
&:hover + .center + .user{ // user node
-webkit-transform: scale(.8);
transform: scale(.8);
}
}
/* (4) Remove center circle event */
&.center{
-webkit-pointer-events: none;
pointer-events: none;
}
/* (5) Make around circles a bit transparent */
svg > circle.op_svg{
opacity: .6;
/* (5) Set colors according to action type */
&.around, &.center{
&.start{ fill: #2cde8b; }
&.stop{ fill: #3a3a3a; }
&.lock{ fill: #e04343; }
&.unlock{ fill: #af1c1c; }
&.signal{ fill: #3258d8; }
&.unsignal{ fill: #2041ab; }
}
/* (6) Set circle colors according to action type */
svg > circle.svg_start{ fill: #2cde8b; }
svg > circle.svg_stop{ fill: #3a3a3a; }
svg > circle.svg_lock{ fill: #e04343; }
svg > circle.svg_unlock{ fill: #af1c1c; }
svg > circle.svg_signal{ fill: #3258d8; }
svg > circle.svg_unsignal{ fill: #2041ab; }
/* (6) Make around circles a bit transparent */
&.around{ opacity: .6; }
/* (7) User circles */
&.user{
fill: none;
stroke-width: 1.5px;
stroke: #777;
stroke-opacity: .3;
}
/* (8) Set user colors according to action type */
&.center.start + .user{ stroke: #2cde8b; }
&.center.stop + .user{ stroke: #3a3a3a; }
&.center.lock + .user{ stroke: #e04343; }
&.center.unlock + .user{ stroke: #af1c1c; }
&.center.signal + .user{ stroke: #3258d8; }
&.center.unsignal + .user{ stroke: #2041ab; }
}
/* (9) Avoid icons inside center circles to block :hover */
image{
-webkit-pointer-events: none;
pointer-events: none;
}
}

View File

@ -698,59 +698,106 @@ article.check-table > div > span input[type='checkbox']:checked + label[for] {
/* [4] Timeline SVG
=========================================================*/
svg.timeline {
/* (1) svg circles -> set right transform-origin */
svg > circle[class^=svg_] {
/* (9) Avoid icons inside center circles to block :hover */
}
svg.timeline circle.timeline {
-webkit-transform-origin: 50% 50% 0;
transform-origin: 50% 50% 0;
transition: transform .2s ease-in-out;
}
/* (2) Set cursor to pointer */
svg > circle[class^=svg_]:not(.op_svg) {
cursor: pointer;
/* (4) Remove center circle event */
/* (5) Set colors according to action type */
/* (6) Make around circles a bit transparent */
/* (7) User circles */
/* (8) Set user colors according to action type */
}
/* (3) Middle circles -> scale+ on hover */
svg > circle[class^=svg_]:not(.op_svg):hover {
svg.timeline circle.timeline.around {
cursor: pointer;
/* (3) Dispatch event to next nodes */
}
svg.timeline circle.timeline.around:hover + .center {
-webkit-transform: scale(1.2);
transform: scale(1.2);
}
/* (4) Avoid icons inside middle circles to block :hover */
svg > circle[class^=svg_]:not(.op_svg) + image {
-webkit-pionter-events: none;
svg.timeline circle.timeline.around:hover + .center + .user {
-webkit-transform: scale(0.8);
transform: scale(0.8);
}
svg.timeline circle.timeline.center {
-webkit-pointer-events: none;
pointer-events: none;
}
/* (5) Make around circles a bit transparent */
svg > circle.op_svg {
opacity: .6;
}
/* (6) Set circle colors according to action type */
svg > circle.svg_start {
svg.timeline circle.timeline.around.start, svg.timeline circle.timeline.center.start {
fill: #2cde8b;
}
svg > circle.svg_stop {
svg.timeline circle.timeline.around.stop, svg.timeline circle.timeline.center.stop {
fill: #3a3a3a;
}
svg > circle.svg_lock {
svg.timeline circle.timeline.around.lock, svg.timeline circle.timeline.center.lock {
fill: #e04343;
}
svg > circle.svg_unlock {
svg.timeline circle.timeline.around.unlock, svg.timeline circle.timeline.center.unlock {
fill: #af1c1c;
}
svg > circle.svg_signal {
svg.timeline circle.timeline.around.signal, svg.timeline circle.timeline.center.signal {
fill: #3258d8;
}
svg > circle.svg_unsignal {
svg.timeline circle.timeline.around.unsignal, svg.timeline circle.timeline.center.unsignal {
fill: #2041ab;
}
svg.timeline circle.timeline.around {
opacity: .6;
}
svg.timeline circle.timeline.user {
fill: none;
stroke-width: 1.5px;
stroke: #777;
stroke-opacity: .3;
}
svg.timeline circle.timeline.center.start + .user {
stroke: #2cde8b;
}
svg.timeline circle.timeline.center.stop + .user {
stroke: #3a3a3a;
}
svg.timeline circle.timeline.center.lock + .user {
stroke: #e04343;
}
svg.timeline circle.timeline.center.unlock + .user {
stroke: #af1c1c;
}
svg.timeline circle.timeline.center.signal + .user {
stroke: #3258d8;
}
svg.timeline circle.timeline.center.unsignal + .user {
stroke: #2041ab;
}
svg.timeline image {
-webkit-pointer-events: none;
pointer-events: none;
}
/*# sourceMappingURL= container.css.map */

View File

@ -151,3 +151,46 @@ if( section.archive.element != null ){
}
/* GESTION DE L'AFFICHAGE DES DETAILS
*
*/
if( section.details.element != null ){
/* (1) Get useful DOM Elements
---------------------------------------------------------*/
section.details.event = {
// svg parent
parent: {
text: section.details.text+' svg ',
element: document.querySelector( section.details.text+' svg ' )
},
// each node
text: section.details.text+' svg circle.around ',
element: document.querySelectorAll( section.details.text+' svg circle.around ' )
};
/* (2) Function: show infobox on click on event
---------------------------------------------------------*/
section.details.event.handler = function(target){
console.log('show infobox on element', target);
};
/* (n) Trigger event */
section.details.event.parent.element.addEventListener('click', function(e){
// {1} Trigger function only if element is an 'around circle' //
if( e.target.nodeName && e.target.getData('user') && e.target.getData('machine') && e.target.getData('action') && e.target.getData('time') )
section.details.event.handler(e.target);
}, false);
}