From b79de99e9aa8ab2af5cbe731100552ba28ad4ab1 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 22 Nov 2020 22:06:01 +0100 Subject: [PATCH 001/127] WIP: Show status of VILLASnode ICs #265 --- src/ic/ic-api-store.js | 49 ++++++++++++++++++++++++++++++++ src/ic/ic-data-data-manager.js | 18 ++++++++++++ src/ic/ic-dialog.js | 51 ++++++++++++++++++++++++++++++++-- src/ic/ics.js | 36 ++++++++++++++++++++---- 4 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 src/ic/ic-api-store.js diff --git a/src/ic/ic-api-store.js b/src/ic/ic-api-store.js new file mode 100644 index 0000000..d02d644 --- /dev/null +++ b/src/ic/ic-api-store.js @@ -0,0 +1,49 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import ArrayStore from '../common/array-store'; +import ICDataDataManager from './ic-data-data-manager'; + +class ICAPIStore extends ArrayStore { + constructor() { + super('ic-api', ICDataDataManager); + } + + + reduce(state, action) { + switch(action.type) { + + case 'ic-api/get-status': + ICDataDataManager.getStatus(action.url, action.socketname, action.token,action.icid); + return super.reduce(state, action); + + case 'ic-api/status-received': + let tempData = action.data; + tempData.icId = action.icid; + return this.updateElements(state, [tempData]); + + case 'ic-api/status-error': + console.log("status error"); + return super.reduce(state, action); + + default: + return super.reduce(state, action); + } + } +} + +export default new ICAPIStore(); diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index 0dcc853..5e10007 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -17,6 +17,7 @@ import WebsocketAPI from '../common/api/websocket-api'; import AppDispatcher from '../common/app-dispatcher'; +import RestAPI from "../common/api/rest-api"; const OFFSET_TYPE = 2; const OFFSET_VERSION = 4; @@ -43,6 +44,23 @@ class IcDataDataManager { } } + getStatus(url,socketname,token,icid){ + RestAPI.get(url, null).then(response => { + AppDispatcher.dispatch({ + type: 'ic-api/status-received', + data: response, + token: token, + socketname: socketname, + icid: icid, + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'ic-api/status-error', + error: error + }) + }) + } + closeAll() { // close every open socket for (var identifier in this._sockets) { diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index b134f95..0a1a411 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -1,6 +1,9 @@ import React from 'react'; -import {FormLabel} from 'react-bootstrap'; +import {FormLabel, Button} from 'react-bootstrap'; import Dialog from '../common/dialogs/dialog'; +import {Collapse} from 'react-collapse'; +import Icon from "../common/icon"; + class ICDialog extends React.Component { @@ -10,10 +13,20 @@ class ICDialog extends React.Component { super(props); this.state = { - ic: props.ic + timezone: false, + kernel: false, + system: false, }; } +/* static getDerivedStateFromProps(props, state){ + let icStatus = props.icStatus; + return { + icStatus: icStatus, + ic: props.ic, + }; + }*/ + onClose(canceled) { this.props.onClose(); } @@ -22,6 +35,19 @@ class ICDialog extends React.Component { } + showFurtherInfo(key){ + switch(key){ + case 'timezone': + this.setState({timezone: !this.state.timezone}); + return; + case 'kernel': + this.setState({kernel: !this.state.kernel}); + return; + case 'system': + this.setState({system: !this.state.system}); + return; + } + } render() { @@ -35,7 +61,26 @@ class ICDialog extends React.Component { size='lg' >
- Infos and Controls + Status + { + typeof this.props.icStatus !== "undefined" && Object.keys(this.props.icStatus).map(statusKey => ( + typeof this.props.icStatus[statusKey] === 'object' ? + (
+ + + { + Object.keys(this.props.icStatus[statusKey]).map(key => ( +
{key + ": " + this.props.icStatus[statusKey][key]}
+ )) + } +
+ +
) + : + (
{statusKey + ": " + this.props.icStatus[statusKey]}
) + )) + }
); diff --git a/src/ic/ics.js b/src/ic/ics.js index 138852f..2789b1e 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -24,6 +24,7 @@ import moment from 'moment' import AppDispatcher from '../common/app-dispatcher'; import InfrastructureComponentStore from './ic-store'; +import ICAPIStore from './ic-api-store'; import Icon from '../common/icon'; import Table from '../common/table'; @@ -38,7 +39,7 @@ import DeleteDialog from '../common/dialogs/delete-dialog'; class InfrastructureComponents extends Component { static getStores() { - return [ InfrastructureComponentStore ]; + return [ InfrastructureComponentStore, ICAPIStore ]; } static statePrio(state) { @@ -73,11 +74,15 @@ class InfrastructureComponents extends Component { return a.stateUpdatedAt < b.stateUpdatedAt; } }); + + const icInfo = ICAPIStore.getState(); return { sessionToken: localStorage.getItem("token"), ics: ics, + icInfo: icInfo, modalIC: {}, + modalICStatus: {}, deleteModal: false, icModal: false, selectedICs: [], @@ -90,11 +95,11 @@ class InfrastructureComponents extends Component { type: 'ics/start-load', token: this.state.sessionToken, }); - - // Start timer for periodic refresh + + // Start timer for periodic refresh this.timer = window.setInterval(() => this.refresh(), 10000); } - + componentWillUnmount() { window.clearInterval(this.timer); } @@ -109,6 +114,19 @@ class InfrastructureComponents extends Component { type: 'ics/start-load', token: this.state.sessionToken, }); + + this.state.ics.forEach(ic => { + if (ic.type === "villas-node" || ic.type === "villas-relay") { + let splitWebsocketURL = ic.websocketurl.split("/"); + AppDispatcher.dispatch({ + type: 'ic-api/get-status', + url: ic.apiurl + "/status", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + icid: ic.id, + }); + } + }) } } @@ -320,12 +338,18 @@ class InfrastructureComponents extends Component { let ic = this.state.ics.find(ic => ic.name === name); let index = this.state.ics.indexOf(ic); if(ic.type === "villas-node" || ic.type === "villas-relay"){ - return } + return } else{ return {name} } } + openICStatus(ic){ + let index = this.state.ics.indexOf(ic); + let icStatus = this.state.icInfo.find(info => info.icID === ic.id); + this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalIndex: index }) + } + render() { const buttonStyle = { marginLeft: '10px' @@ -396,7 +420,7 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} /> + this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} /> this.closeDeleteModal(e)} /> From 0bc737349f123cf8707fadca1d7a1ac0a48c6bb1 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Fri, 27 Nov 2020 13:38:51 +0100 Subject: [PATCH 002/127] WIP: Show graph representing the current config #265 --- src/common/api/rest-api.js | 22 ++++++++++++++++++++++ src/ic/ic-api-store.js | 10 ++++++++++ src/ic/ic-data-data-manager.js | 16 ++++++++++++++++ src/ic/ics.js | 7 +++++++ 4 files changed, 55 insertions(+) diff --git a/src/common/api/rest-api.js b/src/common/api/rest-api.js index 084456c..50ff2de 100644 --- a/src/common/api/rest-api.js +++ b/src/common/api/rest-api.js @@ -170,6 +170,28 @@ class RestAPI { }); } + apiDownload(url, token) { + return new Promise(function (resolve, reject) { + var req = request.get(url).buffer(true).responseType("blob"); + + if (token != null) { + req.set('Authorization', "Bearer " + token); + } + + req.end(function (error, res) { + if (res == null || res.status !== 200) { + if (req.url !== prevURL) error.handled = isNetworkError(error); + prevURL = req.url; + reject(error); + } else { + let parts = url.split("/"); + resolve({data: res.body, type: res.type, id: parts[parts.length-1]}) + } + }); + }); + } + + } export default new RestAPI(); diff --git a/src/ic/ic-api-store.js b/src/ic/ic-api-store.js index d02d644..20d3816 100644 --- a/src/ic/ic-api-store.js +++ b/src/ic/ic-api-store.js @@ -40,6 +40,16 @@ class ICAPIStore extends ArrayStore { console.log("status error"); return super.reduce(state, action); + case 'ic-api/get-graph': + ICDataDataManager.getGraph(action.url, action.socketname, action.token); + return super.reduce(state, action); + + case 'ic-api/graph-received': + return super.reduce(state, action); + + case 'ic-api/graph-error': + return super.reduce(state, action); + default: return super.reduce(state, action); } diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index 5e10007..d82497a 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -61,6 +61,22 @@ class IcDataDataManager { }) } + getGraph(url,socketname,token){ + RestAPI.apiDownload(url, null).then(response => { + AppDispatcher.dispatch({ + type: 'ic-api/status-received', + data: response, + token: token, + socketname: socketname, + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'ic-api/status-error', + error: error + }) + }) + } + closeAll() { // close every open socket for (var identifier in this._sockets) { diff --git a/src/ic/ics.js b/src/ic/ics.js index 2789b1e..a7024e8 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -125,6 +125,13 @@ class InfrastructureComponents extends Component { token: this.state.sessionToken, icid: ic.id, }); + + AppDispatcher.dispatch({ + type: 'ic-api/get-graph', + url: ic.apiurl + "/graph.svg", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + }); } }) } From d605d9c7bb6450425f1684c52c44dd1fd50c1b0a Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sat, 28 Nov 2020 16:44:58 +0100 Subject: [PATCH 003/127] Show status of VILLASnode ICs #265 --- src/ic/ic-dialog.js | 50 +++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 0a1a411..cdb48ad 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -13,20 +13,9 @@ class ICDialog extends React.Component { super(props); this.state = { - timezone: false, - kernel: false, - system: false, }; } -/* static getDerivedStateFromProps(props, state){ - let icStatus = props.icStatus; - return { - icStatus: icStatus, - ic: props.ic, - }; - }*/ - onClose(canceled) { this.props.onClose(); } @@ -36,17 +25,8 @@ class ICDialog extends React.Component { } showFurtherInfo(key){ - switch(key){ - case 'timezone': - this.setState({timezone: !this.state.timezone}); - return; - case 'kernel': - this.setState({kernel: !this.state.kernel}); - return; - case 'system': - this.setState({system: !this.state.system}); - return; - } + if(typeof this.state[key] === 'undefined') this.setState({[key]: false}); + this.setState({[key]: !this.state[key]}); } render() { @@ -68,13 +48,25 @@ class ICDialog extends React.Component { (
- - { - Object.keys(this.props.icStatus[statusKey]).map(key => ( -
{key + ": " + this.props.icStatus[statusKey][key]}
- )) - } -
+ + { + Object.keys(this.props.icStatus[statusKey]).map(key => ( + typeof this.props.icStatus[statusKey][key] === 'object' ? + (
+ + + + {Object.keys(this.props.icStatus[statusKey][key]).map(index => ( +
{index + ": " + this.props.icStatus[statusKey][key][index]}
+ ))} +
+
) + : + (
{key + ": " + this.props.icStatus[statusKey][key]}
) + )) + } +
) : From f88a75a972f2773f93d3ecce4a9285e47a7ebc0d Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sat, 28 Nov 2020 21:17:26 +0100 Subject: [PATCH 004/127] Show graph representing the current config #265 --- src/ic/ic-data-data-manager.js | 11 ++-- src/ic/ic-dialog.js | 17 +++++ src/ic/ic-graph-store.js | 62 +++++++++++++++++++ .../{ic-api-store.js => ic-status-store.js} | 24 +++---- src/ic/ics.js | 32 ++++++---- 5 files changed, 112 insertions(+), 34 deletions(-) create mode 100644 src/ic/ic-graph-store.js rename src/ic/{ic-api-store.js => ic-status-store.js} (70%) diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index d82497a..d0ca2fd 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -47,7 +47,7 @@ class IcDataDataManager { getStatus(url,socketname,token,icid){ RestAPI.get(url, null).then(response => { AppDispatcher.dispatch({ - type: 'ic-api/status-received', + type: 'ic-status/status-received', data: response, token: token, socketname: socketname, @@ -55,23 +55,24 @@ class IcDataDataManager { }); }).catch(error => { AppDispatcher.dispatch({ - type: 'ic-api/status-error', + type: 'ic-status/status-error', error: error }) }) } - getGraph(url,socketname,token){ + getGraph(url,socketname,token,icid){ RestAPI.apiDownload(url, null).then(response => { AppDispatcher.dispatch({ - type: 'ic-api/status-received', + type: 'ic-graph/graph-received', data: response, token: token, socketname: socketname, + icid: icid, }); }).catch(error => { AppDispatcher.dispatch({ - type: 'ic-api/status-error', + type: 'ic-graph/graph-error', error: error }) }) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index cdb48ad..12f65ec 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -29,7 +29,16 @@ class ICDialog extends React.Component { this.setState({[key]: !this.state[key]}); } + graphError(e){ + console.log("graph error"); + } + render() { + + let objectURL='' + if(typeof this.props.icGraph !== "undefined") { + objectURL = this.props.icGraph.objectURL + } return ( {statusKey + ": " + this.props.icStatus[statusKey]}) )) } +
Graph:
+
+ {objectURL !== '' ? ( + this.graphError(e)} alt={"Error"} src={objectURL} /> + ) : ( + Error + )} +
); diff --git a/src/ic/ic-graph-store.js b/src/ic/ic-graph-store.js new file mode 100644 index 0000000..cb44812 --- /dev/null +++ b/src/ic/ic-graph-store.js @@ -0,0 +1,62 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import ArrayStore from '../common/array-store'; +import ICDataDataManager from './ic-data-data-manager'; + +class ICGraphStore extends ArrayStore { + constructor() { + super('ic-graph', ICDataDataManager); + } + + saveGraph(state, action){ + + let icID = parseInt(action.icid); + const dublicate = state.some(element => element.icID === icID); + if(dublicate){ + return state + } + let icGraph = {}; + icGraph["icID"] = icID; + icGraph["data"] = new Blob([action.data.data], {type: action.data.type}); + icGraph["type"] = action.data.type; + icGraph["objectURL"] = URL.createObjectURL(icGraph["data"]); + + this.__emitChange(); + return this.updateElements(state, [icGraph]); + } + + reduce(state, action) { + switch(action.type) { + + case 'ic-graph/get-graph': + ICDataDataManager.getGraph(action.url, action.socketname, action.token, action.icid); + return super.reduce(state, action); + + case 'ic-graph/graph-received': + return this.saveGraph(state, action); + + case 'ic-graph/graph-error': + return super.reduce(state, action); + + default: + return super.reduce(state, action); + } + } +} + +export default new ICGraphStore(); diff --git a/src/ic/ic-api-store.js b/src/ic/ic-status-store.js similarity index 70% rename from src/ic/ic-api-store.js rename to src/ic/ic-status-store.js index 20d3816..c78353a 100644 --- a/src/ic/ic-api-store.js +++ b/src/ic/ic-status-store.js @@ -18,42 +18,32 @@ import ArrayStore from '../common/array-store'; import ICDataDataManager from './ic-data-data-manager'; -class ICAPIStore extends ArrayStore { +class ICStatusStore extends ArrayStore { constructor() { - super('ic-api', ICDataDataManager); + super('ic-status', ICDataDataManager); } reduce(state, action) { switch(action.type) { - case 'ic-api/get-status': + case 'ic-status/get-status': ICDataDataManager.getStatus(action.url, action.socketname, action.token,action.icid); return super.reduce(state, action); - case 'ic-api/status-received': + case 'ic-status/status-received': let tempData = action.data; - tempData.icId = action.icid; + tempData.icID = action.icid; return this.updateElements(state, [tempData]); - case 'ic-api/status-error': + case 'ic-status/status-error': console.log("status error"); return super.reduce(state, action); - case 'ic-api/get-graph': - ICDataDataManager.getGraph(action.url, action.socketname, action.token); - return super.reduce(state, action); - - case 'ic-api/graph-received': - return super.reduce(state, action); - - case 'ic-api/graph-error': - return super.reduce(state, action); - default: return super.reduce(state, action); } } } -export default new ICAPIStore(); +export default new ICStatusStore(); diff --git a/src/ic/ics.js b/src/ic/ics.js index a7024e8..f3ec9d3 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -24,7 +24,8 @@ import moment from 'moment' import AppDispatcher from '../common/app-dispatcher'; import InfrastructureComponentStore from './ic-store'; -import ICAPIStore from './ic-api-store'; +import ICStatusStore from './ic-status-store'; +import ICGraphStore from './ic-graph-store'; import Icon from '../common/icon'; import Table from '../common/table'; @@ -39,7 +40,7 @@ import DeleteDialog from '../common/dialogs/delete-dialog'; class InfrastructureComponents extends Component { static getStores() { - return [ InfrastructureComponentStore, ICAPIStore ]; + return [ InfrastructureComponentStore, ICStatusStore, ICGraphStore ]; } static statePrio(state) { @@ -75,14 +76,17 @@ class InfrastructureComponents extends Component { } }); - const icInfo = ICAPIStore.getState(); - + const icStatus = ICStatusStore.getState(); + const icGraph = ICGraphStore.getState(); + return { sessionToken: localStorage.getItem("token"), ics: ics, - icInfo: icInfo, + icStatus: icStatus, + icGraph: icGraph, modalIC: {}, modalICStatus: {}, + modalICGraph: {}, deleteModal: false, icModal: false, selectedICs: [], @@ -119,7 +123,7 @@ class InfrastructureComponents extends Component { if (ic.type === "villas-node" || ic.type === "villas-relay") { let splitWebsocketURL = ic.websocketurl.split("/"); AppDispatcher.dispatch({ - type: 'ic-api/get-status', + type: 'ic-status/get-status', url: ic.apiurl + "/status", socketname: splitWebsocketURL[splitWebsocketURL.length - 1], token: this.state.sessionToken, @@ -127,10 +131,11 @@ class InfrastructureComponents extends Component { }); AppDispatcher.dispatch({ - type: 'ic-api/get-graph', + type: 'ic-graph/get-graph', url: ic.apiurl + "/graph.svg", socketname: splitWebsocketURL[splitWebsocketURL.length - 1], token: this.state.sessionToken, + icid: ic.id, }); } }) @@ -343,18 +348,21 @@ class InfrastructureComponents extends Component { modifyNameColumn(name){ let ic = this.state.ics.find(ic => ic.name === name); - let index = this.state.ics.indexOf(ic); + if(ic.type === "villas-node" || ic.type === "villas-relay"){ - return } + return } else{ return {name} } } openICStatus(ic){ + let index = this.state.ics.indexOf(ic); - let icStatus = this.state.icInfo.find(info => info.icID === ic.id); - this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalIndex: index }) + let icStatus = this.state.icStatus.find(status => status.icID === ic.id); + let icGraph = this.state.icGraph.find(graph => graph.icID === ic.id); + + this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalICGraph: icGraph, modalIndex: index }) } render() { @@ -427,7 +435,7 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} /> + this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} /> this.closeDeleteModal(e)} /> From be0c656a22c3a67124bab3561ce665f3abb2fc6d Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 29 Nov 2020 15:22:39 +0100 Subject: [PATCH 005/127] WIP: Send control commands from status dialog #265 --- src/ic/confirm-command.js | 48 +++++++++++++++++++++++++++++++++++++++ src/ic/ic-dialog.js | 20 ++++++++++++++++ src/ic/ics.js | 6 ++++- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/ic/confirm-command.js diff --git a/src/ic/confirm-command.js b/src/ic/confirm-command.js new file mode 100644 index 0000000..43b6885 --- /dev/null +++ b/src/ic/confirm-command.js @@ -0,0 +1,48 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import { Button, Modal} from 'react-bootstrap'; + +class ConfirmCommand extends React.Component { + onModalKeyPress = (event) => { + if (event.key === 'Enter') { + event.preventDefault(); + + this.props.onClose(false); + } + } + + render() { + return this.props.onClose(false)} onKeyPress={this.onModalKeyPress}> + + Confirm {this.props.command} + + + + Are you sure you want to {this.props.command} '{this.props.name}'? + + + + + + + ; + } +} + +export default ConfirmCommand; diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 12f65ec..0acbcb5 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -3,6 +3,7 @@ import {FormLabel, Button} from 'react-bootstrap'; import Dialog from '../common/dialogs/dialog'; import {Collapse} from 'react-collapse'; import Icon from "../common/icon"; +import ConfirmCommand from './confirm-command'; @@ -13,6 +14,8 @@ class ICDialog extends React.Component { super(props); this.state = { + confirmCommand: false, + command: '', }; } @@ -33,6 +36,15 @@ class ICDialog extends React.Component { console.log("graph error"); } + closeConfirmModal(canceled){ + if(!canceled){ + this.props.sendControlCommand(this.state.command); + } + + this.setState({confirmCommand: false, command: ''}); + } + + render() { let objectURL='' @@ -91,7 +103,15 @@ class ICDialog extends React.Component { )} + +
Controls
+ + + + this.closeConfirmModal(c)} /> + + ); } } diff --git a/src/ic/ics.js b/src/ic/ics.js index f3ec9d3..c582503 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -365,6 +365,10 @@ class InfrastructureComponents extends Component { this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalICGraph: icGraph, modalIndex: index }) } + sendControlCommand(command){ + console.log(command); + } + render() { const buttonStyle = { marginLeft: '10px' @@ -435,7 +439,7 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} /> + this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command) => this.sendControlCommand(command)}/> this.closeDeleteModal(e)} /> From b24f5cf638f16fd1550dcfa4b0e4dee3b56c5018 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 6 Dec 2020 16:06:15 +0100 Subject: [PATCH 006/127] Send IC control commands from status dialog #265 --- src/ic/ic-data-data-manager.js | 34 ++++++++++++++++++++++++++++++++++ src/ic/ic-dialog.js | 2 +- src/ic/ic-status-store.js | 26 ++++++++++++++++++++++++-- src/ic/ics.js | 23 ++++++++++++++++++++--- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index d0ca2fd..b26675c 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -78,6 +78,40 @@ class IcDataDataManager { }) } + restart(url,socketname,token){ + RestAPI.post(url, null).then(response => { + AppDispatcher.dispatch({ + type: 'ic-status/restart-successful', + data: response, + token: token, + socketname: socketname, + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'ic-status/restart-error', + error: error + }) + }) + } + + shutdown(url,socketname,token){ + RestAPI.post(url, null).then(response => { + AppDispatcher.dispatch({ + type: 'ic-status/shutdown-successful', + data: response, + token: token, + socketname: socketname, + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'ic-status/shutdown-error', + error: error + }) + }) + } + + + closeAll() { // close every open socket for (var identifier in this._sockets) { diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 0acbcb5..4e0e9a4 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -38,7 +38,7 @@ class ICDialog extends React.Component { closeConfirmModal(canceled){ if(!canceled){ - this.props.sendControlCommand(this.state.command); + this.props.sendControlCommand(this.state.command,this.props.ic); } this.setState({confirmCommand: false, command: ''}); diff --git a/src/ic/ic-status-store.js b/src/ic/ic-status-store.js index c78353a..3f701ef 100644 --- a/src/ic/ic-status-store.js +++ b/src/ic/ic-status-store.js @@ -28,7 +28,7 @@ class ICStatusStore extends ArrayStore { switch(action.type) { case 'ic-status/get-status': - ICDataDataManager.getStatus(action.url, action.socketname, action.token,action.icid); + ICDataDataManager.getStatus(action.url, action.socketname, action.token, action.icid); return super.reduce(state, action); case 'ic-status/status-received': @@ -38,7 +38,29 @@ class ICStatusStore extends ArrayStore { case 'ic-status/status-error': console.log("status error"); - return super.reduce(state, action); + return state; + + case 'ic-status/restart': + ICDataDataManager.restart(action.url, action.socketname, action.token); + return state; + + case 'ic-status/restart-successful': + return state; + + case 'ic-status/restart-error': + console.log("restart error"); + return state; + + case 'ic-status/shutdown': + ICDataDataManager.shutdown(action.url, action.socketname, action.token); + return state; + + case 'ic-status/shutdown-successful': + return state; + + case 'ic-status/shutdown-error': + console.log("shutdown error"); + return state; default: return super.reduce(state, action); diff --git a/src/ic/ics.js b/src/ic/ics.js index c582503..4f8e2eb 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -365,8 +365,25 @@ class InfrastructureComponents extends Component { this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalICGraph: icGraph, modalIndex: index }) } - sendControlCommand(command){ - console.log(command); + sendControlCommand(command,ic){ + let splitWebsocketURL = ic.websocketurl.split("/"); + + if(command === "restart"){ + AppDispatcher.dispatch({ + type: 'ic-status/restart', + url: ic.apiurl + "/restart", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + }); + }else if(command === "shutdown"){ + AppDispatcher.dispatch({ + type: 'ic-status/shutdown', + url: ic.apiurl + "/shutdown", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + }); + } + } render() { @@ -439,7 +456,7 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command) => this.sendControlCommand(command)}/> + this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/> this.closeDeleteModal(e)} /> From c5ec4098df18aa24bd20d15008d81d6755f437d9 Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 11 Dec 2020 14:20:20 +0100 Subject: [PATCH 007/127] select login option --- src/router.js | 4 ++- src/styles/app.css | 20 +++++++++++ src/user/login-select.js | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/user/login-select.js diff --git a/src/router.js b/src/router.js index 05cf900..84aa6f7 100644 --- a/src/router.js +++ b/src/router.js @@ -20,6 +20,7 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom'; import App from './app'; import Login from './user/login'; +import LoginSelect from './user/login-select'; import Logout from './user/logout'; import Home from './common/home'; import Scenarios from './scenario/scenarios'; @@ -34,7 +35,8 @@ class Root extends React.Component { return ( - + + diff --git a/src/styles/app.css b/src/styles/app.css index a2d887d..5ca9250 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -219,6 +219,26 @@ body { background-color: white; } +/** + * Login select + */ + .login-select { + position: sticky; + width: 300px; + height: 200px; + top: 50%; + left: 50%; + margin-top: 50px; + margin-bottom: 100px; + transform: translate(-50%); + + + padding: 20px 20px; + + background-color: #a8c7cf; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 9px 18px 0 rgba(0, 0, 0, 0.1); +} /** * Login form diff --git a/src/user/login-select.js b/src/user/login-select.js new file mode 100644 index 0000000..d8f6178 --- /dev/null +++ b/src/user/login-select.js @@ -0,0 +1,72 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React, { Component } from 'react'; +import { Form, Button, FormGroup, FormControl, FormLabel, Col } from 'react-bootstrap'; +//import RecoverPassword from './recover-password' +//import AppDispatcher from '../common/app-dispatcher'; +import Header from '../common/header'; +import Footer from '../common/footer'; +import { withRouter } from 'react-router-dom'; + + + +class LoginSelect extends Component { + constructor(props) { + super(props); + + + + this.state = { + standardlogin: true, + keycloaklogin: true, + jupyterlogin: false + } + } + + villaswebLogin = e => { + this.props.history.replace('/login-villas'); + } + + jupyterLogin() { + + } + + + render() { + + return ( +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ ); + } +} + +export default withRouter(LoginSelect); From ba81bf14e6e88f567fdc6d01ce827e05b59f6d03 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sat, 12 Dec 2020 17:31:22 +0100 Subject: [PATCH 008/127] IC status and graph now load as soon as ICs are loaded #265 --- src/ic/ic-store.js | 21 +++++++++++++++++++++ src/ic/ics.js | 20 -------------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index d73e555..78e90ac 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -19,6 +19,7 @@ import ArrayStore from '../common/array-store'; import ICsDataManager from './ics-data-manager'; import ICDataDataManager from './ic-data-data-manager'; import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; +import AppDispatcher from '../common/app-dispatcher'; class InfrastructureComponentStore extends ArrayStore { constructor() { @@ -28,6 +29,26 @@ class InfrastructureComponentStore extends ArrayStore { reduce(state, action) { switch(action.type) { case 'ics/loaded': + action.data.forEach(ic => { + if (ic.type === "villas-node" || ic.type === "villas-relay") { + let splitWebsocketURL = ic.websocketurl.split("/"); + AppDispatcher.dispatch({ + type: 'ic-status/get-status', + url: ic.apiurl + "/status", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: action.token, + icid: ic.id, + }); + + AppDispatcher.dispatch({ + type: 'ic-graph/get-graph', + url: ic.apiurl + "/graph.svg", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: action.token, + icid: ic.id, + }); + } + }) return super.reduce(state, action); diff --git a/src/ic/ics.js b/src/ic/ics.js index 4f8e2eb..fed6de5 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -119,26 +119,6 @@ class InfrastructureComponents extends Component { token: this.state.sessionToken, }); - this.state.ics.forEach(ic => { - if (ic.type === "villas-node" || ic.type === "villas-relay") { - let splitWebsocketURL = ic.websocketurl.split("/"); - AppDispatcher.dispatch({ - type: 'ic-status/get-status', - url: ic.apiurl + "/status", - socketname: splitWebsocketURL[splitWebsocketURL.length - 1], - token: this.state.sessionToken, - icid: ic.id, - }); - - AppDispatcher.dispatch({ - type: 'ic-graph/get-graph', - url: ic.apiurl + "/graph.svg", - socketname: splitWebsocketURL[splitWebsocketURL.length - 1], - token: this.state.sessionToken, - icid: ic.id, - }); - } - }) } } From 6d59f83002cefaf79ed857652c610cdfc0876111 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sat, 12 Dec 2020 20:08:22 +0100 Subject: [PATCH 009/127] Format IC dialog window #265 --- src/ic/ic-dialog.js | 51 ++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 4e0e9a4..9b29d9c 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -1,5 +1,5 @@ import React from 'react'; -import {FormLabel, Button} from 'react-bootstrap'; +import {Button, Row, Col} from 'react-bootstrap'; import Dialog from '../common/dialogs/dialog'; import {Collapse} from 'react-collapse'; import Icon from "../common/icon"; @@ -47,6 +47,9 @@ class ICDialog extends React.Component { render() { + let icStatus = this.props.icStatus; + delete icStatus['icID']; + let objectURL='' if(typeof this.props.icGraph !== "undefined") { objectURL = this.props.icGraph.objectURL @@ -55,46 +58,52 @@ class ICDialog extends React.Component { return ( this.onClose(c)} valid={true} - size='lg' + size='xl' + blendOutCancel = {true} >
- Status + + +
Status:
{ - typeof this.props.icStatus !== "undefined" && Object.keys(this.props.icStatus).map(statusKey => ( - typeof this.props.icStatus[statusKey] === 'object' ? + typeof icStatus !== "undefined" && Object.keys(icStatus).map(statusKey => ( + typeof icStatus[statusKey] === 'object' ? (
{ - Object.keys(this.props.icStatus[statusKey]).map(key => ( - typeof this.props.icStatus[statusKey][key] === 'object' ? + Object.keys(icStatus[statusKey]).map(key => ( + typeof icStatus[statusKey][key] === 'object' ? (
- {Object.keys(this.props.icStatus[statusKey][key]).map(index => ( -
{index + ": " + this.props.icStatus[statusKey][key][index]}
+ {Object.keys(icStatus[statusKey][key]).map(index => ( +
{index + ": " + icStatus[statusKey][key][index]}
))}
) : - (
{key + ": " + this.props.icStatus[statusKey][key]}
) + (
{key + ": " + icStatus[statusKey][key]}
) )) }
) : - (
{statusKey + ": " + this.props.icStatus[statusKey]}
) + (
{statusKey + ": " + icStatus[statusKey]}
) )) } -
Graph:
+ + + +
Graph:
{objectURL !== '' ? ( this.graphError(e)} alt={"Error"} src={objectURL} /> @@ -102,14 +111,18 @@ class ICDialog extends React.Component { Error )}
- -
Controls
- - + +
Controls:
+
+ + +
this.closeConfirmModal(c)} /> - + +
+
); From 4adc277eb319e52cae790628462cb2cd800560b1 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Mon, 28 Dec 2020 14:27:34 +0100 Subject: [PATCH 010/127] WIP: change button color scheme #276 --- src/common/dialogs/dialog.js | 10 ++++-- src/dashboard/dashboard-button-group.js | 6 ++-- src/dashboard/dashboard.js | 2 +- src/ic/ic-action.js | 2 +- src/ic/ics.js | 4 ++- src/scenario/scenario.js | 15 +++++++-- src/scenario/scenarios.js | 4 ++- src/user/login-form.js | 2 +- src/user/user.js | 45 +++++++++++++------------ src/user/users.js | 2 +- src/widget/toolbox-item.js | 2 +- src/widget/widget-toolbox.js | 32 +++++++++++++----- 12 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/common/dialogs/dialog.js b/src/common/dialogs/dialog.js index 4cc15af..741ecaf 100644 --- a/src/common/dialogs/dialog.js +++ b/src/common/dialogs/dialog.js @@ -45,6 +45,12 @@ class Dialog extends React.Component { } render() { + const buttonStyle = { + backgroundColor: '#527984', + borderColor: '#527984' + }; + + return ( @@ -56,8 +62,8 @@ class Dialog extends React.Component { - {this.props.blendOutCancel?
: } - + {this.props.blendOutCancel?
: } +
); diff --git a/src/dashboard/dashboard-button-group.js b/src/dashboard/dashboard-button-group.js index 7f49292..9f3e1a0 100644 --- a/src/dashboard/dashboard-button-group.js +++ b/src/dashboard/dashboard-button-group.js @@ -25,11 +25,13 @@ class DashboardButtonGroup extends React.Component { const buttonStyle = { marginLeft: '12px', height: '44px', - width : '35px' + width : '35px', + borderColor: '#ffffff', + backgroundColor: '#ffffff' }; const iconStyle = { - color: '#007bff', + color: '#527984', height: '25px', width : '25px' } diff --git a/src/dashboard/dashboard.js b/src/dashboard/dashboard.js index 19bfd36..9f94192 100644 --- a/src/dashboard/dashboard.js +++ b/src/dashboard/dashboard.js @@ -478,7 +478,7 @@ class Dashboard extends Component { return
- {this.state.dashboard.name} +

{this.state.dashboard.name}

- +
; diff --git a/src/ic/ics.js b/src/ic/ics.js index 0d43077..2bf666f 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -328,7 +328,9 @@ class InfrastructureComponents extends Component { render() { const buttonStyle = { - marginLeft: '10px' + marginLeft: '10px', + backgroundColor: '#527984', + borderColor: '#527984' }; return ( diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index c19cfaa..3e6bf83 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -596,15 +596,23 @@ class Scenario extends React.Component { } const buttonStyle = { - marginLeft: '10px' + marginLeft: '10px', + backgroundColor: '#527984', + borderColor: '#527984' }; + const altButtonStyle = { + marginLeft: '10px', + backgroundColor: '#ffffff', + borderColor: 'ffffff' + } + const tableHeadingStyle = { paddingTop: '30px' } const iconStyle = { - color: '#007bff', + color: '#527984', height: '25px', width: '25px' } @@ -616,7 +624,7 @@ class Scenario extends React.Component { return
Add, edit or delete files of scenario } > - @@ -785,6 +793,7 @@ class Scenario extends React.Component { diff --git a/src/scenario/scenarios.js b/src/scenario/scenarios.js index d176d0f..64ad762 100644 --- a/src/scenario/scenarios.js +++ b/src/scenario/scenarios.js @@ -236,7 +236,9 @@ class Scenarios extends Component { render() { const buttonStyle = { - marginLeft: '10px' + marginLeft: '10px', + backgroundColor: '#527984', + borderColor: '#527984' }; return ( diff --git a/src/user/login-form.js b/src/user/login-form.js index 1c2fc5e..a270977 100644 --- a/src/user/login-form.js +++ b/src/user/login-form.js @@ -91,7 +91,7 @@ class LoginForm extends Component { - + diff --git a/src/user/user.js b/src/user/user.js index 850b663..974f7f2 100644 --- a/src/user/user.js +++ b/src/user/user.js @@ -17,7 +17,7 @@ import React, { Component } from 'react'; import { Container } from 'flux/utils'; -import {Button, Col, Row} from 'react-bootstrap'; +import {Button, Col, Row, FormGroup, FormLabel} from 'react-bootstrap'; import AppDispatcher from '../common/app-dispatcher'; import UsersStore from './users-store'; @@ -121,6 +121,16 @@ class User extends Component { render() { + const iconStyle = { + color: '#527984', + } + + const buttonStyle = { + margin: '10px', + borderColor: '#ffffff', + backgroundColor: '#ffffff' + } + return (

Your User Account

@@ -128,27 +138,23 @@ class User extends Component { {this.state.currentUser !== undefined && this.state.currentUser !== null ?
+ - Username: - {this.state.currentUser.username} + +
Username:
+
E-mail:
+
Role:
+
+ +
{this.state.currentUser.username}
+
{this.state.currentUser.mail}
+
{this.state.currentUser.role}
+ +
- - - E-mail: - {this.state.currentUser.mail} - - - - Role: - {this.state.currentUser.role} - - - - - this.closeEditModal(data)} - user={this.state.currentUser}/> + user={this.state.currentUser} /> : "Loading user data..." } @@ -157,8 +163,5 @@ class User extends Component { } } - - - let fluxContainerConverter = require('../common/FluxContainerConverter'); export default Container.create(fluxContainerConverter.convert(User)); diff --git a/src/user/users.js b/src/user/users.js index f6f03df..acde3bf 100644 --- a/src/user/users.js +++ b/src/user/users.js @@ -146,7 +146,7 @@ class Users extends Component { this.setState({ editModal: true, modalData: this.state.users[index] })} onDelete={index => this.setState({ deleteModal: true, modalData: this.state.users[index] })} /> - + this.closeNewModal(data)} /> this.closeEditModal(data)} user={this.state.modalData} /> diff --git a/src/widget/toolbox-item.js b/src/widget/toolbox-item.js index f1ebb93..e2aa402 100644 --- a/src/widget/toolbox-item.js +++ b/src/widget/toolbox-item.js @@ -51,7 +51,7 @@ class ToolboxItem extends React.Component { if (this.props.disabled === false) { return this.props.connectDragSource(
- + {this.props.icon && } {this.props.name} diff --git a/src/widget/widget-toolbox.js b/src/widget/widget-toolbox.js index 9b057ec..a2d231a 100644 --- a/src/widget/widget-toolbox.js +++ b/src/widget/widget-toolbox.js @@ -96,11 +96,25 @@ class WidgetToolbox extends React.Component { const disableDecrease = this.disableDecrease(); // Only one topology widget at the time is supported const iconStyle = { - color: '#007bff', + color: '#527984', height: '25px', width : '25px' } + const buttonStyle = { + marginRight: '3px', + height: '40px', + borderColor: '#527984', + backgroundColor: '#527984' + } + + const altButtonStyle = { + marginRight: '3px', + height: '40px', + borderColor: '#ffffff', + backgroundColor: '#ffffff' + } + const thereIsTopologyWidget = this.props.widgets != null && Object.values(this.props.widgets).filter(w => w.type === 'Topology').length > 0; const topologyItemMsg = thereIsTopologyWidget? 'Currently only one is supported' : ''; @@ -110,16 +124,16 @@ class WidgetToolbox extends React.Component {
Show/ hide available Cosmetic Widgets } > - + Show/ hide available Displaying Widgets } > - + Show/ hide available Manipulation Widgets } > - +
@@ -128,12 +142,12 @@ class WidgetToolbox extends React.Component { Grid: { this.props.grid > 1 ? this.props.grid : 'Disabled' } Increase dashboard height } > - Decrease dashboard height } > - From a6d9edde593a24d722d7dfd92f6e91879ff1b19a Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Mon, 28 Dec 2020 14:48:33 +0100 Subject: [PATCH 011/127] WIP: change DropdownButton color --- src/ic/ic-action.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ic/ic-action.js b/src/ic/ic-action.js index 82dea64..3647662 100644 --- a/src/ic/ic-action.js +++ b/src/ic/ic-action.js @@ -76,7 +76,7 @@ class ICAction extends React.Component { onChange={this.setDelayForAction} /> - + {actionList} From cbec6fe88a014712b1b1e6bd88b3d30d0a570df9 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Tue, 29 Dec 2020 16:36:25 +0100 Subject: [PATCH 012/127] solve warning and error --- src/ic/ic-dialog.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 9b29d9c..6e225a5 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -16,9 +16,18 @@ class ICDialog extends React.Component { this.state = { confirmCommand: false, command: '', + icStatus: {} }; } + static getDerivedStateFromProps(props, state){ + if(typeof props.icStatus !== 'undefined'){ + return {icStatus: props.icStatus} + } else { + return {} + } + } + onClose(canceled) { this.props.onClose(); } @@ -47,7 +56,7 @@ class ICDialog extends React.Component { render() { - let icStatus = this.props.icStatus; + let icStatus = this.state.icStatus; delete icStatus['icID']; let objectURL='' @@ -72,32 +81,32 @@ class ICDialog extends React.Component { { typeof icStatus !== "undefined" && Object.keys(icStatus).map(statusKey => ( typeof icStatus[statusKey] === 'object' ? - (
+ (
{ Object.keys(icStatus[statusKey]).map(key => ( typeof icStatus[statusKey][key] === 'object' ? - (
+ (
{Object.keys(icStatus[statusKey][key]).map(index => ( -
{index + ": " + icStatus[statusKey][key][index]}
+
{index + ": " + icStatus[statusKey][key][index]}
))}
) : - (
{key + ": " + icStatus[statusKey][key]}
) + (
{key + ": " + icStatus[statusKey][key]}
) )) }
) : - (
{statusKey + ": " + icStatus[statusKey]}
) + (
{statusKey + ": " + icStatus[statusKey]}
) )) } From aef3694d1d1e8ee096fc3c3d9ca8c7f7e081f81c Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Wed, 30 Dec 2020 17:30:50 +0100 Subject: [PATCH 013/127] change link color scheme, resolve warnings, change account site layout #276 --- src/common/dialogs/delete-dialog.js | 2 +- src/dashboard/edit-dashboard.js | 2 +- src/dashboard/new-dashboard.js | 2 +- src/ic/ics.js | 2 +- src/ic/import-ic.js | 4 ++-- src/scenario/scenario.js | 2 +- src/styles/app.css | 4 ++++ src/user/user.js | 4 ++-- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common/dialogs/delete-dialog.js b/src/common/dialogs/delete-dialog.js index 8d114f4..179b3cd 100644 --- a/src/common/dialogs/delete-dialog.js +++ b/src/common/dialogs/delete-dialog.js @@ -42,7 +42,7 @@ class DeleteDialog extends React.Component { - + ; diff --git a/src/dashboard/edit-dashboard.js b/src/dashboard/edit-dashboard.js index cb062ce..2df61bf 100644 --- a/src/dashboard/edit-dashboard.js +++ b/src/dashboard/edit-dashboard.js @@ -73,7 +73,7 @@ class EditDashboardDialog extends React.Component { return ( this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
- + Name this.handleChange(e)} /> diff --git a/src/dashboard/new-dashboard.js b/src/dashboard/new-dashboard.js index 6f4b9da..a023ba4 100644 --- a/src/dashboard/new-dashboard.js +++ b/src/dashboard/new-dashboard.js @@ -69,7 +69,7 @@ class NewDashboardDialog extends React.Component { return ( this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> - + Name this.handleChange(e)} /> diff --git a/src/ic/ics.js b/src/ic/ics.js index 2bf666f..2074f5d 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -320,7 +320,7 @@ class InfrastructureComponents extends Component { let ic = this.state.ics.find(ic => ic.name === name); let index = this.state.ics.indexOf(ic); if(ic.type === "villas-node" || ic.type === "villas-relay"){ - return } + return } else{ return {name} } diff --git a/src/ic/import-ic.js b/src/ic/import-ic.js index bde1d9a..a165af6 100644 --- a/src/ic/import-ic.js +++ b/src/ic/import-ic.js @@ -118,7 +118,7 @@ class ImportICDialog extends React.Component { this.loadFile(e.target.files)} /> - + Name this.handleChange(e)} /> @@ -128,7 +128,7 @@ class ImportICDialog extends React.Component { this.handleChange(e)} /> - + UUID this.handleChange(e)} /> diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index 3e6bf83..185590a 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -604,7 +604,7 @@ class Scenario extends React.Component { const altButtonStyle = { marginLeft: '10px', backgroundColor: '#ffffff', - borderColor: 'ffffff' + borderColor: '#ffffff' } const tableHeadingStyle = { diff --git a/src/styles/app.css b/src/styles/app.css index a2d887d..c607fb0 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -388,3 +388,7 @@ body { .badge-outdated { opacity: 0.4; } + +a:link, a:active, a:visited , a:hover { + color: #047cab ; +} diff --git a/src/user/user.js b/src/user/user.js index 974f7f2..72428ba 100644 --- a/src/user/user.js +++ b/src/user/user.js @@ -140,12 +140,12 @@ class User extends Component { - +
Username:
E-mail:
Role:
- +
{this.state.currentUser.username}
{this.state.currentUser.mail}
{this.state.currentUser.role}
From e45de4fc643981342ee5594a502f9973919412f2 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 3 Jan 2021 18:54:28 +0100 Subject: [PATCH 014/127] Change link and button color scheme #276 --- src/file/edit-files.js | 1 + src/ic/ic-action.js | 13 ++++++++----- src/signal/edit-signal-mapping.js | 4 +++- src/user/login-form.js | 2 +- .../edit-widget-color-zones-control.js | 16 ++++++++++++++-- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/file/edit-files.js b/src/file/edit-files.js index 8cc3421..585db3e 100644 --- a/src/file/edit-files.js +++ b/src/file/edit-files.js @@ -145,6 +145,7 @@ class EditFilesDialog extends React.Component { @@ -87,4 +90,4 @@ class ICAction extends React.Component { } } -export default ICAction; +export default ICAction; \ No newline at end of file diff --git a/src/signal/edit-signal-mapping.js b/src/signal/edit-signal-mapping.js index 82a8442..10980ae 100644 --- a/src/signal/edit-signal-mapping.js +++ b/src/signal/edit-signal-mapping.js @@ -176,7 +176,9 @@ class EditSignalMapping extends React.Component { render() { const buttonStyle = { - marginLeft: '10px' + marginLeft: '10px', + backgroundColor: '#527984', + borderColor: '#527984' }; return( diff --git a/src/user/login-form.js b/src/user/login-form.js index a270977..e828de3 100644 --- a/src/user/login-form.js +++ b/src/user/login-form.js @@ -92,7 +92,7 @@ class LoginForm extends Component { - + diff --git a/src/widget/edit-widget/edit-widget-color-zones-control.js b/src/widget/edit-widget/edit-widget-color-zones-control.js index 4ade39f..1fca8d3 100644 --- a/src/widget/edit-widget/edit-widget-color-zones-control.js +++ b/src/widget/edit-widget/edit-widget-color-zones-control.js @@ -175,6 +175,18 @@ class EditWidgetColorZonesControl extends React.Component { render() { + const buttonStyle = { + marginBottom: '10px', + marginLeft: '120px', + borderColor: '#ffffff', + backgroundColor: '#ffffff' + }; + + const iconStyle = { + color: '#527984', + height: '25px', + width : '25px' + } let tempColor = 'FFFFFF'; let collapse = false; @@ -192,7 +204,7 @@ class EditWidgetColorZonesControl extends React.Component { return Color zones - +
{ @@ -245,7 +257,7 @@ class EditWidgetColorZonesControl extends React.Component { - + this.closeEditModal(data)} widget={this.state.widget} zoneIndex={this.state.selectedIndex} controlId={'strokeStyle'} /> From 3a5d828d344ae58729c4fe0772ebe08fbcd83c8b Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 5 Jan 2021 12:03:35 +0100 Subject: [PATCH 015/127] update package-lock --- package-lock.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 745cbea..4479160 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3087,7 +3087,7 @@ }, "asynckit": { "version": "0.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "at-least-node": { @@ -4540,7 +4540,7 @@ }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { @@ -5234,7 +5234,7 @@ }, "delayed-stream": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { @@ -5575,7 +5575,7 @@ }, "encoding": { "version": "0.1.12", - "resolved": false, + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { "iconv-lite": "~0.4.13" @@ -8071,9 +8071,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "internal-ip": { "version": "4.3.0", @@ -11097,7 +11097,7 @@ }, "methods": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "microevent.ts": { @@ -11865,7 +11865,7 @@ }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { @@ -12308,7 +12308,7 @@ }, "performance-now": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { @@ -14416,7 +14416,7 @@ }, "react-textarea-autosize": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", "integrity": "sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==", "requires": { "prop-types": "^15.6.0" @@ -17161,7 +17161,7 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { From d4ec861103628e463234c00953ee08adf1b9852e Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 5 Jan 2021 15:54:08 +0100 Subject: [PATCH 016/127] Add TODO for VILLASnode status updates --- src/ic/ic-status-store.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ic/ic-status-store.js b/src/ic/ic-status-store.js index 3f701ef..62d6d08 100644 --- a/src/ic/ic-status-store.js +++ b/src/ic/ic-status-store.js @@ -34,6 +34,10 @@ class ICStatusStore extends ArrayStore { case 'ic-status/status-received': let tempData = action.data; tempData.icID = action.icid; + + // TODO: edit state (e.g. running) of IC according to received state (only for ICs that are NOT managed externally) + // TODO: playback state info to backend using start-edit dispatch for IC + return this.updateElements(state, [tempData]); case 'ic-status/status-error': From 95f47a3557c01a568ac69bd7e4e9c23ca3a58a8a Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 10 Jan 2021 19:04:47 +0100 Subject: [PATCH 017/127] IC control buttons only available for admins #265 --- src/ic/ic-dialog.js | 5 ++++- src/ic/ics.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 6e225a5..68276e7 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -121,12 +121,15 @@ class ICDialog extends React.Component { )}
- + {this.props.userRole === "Admin"? ( +
Controls:
+
) + : (
) } this.closeConfirmModal(c)} /> diff --git a/src/ic/ics.js b/src/ic/ics.js index aa0fffa..a1e494c 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -436,7 +436,7 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/> + this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} userRole={this.state.currentUser.role} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/> this.closeDeleteModal(e)} />
From 352fc67a743df69158813533b2c58a9b1877fadf Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 12 Jan 2021 13:36:36 +0100 Subject: [PATCH 018/127] White background in dashboard fullscreen mode, show compress and pause buttons in fullscreen mode, closes #154 --- package-lock.json | 16 ++--- src/dashboard/dashboard-button-group.js | 87 ++++++++++++++----------- src/styles/app.css | 1 + 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index 745cbea..28f8bab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3087,7 +3087,7 @@ }, "asynckit": { "version": "0.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "at-least-node": { @@ -4540,7 +4540,7 @@ }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { @@ -5234,7 +5234,7 @@ }, "delayed-stream": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { @@ -11097,7 +11097,7 @@ }, "methods": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "microevent.ts": { @@ -11865,7 +11865,7 @@ }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { @@ -12308,7 +12308,7 @@ }, "performance-now": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { @@ -14416,7 +14416,7 @@ }, "react-textarea-autosize": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", "integrity": "sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==", "requires": { "prop-types": "^15.6.0" @@ -17161,7 +17161,7 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { diff --git a/src/dashboard/dashboard-button-group.js b/src/dashboard/dashboard-button-group.js index 7f49292..b2df4de 100644 --- a/src/dashboard/dashboard-button-group.js +++ b/src/dashboard/dashboard-button-group.js @@ -24,28 +24,28 @@ class DashboardButtonGroup extends React.Component { render() { const buttonStyle = { marginLeft: '12px', - height: '44px', + height: '44px', width : '35px' }; const iconStyle = { color: '#007bff', - height: '25px', + height: '25px', width : '25px' } const buttons = []; let key = 0; - if (this.props.fullscreen) { + /*if (this.props.fullscreen) { return null; - } + }*/ if (this.props.editing) { buttons.push( Save changes } > , Discard changes } > @@ -59,17 +59,25 @@ class DashboardButtonGroup extends React.Component { buttons.push( Change to fullscreen view } > ); + } else { + buttons.push( + Back to normal view } > + + + ); } if (this.props.paused) { buttons.push( Continue simulation } > ); @@ -77,44 +85,49 @@ class DashboardButtonGroup extends React.Component { buttons.push( Pause simulation } > ); } - buttons.push( - Add, edit or delete files of scenario } > - - - ); + if (this.props.fullscreen !== true) { + buttons.push( + Add, edit or delete files of scenario }> + + + ); - buttons.push( - Add, edit or delete input signals } > - - - ); + buttons.push( + Add, edit or delete input signals }> + + + ); - buttons.push( - Add, edit or delete output signals } > - - - ); - - buttons.push( - Add widgets and edit layout } > - - - ); + buttons.push( + Add, edit or delete output signals }> + + + ); + buttons.push( + Add widgets and edit layout }> + + + ); + } } return
diff --git a/src/styles/app.css b/src/styles/app.css index a2d887d..50ddfda 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -323,6 +323,7 @@ body { display: -webkit-flex; display: flex; flex-direction: column; + background-color: #FFFFFF; } .box-header { From 8eefcc31e7d139395f7b1d341bc174dae1ecec28 Mon Sep 17 00:00:00 2001 From: irismarie Date: Wed, 13 Jan 2021 09:55:38 +0100 Subject: [PATCH 019/127] WIP --- package-lock.json | 13 +++++++++++++ package.json | 1 + src/common/table.js | 3 ++- src/signal/edit-signal-mapping.js | 18 ++++++++++++++---- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10f2405..0d23ec6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1184,6 +1184,14 @@ "minimist": "^1.2.0" } }, + "@createnl/grouped-checkboxes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@createnl/grouped-checkboxes/-/grouped-checkboxes-1.1.2.tgz", + "integrity": "sha512-F4SoFF7UwktrpVeJPs/cQFP5Nn+1wpUm5v1/vzUcgseDGxvjXjLrs+F99ivH/TA3VDJbeFdBPi7gymmw2cessQ==", + "requires": { + "lodash.debounce": "^4.0.8" + } + }, "@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -10897,6 +10905,11 @@ "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", diff --git a/package.json b/package.json index 219d249..bb16f31 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@fortawesome/fontawesome-svg-core": "^1.2.32", "@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/react-fontawesome": "^0.1.12", + "@createnl/grouped-checkboxes": "^1.1.2", "babel-runtime": "^6.26.0", "bootstrap": "^4.5.3", "bufferutil": "^4.0.1", diff --git a/src/common/table.js b/src/common/table.js index 12d0ebf..a44be38 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -34,7 +34,8 @@ class CustomTable extends Component { } static defaultProps = { - width: null + width: null, + checked: true }; onClick(event, row, column) { diff --git a/src/signal/edit-signal-mapping.js b/src/signal/edit-signal-mapping.js index 82a8442..e977854 100644 --- a/src/signal/edit-signal-mapping.js +++ b/src/signal/edit-signal-mapping.js @@ -134,6 +134,10 @@ class EditSignalMapping extends React.Component { }; + handleRemove = () => { + + } + handleAdd = (configID = null) => { if(typeof this.props.configs !== "undefined"){ @@ -173,6 +177,10 @@ class EditSignalMapping extends React.Component { this.setState({signals: signals}) } + onSignalChecked(index, signal) { + console.log(index); + } + render() { const buttonStyle = { @@ -185,7 +193,7 @@ class EditSignalMapping extends React.Component { show={this.props.show} title="Edit Signal Mapping" buttonTitle="Save" - blendOutCancel = {true} + blendOutCancel = {false} onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={true} @@ -194,7 +202,8 @@ class EditSignalMapping extends React.Component { {this.props.direction} Mapping Click Index, Name or Unit cell to edit - +
this.onSignalChecked(index, event)} data={this.state.signals}> + this.onSignalChecked(index, event)} width='30' /> this.handleMappingChange(e, row, column)} /> this.handleMappingChange(e, row, column)} /> this.handleMappingChange(e, row, column)} /> @@ -202,8 +211,9 @@ class EditSignalMapping extends React.Component { this.handleDelete(index)} />
-
- +
+ +
From 971fe750fa8c512d2283f8c7a179c6b4dd99be09 Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 14:28:25 +0100 Subject: [PATCH 020/127] add file buttons for result table --- src/common/table.js | 69 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/common/table.js b/src/common/table.js index 12d0ebf..ed936e8 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -29,7 +29,7 @@ class CustomTable extends Component { this.state = { rows: CustomTable.getRows(props), - editCell: [ -1, -1 ] + editCell: [-1, -1] }; } @@ -38,7 +38,7 @@ class CustomTable extends Component { }; onClick(event, row, column) { - this.setState({ editCell: [ column, row ]}); // x, y + this.setState({ editCell: [column, row] }); // x, y } static addCell(data, index, child) { @@ -71,6 +71,12 @@ class CustomTable extends Component { cell.push({content}); } else if (child.props.clickable) { cell.push(); + } else if (linkKey == 'filebuttons') { + content.forEach(element => { + cell.push(Download {element}} > + ); + }); } else { cell.push(content); } @@ -90,8 +96,8 @@ class CustomTable extends Component { cell.push(   - {labelContent} - + {labelContent} + ); } @@ -103,14 +109,14 @@ class CustomTable extends Component { // add buttons if (child.props.editButton) { - let disable = (typeof data.managedexternally !== "undefined" && data.managedexternally); - cell.push({disable? "Externally managed ICs cannot be edited" : "edit"} } > - ); + let disable = (typeof data.managedexternally !== "undefined" && data.managedexternally); + cell.push({disable ? "Externally managed ICs cannot be edited" : "edit"} } > + ); } if (child.props.deleteButton) { cell.push( Delete } > - ); + ); } if (child.props.checkbox) { @@ -121,18 +127,23 @@ class CustomTable extends Component { if (child.props.exportButton) { cell.push( Export } > - ); + ); } if (child.props.duplicateButton) { cell.push( Duplicate } > - ); + ); + } + + if (child.props.downloadAllButton) { + cell.push(Download All Files} > + ); } return cell; } // addCell - static getDerivedStateFromProps(props, state){ + static getDerivedStateFromProps(props, state) { const rows = CustomTable.getRows(props); return { rows }; @@ -147,12 +158,12 @@ class CustomTable extends Component { onCellFocus(index) { // When a cell focus is detected, update the current state in order to uncover the input element - this.setState({ editCell: [ index.cell, index.row ]}); + this.setState({ editCell: [index.cell, index.row] }); } cellLostFocus() { // Reset cell selection state - this.setState({ editCell: [ -1, -1 ] }); + this.setState({ editCell: [-1, -1] }); } static getRows(props) { @@ -164,7 +175,7 @@ class CustomTable extends Component { // check if multiple columns if (Array.isArray(props.children) === false) { // table only has a single column - return [ CustomTable.addCell(data, index, props.children) ]; + return [CustomTable.addCell(data, index, props.children)]; } const row = []; @@ -181,7 +192,7 @@ class CustomTable extends Component { // get children let children = this.props.children; if (Array.isArray(this.props.children) === false) { - children = [ children ]; + children = [children]; } return ( @@ -200,28 +211,28 @@ class CustomTable extends Component { let isCellInlineEditable = children[cellIndex].props.inlineEditable === true; - let tabIndex = isCellInlineEditable? 0 : -1; + let tabIndex = isCellInlineEditable ? 0 : -1; let evtHdls = isCellInlineEditable ? { onCellClick: (event) => this.onClick(event, rowIndex, cellIndex), - onCellFocus: () => this.onCellFocus({cell: cellIndex, row: rowIndex}), + onCellFocus: () => this.onCellFocus({ cell: cellIndex, row: rowIndex }), onCellBlur: () => this.cellLostFocus() } : { - onCellClick: () => {}, - onCellFocus: () => {}, - onCellBlur: () => {} - }; + onCellClick: () => { }, + onCellFocus: () => { }, + onCellBlur: () => { } + }; - return ( - {(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex ) ? ( + return ( + {(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex) ? ( children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} ref={ref => { this.activeInput = ref; }} /> ) : ( - - {cell.map((element, elementIndex) => ( - {element} - ))} - - )} + + {cell.map((element, elementIndex) => ( + {element} + ))} + + )} ) }) } From 374e1d09f1f68af5084c799d3f9f2bb4a07c410d Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 16:35:24 +0100 Subject: [PATCH 021/127] get results from backend --- src/result/result-store.js | 33 ++++++++++++++++++++++++++ src/result/results-data-manager.js | 29 ++++++++++++++++++++++ src/scenario/scenarios-data-manager.js | 6 +++++ 3 files changed, 68 insertions(+) create mode 100644 src/result/result-store.js create mode 100644 src/result/results-data-manager.js diff --git a/src/result/result-store.js b/src/result/result-store.js new file mode 100644 index 0000000..fb6ab36 --- /dev/null +++ b/src/result/result-store.js @@ -0,0 +1,33 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + + +import ArrayStore from '../common/array-store'; +import ResultsDataManager from './results-data-manager'; + +class ResultStore extends ArrayStore { + constructor() { + super('results', ResultsDataManager); + } + + reduce(state, action) { + return super.reduce(state, action); + } + +} + +export default new ResultStore(); \ No newline at end of file diff --git a/src/result/results-data-manager.js b/src/result/results-data-manager.js new file mode 100644 index 0000000..b30c9c1 --- /dev/null +++ b/src/result/results-data-manager.js @@ -0,0 +1,29 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + + +import RestDataManager from '../common/data-managers/rest-data-manager'; + +class ResultsDataManager extends RestDataManager{ + + constructor() { + super('result', '/results'); + } + +} + +export default new ResultsDataManager() \ No newline at end of file diff --git a/src/scenario/scenarios-data-manager.js b/src/scenario/scenarios-data-manager.js index 760493b..e440e92 100644 --- a/src/scenario/scenarios-data-manager.js +++ b/src/scenario/scenarios-data-manager.js @@ -108,6 +108,12 @@ class ScenariosDataManager extends RestDataManager { token: token, param: '?scenarioID=' + scenario.id, }); + + AppDispatcher.dispatch({ + type: 'results/start-load', + token: token, + param: '?scenarioID=' + scenario.id + }) } } } From d8b320dc0e8260f24293a8d2022c7438809eb47d Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 17:06:19 +0100 Subject: [PATCH 022/127] added result table --- src/scenario/scenario.js | 101 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index c19cfaa..c5b90c3 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -36,6 +36,8 @@ import ImportDashboardDialog from "../dashboard/import-dashboard"; import NewDashboardDialog from "../dashboard/new-dashboard"; import EditDashboardDialog from '../dashboard/edit-dashboard'; import EditFiles from '../file/edit-files' +import NewResultDialog from '../result/new-result'; +import EditResultDialog from '../result/edit-result' import ICAction from '../ic/ic-action'; import DeleteDialog from '../common/dialogs/delete-dialog'; @@ -43,13 +45,14 @@ import EditConfigDialog from "../componentconfig/edit-config"; import EditSignalMapping from "../signal/edit-signal-mapping"; import FileStore from "../file/file-store" import WidgetStore from "../widget/widget-store"; +import ResultStore from "../result/result-store" import { Redirect } from 'react-router-dom'; import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; class Scenario extends React.Component { static getStores() { - return [ScenarioStore, ConfigStore, DashboardStore, ICStore, SignalStore, FileStore, WidgetStore]; + return [ScenarioStore, ConfigStore, DashboardStore, ICStore, SignalStore, FileStore, WidgetStore, ResultStore]; } static calculateState(prevState, props) { @@ -68,7 +71,6 @@ class Scenario extends React.Component { }); } - // obtain all component configurations of a scenario let configs = ConfigStore.getState().filter(config => config.scenarioID === parseInt(props.match.params.scenario, 10)); let editConfigModal = prevState.editConfigModal || false; @@ -82,8 +84,11 @@ class Scenario extends React.Component { modalConfigIndex = index; } + let results = ResultStore.getState().filter(result => result.scenarioID === parseInt(props.match.params.scenario, 10)); + return { scenario, + results, sessionToken, configs, editConfigModal, @@ -103,6 +108,11 @@ class Scenario extends React.Component { filesEditModal: prevState.filesEditModal || false, filesEditSaveState: prevState.filesEditSaveState || [], + editResultsModal: false, + modalResultsData: {}, + modalResultsIndex: 0, + newResultModal: false, + editOutputSignalsModal: prevState.editOutputSignalsModal || false, editInputSignalsModal: prevState.editInputSignalsModal || false, @@ -375,6 +385,7 @@ class Scenario extends React.Component { closeNewDashboardModal(data) { this.setState({ newDashboardModal: false }); if (data) { + // TODO: 'newDashboard' not used, check this let newDashboard = data; // add default grid value and scenarioID newDashboard["grid"] = 15; @@ -495,11 +506,11 @@ class Scenario extends React.Component { // TODO do we need this if the dispatches happen in the dialog? } - signalsAutoConf(index){ + signalsAutoConf(index) { let componentConfig = this.state.configs[index]; // determine apiurl of infrastructure component let ic = this.state.ics.find(ic => ic.id === componentConfig.icID) - if(!ic.type.includes("villas-node")){ + if (!ic.type.includes("villas-node")) { let message = "Cannot autoconfigure signals for IC type " + ic.type + " of category " + ic.category + ". This is only possible for gateway ICs of type 'VILLASnode'." console.warn(message); @@ -516,8 +527,8 @@ class Scenario extends React.Component { AppDispatcher.dispatch({ type: 'signals/start-autoconfig', - url: ic.apiurl+"/nodes", - socketname: splitWebsocketURL[splitWebsocketURL.length -1], + url: ic.apiurl + "/nodes", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], token: this.state.sessionToken, configID: componentConfig.id }); @@ -525,7 +536,7 @@ class Scenario extends React.Component { } uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { // eslint-disable-next-line var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); @@ -554,6 +565,41 @@ class Scenario extends React.Component { return fileList; } + /* ############################################## + * Result modification methods + ############################################## */ + + closeNewResultModal(data) { + console.log(this.state.results); + console.log(data); + this.setState({ newResultModal: false }); + if (data) { + data["scenarioID"] = this.state.scenario.id; + AppDispatcher.dispatch({ + type: 'results/start-add', + data, + token: this.state.sessionToken, + }); + } + } + + closeEditResultsModal(data) { + console.log(data); + this.setState({ editResultsModal: false }); + + if (data) { + + } + } + + downloadData(index) { + console.log(index); + } + + closeDeleteResultsModal(e) { + + } + startPintura(configIndex) { let config = this.state.configs[configIndex]; @@ -585,6 +631,7 @@ class Scenario extends React.Component { } } + /* ############################################## * Render method ############################################## */ @@ -613,6 +660,40 @@ class Scenario extends React.Component { return

Loading Scenario...

; } + let resulttable; + if (this.state.results && this.state.results.length > 0) { + resulttable =
+

Results

+
+ + + + + + this.downloadData(index)} + /> + this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} + onDownloadAll={(index) => this.downloadData(this.state.results[index])} + onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} + /> +
+ + this.closeEditResultsModal(data)} /> + this.closeDeleteResultsModal(e)} /> +
+ } + return
Add, edit or delete files of scenario } > @@ -760,6 +841,12 @@ class Scenario extends React.Component { this.closeDeleteDashboardModal(e)} /> + {/*Result table*/} + {resulttable} +
+ +
+ this.closeNewResultModal(data)} /> {/*Scenario Users table*/}

Users sharing this scenario

From 00aa00dd1217ebc3d903d5dab18dbfd6edc2462b Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 17:07:05 +0100 Subject: [PATCH 023/127] added result dialogs --- src/result/edit-result.js | 131 ++++++++++++++++++++++++++++++++++++++ src/result/new-result.js | 81 +++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 src/result/edit-result.js create mode 100644 src/result/new-result.js diff --git a/src/result/edit-result.js b/src/result/edit-result.js new file mode 100644 index 0000000..d7acedc --- /dev/null +++ b/src/result/edit-result.js @@ -0,0 +1,131 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap'; +import Table from "../common/table"; + +import Dialog from '../common/dialogs/dialog'; +import ParametersEditor from '../common/parameters-editor'; + +class EditResultDialog extends React.Component { + valid = true; + + constructor(props) { + super(props); + + this.state = { + configSnapshots: '', + description: '', + resultFileIDs: [], + }; + } + + onClose = canceled => { + if (canceled) { + if (this.props.onClose != null) { + this.props.onClose(); + } + return; + } + + if (this.valid && this.props.onClose != null) { + this.props.onClose(this.state); + } + }; + + handleChange = event => { + this.setState({ [event.target.id]: event.target.value }); + + let description = true; + + if (this.state.description === '') { + description = false; + } + + this.valid = description; + + }; + + resetState = () => { + this.setState({ + configSnapshots: this.props.configSnapshots, + description: this.props.configSnapshots, + resultFileIDs: this.props.resultFileIDs, + }); + }; + + handleStartParametersChange = startParameters => { + this.setState({ startParameters }); + }; + + // TODO: file reading necessary? or can it just be saved? + loadFile = event => { + // get file + const file = event.target.files[0]; + + // create file reader + let reader = new FileReader(); + let self = this; + + reader.onload = event => { + const config = JSON.parse(event.target.result); + + self.imported = true; + self.valid = true; + this.setState({filename: config.name, file: config }); + }; + + reader.readAsBinaryString(file); + } + + render() { + return + + + Description + + + + + + + + Add Result File + + + + + ; + } +} + +export default EditResultDialog; + +/* + + + + + this.deleteFile(index)} + /> +
+ + '*/ diff --git a/src/result/new-result.js b/src/result/new-result.js new file mode 100644 index 0000000..9c2c5d9 --- /dev/null +++ b/src/result/new-result.js @@ -0,0 +1,81 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with VILLASweb. If not, see . + ******************************************************************************/ + +import React from 'react'; +import { FormGroup, FormControl, FormLabel } from 'react-bootstrap'; + +import Dialog from '../common/dialogs/dialog'; + +class NewResultDialog extends React.Component { + + constructor(props) { + super(props); + + this.state = { + ConfigSnapshots: '', + Description: '', + ResultFileIDs: [], + } + } + + onClose(canceled) { + console.log("on close new result"); + if (canceled === false) { + //if (this.valid) { + this.props.onClose(this.state); + // } + } else { + this.props.onClose(); + } + } + + handleChange(e) { + console.log("handle change, e:"); + console.log(e); + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ + ConfigSnapshots: '', + Description: '', + ResultFileIDs: [], + }); + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={true}> +
+ + Config Snapshots + this.handleChange(e)} /> + + + + + Description + this.handleChange(e)} /> + + +
+
+ ); + } +} + +export default NewResultDialog; From 58ad66285b74e4e1dd886f23201ae425b924d917 Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 18:57:44 +0100 Subject: [PATCH 024/127] added add/remove file button --- src/common/table.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/common/table.js b/src/common/table.js index ed936e8..d0e974c 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -114,11 +114,6 @@ class CustomTable extends Component {
); } - if (child.props.deleteButton) { - cell.push( Delete } > - ); - } - if (child.props.checkbox) { const checkboxKey = child.props.checkboxKey; @@ -126,20 +121,30 @@ class CustomTable extends Component { } if (child.props.exportButton) { - cell.push( Export } > + cell.push( Export } > ); } if (child.props.duplicateButton) { - cell.push( Duplicate } > + cell.push( Duplicate } > ); } + if (child.props.addRemoveFilesButton) { + cell.push(Add/remove File(s)} > + ); + } + if (child.props.downloadAllButton) { cell.push(Download All Files} > ); } + if (child.props.deleteButton) { + cell.push( Delete } > + ); + } + return cell; } // addCell From e31c512c2ddd19bac78bc17f972ef933051de1e0 Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 18:59:18 +0100 Subject: [PATCH 025/127] delete result, changed result table --- src/scenario/scenario.js | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index c5b90c3..b89f5ef 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -39,6 +39,7 @@ import EditFiles from '../file/edit-files' import NewResultDialog from '../result/new-result'; import EditResultDialog from '../result/edit-result' + import ICAction from '../ic/ic-action'; import DeleteDialog from '../common/dialogs/delete-dialog'; import EditConfigDialog from "../componentconfig/edit-config"; @@ -112,6 +113,7 @@ class Scenario extends React.Component { modalResultsData: {}, modalResultsIndex: 0, newResultModal: false, + editFilesModal: false, editOutputSignalsModal: prevState.editOutputSignalsModal || false, editInputSignalsModal: prevState.editInputSignalsModal || false, @@ -596,8 +598,18 @@ class Scenario extends React.Component { console.log(index); } - closeDeleteResultsModal(e) { + closeDeleteResultsModal(confirmDelete) { + this.setState({ deleteResultsModal: false }); + if (confirmDelete === false) { + return; + } + + AppDispatcher.dispatch({ + type: 'results/start-remove', + data: this.state.modalResultsData, + token: this.state.sessionToken, + }); } startPintura(configIndex) { @@ -662,9 +674,7 @@ class Scenario extends React.Component { let resulttable; if (this.state.results && this.state.results.length > 0) { - resulttable =
-

