diff options
Diffstat (limited to 'ui/imports')
31 files changed, 670 insertions, 149 deletions
diff --git a/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js b/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js index ec2f6cd..13c877a 100644 --- a/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js +++ b/ui/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data.js @@ -7,6 +7,30 @@ // http://www.apache.org/licenses/LICENSE-2.0 / ///////////////////////////////////////////////////////////////////////////////////////// import { Mongo } from 'meteor/mongo'; +import * as R from 'ramda'; export const NodeHoverAttr = new Mongo.Collection( 'attributes_for_hover_on_data', { idGeneration: 'MONGO' }); + +export const calcAttrsForItem = function (node, attrsDefsRec) { + if (R.isNil(attrsDefsRec)) { + return []; + } + + let attrsDefs = attrsDefsRec.attributes; + + return R.reduce((acc, attrDef) => { + if (R.is(Array, attrDef)) { + let value = R.path(attrDef, node); + if (R.isNil(value)) { return acc; } + let name = R.join('.', attrDef); + return R.append(R.assoc(name, value, {}), acc); + + } else { + return R.ifElse(R.isNil, + R.always(acc), + (attrVal) => R.append(R.assoc(attrDef, attrVal, {}), acc) + )(R.prop(attrDef, node)); + } + }, [], attrsDefs); +}; diff --git a/ui/imports/api/clique-types/methods.js b/ui/imports/api/clique-types/methods.js index a62c22f..4257291 100644 --- a/ui/imports/api/clique-types/methods.js +++ b/ui/imports/api/clique-types/methods.js @@ -96,12 +96,12 @@ export const update = new ValidatedMethod({ 'focal_point_type', 'link_types', 'name', ], - cliqueType), { - environment, - focal_point_type, - link_types, - name, - }); + cliqueType), { + environment, + focal_point_type, + link_types, + name, + }); CliqueTypes.update({ _id: _id }, { $set: cliqueType }); } diff --git a/ui/imports/api/configurations/configurations.js b/ui/imports/api/configurations/configurations.js new file mode 100644 index 0000000..067b69f --- /dev/null +++ b/ui/imports/api/configurations/configurations.js @@ -0,0 +1,29 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others / +// / +// All rights reserved. This program and the accompanying materials / +// are made available under the terms of the Apache License, Version 2.0 / +// which accompanies this distribution, and is available at / +// http://www.apache.org/licenses/LICENSE-2.0 / +///////////////////////////////////////////////////////////////////////////////////////// +import { Mongo } from 'meteor/mongo'; +import { SimpleSchema } from 'meteor/aldeed:simple-schema'; +//import * as R from 'ramda'; + +export const Configurations = new Mongo.Collection('configurations', { idGeneration: 'MONGO' }); + +let schema = { + _id: { type: { _str: { type: String, regEx: SimpleSchema.RegEx.Id } } }, + user_id: { + type: String, + }, + messages_view_backward_delta: { + type: Number, + minCount: 1, + defaultValue: '1209600000', // 2 weeks + } +}; + +let simpleSchema = new SimpleSchema(schema); +Configurations.schema = simpleSchema; +Configurations.attachSchema(Configurations.schema); diff --git a/ui/imports/api/configurations/methods.js b/ui/imports/api/configurations/methods.js new file mode 100644 index 0000000..7366e3e --- /dev/null +++ b/ui/imports/api/configurations/methods.js @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others / +// / +// All rights reserved. This program and the accompanying materials / +// are made available under the terms of the Apache License, Version 2.0 / +// which accompanies this distribution, and is available at / +// http://www.apache.org/licenses/LICENSE-2.0 / +///////////////////////////////////////////////////////////////////////////////////////// +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { Configurations } from '/imports/api/configurations/configurations'; +import * as R from 'ramda'; + +export const save = new ValidatedMethod({ + name: 'configurations.save', + validate: Configurations.simpleSchema() + .pick([ + 'messages_view_backward_delta' + ]).validator({ clean: true, filter: false }), + run({ + messages_view_backward_delta + }) { + + let userId = this.userId; + let conf = Configurations.findOne({ user_id: userId }); + + if (conf) { + Configurations.update({ _id: conf._id}, { $set: { + messages_view_backward_delta: messages_view_backward_delta + }}); + } else { + let item = Configurations.schema.clean({}); + item = R.merge(item, { + user_id: userId, + messages_view_backward_delta: messages_view_backward_delta + }); + Configurations.insert(item); + } + } +}); diff --git a/ui/imports/api/configurations/server/publications.js b/ui/imports/api/configurations/server/publications.js new file mode 100644 index 0000000..fe9f6fd --- /dev/null +++ b/ui/imports/api/configurations/server/publications.js @@ -0,0 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others / +// / +// All rights reserved. This program and the accompanying materials / +// are made available under the terms of the Apache License, Version 2.0 / +// which accompanies this distribution, and is available at / +// http://www.apache.org/licenses/LICENSE-2.0 / +///////////////////////////////////////////////////////////////////////////////////////// +import { Meteor } from 'meteor/meteor'; + +import { Configurations } from '../configurations.js'; + +Meteor.publish('configurations?user', function () { + console.log('server subscribtion: configurations?user'); + + let userId = this.userId; + + let query = { user_id: userId }; + console.log('-query: ', query); + return Configurations.find(query); +}); diff --git a/ui/imports/api/environments/environments.js b/ui/imports/api/environments/environments.js index 5e3b4b2..22e49cf 100644 --- a/ui/imports/api/environments/environments.js +++ b/ui/imports/api/environments/environments.js @@ -32,7 +32,7 @@ export const requiredConfGroups = [ ]; export const optionalConfGroups = [ - // 'NFV_provider', + // 'NFV_provider', 'AMQP', 'Monitoring', 'ACI', @@ -335,8 +335,8 @@ function getSchemaForGroupName(groupName) { return CLISchema; case 'AMQP': return AMQPSchema; -// case 'NFV_provider': -// return NfvProviderSchema; + // case 'NFV_provider': + // return NfvProviderSchema; case 'ACI': return AciSchema; case 'Monitoring': diff --git a/ui/imports/api/inventories/server/methods.js b/ui/imports/api/inventories/server/methods.js index 3daf570..d7e3648 100644 --- a/ui/imports/api/inventories/server/methods.js +++ b/ui/imports/api/inventories/server/methods.js @@ -11,7 +11,8 @@ import * as R from 'ramda'; import { Inventory } from '../inventories'; import { Environments } from '/imports/api/environments/environments'; import { regexEscape } from '/imports/lib/regex-utils'; -import { NodeHoverAttr } from '/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data'; +import { NodeHoverAttr, calcAttrsForItem } from '/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data'; + const AUTO_COMPLETE_RESULTS_LIMIT = 15; Meteor.methods({ @@ -125,7 +126,7 @@ Meteor.methods({ let query = { _id: nodeId }; let node = Inventory.findOne(query); let attrsDefs = NodeHoverAttr.findOne({ 'type': node.type }); - let attributes = calcAttrsForNode(node, attrsDefs); + let attributes = calcAttrsForItem(node, attrsDefs); return { node: node, @@ -134,26 +135,3 @@ Meteor.methods({ }; }, }); - -function calcAttrsForNode(node, attrsDefsRec) { - if (R.isNil(attrsDefsRec)) { - return []; - } - - let attrsDefs = attrsDefsRec.attributes; - - return R.reduce((acc, attrDef) => { - if (R.is(Array, attrDef)) { - let value = R.path(attrDef, node); - if (R.isNil(value)) { return acc; } - let name = R.join('.', attrDef); - return R.append(R.assoc(name, value, {}), acc); - - } else { - return R.ifElse(R.isNil, - R.always(acc), - (attrVal) => R.append(R.assoc(attrDef, attrVal, {}), acc) - )(R.prop(attrDef, node)); - } - }, [], attrsDefs); -} diff --git a/ui/imports/api/links/server/methods.js b/ui/imports/api/links/server/methods.js new file mode 100644 index 0000000..8d3454b --- /dev/null +++ b/ui/imports/api/links/server/methods.js @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) and others / +// / +// All rights reserved. This program and the accompanying materials / +// are made available under the terms of the Apache License, Version 2.0 / +// which accompanies this distribution, and is available at / +// http://www.apache.org/licenses/LICENSE-2.0 / +///////////////////////////////////////////////////////////////////////////////////////// + +import { Links } from '../links'; +import { NodeHoverAttr, calcAttrsForItem } from '/imports/api/attributes_for_hover_on_data/attributes_for_hover_on_data'; +import * as R from 'ramda'; + +Meteor.methods({ + 'linksFind?DataAndAttrs': function (id) { + console.log(`method server: linksFind?DataAndAttrs. ${R.toString(id)}`); + //check(nodeId, ObjectId); + this.unblock(); + + let query = { _id: id }; + let link = Links.findOne(query); + let attrsDefs = NodeHoverAttr.findOne({ 'type': 'link' }); + let attributes = calcAttrsForItem(link, attrsDefs); + + return { + link: link, + linkName: link.link_name, + attributes: attributes + }; + }, +}); diff --git a/ui/imports/api/messages/server/methods.js b/ui/imports/api/messages/server/methods.js index 119e6b0..540c0a1 100644 --- a/ui/imports/api/messages/server/methods.js +++ b/ui/imports/api/messages/server/methods.js @@ -11,7 +11,7 @@ import { Messages } from '/imports/api/messages/messages'; Meteor.methods({ 'messages/get?level&env&page&amountPerPage&sortField&sortDirection': function ( - level, env, page, amountPerPage, sortField, sortDirection) { + level, env, page, amountPerPage, sortField, sortDirection) { logMethodCall('messages/get?level&env&page&amountPerPage&sortField&sortDirection', {level, env, page, amountPerPage}); @@ -27,7 +27,7 @@ Meteor.methods({ query = R.ifElse(R.isNil, R.always(query),R.assoc('level', R.__, query))(level); sortParams = R.ifElse(R.isNil, R.always(sortParams), - R.assoc(R.__, sortDirection, sortParams))(sortField); + R.assoc(R.__, sortDirection, sortParams))(sortField); console.log('sort params:', sortParams); diff --git a/ui/imports/api/messages/server/publications.js b/ui/imports/api/messages/server/publications.js index 13c7c50..6b147f0 100644 --- a/ui/imports/api/messages/server/publications.js +++ b/ui/imports/api/messages/server/publications.js @@ -88,11 +88,28 @@ Meteor.publish('messages/count?level', function (level) { return new Counter(counterName, Messages.find({ level: level })); }); +Meteor.publish('messages/count?backDelta&level', function (backDelta, level) { + const counterName = `messages/count?backDelta=${backDelta}&level=${level}`; + console.log(`subscribe - counter: ${counterName}`); + + let begining = moment().subtract(backDelta); + let query = { + level: level, + timestamp: { $gte: begining.toDate() } + }; + + console.log(`query: ${R.toString(query)}`); + + return new Counter(counterName, Messages.find(query)); +}); + Meteor.publish('messages/count?level&env', function (level, env) { const counterName = `messages/count?level=${level}&env=${env}`; console.log(`subscribe - counter: ${counterName}`); let query = { level: level }; query = R.ifElse(R.isNil, R.always(query), R.assoc('environment', R.__, query))(env); + console.log(`query: ${R.toString(query)}`); - return new Counter(counterName, Messages.find(query)); }); + return new Counter(counterName, Messages.find(query)); +}); diff --git a/ui/imports/lib/images-for-node-type.js b/ui/imports/lib/images-for-node-type.js index fb8e582..19be6ac 100644 --- a/ui/imports/lib/images-for-node-type.js +++ b/ui/imports/lib/images-for-node-type.js @@ -1,14 +1,59 @@ export let imagesForNodeType = { - 'instance': 'ic_computer_black_48dp_2x.png', - 'pnic': 'ic_dns_black_48dp_2x.png', - 'vconnector': 'ic_settings_input_composite_black_48dp_2x.png', + 'instance': { + default: 'ic_computer_black_48dp_2x.png', + ok: 'ic_computer_black_48dp_2x-green.png', + warning: 'ic_computer_black_48dp_2x-orange.png', + error: 'ic_computer_black_48dp_2x-red.png', + }, + 'pnic': { + default: 'ic_dns_black_48dp_2x.png', + ok: 'ic_dns_black_48dp_2x-green.png', + warning: 'ic_dns_black_48dp_2x-orange.png', + error: 'ic_dns_black_48dp_2x-red.png', + }, + 'vconnector': { + default: 'ic_settings_input_composite_black_48dp_2x.png', + ok: 'ic_settings_input_composite_black_48dp_2x-green.png', + warning: 'ic_settings_input_composite_black_48dp_2x-orange.png', + error: 'ic_settings_input_composite_black_48dp_2x-red.png', + }, // 'network': 'ic_cloud_queue_black_48dp_2x.png', - 'network': 'ic_cloud_queue_black_48dp_2x.png', - 'vedge': 'ic_gamepad_black_48dp_2x.png', - 'vservice': 'ic_storage_black_48dp_2x.png', - 'vnic': 'ic_settings_input_hdmi_black_48dp_2x.png', - 'otep':'ic_keyboard_return_black_48dp_2x.png', + 'network': { + default: 'ic_cloud_queue_black_48dp_2x.png', + ok: 'ic_cloud_queue_black_48dp_2x-green.png', + warning: 'ic_cloud_queue_black_48dp_2x-orange.png', + error: 'ic_cloud_queue_black_48dp_2x-red.png', + }, + 'vedge': { + default: 'ic_gamepad_black_48dp_2x.png', + ok: 'ic_gamepad_black_48dp_2x-green.png', + warning: 'ic_gamepad_black_48dp_2x-orange.png', + error: 'ic_gamepad_black_48dp_2x-red.png', + }, + 'vservice': { + default: 'ic_storage_black_48dp_2x.png', + ok: 'ic_storage_black_48dp_2x-green.png', + warning: 'ic_storage_black_48dp_2x-orange.png', + error: 'ic_storage_black_48dp_2x-red.png', + }, + 'vnic': { + default: 'ic_settings_input_hdmi_black_48dp_2x.png', + ok: 'ic_settings_input_hdmi_black_48dp_2x-green.png', + warning: 'ic_settings_input_hdmi_black_48dp_2x-orange.png', + error: 'ic_settings_input_hdmi_black_48dp_2x-red.png', + }, + 'otep': { + default: 'ic_keyboard_return_black_48dp_2x.png', + ok: 'ic_keyboard_return_black_48dp_2x-green.png', + warning: 'ic_keyboard_return_black_48dp_2x-orange.png', + error: 'ic_keyboard_return_black_48dp_2x-red.png', + }, }; -export let defaultNodeTypeImage = 'ic_lens_black_48dp_2x.png'; +export let defaultNodeTypeImage = { + default: 'ic_lens_black_48dp_2x.png', + ok: 'ic_lens_black_48dp_2x-green.png', + warning: 'ic_lens_black_48dp_2x-orange.png', + error: 'ic_lens_black_48dp_2x-red.png', +}; diff --git a/ui/imports/startup/client/index.js b/ui/imports/startup/client/index.js index 93da904..6ac478c 100644 --- a/ui/imports/startup/client/index.js +++ b/ui/imports/startup/client/index.js @@ -36,3 +36,4 @@ import '/imports/ui/components/messages-list/messages-list'; import '/imports/ui/components/message/message'; import '/imports/ui/components/dashboard/dashboard'; import '/imports/ui/components/new-scanning/new-scanning'; +import '/imports/ui/components/configuration/configuration'; diff --git a/ui/imports/startup/server/register-api.js b/ui/imports/startup/server/register-api.js index 3475c53..71d6887 100644 --- a/ui/imports/startup/server/register-api.js +++ b/ui/imports/startup/server/register-api.js @@ -31,6 +31,7 @@ import '../../api/cliques/methods.js'; import '../../api/links/server/publications'; import '../../api/links/methods.js'; +import '../../api/links/server/methods'; import '../../api/statistics/server/publications'; import '../../api/statistics/methods.js'; @@ -53,4 +54,7 @@ import '../../api/accounts/methods'; import '../../api/supported_environments/server/publications'; import '../../api/supported_environments/methods'; +import '../../api/configurations/server/publications'; +import '../../api/configurations/methods'; + import '../../api/migrations/migrations'; 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()}` }); } }; } |