diff options
Diffstat (limited to 'ui/imports/ui')
18 files changed, 435 insertions, 103 deletions
diff --git a/ui/imports/ui/components/alarm-icons/alarm-icons.html b/ui/imports/ui/components/alarm-icons/alarm-icons.html index d584990..e59414e 100644 --- a/ui/imports/ui/components/alarm-icons/alarm-icons.html +++ b/ui/imports/ui/components/alarm-icons/alarm-icons.html @@ -15,34 +15,37 @@ <div class="dropdown"> <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle" - data-badge="{{countOf 'messages/count?level=info'}}" + data-badge="{{ countOf (msgCounterName 'info') }}" type="button" id="dropdownMenu1" data-toggle="modal" data-target="#messagesModalGlobal" data-message-level="info" + title="Info messages" >notifications</div> </div> <div class="dropdown"> <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle" - data-badge="{{countOf 'messages/count?level=warning'}}" + data-badge="{{ countOf (msgCounterName 'warning') }}" type="button" id="dropdownMenu1" data-toggle="modal" data-target="#messagesModalGlobal" data-message-level="warning" + title="Warning messages" >warning</div> </div> <div class="dropdown"> <div class="material-icons mdl-badge mdl-badge--overlap dropdown-toggle" - data-badge="{{countOf 'messages/count?level=error'}}" + data-badge="{{ countOf (msgCounterName 'error') }}" type="button" id="dropdownMenu1" data-toggle="modal" data-target="#messagesModalGlobal" data-message-level="error" + title="Error messages" >error</div> </div> @@ -69,6 +72,10 @@ <a href="{{pathFor route='user-list' query=''}}">Users</a> </li> {{/if }} + + <li class="dropdown-header"> + <a href="{{pathFor route='configuration' query=''}}" >Configuration</a> + </li> </ul> </div> diff --git a/ui/imports/ui/components/alarm-icons/alarm-icons.js b/ui/imports/ui/components/alarm-icons/alarm-icons.js index 5c7af31..e86f8d8 100644 --- a/ui/imports/ui/components/alarm-icons/alarm-icons.js +++ b/ui/imports/ui/components/alarm-icons/alarm-icons.js @@ -13,6 +13,9 @@ import '/imports/ui/components/breadcrumb/breadcrumb'; import { Messages } from '/imports/api/messages/messages'; import { Roles } from 'meteor/alanning:roles'; +import { ReactiveDict } from 'meteor/reactive-dict'; + +import { Configurations } from '/imports/api/configurations/configurations'; import './alarm-icons.html'; @@ -23,10 +26,24 @@ import './alarm-icons.html'; Template.alarmIcons.onCreated(function () { let instance = this; + instance.state = new ReactiveDict(); + instance.state.setDefault({ + msgsViewBackDelta: 1 + }); + instance.autorun(function () { - instance.subscribe('messages/count?level', 'info'); - instance.subscribe('messages/count?level', 'warning'); - instance.subscribe('messages/count?level', 'error'); + instance.subscribe('configurations?user'); + Configurations.find({user_id: Meteor.userId()}).forEach((conf) => { + instance.state.set('msgsViewBackDelta', conf.messages_view_backward_delta); + }); + }); + + instance.autorun(function () { + let msgsViewBackDelta = instance.state.get('msgsViewBackDelta'); + + instance.subscribe('messages/count?backDelta&level', msgsViewBackDelta, 'info'); + instance.subscribe('messages/count?backDelta&level', msgsViewBackDelta, 'warning'); + instance.subscribe('messages/count?backDelta&level', msgsViewBackDelta, 'error'); }); }); @@ -50,4 +67,12 @@ Template.alarmIcons.helpers({ errorsCount: function(){ return Messages.find({level:'error'}).count(); }, + + msgCounterName: function (level) { + let instance = Template.instance(); + let msgsViewBackDelta = instance.state.get('msgsViewBackDelta'); + let counterName = `messages/count?backDelta=${msgsViewBackDelta}&level=${level}`; + + return counterName; + } }); diff --git a/ui/imports/ui/components/clique-type/clique-type.js b/ui/imports/ui/components/clique-type/clique-type.js index ae50b94..9b21442 100644 --- a/ui/imports/ui/components/clique-type/clique-type.js +++ b/ui/imports/ui/components/clique-type/clique-type.js @@ -130,7 +130,7 @@ Template.CliqueType.helpers({ objectTypesList: function () { return R.ifElse(R.isNil, R.always([]), R.prop('data') - )(Constants.findOne({ name: 'object_types_for_links' })); + )(Constants.findOne({ name: 'object_types_for_links' })); }, linkTypesList: function () { @@ -280,7 +280,7 @@ function submitItem( focal_point_type, link_types, name - ) { +) { let action = instance.state.get('action'); @@ -316,7 +316,7 @@ function submitItem( break; default: - // todo + // todo break; } } diff --git a/ui/imports/ui/components/configuration/configuration.html b/ui/imports/ui/components/configuration/configuration.html new file mode 100644 index 0000000..f381453 --- /dev/null +++ b/ui/imports/ui/components/configuration/configuration.html @@ -0,0 +1,31 @@ +<template name="Configuration"> +<div class="os-configuration cards white"> + <h3>Configurations</h3> + <form> + <div class="cl-field-group"> + <label class="cl-field-label">Message view backward delta</label> + <input name="msgsViewBackDelta" + value="{{ getModelField 'messages_view_backward_delta' }}" + class="cl-msgs-view-back-delta cl-input" + type="number" + placeholder="" /> + <div class="cl-field-desc">Backward duration of {{ durationHumanize (getModelField 'messages_view_backward_delta') }} from current date (miliseconds)</div> + </div> + + <button type="button" + class="js-submit-button mdl-button mdl-js-button mdl-button--raised + mdl-js-ripple-effect mdl-button--colored" + >Save</button> + </form> + + {{#if (getState 'message') }} + <div class="js-message-panel alert + {{#if isActionError}}alert-danger{{/if}} + {{#if isActionSuccess}}alert-success{{/if}}" + role="alert"> + {{ getState 'message' }} + </div> + {{/if }} + +</div> +</template> diff --git a/ui/imports/ui/components/configuration/configuration.js b/ui/imports/ui/components/configuration/configuration.js new file mode 100644 index 0000000..493cc36 --- /dev/null +++ b/ui/imports/ui/components/configuration/configuration.js @@ -0,0 +1,114 @@ +/* + * Template Component: Configuration + */ + +//import { Meteor } from 'meteor/meteor'; +import { Template } from 'meteor/templating'; +import { ReactiveDict } from 'meteor/reactive-dict'; +//import { SimpleSchema } from 'meteor/aldeed:simple-schema'; +import * as R from 'ramda'; + +import { save } from '/imports/api/configurations/methods'; +import { Configurations } from '/imports/api/configurations/configurations'; + +import './configuration.html'; + +/* + * Lifecycles + */ + +Template.Configuration.onCreated(function() { + let instance = this; + instance.state = new ReactiveDict(); + instance.state.setDefault({ + model: Configurations.schema.clean({}), + actionResult: 'none', + message: null, + }); + + /* + instance.autorun(function () { + let data = Template.currentData(); + + new SimpleSchema({ + }).validate(data); + }); + */ + + instance.autorun(function () { + instance.subscribe('configurations?user'); + Configurations.find({user_id: Meteor.userId()}).forEach((conf) => { + instance.state.set('model', conf); + }); + }); +}); + +/* +Template.Configuration.rendered = function() { +}; +*/ + +/* + * Events + */ + +Template.Configuration.events({ + 'click .js-submit-button': function (event, instance) { + event.preventDefault(); + let msgsViewBackDelta = instance.$('.cl-msgs-view-back-delta')[0].value; + saveForm(instance, msgsViewBackDelta); + } +}); + +/* + * Helpers + */ + +Template.Configuration.helpers({ + getModelField: function (fieldName) { + let instance = Template.instance(); + return R.path([fieldName], instance.state.get('model')); + }, + + getState: function (key) { + let instance = Template.instance(); + return instance.state.get(key); + }, + + isActionError: function () { + let instance = Template.instance(); + return instance.state.get('actionResult') === 'error'; + }, + + isActionSuccess: function () { + let instance = Template.instance(); + return instance.state.get('actionResult') === 'success'; + }, + + durationHumanize: function (duration) { + return moment.duration(duration).humanize(); + } +}); // end: helpers + +function saveForm(instance, msgsViewBackDelta) { + instance.state.set('actionResult', 'none'); + instance.state.set('message', null); + + save.call({ + messages_view_backward_delta: msgsViewBackDelta + }, (error) => { + if (error) { + instance.state.set('actionResult', 'error'); + if (typeof error === 'string') { + instance.state.set('message', error); + } else { + instance.state.set('message', error.message); + } + + return; + } + + instance.state.set('actionResult', 'success'); + instance.state.set('message', 'record has been updated succesfuly'); + }); +} diff --git a/ui/imports/ui/components/configuration/configuration.styl b/ui/imports/ui/components/configuration/configuration.styl new file mode 100644 index 0000000..b8be180 --- /dev/null +++ b/ui/imports/ui/components/configuration/configuration.styl @@ -0,0 +1,38 @@ +/* Set the component style here */ +// "Configuration" +.os-configuration + display: flex; + flex-flow: column nowrap; + margin: 20px; + + .cl-field-group + display: flex; + flex-flow: row nowrap; + align-items: center; + padding: 5px 0; + + .cl-field-label + width: 120px; + margin: 0 5px; + + >.cl-input + display: block; + width: 100%; + min-height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + width: 400px; + margin: 0 5px; + + .cl-field-desc + margin: 0 5px; + + .js-message-panel + margin-top: 20px; diff --git a/ui/imports/ui/components/dashboard/dashboard.js b/ui/imports/ui/components/dashboard/dashboard.js index 85cede8..6fdc184 100644 --- a/ui/imports/ui/components/dashboard/dashboard.js +++ b/ui/imports/ui/components/dashboard/dashboard.js @@ -13,8 +13,8 @@ //import * as R from 'ramda'; import * as _ from 'lodash'; import { Environments } from '/imports/api/environments/environments'; -import { //Messages, - calcIconForMessageLevel, lastMessageTimestamp, calcColorClassForMessagesInfoBox +import { //Messages, + calcIconForMessageLevel, lastMessageTimestamp, calcColorClassForMessagesInfoBox } from '/imports/api/messages/messages'; import { Template } from 'meteor/templating'; import { Inventory } from '/imports/api/inventories/inventories'; @@ -27,7 +27,7 @@ import { setMainAppSelectedEnvironment } from '/imports/ui/actions/main-app.acti import '/imports/ui/components/messages-info-box/messages-info-box'; import '/imports/ui/components/environment-box/environment-box'; -import './dashboard.html'; +import './dashboard.html'; /* * Lifecycle methods @@ -212,10 +212,10 @@ Template.Dashboard.helpers({ icon: calcIconForMessageLevel(boxDef.level), colorClass: calcColorClassForMessagesInfoBox(boxDef.level), onMoreDetailsReq: function () { - $('#messagesModalGlobal').modal('show', { + $('#messagesModalGlobal').modal('show', { dataset: { messageLevel: boxDef.level, - } + } }); } }; @@ -223,10 +223,10 @@ Template.Dashboard.helpers({ argsEnvBox: function ( environmentName, - regionsCount, - regions, - projectsCount, - projects, + regionsCount, + regions, + projectsCount, + projects, instancesCount, vservicesCount, vconnectorsCount, diff --git a/ui/imports/ui/components/environment-dashboard/environment-dashboard.js b/ui/imports/ui/components/environment-dashboard/environment-dashboard.js index 433096e..82fbb52 100644 --- a/ui/imports/ui/components/environment-dashboard/environment-dashboard.js +++ b/ui/imports/ui/components/environment-dashboard/environment-dashboard.js @@ -26,6 +26,7 @@ import { calcIconForMessageLevel, lastMessageTimestamp, calcColorClassForMessage import { Counts } from 'meteor/tmeasday:publish-counts'; import { Roles } from 'meteor/alanning:roles'; //import { idToStr } from '/imports/lib/utilities'; +import { Counter } from 'meteor/natestrauser:publish-performant-counts'; import '/imports/ui/components/data-cubic/data-cubic'; import '/imports/ui/components/icon/icon'; @@ -123,9 +124,10 @@ Template.EnvironmentDashboard.onCreated(function() { instance.subscribe('inventory?env+type', env.name, 'vconnector'); instance.subscribe('inventory?env+type', env.name, 'project'); instance.subscribe('inventory?env+type', env.name, 'region'); - instance.subscribe('messages?env+level', env.name, 'info'); - instance.subscribe('messages?env+level', env.name, 'warning'); - instance.subscribe('messages?env+level', env.name, 'error'); + + instance.subscribe('messages/count?level&env', 'info', env.name); + instance.subscribe('messages/count?level&env', 'warning', env.name); + instance.subscribe('messages/count?level&env', 'error', env.name); let vConnectorCounterName = 'inventory?env+type!counter?env=' + env.name + '&type=' + 'vconnector'; @@ -316,8 +318,11 @@ Template.EnvironmentDashboard.helpers({ }; } - let count = Counts.get('messages?env+level!counter?env=' + - envName + '&level=' + boxDef.level); + let counterName = `messages/count?level=${boxDef.level}&env=${envName}`; + let count = Counter.get(counterName); + + //let count = Counts.get('messages?env+level!counter?env=' + + // envName + '&level=' + boxDef.level); let title = _.capitalize(boxDef.level); diff --git a/ui/imports/ui/components/environment/environment.js b/ui/imports/ui/components/environment/environment.js index 83574f2..1f0e723 100644 --- a/ui/imports/ui/components/environment/environment.js +++ b/ui/imports/ui/components/environment/environment.js @@ -157,6 +157,7 @@ Template.Environment.onCreated(function () { new SimpleSchema({ _id: _idFieldDef, selectedNodeId: R.assoc('optional', true, _idFieldDef), + refresh: { type: String, optional: true }, }).validate(data); store.dispatch(setEnvEnvId(data._id)); diff --git a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.html b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.html index 53537ca..4ce9787 100644 --- a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.html +++ b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.html @@ -9,8 +9,8 @@ ######################################################################################## --> <template name="GraphTooltipWindow"> - <div class="os-graph-tooltip-window {{#if show}}cl-visible{{/if}}" - style="top: {{ top }}px; left: {{ left }}px;"> +<div class="os-graph-tooltip-window {{#if show}}cl-visible{{/if}}" + style="top: {{ top }}px; left: {{ left }}px;"> <div class="sm-label"><u>{{ label }}</u></div> <div class="sm-title">{{{ title }}}</div> </div> diff --git a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.js b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.js index 48b1903..dbeb55c 100644 --- a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.js +++ b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.js @@ -14,6 +14,8 @@ import { Template } from 'meteor/templating'; //import { ReactiveDict } from 'meteor/reactive-dict'; import { SimpleSchema } from 'meteor/aldeed:simple-schema'; +import { store } from '/imports/ui/store/store'; +import { closeGraphTooltipWindow } from '/imports/ui/actions/graph-tooltip-window.actions'; import './graph-tooltip-window.html'; @@ -45,6 +47,23 @@ Template.GraphTooltipWindow.rendered = function() { */ Template.GraphTooltipWindow.events({ + 'mouseout .os-graph-tooltip-window': function(_e, _instance) { + /* + if (!instance.data.show) { return; } + + e.preventDefault(); + e.stopPropagation(); + store.dispatch(closeGraphTooltipWindow()); + */ + }, + + 'click .os-graph-tooltip-window': function(e, instance) { + if (!instance.data.show) { return; } + + e.preventDefault(); + e.stopPropagation(); + store.dispatch(closeGraphTooltipWindow()); + }, }); /* diff --git a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.styl b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.styl index ec94023..1bbb9d5 100644 --- a/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.styl +++ b/ui/imports/ui/components/graph-tooltip-window/graph-tooltip-window.styl @@ -3,23 +3,26 @@ .os-graph-tooltip-window visibility: hidden; + max-height: 300px; + overflow: auto; position: absolute; text-align: left; opacity: 0 - font: normal 18px sans-serif !important; + font: normal 16px sans-serif; /* width: 60px; */ /* height: 28px; */ padding: 20px; - font: 16px sans-serif; background: dk-gray1; color white border: 2px solid stark-blue - pointer-events: none; + // pointer-events: none; transition: visibility 0.5s, opacity 0.5s linear + .sm-label + font-weight: bold; + .os-graph-tooltip-window.cl-visible visibility: visible opacity: 0.9 transition: visibility 0.2s, opacity 0.2s linear - diff --git a/ui/imports/ui/components/index.styl b/ui/imports/ui/components/index.styl index 2b2b2d3..877d68b 100644 --- a/ui/imports/ui/components/index.styl +++ b/ui/imports/ui/components/index.styl @@ -56,3 +56,4 @@ @import 'network-graph-manager/*'; @import 'network-graph/*'; @import 'environment-box/*'; +@import 'configuration/*'; diff --git a/ui/imports/ui/components/messages-modal/messages-modal.js b/ui/imports/ui/components/messages-modal/messages-modal.js index 59a6ec7..713e8e9 100644 --- a/ui/imports/ui/components/messages-modal/messages-modal.js +++ b/ui/imports/ui/components/messages-modal/messages-modal.js @@ -7,10 +7,10 @@ // http://www.apache.org/licenses/LICENSE-2.0 / ///////////////////////////////////////////////////////////////////////////////////////// /* - * Template Component: MessagesModal + * Template Component: MessagesModal */ - -//import { Meteor } from 'meteor/meteor'; + +//import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; import { ReactiveDict } from 'meteor/reactive-dict'; import { Counter } from 'meteor/natestrauser:publish-performant-counts'; @@ -18,15 +18,15 @@ import * as R from 'ramda'; //import { Messages } from '/imports/api/messages/messages'; import { Environments } from '/imports/api/environments/environments'; import { idToStr } from '/imports/lib/utilities'; - + import '/imports/ui/components/pager/pager'; -import './messages-modal.html'; - -/* +import './messages-modal.html'; + +/* * Lifecycles - */ - + */ + Template.MessagesModal.onCreated(function() { let instance = this; instance.state = new ReactiveDict(); @@ -41,7 +41,7 @@ Template.MessagesModal.onCreated(function() { }); instance.autorun(function () { - + //let amountPerPage = instance.state.get('amountPerPage'); //let page = instance.state.get('page'); let envName = instance.state.get('envName'); @@ -77,11 +77,11 @@ Template.MessagesModal.onCreated(function() { instance.state.set('messages', res); }); }); -}); +}); /* Template.MessagesModal.rendered = function() { -}; +}; */ /* @@ -107,9 +107,9 @@ Template.MessagesModal.events({ ], { wait: false }, function (err, resp) { - if (err) { + if (err) { console.error(R.toString(err)); - return; + return; } if (R.isNil(resp.node)) { @@ -117,12 +117,12 @@ Template.MessagesModal.events({ return; } - Router.go('environment', { - _id: idToStr(environment._id) - }, { + Router.go('environment', { + _id: idToStr(environment._id) + }, { query: { selectedNodeId: idToStr(resp.node._id) - } + } }); instance.$('#messagesModalGlobal').modal('hide'); @@ -131,12 +131,12 @@ Template.MessagesModal.events({ } }); - -/* + +/* * Helpers */ -Template.MessagesModal.helpers({ +Template.MessagesModal.helpers({ iconType: function () { let instance = Template.instance(); return instance.state.get('iconType'); @@ -186,17 +186,17 @@ Template.MessagesModal.helpers({ return { disableNext: currentPage * amountPerPage > totalMessages, disablePrev: currentPage == 1, - totalPages: totalPages, + totalPages: totalPages, currentPage: currentPage, onReqNext: function () { console.log('next'); let page = (currentPage * amountPerPage > totalMessages) ? currentPage : currentPage + 1; - instance.state.set('page', page); + instance.state.set('page', page); }, onReqPrev: function () { console.log('prev'); let page = (currentPage == 1) ? currentPage : currentPage - 1; - instance.state.set('page', page); + instance.state.set('page', page); }, onReqFirst: function () { console.log('req first'); @@ -209,9 +209,9 @@ Template.MessagesModal.helpers({ onReqPage: function (pageNumber) { console.log('req page'); let page; - if (pageNumber <= 1) { - page = 1; - } else if (pageNumber > Math.ceil(totalMessages / amountPerPage)) { + if (pageNumber <= 1) { + page = 1; + } else if (pageNumber > Math.ceil(totalMessages / amountPerPage)) { page = totalPages; } else { page = pageNumber; diff --git a/ui/imports/ui/components/network-graph-manager/network-graph-manager.js b/ui/imports/ui/components/network-graph-manager/network-graph-manager.js index 16637ea..ed556c0 100644 --- a/ui/imports/ui/components/network-graph-manager/network-graph-manager.js +++ b/ui/imports/ui/components/network-graph-manager/network-graph-manager.js @@ -37,7 +37,8 @@ Template.NetworkGraphManager.onCreated(function() { links: [], nodes: [], groups: [], - } + }, + itemOfInterest: null }; instance.autorun(function () { @@ -95,6 +96,13 @@ Template.NetworkGraphManager.helpers({ return; } + if (instance.simpleState.itemOfInterest === nodeId) { + instance.simpleState.itemOfInterest = null; + return; + } + + instance.simpleState.itemOfInterest = nodeId; + Meteor.apply('inventoryFindNode?DataAndAttrs', [ nodeId ], { wait: false }, function (err, res) { if (err) { @@ -103,11 +111,11 @@ Template.NetworkGraphManager.helpers({ } store.dispatch( - activateGraphTooltipWindow(res.nodeName, res.attributes, x, y)); + activateGraphTooltipWindow(res.nodeName, res.attributes, x - 30, y - 10)); }); }, onNodeOut: function (_nodeId) { - store.dispatch(closeGraphTooltipWindow()); + //store.dispatch(closeGraphTooltipWindow()); }, onNodeClick: function (_nodeId) { }, @@ -118,6 +126,33 @@ Template.NetworkGraphManager.helpers({ onDragEnd: function () { isDragging = false; }, + onGroupOver: function () { + instance.simpleState.itemOfInterest = null; + store.dispatch(closeGraphTooltipWindow()); + }, + onLinkOver: function (linkId, x, y) { + if (isDragging) { + return; + } + + if (instance.simpleState.itemOfInterest === linkId) { + instance.simpleState.itemOfInterest = null; + return; + } + + instance.simpleState.itemOfInterest = linkId; + + Meteor.apply('linksFind?DataAndAttrs', [ linkId ], + { wait: false }, function (err, res) { + if (err) { + console.error(`error fetching attrs for link for showing: ${R.toString(err)}`); + return; + } + + store.dispatch( + activateGraphTooltipWindow(res.linkName, res.attributes, x - 30, y - 10)); + }); + }, }; }, @@ -180,7 +215,11 @@ function addLinkToGraph(link, graphData) { sourceId: link.source, targetId: link.target, label: link.link_name, - _osid: link._id + _osid: link._id, + _osmeta: { + status: link.status, + linkId: link._id + } }; let links = R.unionWith(R.eqBy(R.prop('_osid')), graphData.links, [newLink]); @@ -215,6 +254,7 @@ function addNodeToGraph(node, graphData) { _osmeta: { type: node.type, nodeId: node._id, + status: node.status, }, width: 60, height: 40, diff --git a/ui/imports/ui/components/network-graph/network-graph.js b/ui/imports/ui/components/network-graph/network-graph.js index 04a33b0..68b3a57 100644 --- a/ui/imports/ui/components/network-graph/network-graph.js +++ b/ui/imports/ui/components/network-graph/network-graph.js @@ -9,6 +9,7 @@ import { SimpleSchema } from 'meteor/aldeed:simple-schema'; import * as R from 'ramda'; import * as cola from 'webcola'; import { imagesForNodeType, defaultNodeTypeImage } from '/imports/lib/images-for-node-type'; +import * as _ from 'lodash'; import './network-graph.html'; @@ -37,6 +38,8 @@ Template.NetworkGraph.onCreated(function() { onNodeClick: { type: Function, optional: true }, onDragStart: { type: Function, optional: true }, onDragEnd: { type: Function, optional: true }, + onGroupOver: { type: Function, optional: true }, + onLinkOver: { type: Function, optional: true }, }).validate(data); instance.simpleState.graphData = data.graphData; @@ -46,6 +49,8 @@ Template.NetworkGraph.onCreated(function() { instance.onNodeClick = R.defaultTo(() => {}, data.onNodeClick); instance.onDragStart = R.defaultTo(() => {}, data.onDragStart); instance.onDragEnd = R.defaultTo(() => {}, data.onDragEnd); + instance.onGroupOver = R.defaultTo(() => {}, data.onGroupOver); + instance.onLinkOver = R.defaultTo(() => {}, data.onLinkOver); }); }); @@ -66,7 +71,9 @@ Template.NetworkGraph.rendered = function() { instance.onNodeOut, instance.onNodeClick, instance.onDragStart, - instance.onDragEnd + instance.onDragEnd, + instance.onGroupOver, + instance.onLinkOver ); }); }; @@ -129,7 +136,9 @@ function renderGraph( onNodeOut, onNodeClick, onDragStart, - onDragEnd + onDragEnd, + onGroupOver, + onLinkOver ) { let force = genForceCola(cola, d3, w, h); @@ -167,10 +176,15 @@ function renderGraph( nodesEl, linksEl, drag, zoom, config, - onNodeOver, onNodeOut, onNodeClick); + onNodeOver, + onNodeOut, + onNodeClick, + onGroupOver, + onLinkOver + ); } - // d3.select(window).on('resize', resize); +// d3.select(window).on('resize', resize); function genSvg(d3, mainElement) { let svg = d3.select(mainElement).append('svg'); @@ -183,17 +197,24 @@ function genSvg(d3, mainElement) { return svg; } -function genSvgLinks(g, links, nominal_stroke, default_link_color, initialLinkLabelsFontSize) { +function genSvgLinks( + g, + links, + nominal_stroke, + default_link_color, + initialLinkLabelsFontSize, + onLinkOver +) { let svgLinks = g.selectAll('.link-group') .data(links, (d) => d._osid); let svgLinksEnter = svgLinks .enter() - .append('g') - .attr('class', 'link-group') - .attr('data-link-id', function (d) { - return d._osid; - }) + .append('g') + .attr('class', 'link-group') + .attr('data-link-id', function (d) { + return d._osid; + }) ; //let svgLinksExit = @@ -205,9 +226,24 @@ function genSvgLinks(g, links, nominal_stroke, default_link_color, initialLinkLa .attr('class', 'link-line') .style('stroke-width', nominal_stroke) .style('stroke', - function(_d) { - return default_link_color; - }); + function(d) { + let status = R.defaultTo('', R.path(['_osmeta', 'status'], d)); + status = _.toLower(status); + switch(status) { + case 'ok': + return 'green'; + case 'warning': + return 'orange'; + case 'error': + return 'red'; + default: + return default_link_color; + } + }) + .on('mouseover', function (d) { + onLinkOver(d._osmeta.linkId, d3.event.pageX, d3.event.pageY); + }) + ; let svgLinkLabels = svgLinksEnter .append('text') @@ -232,9 +268,9 @@ function genSvgNodes(g, nodes, drag, onNodeOver, onNodeOut, onNodeClick, onGroup let svgNodesEnter = svgNodes .enter() .append('g') - .attr('class', 'node') - .attr('data-node-id', (d) => d._osid) - .call(drag); + .attr('class', 'node') + .attr('data-node-id', (d) => d._osid) + .call(drag); //let svgNodesExit = svgNodes @@ -244,7 +280,9 @@ function genSvgNodes(g, nodes, drag, onNodeOver, onNodeOut, onNodeClick, onGroup let svgImages = svgNodesEnter.append('image') .attr('class', 'node-image') .attr('xlink:href', function(d) { - return `/${calcImageForNodeType(d._osmeta.type)}`; + let status = R.defaultTo('', R.path(['_osmeta', 'status'], d)); + status = _.toLower(status); + return `/${calcImageForNodeType(d._osmeta.type, status)}`; }) .attr('x', -(Math.floor(imageLength / 2))) .attr('y', -(Math.floor(imageLength / 2))) @@ -268,8 +306,13 @@ function genSvgNodes(g, nodes, drag, onNodeOver, onNodeOut, onNodeClick, onGroup //return [svgNodes]; } -function calcImageForNodeType(nodeType) { - return R.defaultTo(defaultNodeTypeImage, R.prop(nodeType, imagesForNodeType)); +function calcImageForNodeType(nodeType, status) { + let image = R.defaultTo(defaultNodeTypeImage, R.prop(nodeType, imagesForNodeType)); + if (typeof image === 'object') { + image = R.defaultTo(image.default, image[status]); + } + + return image; } function genZoomBehavior(d3, config) { @@ -292,7 +335,7 @@ function genForceD3(d3, w, h) { function genForceCola(cola, d3, w, h) { let force = cola.d3adaptor(d3) .convergenceThreshold(0.1) - // .convergenceThreshold(1e-9) + //.convergenceThreshold(1e-9) .linkDistance(120) .size([w,h]); @@ -308,7 +351,7 @@ function activateForce(force, nodes, links, groups) { .handleDisconnected(true) .avoidOverlaps(true) .start(50, 100, 200); - //.start(); + //.start(); } /* @@ -340,7 +383,10 @@ function renderView(force, config, onNodeOver, onNodeOut, - onNodeClick) { + onNodeClick, + onGroupOver, + onLinkOver +) { state.viewGraph = calcViewGraph(state.graph, state.viewGraph); @@ -348,13 +394,14 @@ function renderView(force, zoom.on('zoom', zoomFn); - genSvgGroups(groupsEl, state.viewGraph.groups, drag, onRenderViewReq); + genSvgGroups(groupsEl, state.viewGraph.groups, drag, onRenderViewReq, onGroupOver); genSvgLinks( linksEl, state.viewGraph.links, config.nominal_stroke, config.default_link_color, - config.initialLinkLabelsFontSize + config.initialLinkLabelsFontSize, + onLinkOver ); genSvgNodes( @@ -366,7 +413,7 @@ function renderView(force, state.viewGraph = renderView(force, state, mainEl, groupsEl, nodesEl, linksEl, drag, zoom, config, - onNodeOver, onNodeOut, onNodeClick); + onNodeOver, onNodeOut, onNodeClick, onGroupOver, onLinkOver); }); force.on('tick', tickFn); @@ -375,7 +422,7 @@ function renderView(force, state.viewGraph = renderView(force, state, mainEl, groupsEl, nodesEl, linksEl, drag, zoom, config, - onNodeOver, onNodeOut, onNodeClick); + onNodeOver, onNodeOut, onNodeClick, onGroupOver, onLinkOver); } function tickFn() { @@ -477,30 +524,33 @@ function renderView(force, return state.viewGraph; } -function genSvgGroups(g, groups, drag, onRenderViewReq) { +function genSvgGroups(g, groups, drag, onRenderViewReq, onGroupOver) { let svgGroups = g.selectAll('.group') - .data(groups, (d) => d._osid); + .data(groups, (d) => d._osid); let enterGroups = svgGroups.enter(); let groupsContainers = enterGroups .append('g') - .attr('class', 'group') - .attr('data-group-id', (d) => d._osid) - .call(drag) - .on('click', function (d) { - console.log('click', d); - d.isExpanded = !d.isExpanded; - onRenderViewReq(); - }); + .attr('class', 'group') + .attr('data-group-id', (d) => d._osid) + .call(drag) + .on('mouseover', function (_d) { + onGroupOver(); + }) + .on('click', function (d) { + console.log('click', d); + d.isExpanded = !d.isExpanded; + onRenderViewReq(); + }); groupsContainers .append('rect') - .attr('class', 'group-shape') - .attr('rx', 8) - .attr('ry', 8) - .style('fill', function (_d, _i) { return 'lightblue'; }) + .attr('class', 'group-shape') + .attr('rx', 8) + .attr('ry', 8) + .style('fill', function (_d, _i) { return 'lightblue'; }) ; groupsContainers diff --git a/ui/imports/ui/components/scans-list/scans-list.html b/ui/imports/ui/components/scans-list/scans-list.html index f8998dd..6466068 100644 --- a/ui/imports/ui/components/scans-list/scans-list.html +++ b/ui/imports/ui/components/scans-list/scans-list.html @@ -27,7 +27,6 @@ <th>Scan Only Iventory</th> <th>Scan Only Links</th> <th>Scan Only Cliques</th> - <th>Scan Completed</th> <th> <a class="sm-table-header" data-is-sortable="true" @@ -63,7 +62,6 @@ <td>{{ scan.scan_only_inventory }}</td> <td>{{ scan.scan_only_links }}</td> <td>{{ scan.scan_only_cliques }}</td> - <td>{{ scan.scan_completed }}</td> <td>{{ scan.submit_timestamp }}</td> <td>{{ scan.start_timestamp }}</td> <td>{{ scan.end_timestamp }}</td> diff --git a/ui/imports/ui/components/top-navbar-menu/top-navbar-menu.js b/ui/imports/ui/components/top-navbar-menu/top-navbar-menu.js index 6968060..1b86cad 100644 --- a/ui/imports/ui/components/top-navbar-menu/top-navbar-menu.js +++ b/ui/imports/ui/components/top-navbar-menu/top-navbar-menu.js @@ -121,7 +121,7 @@ Template.TopNavbarMenu.helpers({ return { selectedEnvironment: selectedEnvironment, onEnvSelected: function (env) { - Router.go('environment', { _id: idToStr(env._id) }, { }); + Router.go('environment', { _id: idToStr(env._id) }, { query: `r=${Date.now()}` }); } }; } |