Results

-
+ resulttable =
@@ -679,19 +689,21 @@ class Scenario extends React.Component { /> this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} + onAddRemove={(index) => this.setState({ editFilesModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index})} onDownloadAll={(index) => this.downloadData(this.state.results[index])} onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} />
this.closeEditResultsModal(data)} /> - this.closeDeleteResultsModal(e)} /> -
+ this.closeDeleteResultsModal(e)} /> +
} return
@@ -842,10 +854,18 @@ class Scenario extends React.Component { this.closeDeleteDashboardModal(e)} /> {/*Result table*/} +
+

Results + +

+ +
{resulttable} + {/*
+ */} this.closeNewResultModal(data)} /> {/*Scenario Users table*/} From 2b5f5568c842fa6f0c458c5b6e4ba8315f03ac4f Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 15 Jan 2021 19:17:02 +0100 Subject: [PATCH 026/127] minor changes --- src/result/edit-result.js | 15 ++++----------- src/result/new-result.js | 4 ---- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/result/edit-result.js b/src/result/edit-result.js index d7acedc..2b2fac6 100644 --- a/src/result/edit-result.js +++ b/src/result/edit-result.js @@ -32,6 +32,7 @@ class EditResultDialog extends React.Component { configSnapshots: '', description: '', resultFileIDs: [], + id:0, }; } @@ -50,22 +51,14 @@ class EditResultDialog extends React.Component { handleChange = event => { this.setState({ [event.target.id]: event.target.value }); - - let description = true; - - if (this.state.description === '') { - description = false; - } - - this.valid = description; - }; resetState = () => { this.setState({ configSnapshots: this.props.configSnapshots, - description: this.props.configSnapshots, + description: this.props.description, resultFileIDs: this.props.resultFileIDs, + id: this.props.id, }); }; @@ -94,7 +87,7 @@ class EditResultDialog extends React.Component { } render() { - return + return
Description diff --git a/src/result/new-result.js b/src/result/new-result.js index 9c2c5d9..d07f94e 100644 --- a/src/result/new-result.js +++ b/src/result/new-result.js @@ -35,17 +35,13 @@ class NewResultDialog extends React.Component { onClose(canceled) { console.log("on close new result"); if (canceled === false) { - //if (this.valid) { this.props.onClose(this.state); - // } } else { this.props.onClose(); } } handleChange(e) { - console.log("handle change, e:"); - console.log(e); this.setState({ [e.target.id]: e.target.value }); } From 3e74e0886164ff0d5dbec70001b23ddd475280ff Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 17 Jan 2021 16:06:13 +0100 Subject: [PATCH 027/127] Reformat IC status list --- src/ic/ic-dialog.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 68276e7..33cb9e5 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -63,6 +63,11 @@ class ICDialog extends React.Component { if(typeof this.props.icGraph !== "undefined") { objectURL = this.props.icGraph.objectURL } + + let spanStyle = { + display: 'inline-block', + width: '130px' + } return ( ( typeof icStatus[statusKey] === 'object' ? (
- { @@ -94,19 +99,19 @@ class ICDialog extends React.Component { {Object.keys(icStatus[statusKey][key]).map(index => ( -
{index + ": " + icStatus[statusKey][key][index]}
+
{index + ":"}{icStatus[statusKey][key][index]}
))}
) : - (
{key + ": " + icStatus[statusKey][key]}
) + (
{key + ":"}{icStatus[statusKey][key]}
) )) }
) : - (
{statusKey + ": " + icStatus[statusKey]}
) + (
{statusKey + ":"}{icStatus[statusKey]}
) )) } From 8edeb4b04c4dc0a64517d1a2612566f848439572 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 17 Jan 2021 18:47:08 +0100 Subject: [PATCH 028/127] edit state of IC according to received state #265 --- src/ic/ic-data-data-manager.js | 12 +++++++++++- src/ic/ic-status-store.js | 2 +- src/ic/ic-store.js | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index b26675c..087b294 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -44,15 +44,25 @@ class IcDataDataManager { } } - getStatus(url,socketname,token,icid){ + getStatus(url,socketname,token,icid,ic){ RestAPI.get(url, null).then(response => { + let tempIC = ic; + tempIC.state = response.state; AppDispatcher.dispatch({ type: 'ic-status/status-received', data: response, token: token, socketname: socketname, icid: icid, + ic: ic }); + if(!ic.managedexternally){ + AppDispatcher.dispatch({ + type: 'ics/start-edit', + data: tempIC, + token: token, + }); + } }).catch(error => { AppDispatcher.dispatch({ type: 'ic-status/status-error', diff --git a/src/ic/ic-status-store.js b/src/ic/ic-status-store.js index 62d6d08..36526c6 100644 --- a/src/ic/ic-status-store.js +++ b/src/ic/ic-status-store.js @@ -28,7 +28,7 @@ class ICStatusStore extends ArrayStore { switch(action.type) { case 'ic-status/get-status': - ICDataDataManager.getStatus(action.url, action.socketname, action.token, action.icid); + ICDataDataManager.getStatus(action.url, action.socketname, action.token, action.icid, action.ic); return super.reduce(state, action); case 'ic-status/status-received': diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index 78e90ac..acc6c1e 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -38,6 +38,7 @@ class InfrastructureComponentStore extends ArrayStore { socketname: splitWebsocketURL[splitWebsocketURL.length - 1], token: action.token, icid: ic.id, + ic: ic }); AppDispatcher.dispatch({ From 8edec65fadfec2cd0180b494d2ef575b7841769e Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 18 Jan 2021 14:33:46 +0100 Subject: [PATCH 029/127] round when unix time stamp in IC action message --- src/scenario/scenario.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index b89f5ef..d0f90fe 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -85,7 +85,7 @@ class Scenario extends React.Component { modalConfigIndex = index; } - let results = ResultStore.getState().filter(result => result.scenarioID === parseInt(props.match.params.scenario, 10)); + let results = ResultStore.getState().filter(result => result.scenarioID === parseInt(props.match.params.scenario, 10)); return { scenario, @@ -359,7 +359,7 @@ class Scenario extends React.Component { } // Unix time stamp + delay - action.data.when = Date.now() / 1000.0 + delay + action.data.when = Math.round(Date.now() / 1000.0 + delay) console.log("Sending action: ", action.data) @@ -643,7 +643,7 @@ class Scenario extends React.Component { } } - + /* ############################################## * Render method ############################################## */ @@ -674,7 +674,7 @@ class Scenario extends React.Component { let resulttable; if (this.state.results && this.state.results.length > 0) { - resulttable =
+ resulttable =
@@ -858,7 +858,7 @@ class Scenario extends React.Component {

Results

- + {resulttable} {/* From 1d976940168c3453cc8355f5125f85046c39e472 Mon Sep 17 00:00:00 2001 From: irismarie Date: Tue, 19 Jan 2021 17:57:45 +0100 Subject: [PATCH 030/127] add result file, simplify timestamp --- src/result/edit-result.js | 90 ++++++++++++++++++++---------- src/result/result-store.js | 77 ++++++++++++++++++++++++- src/result/results-data-manager.js | 64 ++++++++++++++++++++- src/scenario/scenario.js | 11 ++-- 4 files changed, 206 insertions(+), 36 deletions(-) diff --git a/src/result/edit-result.js b/src/result/edit-result.js index 2b2fac6..c956c06 100644 --- a/src/result/edit-result.js +++ b/src/result/edit-result.js @@ -16,7 +16,9 @@ ******************************************************************************/ import React from 'react'; -import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap'; +import {FormGroup, FormControl, FormLabel, Col, Button, ProgressBar} from 'react-bootstrap'; +import AppDispatcher from "../common/app-dispatcher"; + import Table from "../common/table"; import Dialog from '../common/dialogs/dialog'; @@ -29,10 +31,10 @@ class EditResultDialog extends React.Component { super(props); this.state = { - configSnapshots: '', + id: 0, description: '', - resultFileIDs: [], - id:0, + uploadFile: null, + uploadProgress: 0, }; } @@ -55,10 +57,9 @@ class EditResultDialog extends React.Component { resetState = () => { this.setState({ - configSnapshots: this.props.configSnapshots, - description: this.props.description, - resultFileIDs: this.props.resultFileIDs, - id: this.props.id, + id: this.props.result.id, + description: this.props.result.description, + result: this.props.result, }); }; @@ -66,28 +67,46 @@ class EditResultDialog extends React.Component { this.setState({ startParameters }); }; - // TODO: file reading necessary? or can it just be saved? - loadFile = event => { - // get file - const file = event.target.files[0]; - - // create file reader - let reader = new FileReader(); - let self = this; + selectUploadFile(event) { + this.setState({ uploadFile: event.target.files[0] }); + }; - reader.onload = event => { - const config = JSON.parse(event.target.result); + startFileUpload(){ + // upload file + const formData = new FormData(); + formData.append("file", this.state.uploadFile); + //console.log("formData: "); + //console.log(formData); - self.imported = true; - self.valid = true; - this.setState({filename: config.name, file: config }); - }; + AppDispatcher.dispatch({ + type: 'resultfiles/start-upload', + data: formData, + resultID: this.state.id, + token: this.props.sessionToken, + progressCallback: this.updateUploadProgress, + finishedCallback: this.clearProgress, + scenarioID: this.props.scenarioID, + }); - reader.readAsBinaryString(file); - } + this.setState({ uploadFile: null }); + }; + + clearProgress = (newFileID) => { + this.setState({ uploadProgress: 0 }); + }; + + updateUploadProgress = (event) => { + this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); + }; render() { - return + return + Description @@ -95,12 +114,27 @@ class EditResultDialog extends React.Component { - - Add Result File - + this.selectUploadFile(event)} /> + + + + + + + + ; diff --git a/src/result/result-store.js b/src/result/result-store.js index fb6ab36..15958cf 100644 --- a/src/result/result-store.js +++ b/src/result/result-store.js @@ -24,8 +24,83 @@ class ResultStore extends ArrayStore { super('results', ResultsDataManager); } + saveFile(state, action){ + + let fileID = parseInt(action.id) + state.forEach((element, index, array) => { + if (element.id === fileID) { + // save blob object + array[index]["data"] = new Blob([action.data.data], {type: action.data.type}); + // update file type + array[index]["type"] = action.data.type; + + if (array[index]["objectURL"] !== ''){ + // free memory of previously generated object URL + URL.revokeObjectURL(array[index]["objectURL"]); + } + // create an object URL for the file + array[index]["objectURL"] = URL.createObjectURL(array[index]["data"]) + } + }); + + // announce change to listeners + this.__emitChange(); + return state + } + + simplify(timestamp) { + let parts = timestamp.split("T"); + let datestr = parts[0]; + let time = parts[1].split("."); + + return datestr + ', ' + time[0];; + } + + simplifyTimestamps(data) { + data.forEach((result) => { + result.createdAt = this.simplify(result.createdAt); + result.updatedAt = this.simplify(result.updatedAt); + }); + } + reduce(state, action) { - return super.reduce(state, action); + switch (action.type) { + case 'results/loaded': + this.simplifyTimestamps(action.data); + return super.reduce(state, action); + + case 'resultfiles/start-download': + ResultsDataManager.download(action) + return state + + case 'resultfiles/start-upload': + ResultsDataManager.upload(action.data, action.resultID, action.token, action.progressCallback, action.finishedCallback, action.scenarioID); + return state; + + case 'resultfiles/uploaded': + return state; + + case 'resultfiles/upload-error': + console.log(action.error); + return state; + + case 'resultfiles/downloaded': + // in this case a file is contained in the response (no JSON) + return this.saveFile(state, action); + + case 'resultfiles/start-edit': + ResultsDataManager.update(action.data, action.token, action.id); + return state; + + case 'resultfiles/edited': + return this.updateElements(state, [action.data]); + + case 'resultfiles/edit-error': + return state; + + default: + return super.reduce(state, action); + } } } diff --git a/src/result/results-data-manager.js b/src/result/results-data-manager.js index b30c9c1..6720b3d 100644 --- a/src/result/results-data-manager.js +++ b/src/result/results-data-manager.js @@ -15,8 +15,9 @@ * along with VILLASweb. If not, see . ******************************************************************************/ - import RestDataManager from '../common/data-managers/rest-data-manager'; +import RestAPI from '../common/api/rest-api'; +import AppDispatcher from '../common/app-dispatcher'; class ResultsDataManager extends RestDataManager{ @@ -24,6 +25,65 @@ class ResultsDataManager extends RestDataManager{ super('result', '/results'); } + upload(file, resultID, token = null, progressCallback = null, finishedCallback = null, scenarioID) { + RestAPI.upload(this.makeURL(this.url + '/' + resultID + '/file') , file, token, progressCallback, scenarioID).then(response => { + + AppDispatcher.dispatch({ + type: 'resultfiles/uploaded', + }); + + // Trigger a files reload + AppDispatcher.dispatch({ + type: 'results/start-load', + param: '?scenarioID=' + scenarioID, + token: token + }); + + if (finishedCallback) { + finishedCallback(response.result.id); + } + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/upload-error', + error + }); + }); + } + + download(action){ + RestAPI.download(this.makeURL(this.url), action.token, action.data).then(response => { + AppDispatcher.dispatch({ + type: 'resultfiles/downloaded', + data: response, + id: action.data, + token: action.token + }); + + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/load-error', + error: error + }); + }); + } + + /* only update file ids + update(file, token, id) { + + RestAPI.put(this.makeURL(this.url + '/' + id), file, token).then(response => { + AppDispatcher.dispatch({ + type: 'resultfiles/edited', + data: response[this.type] + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/edit-error', + error: error + }); + }); + } + }*/ + } -export default new ResultsDataManager() \ No newline at end of file +export default new ResultsDataManager(); \ No newline at end of file diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index b89f5ef..806e4ee 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -572,8 +572,6 @@ class Scenario extends React.Component { ############################################## */ closeNewResultModal(data) { - console.log(this.state.results); - console.log(data); this.setState({ newResultModal: false }); if (data) { data["scenarioID"] = this.state.scenario.id; @@ -691,17 +689,20 @@ class Scenario extends React.Component { title='Options' width='300' editButton - addRemoveFilesButton downloadAllButton deleteButton onEdit={index => this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} - onAddRemove={(index) => this.setState({ editFilesModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index})} onDownloadAll={(index) => this.downloadData(this.state.results[index])} onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} />
- this.closeEditResultsModal(data)} /> + this.closeEditResultsModal(data)} /> this.closeDeleteResultsModal(e)} />
} From 95f8161c755a20a25f9f3b305911bc66d6c3218a Mon Sep 17 00:00:00 2001 From: irismarie Date: Wed, 20 Jan 2021 14:10:34 +0100 Subject: [PATCH 031/127] simplify timestamp after adding result --- src/result/result-store.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/result/result-store.js b/src/result/result-store.js index 15958cf..3437d7e 100644 --- a/src/result/result-store.js +++ b/src/result/result-store.js @@ -69,6 +69,10 @@ class ResultStore extends ArrayStore { this.simplifyTimestamps(action.data); return super.reduce(state, action); + case 'results/added': + this.simplifyTimestamps([action.data]); + return super.reduce(state, action); + case 'resultfiles/start-download': ResultsDataManager.download(action) return state From 0bc990faf4553636815d2838f5d05d25b488ac65 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 22 Jan 2021 11:30:04 +0100 Subject: [PATCH 032/127] treat case where upload progress is not known --- src/file/edit-files.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/file/edit-files.js b/src/file/edit-files.js index 8cc3421..af5d251 100644 --- a/src/file/edit-files.js +++ b/src/file/edit-files.js @@ -40,7 +40,7 @@ class EditFilesDialog extends React.Component { } onClose() { - + this.props.onClose(); } @@ -66,7 +66,12 @@ class EditFilesDialog extends React.Component { }; updateUploadProgress = (event) => { - this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); + if (event.hasOwnProperty("percent")){ + this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); + } else { + this.setState({ uploadProgress: 0 }); + } + }; clearProgress = (newFileID) => { @@ -83,7 +88,7 @@ class EditFilesDialog extends React.Component { }; closeEditModal(){ - + this.setState({editModal: false}); } @@ -167,7 +172,7 @@ class EditFilesDialog extends React.Component {
- + ); } } From a6d25617c1b02fdcbd544ab15e863a19aa037b17 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 22 Jan 2021 11:33:01 +0100 Subject: [PATCH 033/127] CI: use branch name instead of commit to tag images, deactivate kaniko cache --- .gitlab-ci.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d892544..e8ae18b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ variables: GIT_SUBMODULE_STRATEGY: normal - DOCKER_TAG: ${CI_COMMIT_SHORT_SHA} + DOCKER_TAG: ${CI_COMMIT_BRANCH} DOCKER_IMAGE: ${CI_REGISTRY_IMAGE} cache: @@ -42,8 +42,5 @@ deploy: --dockerfile ${CI_PROJECT_DIR}/Dockerfile --destination ${DOCKER_IMAGE}:${DOCKER_TAG} --snapshotMode=redo - --cache=true - --cache-ttl=12h - only: - refs: - - master + dependencies: + - build From ddc9da537c64dda5d307bae38904116b79e9eefb Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Fri, 22 Jan 2021 18:22:25 +0100 Subject: [PATCH 034/127] Use JsonView to display status #265 --- src/ic/ic-dialog.js | 99 +++++++++++++++------------------------ src/ic/ic-status-store.js | 3 -- 2 files changed, 39 insertions(+), 63 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index 33cb9e5..bdd4488 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -1,9 +1,9 @@ import React from 'react'; import {Button, Row, Col} from 'react-bootstrap'; import Dialog from '../common/dialogs/dialog'; -import {Collapse} from 'react-collapse'; import Icon from "../common/icon"; import ConfirmCommand from './confirm-command'; +import JsonView from 'react-json-view'; @@ -72,76 +72,55 @@ class ICDialog extends React.Component { return ( this.onClose(c)} valid={true} size='xl' - blendOutCancel = {true} + blendOutCancel={true} >
- - -
Status:
- { - typeof icStatus !== "undefined" && Object.keys(icStatus).map(statusKey => ( - typeof icStatus[statusKey] === 'object' ? - (
- - - { - Object.keys(icStatus[statusKey]).map(key => ( - typeof icStatus[statusKey][key] === 'object' ? - (
- - + + +
Status:
- {Object.keys(icStatus[statusKey][key]).map(index => ( -
{index + ":"}{icStatus[statusKey][key][index]}
- ))} -
-
) - : - (
{key + ":"}{icStatus[statusKey][key]}
) - )) - } -
+ -
) - : - (
{statusKey + ":"}{icStatus[statusKey]}
) - )) - } - - - -
Graph:
-
- {objectURL !== '' ? ( - this.graphError(e)} alt={"Error"} src={objectURL} /> - ) : ( - Error - )} -
- - {this.props.userRole === "Admin"? ( -
-
Controls:
-
- - -
-
) - : (
) } + - this.closeConfirmModal(c)} /> - -
+ +
Graph:
+
+ {objectURL !== '' ? ( + this.graphError(e)} alt={"Error"} src={objectURL} /> + ) : ( + Error + )} +
+ + {this.props.userRole === "Admin" ? ( +
+
Controls:
+
+ + +
+
) + : (
)} + + this.closeConfirmModal(c)} /> + +
- + ); } } diff --git a/src/ic/ic-status-store.js b/src/ic/ic-status-store.js index 36526c6..4895a8d 100644 --- a/src/ic/ic-status-store.js +++ b/src/ic/ic-status-store.js @@ -35,9 +35,6 @@ class ICStatusStore extends ArrayStore { let tempData = action.data; tempData.icID = action.icid; - // TODO: edit state (e.g. running) of IC according to received state (only for ICs that are NOT managed externally) - // TODO: playback state info to backend using start-edit dispatch for IC - return this.updateElements(state, [tempData]); case 'ic-status/status-error': From 1ae816d629d877f3512fe1a1724964455e22ddd5 Mon Sep 17 00:00:00 2001 From: irismarie Date: Fri, 22 Jan 2021 19:09:58 +0100 Subject: [PATCH 035/127] remove, download result files --- src/result/edit-result.js | 60 +++++++++++++++++++----------- src/result/new-result.js | 8 +--- src/result/result-store.js | 5 ++- src/result/results-data-manager.js | 15 ++++++-- src/scenario/scenario.js | 54 +++++++++++++++++++++++++-- 5 files changed, 103 insertions(+), 39 deletions(-) diff --git a/src/result/edit-result.js b/src/result/edit-result.js index c956c06..d1fa784 100644 --- a/src/result/edit-result.js +++ b/src/result/edit-result.js @@ -18,11 +18,13 @@ import React from 'react'; import {FormGroup, FormControl, FormLabel, Col, Button, ProgressBar} from 'react-bootstrap'; import AppDispatcher from "../common/app-dispatcher"; +import FileStore from "../file/file-store" + import Table from "../common/table"; +import TableColumn from "../common/table-column"; import Dialog from '../common/dialogs/dialog'; -import ParametersEditor from '../common/parameters-editor'; class EditResultDialog extends React.Component { valid = true; @@ -35,6 +37,9 @@ class EditResultDialog extends React.Component { description: '', uploadFile: null, uploadProgress: 0, + fileIDs: [], + files: null, + resultChanged: false, }; } @@ -60,6 +65,8 @@ class EditResultDialog extends React.Component { id: this.props.result.id, description: this.props.result.description, result: this.props.result, + fileIDs: this.props.result.resultFileIDs, + files: FileStore.getState().filter(file => this.props.result.resultFileIDs.includes(file.id)), }); }; @@ -72,11 +79,8 @@ class EditResultDialog extends React.Component { }; startFileUpload(){ - // upload file const formData = new FormData(); formData.append("file", this.state.uploadFile); - //console.log("formData: "); - //console.log(formData); AppDispatcher.dispatch({ type: 'resultfiles/start-upload', @@ -99,21 +103,47 @@ class EditResultDialog extends React.Component { this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); }; + deleteFile(index){ + let file = this.state.files[index] + AppDispatcher.dispatch({ + type: 'files/start-remove', + data: file, + token: this.props.sessionToken + }); + } + render() { + if (this.props.show) { + } return + valid={true} + size = 'lg'> -
+ +
Description + + + + + + this.deleteFile(index)} + /> +
+ + Add Result File this.selectUploadFile(event)} /> @@ -136,23 +166,9 @@ class EditResultDialog extends React.Component { /> - +
; } } -export default EditResultDialog; - -/* - - - - - this.deleteFile(index)} - /> -
- - '*/ +export default EditResultDialog; \ No newline at end of file diff --git a/src/result/new-result.js b/src/result/new-result.js index d07f94e..11023a1 100644 --- a/src/result/new-result.js +++ b/src/result/new-result.js @@ -56,13 +56,7 @@ class NewResultDialog extends React.Component { render() { return ( this.onClose(c)} onReset={() => this.resetState()} valid={true}> -
- - Config Snapshots - this.handleChange(e)} /> - - - + Description this.handleChange(e)} /> diff --git a/src/result/result-store.js b/src/result/result-store.js index 3437d7e..f59edb4 100644 --- a/src/result/result-store.js +++ b/src/result/result-store.js @@ -18,6 +18,7 @@ import ArrayStore from '../common/array-store'; import ResultsDataManager from './results-data-manager'; +import FilesDataManager from '../file/files-data-manager' class ResultStore extends ArrayStore { constructor() { @@ -74,11 +75,11 @@ class ResultStore extends ArrayStore { return super.reduce(state, action); case 'resultfiles/start-download': - ResultsDataManager.download(action) + //FilesDataManager.download(action) return state case 'resultfiles/start-upload': - ResultsDataManager.upload(action.data, action.resultID, action.token, action.progressCallback, action.finishedCallback, action.scenarioID); + ResultsDataManager.uploadFile(action.data, action.resultID, action.token, action.progressCallback, action.finishedCallback, action.scenarioID); return state; case 'resultfiles/uploaded': diff --git a/src/result/results-data-manager.js b/src/result/results-data-manager.js index 6720b3d..bae49da 100644 --- a/src/result/results-data-manager.js +++ b/src/result/results-data-manager.js @@ -25,16 +25,23 @@ class ResultsDataManager extends RestDataManager{ super('result', '/results'); } - upload(file, resultID, token = null, progressCallback = null, finishedCallback = null, scenarioID) { + uploadFile(file, resultID, token = null, progressCallback = null, finishedCallback = null, scenarioID) { RestAPI.upload(this.makeURL(this.url + '/' + resultID + '/file') , file, token, progressCallback, scenarioID).then(response => { AppDispatcher.dispatch({ - type: 'resultfiles/uploaded', + type: 'files/uploaded', + }); + + // Trigger a results reload + AppDispatcher.dispatch({ + type: 'results/start-load', + param: '?scenarioID=' + scenarioID, + token: token }); // Trigger a files reload AppDispatcher.dispatch({ - type: 'results/start-load', + type: 'files/start-load', param: '?scenarioID=' + scenarioID, token: token }); @@ -44,7 +51,7 @@ class ResultsDataManager extends RestDataManager{ } }).catch(error => { AppDispatcher.dispatch({ - type: 'resultfiles/upload-error', + type: 'files/upload-error', error }); }); diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index 5b3f3e6..217d98e 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -50,6 +50,8 @@ import ResultStore from "../result/result-store" import { Redirect } from 'react-router-dom'; import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; +var JSZip = require("jszip"); + class Scenario extends React.Component { static getStores() { @@ -114,6 +116,7 @@ class Scenario extends React.Component { modalResultsIndex: 0, newResultModal: false, editFilesModal: false, + filesToDownload: [], editOutputSignalsModal: prevState.editOutputSignalsModal || false, editInputSignalsModal: prevState.editInputSignalsModal || false, @@ -154,6 +157,33 @@ class Scenario extends React.Component { }); } + componentDidUpdate(prevProps, prevState) { + // check whether file data has been loaded + if (this.state.filesToDownload.length > 0 ) { // todo: for download all - wait until data for all has arrived + console.log(this.state.files); + if (this.state.filesToDownload.length === 1) { + let fileToDownload = FileStore.getState().filter(file => file.id === this.state.filesToDownload[0]) + if (fileToDownload.length === 1 && fileToDownload[0].data) { + const blob = new Blob([fileToDownload[0].data], {type: fileToDownload[0].type}); + FileSaver.saveAs(blob, fileToDownload[0].name); + this.setState({ filesToDownload: [] }); + } + } else { // zip and save several files + let filesToDownload = FileStore.getState().filter(file => this.state.filesToDownload.includes(file.id) && file.data); + if (filesToDownload.length === this.state.filesToDownload.length) { // all requested files have been loaded + var zip = new JSZip(); + filesToDownload.forEach(file => { + zip.file(file.name, file.data); + }); + zip.generateAsync({type: "blob"}).then(function(content) { + saveAs(content, "results.zip"); + }); + this.setState({ filesToDownload: [] }); + } + } + } + } + /* ############################################## * User modification methods ############################################## */ @@ -592,8 +622,24 @@ class Scenario extends React.Component { } } - downloadData(index) { - console.log(index); + downloadResultData(param) { + let toDownload = []; + + if (typeof(param) === 'object') { + toDownload = param.resultFileIDs; + } else { + toDownload.push(param); + } + + toDownload.forEach(fileid => { + AppDispatcher.dispatch({ + type: 'files/start-download', + data: fileid, + token: this.state.sessionToken + }); + }); + + this.setState({ filesToDownload: toDownload }); } closeDeleteResultsModal(confirmDelete) { @@ -683,7 +729,7 @@ class Scenario extends React.Component { dataKey='resultFileIDs' linkKey='filebuttons' width='300' - onDownload={(index) => this.downloadData(index)} + onDownload={(index) => this.downloadResultData(index)} /> this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} - onDownloadAll={(index) => this.downloadData(this.state.results[index])} + onDownloadAll={(index) => this.downloadResultData(this.state.results[index])} onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} /> From 0405ec82da263e64485f119a48d46790be770bb1 Mon Sep 17 00:00:00 2001 From: Laura Fuentes Grau Date: Sun, 24 Jan 2021 16:43:19 +0100 Subject: [PATCH 036/127] make graph downloadable #265 --- src/ic/ic-dialog.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ic/ic-dialog.js b/src/ic/ic-dialog.js index bdd4488..591db53 100644 --- a/src/ic/ic-dialog.js +++ b/src/ic/ic-dialog.js @@ -4,7 +4,7 @@ import Dialog from '../common/dialogs/dialog'; import Icon from "../common/icon"; import ConfirmCommand from './confirm-command'; import JsonView from 'react-json-view'; - +import FileSaver from 'file-saver'; class ICDialog extends React.Component { @@ -53,6 +53,10 @@ class ICDialog extends React.Component { this.setState({confirmCommand: false, command: ''}); } + downloadGraph(url){ + FileSaver.saveAs(url, this.props.ic.name + ".svg"); +} + render() { @@ -63,11 +67,6 @@ class ICDialog extends React.Component { if(typeof this.props.icGraph !== "undefined") { objectURL = this.props.icGraph.objectURL } - - let spanStyle = { - display: 'inline-block', - width: '130px' - } return ( +
+ +
Graph:
{objectURL !== '' ? ( From bfed3e231bac38f4aae3797762949ac733ce0b35 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 10:30:46 +0100 Subject: [PATCH 037/127] fix bug in outdated computation; show no icon if IC is not managed via AMQP --- src/ic/ics.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ic/ics.js b/src/ic/ics.js index a1e494c..b96cf89 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -75,10 +75,10 @@ class InfrastructureComponents extends Component { return a.stateUpdatedAt < b.stateUpdatedAt; } }); - + const icStatus = ICStatusStore.getState(); const icGraph = ICGraphStore.getState(); - + return { sessionToken: localStorage.getItem("token"), ics: ics, @@ -99,11 +99,11 @@ class InfrastructureComponents extends Component { type: 'ics/start-load', token: this.state.sessionToken, }); - + // Start timer for periodic refresh this.timer = window.setInterval(() => this.refresh(), 10000); } - + componentWillUnmount() { window.clearInterval(this.timer); } @@ -228,12 +228,12 @@ class InfrastructureComponents extends Component { } static isICOutdated(component) { - if (!component.stateUpdatedAt) + if (!component.stateUpdateAt) return true; const fiveMinutes = 5 * 60 * 1000; - return Date.now() - new Date(component.stateUpdatedAt) > fiveMinutes; + return Date.now() - new Date(component.stateUpdateAt) > fiveMinutes; } stateLabelStyle(state, component){ @@ -312,7 +312,7 @@ class InfrastructureComponents extends Component { if(managedExternally){ return } else { - return + return "" } } @@ -328,7 +328,7 @@ class InfrastructureComponents extends Component { modifyNameColumn(name){ let ic = this.state.ics.find(ic => ic.name === name); - + if(ic.type === "villas-node" || ic.type === "villas-relay"){ return } else{ @@ -337,17 +337,17 @@ class InfrastructureComponents extends Component { } openICStatus(ic){ - + let index = this.state.ics.indexOf(ic); let icStatus = this.state.icStatus.find(status => status.icID === ic.id); let icGraph = this.state.icGraph.find(graph => graph.icID === ic.id); - + this.setState({ icModal: true, modalIC: ic, modalICStatus: icStatus, modalICGraph: icGraph, modalIndex: index }) } sendControlCommand(command,ic){ let splitWebsocketURL = ic.websocketurl.split("/"); - + if(command === "restart"){ AppDispatcher.dispatch({ type: 'ic-status/restart', From 314bf53b23655b3f6b3267d027c63f65b0d9c4d1 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 11:34:39 +0100 Subject: [PATCH 038/127] send token in query string for file download, omit auth header --- src/common/api/rest-api.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common/api/rest-api.js b/src/common/api/rest-api.js index 4e554c7..f40064e 100644 --- a/src/common/api/rest-api.js +++ b/src/common/api/rest-api.js @@ -41,7 +41,8 @@ let prevURL = null; class RestAPI { get(url, token) { return new Promise(function (resolve, reject) { - var req = request.get(url); + + let req = request.get(url); if (token != null) { req.set('Authorization', "Bearer " + token); @@ -61,7 +62,7 @@ class RestAPI { post(url, body, token) { return new Promise(function (resolve, reject) { - var req = request.post(url).send(body).timeout({ response: 5000 }); // Simple response start timeout (3s) + let req = request.post(url).send(body).timeout({ response: 5000 }); // Simple response start timeout (3s) if (token != null) { req.set('Authorization', "Bearer " + token); @@ -82,7 +83,7 @@ class RestAPI { delete(url, token) { return new Promise(function (resolve, reject) { - var req = request.delete(url); + let req = request.delete(url); if (token != null) { req.set('Authorization', "Bearer " + token); @@ -101,7 +102,7 @@ class RestAPI { put(url, body, token) { return new Promise(function (resolve, reject) { - var req = request.put(url).send(body); + let req = request.put(url).send(body); if (token != null) { req.set('Authorization', "Bearer " + token); @@ -140,11 +141,14 @@ class RestAPI { download(url, token, fileID) { return new Promise(function (resolve, reject) { - let req = request.get(url + "/" + fileID).buffer(true).responseType("blob") - // use blob response type and buffer - if (token != null) { - req.set('Authorization', "Bearer " + token); + + let completeURL = url + "/" + fileID; + if (token != null){ + completeURL = completeURL + "?token=" + action.token } + let req = request.get(completeURL).buffer(true).responseType("blob") + // use blob response type and buffer + // Do not use auth header for file download req.end(function (error, res) { if (error !== null || res.status !== 200) { @@ -161,7 +165,7 @@ class RestAPI { apiDownload(url, token) { return new Promise(function (resolve, reject) { - var req = request.get(url).buffer(true).responseType("blob"); + let req = request.get(url).buffer(true).responseType("blob"); if (token != null) { req.set('Authorization', "Bearer " + token); From 011159b1cc247da1d6873ec3f8e16f6a34b28127 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 11:57:44 +0100 Subject: [PATCH 039/127] add time zone format to moment of update #264 --- src/ic/ics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ic/ics.js b/src/ic/ics.js index b96cf89..9868231 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -302,7 +302,7 @@ class InfrastructureComponents extends Component { } stateUpdateModifier(updatedAt) { - let dateFormat = 'DD MMM YYYY HH:mm:ss'; + let dateFormat = 'ddd, DD MMM YYYY HH:mm:ss zz'; let dateTime = moment(updatedAt, dateFormat); return dateTime.fromNow() } From 5903bcc83256c8fc9783881722a313dae242da44 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 12:33:37 +0100 Subject: [PATCH 040/127] request VILLASnode status only if APIurl exists; make API url editable --- src/ic/edit-ic.js | 16 ++++++++-------- src/ic/ic-data-data-manager.js | 6 +++--- src/ic/ic-store.js | 3 ++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/ic/edit-ic.js b/src/ic/edit-ic.js index 7ce6d5d..0c8403e 100644 --- a/src/ic/edit-ic.js +++ b/src/ic/edit-ic.js @@ -50,13 +50,13 @@ class EditICDialog extends React.Component { data.name = this.state.name; } - if (this.state.websocketurl != null && this.state.websocketurl !== "" && this.state.websocketurl !== "http://" && this.state.websocketurl !== this.props.ic.websocketurl) { - data.websocketurl = this.state.websocketurl; - } - if (this.state.apiurl != null && this.state.apiurl !== "" && this.state.apiurl !== "http://" && this.state.apiurl !== this.props.ic.apiurl) { - data.apiurl = this.state.apiurl; - } + data.websocketurl = this.state.websocketurl; + + + + data.apiurl = this.state.apiurl; + if (this.state.location != null && this.state.location !== this.props.ic.location) { data.location = this.state.location; @@ -175,12 +175,12 @@ class EditICDialog extends React.Component { Websocket URL - this.handleChange(e)} /> + this.handleChange(e)} /> API URL - this.handleChange(e)} /> + this.handleChange(e)} /> diff --git a/src/ic/ic-data-data-manager.js b/src/ic/ic-data-data-manager.js index 087b294..6bfb421 100644 --- a/src/ic/ic-data-data-manager.js +++ b/src/ic/ic-data-data-manager.js @@ -46,8 +46,6 @@ class IcDataDataManager { getStatus(url,socketname,token,icid,ic){ RestAPI.get(url, null).then(response => { - let tempIC = ic; - tempIC.state = response.state; AppDispatcher.dispatch({ type: 'ic-status/status-received', data: response, @@ -56,7 +54,9 @@ class IcDataDataManager { icid: icid, ic: ic }); - if(!ic.managedexternally){ + if(!ic.managedexternally){ + let tempIC = ic; + tempIC.state = response.state; AppDispatcher.dispatch({ type: 'ics/start-edit', data: tempIC, diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index 7210477..5a77642 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -31,7 +31,8 @@ class InfrastructureComponentStore extends ArrayStore { switch(action.type) { case 'ics/loaded': action.data.forEach(ic => { - if (ic.type === "villas-node" || ic.type === "villas-relay") { + if ((ic.type === "villas-node" || ic.type === "villas-relay") + && ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null) { let splitWebsocketURL = ic.websocketurl.split("/"); AppDispatcher.dispatch({ type: 'ic-status/get-status', From 88bc504d0d9bc18348df761fcf685399a3219517 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 12:44:26 +0100 Subject: [PATCH 041/127] perform VILLASnode status and graph query in refresh method for ICs --- src/ic/ic-store.js | 22 ---------------------- src/ic/ics.js | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index 5a77642..21d84ea 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -30,28 +30,6 @@ class InfrastructureComponentStore extends ArrayStore { reduce(state, action) { switch(action.type) { case 'ics/loaded': - action.data.forEach(ic => { - if ((ic.type === "villas-node" || ic.type === "villas-relay") - && ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null) { - let splitWebsocketURL = ic.websocketurl.split("/"); - AppDispatcher.dispatch({ - type: 'ic-status/get-status', - url: ic.apiurl + "/status", - socketname: splitWebsocketURL[splitWebsocketURL.length - 1], - token: action.token, - icid: ic.id, - ic: ic - }); - - AppDispatcher.dispatch({ - type: 'ic-graph/get-graph', - url: ic.apiurl + "/graph.svg", - socketname: splitWebsocketURL[splitWebsocketURL.length - 1], - token: action.token, - icid: ic.id, - }); - } - }) return super.reduce(state, action); diff --git a/src/ic/ics.js b/src/ic/ics.js index 9868231..5c8c6fb 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -119,6 +119,30 @@ class InfrastructureComponents extends Component { token: this.state.sessionToken, }); + // get status and graph of VILLASnode and VILLASrelay ICs + this.state.ics.forEach(ic => { + if ((ic.type === "villas-node" || ic.type === "villas-relay") + && ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null) { + let splitWebsocketURL = ic.websocketurl.split("/"); + AppDispatcher.dispatch({ + type: 'ic-status/get-status', + url: ic.apiurl + "/status", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + icid: ic.id, + ic: ic + }); + + AppDispatcher.dispatch({ + type: 'ic-graph/get-graph', + url: ic.apiurl + "/graph.svg", + socketname: splitWebsocketURL[splitWebsocketURL.length - 1], + token: this.state.sessionToken, + icid: ic.id, + }); + } + }) + } } From 855e0199a25a0b011aa12664b725f0dd1f0593c6 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 13:53:01 +0100 Subject: [PATCH 042/127] fix a typo in download funtion of API --- src/common/api/rest-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/api/rest-api.js b/src/common/api/rest-api.js index f40064e..b33c36f 100644 --- a/src/common/api/rest-api.js +++ b/src/common/api/rest-api.js @@ -144,7 +144,7 @@ class RestAPI { let completeURL = url + "/" + fileID; if (token != null){ - completeURL = completeURL + "?token=" + action.token + completeURL = completeURL + "?token=" + token } let req = request.get(completeURL).buffer(true).responseType("blob") // use blob response type and buffer From aa9b8906dd081cde23d7842da3f60c1b84a51d91 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 14:42:09 +0100 Subject: [PATCH 043/127] fix for time formatting #264 --- src/ic/ics.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ic/ics.js b/src/ic/ics.js index 5c8c6fb..44b2563 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -76,14 +76,11 @@ class InfrastructureComponents extends Component { } }); - const icStatus = ICStatusStore.getState(); - const icGraph = ICGraphStore.getState(); - return { sessionToken: localStorage.getItem("token"), ics: ics, - icStatus: icStatus, - icGraph: icGraph, + icStatus: ICStatusStore.getState(), + icGraph: ICGraphStore.getState(), modalIC: {}, modalICStatus: {}, modalICGraph: {}, @@ -326,7 +323,7 @@ class InfrastructureComponents extends Component { } stateUpdateModifier(updatedAt) { - let dateFormat = 'ddd, DD MMM YYYY HH:mm:ss zz'; + let dateFormat = 'ddd, DD MMM YYYY HH:mm:ss ZZ'; let dateTime = moment(updatedAt, dateFormat); return dateTime.fromNow() } @@ -460,7 +457,15 @@ class InfrastructureComponents extends Component { this.closeNewModal(data)} /> this.closeEditModal(data)} ic={this.state.modalIC} /> this.closeImportModal(data)} /> - this.closeICModal(data)} ic={this.state.modalIC} token={this.state.sessionToken} userRole={this.state.currentUser.role} icStatus={this.state.modalICStatus} icGraph={this.state.modalICGraph} sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/> + this.closeICModal(data)} + ic={this.state.modalIC} + token={this.state.sessionToken} + userRole={this.state.currentUser.role} + icStatus={this.state.modalICStatus} + icGraph={this.state.modalICGraph} + sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/> this.closeDeleteModal(e)} />
From 39af80297924cbd09b1bcc12ef143661f352d26b Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 15:37:02 +0100 Subject: [PATCH 044/127] revoke objectURL of graphs instead of creating a new one --- src/ic/ic-graph-store.js | 43 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/ic/ic-graph-store.js b/src/ic/ic-graph-store.js index cb44812..7a4a05c 100644 --- a/src/ic/ic-graph-store.js +++ b/src/ic/ic-graph-store.js @@ -25,19 +25,46 @@ class ICGraphStore extends ArrayStore { saveGraph(state, action){ - let icID = parseInt(action.icid); - const dublicate = state.some(element => element.icID === icID); - if(dublicate){ - return state - } let icGraph = {}; - icGraph["icID"] = icID; + icGraph["icID"] = action.icid; icGraph["data"] = new Blob([action.data.data], {type: action.data.type}); icGraph["type"] = action.data.type; icGraph["objectURL"] = URL.createObjectURL(icGraph["data"]); - + + let newElements = [icGraph] + + // search for existing element to update + state.forEach((element, index, array) => { + newElements = newElements.filter((updateElement, newIndex) => { + if (element.icID === updateElement.icID) { + console.log("Updating graph:", icGraph.icID) + // update each property + for (var key in updateElement) { + if (updateElement.hasOwnProperty(key) && key === "objectURL") { + URL.revokeObjectURL(array[index][key]); + console.log("revoked objectURL", array[index][key]) + } else if (updateElement.hasOwnProperty(key)){ + array[index][key] = updateElement[key]; + } + + } + + // remove updated element from update list + return false; + } + + return true; + }); + }); + + // all elements still in the list will just be added + state = state.concat(newElements); + + // announce change to listeners this.__emitChange(); - return this.updateElements(state, [icGraph]); + + return state; + } reduce(state, action) { From 1311806b96ee0a1b7943f2f1d1d5408bcf251758 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 25 Jan 2021 15:40:01 +0100 Subject: [PATCH 045/127] remove debug output --- src/ic/ic-graph-store.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ic/ic-graph-store.js b/src/ic/ic-graph-store.js index 7a4a05c..1f3d27a 100644 --- a/src/ic/ic-graph-store.js +++ b/src/ic/ic-graph-store.js @@ -37,12 +37,10 @@ class ICGraphStore extends ArrayStore { state.forEach((element, index, array) => { newElements = newElements.filter((updateElement, newIndex) => { if (element.icID === updateElement.icID) { - console.log("Updating graph:", icGraph.icID) // update each property for (var key in updateElement) { if (updateElement.hasOwnProperty(key) && key === "objectURL") { URL.revokeObjectURL(array[index][key]); - console.log("revoked objectURL", array[index][key]) } else if (updateElement.hasOwnProperty(key)){ array[index][key] = updateElement[key]; } From 0d96b11eae240290e336ac157a02fdb76b480003 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 26 Jan 2021 11:38:34 +0100 Subject: [PATCH 046/127] add PUBLIC_URL again #57 --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 3f833df..47e21e6 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,7 @@ - + !z8}aYQ79_?qBmMS}uTCR9sAQ z9wtobhaalxcR)GC^^A3rOaNSb1AJQGv%8w&CZh(<0$!u}#zVP><#yPbXw~LF1eV40 zd)Svs#j+ea7&Ird@e|v3ACaFGGqdprwM|_V@Y!==6@n@DItMZ4KR<3m>M#|*pOW(4 zZyE*3Bf|u-3Pzj*qxoxyUH!Zy8#8YUqUi#e!f`YeK+HeuweAzC_^wl2>J@hWb^a?Q z(55+3*MP-f=y|I;o4W!si)djb@R7uRh#+0@blNYZgW4xlv4?*js@`2w$#^X(M1fx0 z?8~~)bfMf3{XX{*S$%w!z&Cgto=q3QeO@&}E!AI8&*5iSNdKSt>>?xzgB$fYRdwOb zYtU-e%5?svVDQB1YoSJ#RS4xX*G;0L!qD|M^&B^bI0f<#nXHpRUaK{94du#U01RMP zh}(^aw0?kC5mIHP{2Qarmf$g0cBM#OYu&(q#rwKDPf5W@ST}2l4`3ruU~#qNJCKxF zJo+H3cJ7GoG@vU6{brCUDaa~dFX~9&U;=nbKk7Se*G#7ntb&dz!8K9n0|-uA&3^_) z1LmeJZ(+C!&~Hk6A>m2AlvFYp`>)#b>EyB(GK<1PdH_!SX7bvuStrWp4bLxi=zrEA zF~6sf9l^mu_*yH^?*$0;+t80yNOQ&y_n5NZ*01OcAtS4iq-1B zT$P+F^$H@PVc+sc$=2A{bl@^iQh(bIxvnutF&Y4VXE2}L38-fgzf|F|F04bvjb?CE z+sPNc2(z#f6yQOzl{ye>S=-Q^d1W+Errf^ z)mW_UUI1E5IyLo75ic;bAe};{@rX(KP!X%?GC=vIJO=DWOYS@yRnrr0RpFV*;!3@% zU309y*}$KN{%uze65Y`@HC;I+4zY!(ljj5$MNYYINTQ3Bh+w)iTLRNlU1P1%7$SgH zZo%uqy{rKN;u3QGWnfTP5%1MoNlYaDVnRWj{ym%bYGxF`GTLTfCS=;&w~Iv&8h1Zu zOHGDZMTZm;zU4O?@e(M9IVGwOEW)q6@Q=uUuOSdRdVLH3DC@0Ivg(|q6n)4?u*^{k zMM@63{#1?RThW*8%DC37_^zubneN62iA+FKsrqmXe=(n_F8Wz|Rmf>EPJaEc^>ZuL zGHD|M3IIr*lvbZ>wu0QDxSyt%TE=PPKSy30PLTqMLDDCs!OnOdnrbv$Yl%d^)cw-VvrH0lap)DMq^^GN{hY8?1F zXH^r{qw`9l^G;QfS;}$NYCNiEiiESyQzA80WsNsFbz7AhM@Y=*6sM!}bs02?;ggD3Kur@Hmt>P6U?G|S zKN8E1E@mG1-LK)5JS^opkq{?(y$)|^1VY5Urmhw#&Y9e%adS{}*rtzWa~OHe0Q;xs z`3GN{`L=1)iVpgY9zAkUe-n`@*Isac(pkt}neiJk$Ih4giOt~u1%(EB`IB_UZ~=+i zApZb5OWyNCmprK%^PlBX^pvl|F3Aw7_!0cYOhYMCw=F0m!QR)}31lPy|6Sh#*eh zwNi#xB~LF3Bb8mfYHhvicHkFP%qNvYc=M<1wI2@cC~V4hAbIRXTiok6KY>#K6@+X7 zf;xgLKK7)gpcjgiq@HBRg%19riOYtyD`Jp+;5|>vwJS}#a?w170zrUAQ|VJvs4%cD z5(qmPA4#nakE$i>bV9w*VhO+f?C(@Av*KL1O3;$olad0Ye6w3~t)MnRP$(yq`GN+1rm^~6^4`6E-DD^jRK3Meh0_awn1 z(z&@j$~gx)%@mI>fuo=Gpbw#*GfPPdq07x`OCX#&U{sJhD4G%#@N157+jBQ6afd+?M4AOyt z{OAn!Kcy&V$oWtQ1HVe?TpMX}J(cR3jj4;I>MgCg{xr6c%$#=$P|b4ir7dc0vQnZ6 zDkCPDPz8xdhZ{NWO-FY#8yF+?sOpJ0pf~i%qlp`2nWruJav}ue($z0G7d)+`#E-pA zVV1#Nx_~7@Qlq&ZwBsdZ#HD|k1_AE}2A?a1$Ul^06XjDaTjsF^K+2#4>Ua9on*^5m z(*tM#iIP9>K?_n{2{`6aJ9$M*Qi7J51K&?9j`Wt2(k>fOC*4GPcKV95Y80hN-7_9W zr&>s7mw3R$cB0f;ZAlAI*zfCA53DvvE=VI|gP-VBb+6x6U?>;|F&TsKnst{_(@6@D z4%zG9&3G8b+v^gh#;In8K{?cX?1BRf=6NV zrP?4gvf>XfHbEWxQd4lGx?q9nx8Puh@4^&KfBi9I^e7@odVM>Lba zN+LvxN#8vvy!y~H@bjQe7c~M!1nelNhpv5SPnX7zF-O1ip+71xf7XXwQOMuD7(WU* z6V#t7>i*BJ6>q?8+AI!Br`>r39Fmm;s2@0wg>svp_=Z2Ol@)*AyZ%~+e<<}d&wpAz zls}02QK$R5l%L4I^3;|80C{igPbdC0KBMhK=l6%y{moul{{V2$p!HcH-RDb?pdY`eUE`Pae{Hps3 zU6NDaj1S5>dDQ+I{{YX?{$u|D`qtZ4{^74**0Og00NuPl>R0Jnjt<@5RQ;;ki;$D1 zUP?jz*y-t9rtACR>H5;_fbmWRT(`IWs-V_D4?6-g`c*}r_u{TU`A?7QP}BYS^!+O8 z+x`-pxc=;Vif+&TF#1%{`Nyq2WB&jWpHJ4OUH<@g@v8d|{J|gP{c8Tp{xz@jQ@YRh zzx%$%xt6E@02=T4%`-ssp+(TqoxU`lzX~zdfuMG#)kHV=(JN{}>p_2|B=w~K0Id?X zpgPeif$2p;f_5}rD2DWBtrZvX3R*Uy+Kg>NfZyjzADntuSJ5%Sd@UIL{omYI|JijR B^i2Q& literal 0 HcmV?d00001 diff --git a/src/styles/app.css b/src/styles/app.css index 8a9c72e..69894a4 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -112,6 +112,13 @@ body { } } +.verticalhorizontal { + display: flex; + justify-content: center; + align-items: center; + height: 800px; +} + /** * Menus */ diff --git a/src/user/login-complete.js b/src/user/login-complete.js index d8dd65c..4fd4922 100644 --- a/src/user/login-complete.js +++ b/src/user/login-complete.js @@ -21,10 +21,6 @@ import AppDispatcher from '../common/app-dispatcher'; import LoginStore from './login-store' import { Container } from 'flux/utils'; -import NotificationsDataManager from '../common/data-managers/notifications-data-manager'; -import NotificationsFactory from "../common/data-managers/notifications-factory"; - - class LoginComplete extends React.Component { constructor(props) { @@ -83,9 +79,7 @@ class LoginComplete extends React.Component { } countDown() { - console.log("count down, seconds:"); let seconds = this.state.secondsToWait - 1; - console.log(seconds); this.setState({secondsToWait: seconds}); // waiting time over, stop counting down @@ -95,20 +89,16 @@ class LoginComplete extends React.Component { } render() { - console.log("render, seconds to wait:"); - console.log(this.state.secondsToWait); if (this.state.currentUser && this.state.currentUser !== "") { - console.log("user:"); - console.log(this.state.currentUser); this.stopTimer(); return (); } else if (this.state.secondsToWait == 0) { this.stopTimer(); return (); - } else { // authenticating failed - //NotificationsFactory.LOAD_ERROR('Backend did not return user after external auth'); - return (

Authenticating.. {this.state.secondsToWait}

); + } else { + return (
+ Waiting Dog
); } } } From ac944a5beda0529b81745189074a72d52e93851c Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 23 Feb 2021 13:35:24 +0100 Subject: [PATCH 118/127] IC actions for delete, shutdown, reset are working; add notification for actions --- .../data-managers/notifications-factory.js | 8 +++++++ src/ic/ic-action.js | 21 ++++++++++++++++--- src/ic/ic-store.js | 4 ++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/common/data-managers/notifications-factory.js b/src/common/data-managers/notifications-factory.js index ece1e8b..a032db0 100644 --- a/src/common/data-managers/notifications-factory.js +++ b/src/common/data-managers/notifications-factory.js @@ -138,6 +138,14 @@ class NotificationsFactory { }; } + static ACTION_INFO() { + return { + title: 'Action successfully requested', + level: 'info' + }; + } + + } export default NotificationsFactory; diff --git a/src/ic/ic-action.js b/src/ic/ic-action.js index 33fe9a8..c2e4455 100644 --- a/src/ic/ic-action.js +++ b/src/ic/ic-action.js @@ -18,6 +18,8 @@ import React from 'react'; import { Button, DropdownButton, Dropdown, InputGroup, FormControl } from 'react-bootstrap'; import AppDispatcher from "../common/app-dispatcher"; +import NotificationsFactory from "../common/data-managers/notifications-factory"; +import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; class ICAction extends React.Component { constructor(props) { @@ -92,13 +94,26 @@ class ICAction extends React.Component { if (newAction.action === "delete"){ // prepare parameters for delete incl. correct IC id newAction["parameters"] = {}; - newAction.parameters["uuid"] = ic.id; - icID = ic.manager; // send delete action to manager of IC + newAction.parameters["uuid"] = ic.uuid; + // get the ID of the manager IC + let managerIC = null; + for (let i of this.props.ics){ + if (i.uuid === ic.manager){ + managerIC = i; + } + } + if (managerIC == null){ + console.log("DELETE action", newAction); + NotificationsDataManager.addNotification(NotificationsFactory.DELETE_ERROR("Could not find manager IC with UUID " + ic.manager)); + continue; + } + + icID = managerIC.id; // send delete action to manager of IC } AppDispatcher.dispatch({ type: 'ics/start-action', - icid: ic.id, + icid: icID, action: newAction, result: null, token: this.props.token diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index d2e00de..39b1c8c 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -72,6 +72,10 @@ class InfrastructureComponentStore extends ArrayStore { ICsDataManager.doActions(action.icid, action.action, action.token, action.result); return state; + case 'ics/action-started': + NotificationsDataManager.addNotification(NotificationsFactory.ACTION_INFO()); + return state; + case 'ics/action-error': console.log(action.error); return state; From 939a168ce3e0a72ff50b340746efad3a8682eb25 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 23 Feb 2021 15:57:50 +0100 Subject: [PATCH 119/127] enable create action via add IC button for new externally managed IC --- src/ic/ic-action.js | 1 - src/ic/ics.js | 36 +++++++++++++++++++++++++++++++----- src/ic/new-ic.js | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/ic/ic-action.js b/src/ic/ic-action.js index c2e4455..02d3a63 100644 --- a/src/ic/ic-action.js +++ b/src/ic/ic-action.js @@ -103,7 +103,6 @@ class ICAction extends React.Component { } } if (managerIC == null){ - console.log("DELETE action", newAction); NotificationsDataManager.addNotification(NotificationsFactory.DELETE_ERROR("Could not find manager IC with UUID " + ic.manager)); continue; } diff --git a/src/ic/ics.js b/src/ic/ics.js index 9c1f2db..95d224f 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -36,6 +36,8 @@ import ICDialog from './ic-dialog'; import ICAction from './ic-action'; import DeleteDialog from '../common/dialogs/delete-dialog'; +import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; +import NotificationsFactory from "../common/data-managers/notifications-factory"; class InfrastructureComponents extends Component { static getStores() { @@ -149,11 +151,35 @@ class InfrastructureComponents extends Component { this.setState({ newModal : false }); if (data) { - AppDispatcher.dispatch({ - type: 'ics/start-add', - data, - token: this.state.sessionToken, - }); + if (!data.managedexternally) { + AppDispatcher.dispatch({ + type: 'ics/start-add', + data, + token: this.state.sessionToken, + }); + } else { + // externally managed IC: dispatch create action to selected manager + let newAction = {}; + newAction["action"] = "create"; + newAction["parameters"] = data; + newAction["when"] = new Date() + + // find the manager IC + let managerIC = this.state.ics.find(ic => ic.uuid === data.manager) + if (managerIC === null || managerIC === undefined){ + NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Could not find manager IC with UUID " + data.manager)); + return; + } + + AppDispatcher.dispatch({ + type: 'ics/start-action', + icid: managerIC.id, + action: newAction, + result: null, + token: this.state.sessionToken + }); + + } } } diff --git a/src/ic/new-ic.js b/src/ic/new-ic.js index 2e81887..39aeebd 100644 --- a/src/ic/new-ic.js +++ b/src/ic/new-ic.js @@ -170,7 +170,7 @@ class NewICDialog extends React.Component { {this.props.managers.length > 0 ? <> - An externally managed component is created and managed by an IC manager via AMQP} > + An externally managed component is created and managed by an IC manager via AMQP} > this.handleChange(e)}> From e22c0ca6592dedd44bdabfd6f1328bbacc852351 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 23 Feb 2021 17:19:43 +0100 Subject: [PATCH 120/127] clean up and improve code structure of table --- src/common/table.js | 115 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/src/common/table.js b/src/common/table.js index 8774d10..5471244 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -79,9 +79,19 @@ class CustomTable extends Component { cell.push(); } else if (linkKey === 'filebuttons') { content.forEach((contentvalue, contentkey) => { - cell.push(Download {contentvalue}} > - ); + cell.push( + Download {contentvalue}} > + + ); }); } else { cell.push(content); @@ -115,9 +125,18 @@ class CustomTable extends Component { // add buttons if (child.props.editButton) { - let disable = (typeof data.managedexternally !== "undefined" && data.managedexternally); - cell.push({disable ? "Externally managed ICs cannot be edited" : "edit"} } > - ); + cell.push( + Edit }> + + ); } if (child.props.checkbox) { @@ -137,28 +156,78 @@ class CustomTable extends Component { } if (child.props.exportButton) { - cell.push( Export } > - ); + cell.push( + Export } > + + ); } if (child.props.duplicateButton) { - cell.push( Duplicate } > - ); + cell.push( + Duplicate } > + + ); } if (child.props.addRemoveFilesButton) { - cell.push(Add/remove File(s)} > - ); + cell.push( + Add/remove File(s)} > + + ); } if (child.props.downloadAllButton) { - cell.push(Download All Files} > - ); + cell.push( + Download All Files} > + + ); } if (child.props.deleteButton) { - cell.push( Delete } > - ); + cell.push( + Delete } > + + ); } return cell; @@ -244,9 +313,19 @@ class CustomTable extends Component { onCellBlur: () => { } }; - return ( + return ( {(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex) ? ( - children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} ref={ref => { this.activeInput = ref; }} /> + children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} + ref={ref => { this.activeInput = ref; }} /> ) : ( {cell.map((element, elementIndex) => ( From 64e504b939862ebcacf3522bc66fc06591eacf2d Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 23 Feb 2021 17:25:22 +0100 Subject: [PATCH 121/127] fix determination of external IC in IC table --- src/ic/ics.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ic/ics.js b/src/ic/ics.js index 95d224f..6024e10 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -393,8 +393,8 @@ class InfrastructureComponents extends Component { } } - isExternalIC(index){ - let ic = this.state.ics[index] + isExternalIC(index, ics){ + let ic = ics[index] return ic.managedexternally } @@ -405,7 +405,7 @@ class InfrastructureComponents extends Component { this.isExternalIC(index)} + checkboxDisabled={(index) => this.isExternalIC(index, ics)} onChecked={(ic, event) => this.onICChecked(ic, event)} width='30' /> @@ -438,9 +438,9 @@ class InfrastructureComponents extends Component { {this.state.currentUser.role === "Admin" ? !this.isExternalIC(index)} + editButton = {(index) => !this.isExternalIC(index, ics)} exportButton - deleteButton = {(index) => !this.isExternalIC(index)} + deleteButton = {(index) => !this.isExternalIC(index, ics)} onEdit={index => this.setState({editModal: true, modalIC: ics[index], modalIndex: index})} onExport={index => this.exportIC(index)} onDelete={index => this.setState({deleteModal: true, modalIC: ics[index], modalIndex: index})} From a4223aa6d0f24d2295d6cfc6cc42edc6ca15b729 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 24 Feb 2021 11:28:46 +0100 Subject: [PATCH 122/127] another fix for determination of external IC in IC table, edit and delete buttons should not show in case of external ic --- src/ic/ics.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ic/ics.js b/src/ic/ics.js index 6024e10..026695b 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -405,7 +405,7 @@ class InfrastructureComponents extends Component {
this.isExternalIC(index, ics)} + checkboxDisabled={(index) => this.isExternalIC(index, ics) === true} onChecked={(ic, event) => this.onICChecked(ic, event)} width='30' /> @@ -438,9 +438,9 @@ class InfrastructureComponents extends Component { {this.state.currentUser.role === "Admin" ? !this.isExternalIC(index, ics)} + editButton = {(index) => this.isExternalIC(index, ics) !== true} exportButton - deleteButton = {(index) => !this.isExternalIC(index, ics)} + deleteButton = {(index) => this.isExternalIC(index, ics) !== true} onEdit={index => this.setState({editModal: true, modalIC: ics[index], modalIndex: index})} onExport={index => this.exportIC(index)} onDelete={index => this.setState({deleteModal: true, modalIC: ics[index], modalIndex: index})} From d4b9acd08d4a6e0352e0a3a7ebacfb1d981161f6 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 24 Feb 2021 12:32:07 +0100 Subject: [PATCH 123/127] check if true for editButton and deleteButton --- src/common/table.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/table.js b/src/common/table.js index 5471244..d5c750c 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -124,7 +124,7 @@ class CustomTable extends Component { } // add buttons - if (child.props.editButton) { + if (child.props.editButton === true) { cell.push( ); } - if (child.props.deleteButton) { + if (child.props.deleteButton === true) { cell.push( Date: Wed, 24 Feb 2021 12:40:12 +0100 Subject: [PATCH 124/127] START command: Use a list of urls for all associated files instead of just one file --- src/ic/ic-action.js | 8 ++++++-- src/ic/ic-store.js | 11 ++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ic/ic-action.js b/src/ic/ic-action.js index 02d3a63..2d08af4 100644 --- a/src/ic/ic-action.js +++ b/src/ic/ic-action.js @@ -182,8 +182,12 @@ class ICAction extends React.Component { newAction["model"] = {} newAction.model["type"] = "url" newAction.model["token"] = this.props.token - // TODO do not default to the first file of the config - newAction.model["url"] = "/files/" + config.fileIDs[0].toString() + + let fileURLs = [] + for (let fileID of config.fileIDs){ + fileURLs.push("/files/" + fileID.toString()) + } + newAction.model["url"] = fileURLs } newAction["results"] = {} diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index 39b1c8c..36abb91 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -91,9 +91,14 @@ class InfrastructureComponentStore extends ArrayStore { a.results.url = window.location.host + a.results.url; } if (a.model !== undefined && a.model != null && JSON.stringify(a.model) !== JSON.stringify({})) { - // adapt URL for model file - a.model.url = ICsDataManager.makeURL(a.model.url); - a.model.url = window.location.host + a.model.url; + // adapt URL(s) for model file + let modelURLs = [] + for (let url of a.model.url){ + let modifiedURL = ICsDataManager.makeURL(url); + modifiedURL = window.location.host + modifiedURL; + modelURLs.push(modifiedURL) + } + a.model.url = modelURLs } ICsDataManager.doActions(a.icid, [a], action.token) } From 27c3b2dffad085dff813e2feea803f89c985d5e7 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 24 Feb 2021 13:41:07 +0100 Subject: [PATCH 125/127] fixed column indexes of edit signal mapping dialog (shifted because of additional checkbox column) --- src/signal/edit-signal-mapping.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/signal/edit-signal-mapping.js b/src/signal/edit-signal-mapping.js index b1df22c..3b75386 100644 --- a/src/signal/edit-signal-mapping.js +++ b/src/signal/edit-signal-mapping.js @@ -97,23 +97,24 @@ class EditSignalMapping extends React.Component { let signals = this.state.signals; let modifiedSignals = this.state.modifiedSignalIDs; - - if (column === 1) { // Name change + console.log("HandleMappingChange", row, column) + if (column === 2) { // Name change signals[row].name = event.target.value; if (modifiedSignals.find(id => id === signals[row].id) === undefined){ modifiedSignals.push(signals[row].id); } - } else if (column === 2) { // unit change + } else if (column === 3) { // unit change signals[row].unit = event.target.value; if (modifiedSignals.find(id => id === signals[row].id) === undefined){ modifiedSignals.push(signals[row].id); } - } else if (column === 3) { // scaling factor change + } else if (column === 4) { // scaling factor change signals[row].scalingFactor = parseFloat(event.target.value); if (modifiedSignals.find(id => id === signals[row].id) === undefined){ modifiedSignals.push(signals[row].id); } - } else if (column === 0) { //index change + } else if (column === 1) { //index change + console.log("Index change") signals[row].index =parseInt(event.target.value, 10); if (modifiedSignals.find(id => id === signals[row].id) === undefined){ modifiedSignals.push(signals[row].id); From 1c0a2403d294b219cac065ef04f019f55e8eaac2 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 25 Feb 2021 10:31:11 +0100 Subject: [PATCH 126/127] disable showing of edit and delete button through additional table column parameter --- src/common/table-column.js | 2 ++ src/common/table.js | 71 +++++++++++++++++++++++--------------- src/ic/ics.js | 12 ++++--- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/common/table-column.js b/src/common/table-column.js index 9d77606..460eb4b 100644 --- a/src/common/table-column.js +++ b/src/common/table-column.js @@ -23,7 +23,9 @@ class TableColumn extends Component { modifier: null, width: null, editButton: false, + showEditButton: null, deleteButton: false, + showDeleteButton: null, exportButton: false, duplicateButton: false, link: '/', diff --git a/src/common/table.js b/src/common/table.js index d5c750c..f54335e 100644 --- a/src/common/table.js +++ b/src/common/table.js @@ -124,26 +124,34 @@ class CustomTable extends Component { } // add buttons - if (child.props.editButton === true) { - cell.push( - Edit }> - - ); + let showEditButton = true + if (child.props.showEditButton !== null) + { + showEditButton = child.props.showEditButton(index) } + if(showEditButton){ + if (child.props.editButton) { + cell.push( + Edit }> + + ); + } + } + if (child.props.checkbox) { const checkboxKey = child.props.checkboxKey; let isDisabled = false; if (child.props.checkboxDisabled != null){ - isDisabled = !child.props.checkboxDisabled(index) + isDisabled = child.props.checkboxDisabled(index) } cell.push( ); } - if (child.props.deleteButton === true) { - cell.push( - Delete } > - - ); + let showDeleteButton = true; + if (child.props.showDeleteButton !== null){ + showDeleteButton = child.props.showDeleteButton(index) } + if (showDeleteButton){ + if (child.props.deleteButton) { + cell.push( + Delete } > + + ); + } + } + + + return cell; } // addCell diff --git a/src/ic/ics.js b/src/ic/ics.js index 026695b..bb97ef1 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -393,9 +393,9 @@ class InfrastructureComponents extends Component { } } - isExternalIC(index, ics){ + isLocalIC(index, ics){ let ic = ics[index] - return ic.managedexternally + return !ic.managedexternally } getICCategoryTable(ics, editable, title){ @@ -405,7 +405,7 @@ class InfrastructureComponents extends Component {
this.isExternalIC(index, ics) === true} + checkboxDisabled={(index) => this.isLocalIC(index, ics) === true} onChecked={(ic, event) => this.onICChecked(ic, event)} width='30' /> @@ -438,9 +438,11 @@ class InfrastructureComponents extends Component { {this.state.currentUser.role === "Admin" ? this.isExternalIC(index, ics) !== true} + editButton + showEditButton ={(index) => this.isLocalIC(index, ics)} exportButton - deleteButton = {(index) => this.isExternalIC(index, ics) !== true} + deleteButton + showDeleteButton = {(index) => this.isLocalIC(index, ics)} onEdit={index => this.setState({editModal: true, modalIC: ics[index], modalIndex: index})} onExport={index => this.exportIC(index)} onDelete={index => this.setState({deleteModal: true, modalIC: ics[index], modalIndex: index})} From 460cd2c4af7760ca2543a0c5e44e8ae1795f55dd Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 26 Feb 2021 15:55:30 +0100 Subject: [PATCH 127/127] query statistics endpoint of VILLASnode in addition to status endpoint - commented parts to be uncommented as soon as fix in VILLASnode is deployable --- src/ic/ic-store.js | 28 +++++++++++++++++++++++++++- src/ic/ics-data-manager.js | 26 ++++++++++++++++++++------ src/ic/ics.js | 2 +- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js index 36abb91..b852b94 100644 --- a/src/ic/ic-store.js +++ b/src/ic/ic-store.js @@ -117,7 +117,12 @@ class InfrastructureComponentStore extends ArrayStore { if(!tempIC.managedexternally){ tempIC.state = action.data.state; tempIC.uptime = action.data.time_now - action.data.time_started; - tempIC.statusupdateraw = action.data; + if (tempIC.statusupdateraw === null || tempIC.statusupdateraw === undefined){ + tempIC.statusupdateraw = {}; + tempIC.statusupdateraw["status"] = action.data; + } else { + tempIC.statusupdateraw["status"] = action.data; + } AppDispatcher.dispatch({ type: 'ics/start-edit', data: tempIC, @@ -130,6 +135,27 @@ class InfrastructureComponentStore extends ArrayStore { console.log("status error:", action.error); return super.reduce(state, action); + case 'ics/nodestats-received': + let tempIC2 = action.ic; + if(!tempIC2.managedexternally){ + if (tempIC2.statusupdateraw === null || tempIC2.statusupdateraw === undefined){ + tempIC2.statusupdateraw = {}; + tempIC2.statusupdateraw["statistics"] = action.data; + } else { + tempIC2.statusupdateraw["statistics"] = action.data; + } + AppDispatcher.dispatch({ + type: 'ics/start-edit', + data: tempIC2, + token: action.token, + }); + } + return super.reduce(state, action); + + case 'ics/nodestats-error': + console.log("nodestats error:", action.error); + return super.reduce(state, action); + case 'ics/restart': ICsDataManager.restart(action.url, action.token); return super.reduce(state, action); diff --git a/src/ic/ics-data-manager.js b/src/ic/ics-data-manager.js index a2382fc..74b4080 100644 --- a/src/ic/ics-data-manager.js +++ b/src/ic/ics-data-manager.js @@ -97,17 +97,12 @@ class IcsDataManager extends RestDataManager { error }); }); - - } - - - } } getStatus(url,token,ic){ - RestAPI.get(url, null).then(response => { + RestAPI.get(url + "/status", null).then(response => { AppDispatcher.dispatch({ type: 'ics/status-received', data: response, @@ -120,6 +115,25 @@ class IcsDataManager extends RestDataManager { error: error }) }) + + // get name of websocket + /*let ws_api = ic.websocketurl.split("/") + let ws_name = ws_api[ws_api.length-1] // websocket name is the last element in the websocket url + + RestAPI.get(url + "/node/" + ws_name + "/stats", null).then(response => { + AppDispatcher.dispatch({ + type: 'ics/nodestats-received', + data: response, + token: token, + ic: ic + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'ics/nodestats-error', + error: error + }) + })*/ + } restart(url,token){ diff --git a/src/ic/ics.js b/src/ic/ics.js index bb97ef1..6152c17 100644 --- a/src/ic/ics.js +++ b/src/ic/ics.js @@ -137,7 +137,7 @@ class InfrastructureComponents extends Component { && ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null && !ic.managedexternally) { AppDispatcher.dispatch({ type: 'ics/get-status', - url: ic.apiurl + "/status", + url: ic.apiurl, token: this.state.sessionToken, ic: ic });