diff --git a/build/api/module/historyDefault.php b/build/api/module/historyDefault.php index 50797d1..ea5c71e 100755 --- a/build/api/module/historyDefault.php +++ b/build/api/module/historyDefault.php @@ -199,6 +199,39 @@ } + + + + /* (x) Return the entry data for an history id + * + * @id_entry UID of the history entry + * + * @return data 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 ]; + + } + } diff --git a/build/generic/view/history/details/main.php b/build/generic/view/history/details/main.php index d4b2b03..3e0ba1e 100644 --- a/build/generic/view/history/details/main.php +++ b/build/generic/view/history/details/main.php @@ -9,8 +9,9 @@ class main extends i_view{ - public $id_history; + 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 .= ""; + /* (5) Svg tag */ + $RAW .= ""; - /* (2) Start CIRCLE */ - $RAW .= ""; - $RAW .= ""; + /* (6) Start CIRCLE */ + $RAW .= ""; + $RAW .= ""; - 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 .= ""; + /* (1) Default TIMELINE */ + if( $r < $rl ){ + + $RAW .= ""; + + /* (2) Last TIMELINE (can be shorter) */ + }else{ // if last line -> trace timeline until last element +1 + + $len = 100 + $lrl*800/$r_size; + $RAW .= ""; + + } + /* (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 .= ""; - $RAW .= ""; + /* (4) Draw entry circles */ + $RAW .= ""; + $RAW .= ""; - // inside icon + /* (5) Draw circle around if not current user */ + if( $this->entry['id_user'] != $entry['id_user'] ) + $RAW .= ""; + + + /* (6) Draw entry icon (action) */ $y_decal = $y - 5.5; - $RAW .= ""; + $RAW .= "\t"; } - // 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 .= ""; - $RAW .= ""; + $RAW .= ""; + $RAW .= ""; - /* (5) Close SVG tag */ + /* (2) Close SVG tag */ $RAW .= ""; diff --git a/config/modules.json b/config/modules.json index ef7873a..c34725f 100755 --- a/config/modules.json +++ b/config/modules.json @@ -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" } + } } }, diff --git a/public_html/css/container.scss b/public_html/css/container.scss index f6329dc..45e6e45 100755 --- a/public_html/css/container.scss +++ b/public_html/css/container.scss @@ -540,43 +540,83 @@ article.check-table{ /* [4] Timeline SVG =========================================================*/ -/* (1) svg circles -> set right transform-origin */ -svg > circle[class^=svg_]{ +svg.timeline{ - -webkit-transform-origin: 50% 50% 0; - transform-origin: 50% 50% 0; + /* (1) svg circles -> set right transform-origin */ + & circle.timeline{ - transition: transform .2s ease-in-out; + -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; -} + /* (2) Set cursor to pointer */ + &.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); -} + -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; - pointer-events: none; -} + } -/* (5) Make around circles a bit transparent */ -svg > circle.op_svg{ - opacity: .6; -} + &:hover + .center + .user{ // user node -/* (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; } \ No newline at end of file + -webkit-transform: scale(.8); + transform: scale(.8); + + } + + } + + /* (4) Remove center circle event */ + &.center{ + + -webkit-pointer-events: none; + pointer-events: none; + + } + + /* (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) 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; + + } +} \ No newline at end of file diff --git a/public_html/css/min/container.css b/public_html/css/min/container.css index 256e1f2..4ca8009 100644 --- a/public_html/css/min/container.css +++ b/public_html/css/min/container.css @@ -698,59 +698,106 @@ article.check-table > div > span input[type='checkbox']:checked + label[for] { /* [4] Timeline SVG =========================================================*/ -/* (1) svg circles -> set right transform-origin */ -svg > circle[class^=svg_] { +svg.timeline { + /* (1) svg circles -> set right transform-origin */ + /* (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 */ + /* (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 */ } -/* (2) Set cursor to pointer */ -svg > circle[class^=svg_]:not(.op_svg) { +svg.timeline circle.timeline.around { cursor: pointer; + /* (3) Dispatch event to next nodes */ } -/* (3) Middle circles -> scale+ on hover */ -svg > circle[class^=svg_]:not(.op_svg):hover { +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 */ \ No newline at end of file diff --git a/public_html/view/js/history.js b/public_html/view/js/history.js index 9091dbd..dacc3d4 100644 --- a/public_html/view/js/history.js +++ b/public_html/view/js/history.js @@ -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); + + +} \ No newline at end of file