diff --git a/package-lock.json b/package-lock.json
index 7d5f657..b1fb78b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17872,7 +17872,8 @@
},
"ssri": {
"version": "6.0.1",
- "resolved": "",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"requires": {
"figgy-pudding": "^3.5.1"
}
diff --git a/src/common/color-picker.js b/src/common/color-picker.js
new file mode 100644
index 0000000..775e9c7
--- /dev/null
+++ b/src/common/color-picker.js
@@ -0,0 +1,110 @@
+/**
+ * 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 { Form } from 'react-bootstrap';
+import { SketchPicker } from 'react-color';
+import Dialog from './dialogs/dialog';
+import PropTypes from 'prop-types'
+
+class ColorPicker extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rgbColor: {},
+ };
+ }
+
+ componentDidUpdate(prevProps: Readonly
, prevState: Readonly, snapshot: SS) {
+
+ if(this.props.show !== prevProps.show){
+ // update color if show status of color picker has changed
+ this.setState({rgbColor: ColorPicker.hexToRgb(this.props.hexcolor,this.props.opacity)})
+ }
+ }
+
+ static hexToRgb(hex,opacity) {
+ let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16),
+ a: opacity
+ } : null;
+ }
+
+ handleChangeComplete = (color) => {
+ this.setState({rgbColor: color.rgb})
+ };
+
+ onClose = canceled => {
+ if (canceled) {
+ if (this.props.onClose != null) {
+ this.props.onClose();
+ }
+
+ return;
+ }
+
+ if (this.props.onClose != null) {
+
+ const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => {
+ const hex = x.toString(16)
+ return hex.length === 1 ? '0' + hex : hex
+ }).join('')
+
+ let data = {
+ hexcolor: rgbToHex(this.state.rgbColor.r, this.state.rgbColor.g,this.state.rgbColor.b),
+ opacity: this.state.rgbColor.a,
+ }
+
+ this.props.onClose(data);
+ }
+ };
+
+ render() {
+
+ return this.onClose(c)}
+ valid={true}
+ >
+
+ ;
+ }
+}
+
+ColorPicker.propTypes = {
+ onClose: PropTypes.func,
+ disableOpacity: PropTypes.bool,
+ show: PropTypes.bool,
+ hexcolor: PropTypes.string,
+ opacity: PropTypes.number
+}
+
+export default ColorPicker;
diff --git a/src/dashboard/dashboard.js b/src/dashboard/dashboard.js
index 077de3f..d15a671 100644
--- a/src/dashboard/dashboard.js
+++ b/src/dashboard/dashboard.js
@@ -186,17 +186,17 @@ class Dashboard extends Component {
componentDidUpdate(prevProps: Readonly
, prevState: Readonly, snapshot: SS) {
// open web sockets if ICs are already known and sockets are not opened yet
if (this.state.ics !== undefined && !Dashboard.webSocketsOpened) {
- // only open sockets of ICs with configured input or output signals
- let relevantICs = this.state.ics.filter(ic => {
- let result = false;
- this.state.configs.forEach(config => {
- if(ic.id === config.icID && (config.inputLength !== 0 || config.outputLength !== 0)){
- result = true;
- }
+ // only open sockets of ICs with configured input or output signals
+ let relevantICs = this.state.ics.filter(ic => {
+ let result = false;
+ this.state.configs.forEach(config => {
+ if(ic.id === config.icID && (config.inputLength !== 0 || config.outputLength !== 0)){
+ result = true;
+ }
+ })
+ return result;
})
- return result;
- })
-
+
if (relevantICs.length > 0) {
console.log("Starting to open IC websockets:", relevantICs);
AppDispatcher.dispatch({
diff --git a/src/ic/ic-data-store.js b/src/ic/ic-data-store.js
index 7a81698..a6c53e0 100644
--- a/src/ic/ic-data-store.js
+++ b/src/ic/ic-data-store.js
@@ -110,6 +110,7 @@ class ICDataStore extends ReduceStore {
state[action.ic].input.timestamp = Date.now();
state[action.ic].input.sequence++;
state[action.ic].input.values[action.signal-1] = action.data;
+ state[action.ic].input.length = state[action.ic].input.values.length
// copy of state needed because changes are not yet propagated
let input = JSON.parse(JSON.stringify(state[action.ic].input));
diff --git a/src/ic/ic-store.js b/src/ic/ic-store.js
index bc9aa18..eb6acf9 100644
--- a/src/ic/ic-store.js
+++ b/src/ic/ic-store.js
@@ -114,15 +114,21 @@ class InfrastructureComponentStore extends ArrayStore {
case 'ics/status-received':
let tempIC = action.ic;
- if(!tempIC.managedexternally){
+ if (!tempIC.managedexternally) {
tempIC.state = action.data.state;
tempIC.uptime = action.data.time_now - action.data.time_started;
tempIC.statusupdateraw = action.data
+ tempIC.uuid = action.data.uuid
AppDispatcher.dispatch({
type: 'ics/start-edit',
data: tempIC,
token: action.token,
});
+
+ if(tempIC.type === "villas-node"){
+ ICsDataManager.getConfig(action.url, action.token, tempIC);
+ }
+
}
return super.reduce(state, action);
@@ -130,9 +136,48 @@ class InfrastructureComponentStore extends ArrayStore {
console.log("status error:", action.error);
return super.reduce(state, action);
- case 'ics/nodestats-received':
+ case 'ics/config-error':
+ console.log("config error:", action.error);
+ return super.reduce(state, action);
+
+ case 'ics/config-received':
+ let temp = action.ic;
+ if (!temp.managedexternally) {
+ if (temp.statusupdateraw === null || temp.statusupdateraw === undefined) {
+ temp.statusupdateraw = {};
+ }
+ temp.statusupdateraw["config"] = action.data;
+ AppDispatcher.dispatch({
+ type: 'ics/start-edit',
+ data: temp,
+ token: action.token,
+ });
+
+ if(temp.type === "villas-node") {
+ ICsDataManager.getStatistics(action.url, action.token, temp);
+ }
+
+ }
+ return super.reduce(state, action);
+
+ case 'ics/statistics-error':
+ if (action.error.status === 400){
+ // in case of bad request add the error message to the raw status
+ // most likely the statistics collection is disabled for this node
+ AppDispatcher.dispatch({
+ type: 'ics/statistics-received',
+ data: action.error.response.text,
+ token: action.token,
+ ic: action.ic
+ });
+ } else {
+ console.log("statistics error:", action.error);
+ }
+ return super.reduce(state, action);
+
+ case 'ics/statistics-received':
let tempIC2 = action.ic;
- if(!tempIC2.managedexternally){
+ if (!tempIC2.managedexternally) {
if (tempIC2.statusupdateraw === null || tempIC2.statusupdateraw === undefined) {
tempIC2.statusupdateraw = {};
}
@@ -145,10 +190,6 @@ class InfrastructureComponentStore extends ArrayStore {
}
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/ic.js b/src/ic/ic.js
index 9bb730f..40e300b 100644
--- a/src/ic/ic.js
+++ b/src/ic/ic.js
@@ -16,225 +16,320 @@
******************************************************************************/
import React from 'react';
-import InfrastructureComponentStore from './ic-store';
+import ICstore from './ic-store';
+import ICdataStore from './ic-data-store'
import { Container as FluxContainer } from 'flux/utils';
import AppDispatcher from '../common/app-dispatcher';
import { Container, Col, Row, Table, Button } from 'react-bootstrap';
import moment from 'moment';
import ReactJson from 'react-json-view';
import ConfirmCommand from './confirm-command';
-import Icon from "../common/icon";
+import IconButton from '../common/icon-button';
+import FileSaver from 'file-saver';
class InfrastructureComponent extends React.Component {
- constructor(props) {
- super(props);
+ constructor(props) {
+ super(props);
- this.state = {
- confirmCommand: false,
- command: '',
- };
+ this.state = {
+ confirmCommand: false,
+ command: '',
+ sessionToken: localStorage.getItem("token"),
+ currentUser: JSON.parse(localStorage.getItem("currentUser")),
+ };
+ }
+
+ static getStores() {
+ return [ICstore, ICdataStore];
+ }
+
+ static calculateState(prevState, props) {
+ return {
+ ic: ICstore.getState().find(ic => ic.id === parseInt(props.match.params.ic, 10))
+ }
+ }
+
+ componentDidMount() {
+ let icID = parseInt(this.props.match.params.ic, 10);
+
+ AppDispatcher.dispatch({
+ type: 'ics/start-load',
+ data: icID,
+ token: this.state.sessionToken,
+ });
+ }
+
+
+ refresh() {
+ // get status of VILLASnode and VILLASrelay ICs
+ if (this.state.ic.category === "gateway" && (this.state.ic.type === "villas-node" || this.state.ic.type === "villas-relay")
+ && this.state.ic.apiurl !== '' && this.state.ic.apiurl !== undefined && this.state.ic.apiurl !== null && !this.state.ic.managedexternally) {
+ AppDispatcher.dispatch({
+ type: 'ics/get-status',
+ url: this.state.ic.apiurl,
+ token: this.state.sessionToken,
+ ic: this.state.ic
+ });
+ }
+ }
+
+ isJSON(data) {
+ if (data === undefined || data === null) {
+ return false;
+ }
+ let str = JSON.stringify(data);
+ try {
+ JSON.parse(str)
+ }
+ catch (ex) {
+ return false
+ }
+ return true
+ }
+
+ async downloadGraph(url) {
+ let blob = await fetch(url).then(r => r.blob())
+ FileSaver.saveAs(blob, this.state.ic.name + ".svg");
+ }
+
+ sendControlCommand() {
+ if (this.state.command === "restart") {
+ AppDispatcher.dispatch({
+ type: 'ics/restart',
+ url: this.state.ic.apiurl + "/restart",
+ token: this.state.sessionToken,
+ });
+ } else if (this.state.command === "shutdown") {
+ AppDispatcher.dispatch({
+ type: 'ics/shutdown',
+ url: this.state.ic.apiurl + "/shutdown",
+ token: this.state.sessionToken,
+ });
+ }
+ }
+
+ confirmCommand(canceled){
+ if(!canceled){
+ this.sendControlCommand();
}
- static getStores() {
- return [InfrastructureComponentStore];
+ this.setState({confirmCommand: false, command: ''});
+ }
+
+
+ render() {
+ if (this.state.ic === undefined) {
+ return Loading Infrastructure Component... ;
}
- static calculateState(prevState, props) {
- if (prevState == null) {
- prevState = {};
- }
-
- return {
- sessionToken: localStorage.getItem("token"),
- currentUser: JSON.parse(localStorage.getItem("currentUser")),
- ic: InfrastructureComponentStore.getState().find(ic => ic.id === parseInt(props.match.params.ic, 10))
- }
+ let graphURL = ""
+ if (this.state.ic.apiurl !== "") {
+ graphURL = this.state.ic.apiurl + "/graph.svg"
}
- componentDidMount() {
- let icID = parseInt(this.props.match.params.ic, 10);
-
- AppDispatcher.dispatch({
- type: 'ics/start-load',
- data: icID,
- token: this.state.sessionToken,
- });
+ const buttonStyle = {
+ marginLeft: '5px',
}
- isJSON(data) {
- if (data === undefined || data === null) {
- return false;
- }
- let str = JSON.stringify(data);
- try {
- JSON.parse(str)
- }
- catch (ex) {
- return false
- }
- return true
+ const iconStyle = {
+ height: '25px',
+ width: '25px'
}
- async downloadGraph(url) {
- let blob = await fetch(url).then(r => r.blob())
- FileSaver.saveAs(blob, this.state.ic.name + ".svg");
- }
+ return
+
{this.state.ic.name}
+
+
+
+
+
+
+ Name
+ {this.state.ic.name}
+
+
+ Description
+ {this.state.ic.description}
+
+
+ UUID
+ {this.state.ic.uuid}
+
+
+ State
+ {this.state.ic.state}
+
+
+ Category
+ {this.state.ic.category}
+
+
+ Type
+ {this.state.ic.type}
+
+
+ Uptime
+ {moment.duration(this.state.ic.uptime, "seconds").humanize()}
+
+
+ Location
+ {this.state.ic.location}
+
+
+ Websocket URL
+ {this.state.ic.websocketurl}
+
+
+ API URL
+ {this.state.ic.apiurl}
+
+
+ Start parameter schema
+
+ {this.isJSON(this.state.ic.startparameterschema) ?
+ : No Start parameter schema JSON available.
}
+
+
+
+
+
+
+ {this.state.category ==="gateway" && this.state.ic.type === "villas-node" ?
+ <>
+
+ this.downloadGraph(graphURL)}
+ icon='download'
+ buttonStyle={buttonStyle}
+ iconStyle={iconStyle}
+ />
+
+
+ Graph:
+
+
+
- sendControlCommand() {
- if (this.state.command === "restart") {
- AppDispatcher.dispatch({
- type: 'ics/restart',
- url: this.state.ic.apiurl + "/restart",
- token: this.state.sessionToken,
- });
- } else if (this.state.command === "shutdown") {
- AppDispatcher.dispatch({
- type: 'ics/shutdown',
- url: this.state.ic.apiurl + "/shutdown",
- token: this.state.sessionToken,
- });
- }
- }
+ {this.state.currentUser.role === "Admin" ?
+
+
+
Controls:
+
+ this.setState({ confirmCommand: true, command: 'restart' })}>Restart
+ this.setState({
+ confirmCommand: true,
+ command: 'shutdown'
+ })}>Shutdown
+
+
+ :
+ }
+ this.confirmCommand(c)}
+ />
+ >
+ :
}
- confirmCommand(canceled){
- if(!canceled){
- this.sendControlCommand();
- }
-
- this.setState({confirmCommand: false, command: ''});
- }
-
- async downloadGraph(url) {
-
- let blob = await fetch(url).then(r => r.blob())
- FileSaver.saveAs(blob, this.props.ic.name + ".svg");
- }
+ {this.state.category ==="gateway" && this.state.ic.type === "villas-relay" ?
+ <>
+
+ this.refresh()}
+ icon='sync-alt'
+ buttonStyle={buttonStyle}
+ iconStyle={iconStyle}
+ />
+
+
+ Raw Status
+ {this.state.ic.statusupdateraw !== null && this.isJSON(this.state.ic.statusupdateraw) ?
+ : No valid JSON raw data available.
}
+ >
+ :
+
}
+
+
+ {this.state.category ==="gateway" && this.state.ic.type === "villas-node" ?
+ <>
+
+ this.refresh()}
+ icon='sync-alt'
+ buttonStyle={buttonStyle}
+ iconStyle={iconStyle}
+ />
+
+
- render() {
- if (this.state.ic === undefined) {
- return Loading Infrastructure Component... ;
- }
-
- let graphURL = ""
- if (this.state.ic.apiurl !== "") {
- graphURL = this.state.ic.apiurl + "/graph.svg"
- }
-
- return
-
{this.state.ic.name}
-
-
-
-
-
-
- Name
- {this.state.ic.name}
-
-
- Description
- {this.state.ic.description}
-
-
- UUID
- {this.state.ic.uuid}
-
-
- State
- {this.state.ic.state}
-
-
- Category
- {this.state.ic.category}
-
-
- Type
- {this.state.ic.type}
-
-
- Uptime
- {moment.duration(this.state.ic.uptime, "seconds").humanize()}
-
-
- Location
- {this.state.ic.location}
-
-
- Websocket URL
- {this.state.ic.websocketurl}
-
-
- API URL
- {this.state.ic.apiurl}
-
-
- Start parameter schema
-
-
-
-
-
-
-
- Raw Status
- {this.isJSON(this.state.ic.statusupdateraw) ?
- : No valid JSON raw data available.
}
-
- {this.state.ic.type === "villas-node" ?
- <>
-
- this.downloadGraph(graphURL)}>
-
-
- Graph:
-
-
-
-
- {this.state.currentUser.role === "Admin" ?
-
-
-
Controls:
-
- this.setState({ confirmCommand: true, command: 'restart' })}>Restart
- this.setState({
- confirmCommand: true,
- command: 'shutdown'
- })}>Shutdown
-
-
- :
- }
- this.confirmCommand(c)} />
- >
- :
}
-
-
-
-
-
;
- }
+
+ Raw Status
+ {this.state.ic.statusupdateraw !== null && this.isJSON(this.state.ic.statusupdateraw) ?
+ : No valid JSON raw data available.
}
+
+
+ Raw Config
+ {this.state.ic.statusupdateraw && this.isJSON(this.state.ic.statusupdateraw["config"]) ?
+ : No valid config JSON raw data available.
}
+
+
+ Raw Statistics
+ {this.state.ic.statusupdateraw && this.isJSON(this.state.ic.statusupdateraw["statistics"]) ?
+ : No valid statistics JSON raw data available.
}
+
+
+ >:
}
+
+
;
+ }
}
let fluxContainerConverter = require('../common/FluxContainerConverter');
-export default FluxContainer.create(fluxContainerConverter.convert(InfrastructureComponent), { withProps: true });
\ No newline at end of file
+export default FluxContainer.create(fluxContainerConverter.convert(InfrastructureComponent), { withProps: true });
diff --git a/src/ic/ics-data-manager.js b/src/ic/ics-data-manager.js
index 74b4080..fcfd5db 100644
--- a/src/ic/ics-data-manager.js
+++ b/src/ic/ics-data-manager.js
@@ -102,12 +102,17 @@ class IcsDataManager extends RestDataManager {
}
getStatus(url,token,ic){
- RestAPI.get(url + "/status", null).then(response => {
+ let requestURL = url;
+ if(ic.type === "villas-node"){
+ requestURL += "/status";
+ }
+ RestAPI.get(requestURL, null).then(response => {
AppDispatcher.dispatch({
type: 'ics/status-received',
data: response,
token: token,
- ic: ic
+ ic: ic,
+ url: url
});
}).catch(error => {
AppDispatcher.dispatch({
@@ -116,24 +121,51 @@ class IcsDataManager extends RestDataManager {
})
})
- // 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
+ }
+
+ getConfig(url,token,ic){
+
+ // get the name of the node
+ let ws_api = ic.websocketurl.split("/")
+ let ws_name = ws_api[ws_api.length-1] // name is the last element in the websocket url
+
+ RestAPI.get(url + "/node/" + ws_name, null).then(response => {
+ AppDispatcher.dispatch({
+ type: 'ics/config-received',
+ data: response,
+ token: token,
+ ic: ic,
+ url: url
+ });
+ }).catch(error => {
+ AppDispatcher.dispatch({
+ type: 'ics/config-error',
+ error: error
+ })
+ })
+ }
+
+ getStatistics(url,token,ic){
+
+ // get the name of the node
+ let ws_api = ic.websocketurl.split("/")
+ let ws_name = ws_api[ws_api.length-1] // 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',
+ type: 'ics/statistics-received',
data: response,
token: token,
ic: ic
});
}).catch(error => {
AppDispatcher.dispatch({
- type: 'ics/nodestats-error',
- error: error
+ type: 'ics/statistics-error',
+ error: error,
+ token: token,
+ ic: ic
})
- })*/
-
+ })
}
restart(url,token){
diff --git a/src/ic/ics.js b/src/ic/ics.js
index 1b3358f..7452fa8 100644
--- a/src/ic/ics.js
+++ b/src/ic/ics.js
@@ -133,20 +133,21 @@ class InfrastructureComponents extends Component {
token: this.state.sessionToken,
});
- // get status 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 && !ic.managedexternally) {
- AppDispatcher.dispatch({
- type: 'ics/get-status',
- url: ic.apiurl,
- token: this.state.sessionToken,
- ic: ic
- });
- }
- })
-
}
+
+ // get status of VILLASnode and VILLASrelay ICs
+ this.state.ics.forEach(ic => {
+ if (ic.category === "gateway" && (ic.type === "villas-node" || ic.type === "villas-relay")
+ && ic.apiurl !== '' && ic.apiurl !== undefined && ic.apiurl !== null && !ic.managedexternally) {
+ AppDispatcher.dispatch({
+ type: 'ics/get-status',
+ url: ic.apiurl,
+ token: this.state.sessionToken,
+ ic: ic
+ });
+ }
+ })
+
}
closeNewModal(data) {
@@ -201,9 +202,6 @@ class InfrastructureComponents extends Component {
}
}
- closeICModal(data){
- this.setState({ icModal : false });
- }
closeDeleteModal(confirmDelete){
this.setState({ deleteModal: false });
@@ -345,14 +343,6 @@ class InfrastructureComponents extends Component {
return dateTime.fromNow()
}
- modifyManagedExternallyColumn(managedExternally, component){
- if(managedExternally){
- return
- } else {
- return ""
- }
- }
-
modifyUptimeColumn(uptime, component){
if(uptime >= 0){
let momentDurationFormatSetup = require("moment-duration-format");
@@ -366,11 +356,6 @@ class InfrastructureComponents extends Component {
}
}
- openICStatus(ic){
- let index = this.state.ics.indexOf(ic);
- this.setState({ icModal: true, modalIC: ic, modalIndex: index })
- }
-
isLocalIC(index, ics){
let ic = ics[index]
return !ic.managedexternally
@@ -450,10 +435,9 @@ class InfrastructureComponents extends Component {
}
render() {
-
const buttonStyle = {
- marginLeft: '10px'
- };
+ marginLeft: '10px',
+ }
const iconStyle = {
height: '30px',
diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js
index d9e9d4c..57e7a15 100644
--- a/src/scenario/scenario.js
+++ b/src/scenario/scenario.js
@@ -30,6 +30,7 @@ import SignalStore from '../signal/signal-store'
import FileStore from "../file/file-store"
import WidgetStore from "../widget/widget-store";
import ResultStore from "../result/result-store"
+import UsersStore from "../user/users-store"
import DashboardTable from '../dashboard/dashboard-table'
import ResultTable from "../result/result-table";
@@ -67,7 +68,7 @@ class Scenario extends React.Component {
}
static getStores() {
- return [ScenarioStore, ConfigStore, DashboardStore, ICStore, SignalStore, FileStore, WidgetStore, ResultStore];
+ return [ScenarioStore, ConfigStore, DashboardStore, ICStore, SignalStore, FileStore, WidgetStore, ResultStore, UsersStore];
}
componentDidMount() {
diff --git a/src/scenario/scenarios.js b/src/scenario/scenarios.js
index 51f8d3e..4a3ecbb 100644
--- a/src/scenario/scenarios.js
+++ b/src/scenario/scenarios.js
@@ -18,21 +18,19 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import FileSaver from 'file-saver';
-
import AppDispatcher from '../common/app-dispatcher';
import ScenarioStore from './scenario-store';
import DashboardStore from '../dashboard/dashboard-store';
import WidgetStore from "../widget/widget-store";
import ConfigStore from '../componentconfig/config-store';
import SignalStore from '../signal/signal-store'
-
-import Icon from '../common/icon';
+import ResultStore from '../result/result-store'
+import FileStore from '../file/file-store'
import Table from '../common/table';
import TableColumn from '../common/table-column';
import NewScenarioDialog from './new-scenario';
import EditScenarioDialog from './edit-scenario';
import ImportScenarioDialog from './import-scenario';
-
import DeleteDialog from '../common/dialogs/delete-dialog';
import IconButton from '../common/icon-button';
@@ -40,7 +38,7 @@ import IconButton from '../common/icon-button';
class Scenarios extends Component {
static getStores() {
- return [ScenarioStore, DashboardStore, WidgetStore, ConfigStore, SignalStore];
+ return [ScenarioStore, DashboardStore, WidgetStore, ConfigStore, SignalStore, ResultStore, FileStore];
}
static calculateState(prevState, props) {
diff --git a/src/signal/edit-signal-mapping.js b/src/signal/edit-signal-mapping.js
index e5bba88..cc235d2 100644
--- a/src/signal/edit-signal-mapping.js
+++ b/src/signal/edit-signal-mapping.js
@@ -95,7 +95,6 @@ class EditSignalMappingDialog extends React.Component {
let signals = this.state.signals;
let modifiedSignals = this.state.modifiedSignalIDs;
- 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) {
@@ -112,7 +111,6 @@ class EditSignalMappingDialog extends React.Component {
modifiedSignals.push(signals[row].id);
}
} 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);
diff --git a/src/signal/signals-data-manager.js b/src/signal/signals-data-manager.js
index 1a5ee0c..b833534 100644
--- a/src/signal/signals-data-manager.js
+++ b/src/signal/signals-data-manager.js
@@ -71,7 +71,7 @@ class SignalsDataManager extends RestDataManager{
let configured = false;
let error = false;
for(let nodeConfig of nodes){
- console.log("parsing node config: ", nodeConfig)
+ //console.log("parsing node config: ", nodeConfig)
if(!nodeConfig.hasOwnProperty("name")){
console.warn("Could not parse the following node config because it lacks a name parameter:", nodeConfig);
} else if(nodeConfig.name === socketname){
@@ -128,7 +128,6 @@ class SignalsDataManager extends RestDataManager{
for (let outSig of nodeConfig.out.signals) {
if (outSig.enabled) {
- console.log("adding output signal:", outSig);
let newSignal = {
configID: configID,
direction: 'out',
diff --git a/src/user/edit-own-user.js b/src/user/edit-own-user.js
index a5b7756..8b49221 100644
--- a/src/user/edit-own-user.js
+++ b/src/user/edit-own-user.js
@@ -19,17 +19,16 @@ import React from 'react';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
+import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
+import NotificationsFactory from "../common/data-managers/notifications-factory";
class EditOwnUserDialog extends React.Component {
- valid: true;
-
constructor(props) {
super(props);
this.state = {
- username: this.props.user.username,
- id: this.props.user.id,
+ username: "",
mail: this.props.user.mail,
password: '',
oldPassword: '',
@@ -39,9 +38,29 @@ class EditOwnUserDialog extends React.Component {
onClose(canceled) {
if (canceled === false) {
- if (this.valid) {
- this.props.onClose(this.state);
+
+ let user = {};
+ user.id = this.props.user.id;
+ user.password = this.state.password;
+ user.oldPassword = this.state.oldPassword;
+ user.confirmPassword = this.state.confirmPassword
+
+ if (this.state.username != null && this.state.username !== this.props.user.username){
+ user.username = this.state.username;
}
+
+ if (this.state.mail != null && this.state.mail !== this.props.user.mail){
+ user.mail = this.state.mail;
+ }
+
+ if (this.state.password !== '' && this.state.oldPassword !== '' && this.state.password === this.state.confirmPassword ) {
+ user.password = this.state.password;
+ user.oldPassword = this.state.oldPassword;
+ } else if (this.state.password !== '' && this.state.password !== this.state.confirmPassword) {
+ NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR('New password not correctly confirmed'));
+ }
+
+ this.props.onClose(user);
} else {
this.props.onClose();
}
@@ -49,42 +68,11 @@ class EditOwnUserDialog extends React.Component {
handleChange(e) {
this.setState({ [e.target.id]: e.target.value });
-
- // check all controls
- let username = true;
- let mail = true;
- let pw = true;
- let oldPassword = true;
- let confirmPassword = true;
-
- if (this.state.username === '') {
- username = false;
- }
-
- if (this.state.mail === '') {
- mail = false;
- }
-
- if (this.state.password === '') {
- pw = false;
- }
-
- if (this.state.oldPassword === '') {
- oldPassword = false;
- }
-
- if (this.state.confirmPassword === '') {
- confirmPassword = false;
- }
-
- // form is valid if the following condition is met
- this.valid = username || mail || (oldPassword && pw && confirmPassword);
}
resetState() {
this.setState({
username: this.props.user.username,
- id: this.props.user.id,
mail: this.props.user.mail,
oldPassword: '',
confirmPassword: '',
@@ -94,28 +82,28 @@ class EditOwnUserDialog extends React.Component {
render() {
return (
- this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
+ this.onClose(c)} onReset={() => this.resetState()} valid={true}>
Username
- this.handleChange(e)} autocomplete="username" />
+ this.handleChange(e)} autoComplete="username" />
E-mail
- this.handleChange(e)} autocomplete="email" />
+ this.handleChange(e)} autoComplete="email" />
Old Password
- this.handleChange(e)} autocomplete="current-password" />
+ this.handleChange(e)} autoComplete="current-password" />
New Password
- this.handleChange(e)} autocomplete="new-password" />
+ this.handleChange(e)} autoComplete="new-password" />
Confirm New Password
- this.handleChange(e)} autocomplete="new-password" />
+ this.handleChange(e)} autoComplete="new-password" />
diff --git a/src/user/edit-user.js b/src/user/edit-user.js
index 5d20bf4..26a9fcb 100644
--- a/src/user/edit-user.js
+++ b/src/user/edit-user.js
@@ -19,30 +19,55 @@ import React from 'react';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
+import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
+import NotificationsFactory from "../common/data-managers/notifications-factory";
class EditUserDialog extends React.Component {
- valid: true;
-
constructor(props) {
super(props);
this.state = {
- username: props.user.username,
- mail: props.user.mail,
- role: props.user.role,
- id: props.user.id,
- active: props.user.active,
- password: '',
- confirmPassword: '',
- oldPassword: ''
+ username: '',
+ mail: '',
+ role: '',
+ active: '',
+ password: "",
+ confirmPassword: "",
+ oldPassword: "",
}
}
onClose(canceled) {
if (canceled === false) {
- if (this.valid) {
- this.props.onClose(this.state);
+
+ let user = {}
+ user.id = this.props.user.id;
+
+ if (this.state.username != null && this.state.username !== this.props.user.username){
+ user.username = this.state.username
}
+
+ if (this.state.mail != null && this.state.mail !== this.props.user.mail){
+ user.mail = this.state.mail
+ }
+
+ if (this.state.role != null && this.state.role !== this.props.user.role){
+ user.role = this.state.role
+ }
+
+ if (this.state.active != null && this.state.active !== this.props.user.active){
+ user.active = this.state.active
+ }
+
+ if (this.state.password !== '' && this.state.oldPassword !== '' && this.state.password === this.state.confirmPassword) {
+ user.password = this.state.password;
+ user.oldpassword = this.state.oldPassword
+ } else if (this.state.password !== '' && this.state.password !== this.state.confirmPassword){
+ NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR("New password not correctly confirmed"))
+ }
+
+ this.props.onClose(user);
+
} else {
this.props.onClose();
}
@@ -50,46 +75,6 @@ class EditUserDialog extends React.Component {
handleChange(e) {
this.setState({ [e.target.id]: e.target.value });
-
- // check all controls
- var username = true;
- var role = true;
- var mail = true;
- var pw = true;
- var active = true;
- var confirmPassword = true;
- var oldPassword = true;
-
- if (this.state.username === '') {
- username = false;
- }
-
- if (this.state.role === '') {
- role = false;
- }
-
- if (this.state.mail === '') {
- mail = false;
- }
-
- if (this.state.password === '') {
- pw = false;
- }
-
- if (this.state.active === '') {
- active = false;
- }
-
- if (this.state.confirmPassword === '') {
- confirmPassword = false;
- }
-
- if (this.state.oldPassword === '') {
- oldPassword = false;
- }
-
- // form is valid if any of the fields contain somethig
- this.valid = username || role || mail || pw || active || confirmPassword || oldPassword;
}
resetState() {
@@ -97,38 +82,40 @@ class EditUserDialog extends React.Component {
username: this.props.user.username,
mail: this.props.user.mail,
role: this.props.user.role,
- id: this.props.user.id
+ password: "",
+ confirmPassword: "",
+ oldPassword: "",
});
}
render() {
return (
- this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
+ this.onClose(c)} onReset={() => this.resetState()} valid={true}>
Username
- this.handleChange(e)} />
+ this.handleChange(e)} />
E-mail
- this.handleChange(e)} />
+ this.handleChange(e)} />
Admin Password
this.handleChange(e)} />
- Password
+ New User Password
this.handleChange(e)} />
- Confirm New Password
+ Confirm new Password
this.handleChange(e)} />
Role
- this.handleChange(e)}>
+ this.handleChange(e)}>
Administrator
User
Guest
diff --git a/src/user/login-store.js b/src/user/login-store.js
index 3e0eba6..f5cea52 100644
--- a/src/user/login-store.js
+++ b/src/user/login-store.js
@@ -66,11 +66,14 @@ class LoginStore extends ReduceStore {
return Object.assign({}, state, { token: null, currentUser: null, loginMessage: null});
case 'users/logged-in':
- // save login in local storage
- localStorage.setItem('token', action.token);
+ // save login data in local storage and loginStore
+ let newState = state
+ if (action.token != null){
+ localStorage.setItem('token', action.token);
+ newState = Object.assign({}, state, {token: action.token})
+ }
localStorage.setItem('currentUser', JSON.stringify(action.currentUser));
-
- return Object.assign({}, state, { token: action.token, currentUser: action.currentUser});
+ return Object.assign({}, newState, { currentUser: action.currentUser});
case 'users/login-error':
if (action.error && !action.error.handled) {
diff --git a/src/user/user.js b/src/user/user.js
index 26bd161..a4cdfd6 100644
--- a/src/user/user.js
+++ b/src/user/user.js
@@ -15,100 +15,74 @@
* along with VILLASweb. If not, see .
******************************************************************************/
-import React, { Component } from 'react';
+import React from 'react';
import { Container } from 'flux/utils';
-import { Button, Form, Row, Col } from 'react-bootstrap';
-
+import { Form, Row, Col } from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
-import UsersStore from './users-store';
-
-import Icon from '../common/icon';
import EditOwnUserDialog from './edit-own-user'
-import NotificationsDataManager from "../common/data-managers/notifications-data-manager"
-import NotificationsFactory from "../common/data-managers/notifications-factory";
+import IconButton from "../common/icon-button";
+import LoginStore from './login-store'
+
+class User extends React.Component {
+ constructor() {
+ super();
+
+ this.state = {
+ token: LoginStore.getState().token,
+ editModal: false,
+ }
+ }
-class User extends Component {
static getStores() {
- return [ UsersStore ];
+ return [ LoginStore ];
}
static calculateState(prevState, props) {
- prevState = prevState || {};
-
- let currentUserID = JSON.parse(localStorage.getItem("currentUser")).id;
- let currentUser = UsersStore.getState().find(user => user.id === parseInt(currentUserID, 10));
-
return {
- currentUser,
- token: localStorage.getItem("token"),
- editModal: false,
- };
- }
-
- componentDidMount() {
- let currentUserID = JSON.parse(localStorage.getItem("currentUser")).id;
-
- AppDispatcher.dispatch({
- type: 'users/start-load',
- data: parseInt(currentUserID, 10),
- token: this.state.token
- });
+ currentUser: LoginStore.getState().currentUser
+ }
}
closeEditModal(data) {
this.setState({ editModal: false });
- let updatedData = {}
- let updatedUser = this.state.currentUser;
- let hasChanged = false;
- let pwChanged = false;
-
- updatedData.id = this.state.currentUser.id;
-
if (data) {
- if (data.username !== this.state.currentUser.username) {
- hasChanged = true;
- updatedData.username = data.username;
- updatedUser.username = data.username
- }
-
- if (data.mail !== this.state.currentUser.mail) {
- hasChanged = true;
- updatedData.mail = data.mail;
- updatedUser.mail = data.mail;
- }
-
- if (data.password !== '' && data.oldPassword !== '' && data.password === data.confirmPassword ) {
- pwChanged = true;
- updatedData.password = data.password;
- updatedData.oldPassword = data.oldPassword;
- } else if (data.password !== '' && data.password !== data.confirmPassword) {
- NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR('New password not correctly confirmed'));
- return
- }
-
- if (hasChanged || pwChanged) {
- if (hasChanged){
- this.setState({ currentUser: updatedUser })
- }
-
- AppDispatcher.dispatch({
- type: 'users/start-edit',
- data: updatedData,
- token: this.state.token
- });
- } else {
- NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_WARNING('No update requested, no input data'));
- }
+ AppDispatcher.dispatch({
+ type: 'users/start-edit',
+ data: data,
+ token: this.state.token,
+ currentUser: this.state.currentUser,
+ });
}
}
render() {
let user = this.state.currentUser;
+ const buttonStyle = {
+ marginLeft: '10px',
+ }
+
+ const iconStyle = {
+ height: '30px',
+ width: '30px'
+ }
+
return (
-
Account
+ Account
+
+
+ this.setState({ editModal: true })}
+ icon='edit'
+ buttonStyle={buttonStyle}
+ iconStyle={iconStyle}
+ />
+
+
{user ?
<>
@@ -116,30 +90,28 @@ class User extends Component {
Username
-
+
E-mail
-
+
Role
-
+
Created at
-
+
- this.setState({ editModal: true })}>
- Edit
-
+
.
- ******************************************************************************/
-
-import React from 'react';
-import { Form } from 'react-bootstrap';
-import { SketchPicker } from 'react-color';
-import Dialog from '../../common/dialogs/dialog';
-
-
-class ColorPicker extends React.Component {
- valid = true;
-
- constructor(props) {
- super(props);
-
- this.state = {
- widget: {}
- };
- }
-
- static getDerivedStateFromProps(props, state) {
- return {
- widget: props.widget
- };
- }
-
- hexToRgb = (hex,opacity) => {
- var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
- r: parseInt(result[1], 16),
- g: parseInt(result[2], 16),
- b: parseInt(result[3], 16),
- a: opacity
- } : null;
- }
-
- handleChangeComplete = (color) => {
- let temp = this.state.widget;
-
- if (this.props.controlId === 'strokeStyle'){
- temp.customProperties.zones[this.props.zoneIndex]['strokeStyle'] = color.hex;
- }
- else if (this.props.controlId === 'lineColor'){
- temp.customProperties.lineColors[this.props.lineIndex] = color.hex;
- }
- else {
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
-
- if (parts.length === 1){
- isCustomProperty = false;
- }
-
- isCustomProperty ? temp[parts[0]][parts[1]] = color.hex : temp[this.props.controlId] = color.hex;
- isCustomProperty ? temp[parts[0]][parts[1] + "_opacity"] = color.rgb.a : temp[this.props.controlId +"_opacity"] = color.rgb.a;
- }
-
- this.setState({ widget: temp });
- };
-
- 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.widget);
- }
- };
-
- render() {
- let hexColor;
- let opacity = 1;
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length === 1) {
- isCustomProperty = false;
- }
-
- if (this.props.controlId === 'strokeStyle') {
- if (typeof this.state.widget.customProperties.zones[this.props.zoneIndex] !== 'undefined') {
- hexColor = this.state.widget.customProperties.zones[this.props.zoneIndex]['strokeStyle'];
- }
- }
- else if (this.props.controlId === 'lineColor') {
- if (typeof this.state.widget.customProperties.lineColors[this.props.lineIndex] !== 'undefined') {
- hexColor = this.state.widget.customProperties.lineColors[this.props.lineIndex];
- }
- }
- else{
- hexColor = isCustomProperty ? this.state.widget[parts[0]][parts[1]]: this.state.widget[this.props.controlId];
- opacity = isCustomProperty ? this.state.widget[parts[0]][parts[1] + "_opacity"]: this.state.widget[this.props.controlId + "_opacity"];
- }
-
- let rgbColor = this.hexToRgb(hexColor, opacity);
- return this.onClose(c)}
- valid={true}
- >
-
- ;
- }
-}
-
-export default ColorPicker;
diff --git a/src/widget/edit-widget/edit-widget-checkbox-control.js b/src/widget/edit-widget/edit-widget-checkbox-control.js
index 6dd8ce0..9ea51c4 100644
--- a/src/widget/edit-widget/edit-widget-checkbox-control.js
+++ b/src/widget/edit-widget/edit-widget-checkbox-control.js
@@ -21,21 +21,19 @@ import { Form } from 'react-bootstrap';
class EditWidgetCheckboxControl extends React.Component {
constructor(props) {
super(props);
+ }
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length ===1){
- isCustomProperty = false;
- }
-
+ static getDerivedStateFromProps(props, state) {
+ let parts = props.controlId.split('.');
let isChecked;
- if (isCustomProperty){
- isChecked = this.props.widget[parts[0]][parts[1]]
- } else{
- isChecked = this.props.widget[this.props.controlId]
+
+ if (parts.length ===1){
+ isChecked = props.widget[props.controlId]
+ } else {
+ isChecked = props.widget[parts[0]][parts[1]]
}
- this.state = {
+ return {
isChecked
};
}
diff --git a/src/widget/edit-widget/edit-widget-color-control.js b/src/widget/edit-widget/edit-widget-color-control.js
index 6fd82f7..0995453 100644
--- a/src/widget/edit-widget/edit-widget-color-control.js
+++ b/src/widget/edit-widget/edit-widget-color-control.js
@@ -17,7 +17,7 @@
import React, { Component } from 'react';
import { Form, OverlayTrigger, Tooltip, Button, Col } from 'react-bootstrap';
-import ColorPicker from './color-picker'
+import ColorPicker from '../../common/color-picker'
import Icon from "../../common/icon";
// schemeCategory20 no longer available in d3
@@ -28,55 +28,51 @@ class EditWidgetColorControl extends Component {
super(props);
this.state = {
- widget: {},
+ color: null,
+ opacity: null,
showColorPicker: false,
originalColor: null
};
}
static getDerivedStateFromProps(props, state){
+ let parts = props.controlId.split('.');
+ let isCustomProperty = true;
+ if (parts.length === 1){
+ isCustomProperty = false;
+ }
+
+ let color = (isCustomProperty ? props.widget[parts[0]][parts[1]] : props.widget[props.controlId]);
+ let opacity = (isCustomProperty ? props.widget[parts[0]][parts[1] + "_opacity"] : props.widget[props.controlId + "_opacity"]);
+
return {
- widget: props.widget
+ color: color,
+ opacity: opacity,
};
}
openColorPicker = () =>{
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length === 1){
- isCustomProperty = false;
- }
- let color = (isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]);
-
- this.setState({showColorPicker: true, originalColor: color});
+ this.setState({showColorPicker: true, originalColor: this.state.color});
}
- closeEditModal = (data) => {
+ closeColorPickerEditModal = (data) => {
this.setState({showColorPicker: false})
- if(typeof data === 'undefined'){
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length === 1) {
- isCustomProperty = false;
- }
- let temp = this.state.widget;
- isCustomProperty ? temp[parts[0]][parts[1]] = this.state.originalColor : temp[this.props.controlId] = this.state.originalColor;
- this.setState({ widget: temp });
+ if(typeof data === 'undefined'){
+
+ this.setState({ color: this.state.originalColor });
+ } else {
+ // color picker with result data {hexcolor, opacity}
+ this.setState({color: data.hexcolor, opacity: data.opacity})
+ this.props.handleChange({target: { id: this.props.controlId, value: data.hexcolor} })
+ this.props.handleChange({target: { id: this.props.controlId + "_opacity", value: data.opacity} })
}
}
render() {
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length === 1){
- isCustomProperty = false;
- }
- let color = (isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]);
- let opacity = (isCustomProperty ? this.props.widget[parts[0]][parts[1] + "_opacity"] : this.props.widget[this.props.controlId + "_opacity"]);
let style = {
- backgroundColor: color,
- opacity: opacity,
+ backgroundColor: this.state.color,
+ opacity: this.state.opacity,
width: '80px',
height: '40px',
}
@@ -85,8 +81,6 @@ class EditWidgetColorControl extends Component {
if(this.props.disableOpacity){
tooltipText = "Change border color";
}
-
-
return (
{this.props.label}
@@ -100,7 +94,13 @@ class EditWidgetColorControl extends Component {
- this.closeEditModal(data)} widget={this.state.widget} controlId={this.props.controlId} disableOpacity={this.props.disableOpacity}/>
+ this.closeColorPickerEditModal(data)}
+ hexcolor={this.state.color}
+ opacity={this.state.opacity}
+ disableOpacity={this.props.disableOpacity}
+ />
);
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 dd990bb..46b4ed7 100644
--- a/src/widget/edit-widget/edit-widget-color-zones-control.js
+++ b/src/widget/edit-widget/edit-widget-color-zones-control.js
@@ -17,8 +17,7 @@
import React from 'react';
import { Form, Table, Button, Tooltip, OverlayTrigger } from 'react-bootstrap';
-import ColorPicker from './color-picker'
-
+import ColorPicker from '../../common/color-picker'
import Icon from '../../common/icon';
import { Collapse } from 'react-collapse';
@@ -27,12 +26,7 @@ class EditWidgetColorZonesControl extends React.Component {
super(props);
this.state = {
- widget: {
- customProperties:{
- zones: []
- }
- },
- selectedZone: null,
+ colorZones: [],
selectedIndex: null,
showColorPicker: false,
originalColor: null,
@@ -43,89 +37,88 @@ class EditWidgetColorZonesControl extends React.Component {
static getDerivedStateFromProps(props, state){
return {
- widget: props.widget
+ colorZones: props.widget.customProperties.zones
};
}
addZone = () => {
// add row
- const widget = this.state.widget;
- widget.customProperties.zones.push({ strokeStyle: '#d3cbcb', min: 0, max: 100 });
+ const zones = JSON.parse(JSON.stringify(this.state.colorZones));
+ zones.push({ strokeStyle: '#d3cbcb', min: 0, max: 100 });
- if(widget.customProperties.zones.length > 0){
- let length = widget.customProperties.zones.length
+ if(zones.length > 0){
+ let length = zones.length
- for(let i= 0 ; i < length; i++){
- widget.customProperties.zones[i].min = i* 100/length;
- widget.customProperties.zones[i].max = (i+1)* 100/length;
- }
+ for(let i= 0 ; i < length; i++){
+ zones[i].min = i* 100/length;
+ zones[i].max = (i+1)* 100/length;
+ }
}
- this.setState({ widget, selectedZone: null, selectedIndex: null });
-
- this.sendEvent(widget);
+ this.setState({ colorZones: zones, selectedIndex: null });
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
removeZones = () => {
-
- let temp = this.state.widget;
-
- temp.customProperties.zones.splice(this.state.selectedIndex, 1);
-
- if(temp.customProperties.zones.length > 0){
- let length = temp.customProperties.zones.length
-
+ let zones = JSON.parse(JSON.stringify(this.state.colorZones));
+ zones.splice(this.state.selectedIndex, 1);
+ if(zones.length > 0){
+ let length = zones.length
for(let i= 0 ; i < length; i++){
- temp.customProperties.zones[i].min = i* 100/length;
- temp.customProperties.zones[i].max = (i+1)* 100/length;
+ zones[i].min = i* 100/length;
+ zones[i].max = (i+1)* 100/length;
}
- }
-
- this.setState({widget: temp,selectedZone: null, selectedIndex: null});
-
+ }
+ this.setState({colorZones: zones, selectedIndex: null});
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
changeCell = (event, row, column) => {
// change row
- const widget = this.state.widget;
+ const zones = JSON.parse(JSON.stringify(this.state.colorZones))
if (column === 1) {
- widget.customProperties.zones[row].strokeStyle = event.target.value;
+ zones[row].strokeStyle = event.target.value;
} else if (column === 2) {
- widget.customProperties.zones[row].min = event.target.value;
+ zones[row].min = event.target.value;
} else if (column === 3) {
- widget.customProperties.zones[row].max = event.target.value;
+ zones[row].max = event.target.value;
}
- this.setState({ widget });
-
- this.sendEvent(widget);
+ this.setState({ colorZones: zones });
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
editColorZone = (index) => {
if(this.state.selectedIndex !== index){
- this.setState({selectedZone: this.state.widget.customProperties.zones[index], selectedIndex: index , minValue: this.state.widget.customProperties.zones[index].min, maxValue: this.state.widget.customProperties.zones[index].max});
+ this.setState({
+ selectedIndex: index ,
+ minValue: this.state.colorZones[index].min,
+ maxValue: this.state.colorZones[index].max}
+ );
}
else{
- this.setState({selectedZone: null, selectedIndex: null});
+ this.setState({selectedIndex: null});
}
}
openColorPicker = () => {
-
- let color = this.state.selectedZone.strokeStyle;
-
+ let color = this.state.colorZones[this.state.selectedIndex].strokeStyle;
this.setState({showColorPicker: true, originalColor: color});
}
- closeEditModal = (data) => {
+ closeColorPickerEditModal = (data) => {
this.setState({showColorPicker: false})
+ let zones = JSON.parse(JSON.stringify(this.state.colorZones))
if(typeof data === 'undefined'){
- let temp = this.state.selectedZone;
- temp.strokeStyle = this.state.originalColor;
-
- this.setState({ selectedZone : temp });
+ zones[this.state.selectedIndex].strokeStyle = this.state.originalColor
+ this.setState({ colorZones : zones });
+ } else {
+ // color picker with result data {hexcolor, opacity}
+ zones[this.state.selectedIndex].strokeStyle = data.hexcolor
+ this.setState({ colorZones : zones });
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
}
@@ -134,14 +127,15 @@ class EditWidgetColorZonesControl extends React.Component {
if(e.target.value < 0) return;
this.setState({minValue: e.target.value});
- let temp = this.state.widget;
- temp.customProperties.zones[this.state.selectedIndex]['min'] = e.target.value;
+ let zones = JSON.parse(JSON.stringify(this.state.colorZones));
+ zones[this.state.selectedIndex]['min'] = e.target.value;
if(this.state.selectedIndex !== 0){
- temp.customProperties.zones[this.state.selectedIndex - 1]['max'] = e.target.value
+ zones[this.state.selectedIndex - 1]['max'] = e.target.value
}
- this.setState({ widget: temp });
+ this.setState({ colorZones: zones });
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
handleMaxChange = (e) => {
@@ -149,32 +143,19 @@ class EditWidgetColorZonesControl extends React.Component {
if(e.target.value > 100) return;
this.setState({maxValue: e.target.value});
- let temp = this.state.widget;
- temp.customProperties.zones[this.state.selectedIndex]['max'] = e.target.value;
+ let zones = JSON.parse(JSON.stringify(this.state.colorZones));
+ zones[this.state.selectedIndex]['max'] = e.target.value;
- if(this.state.selectedIndex !== this.state.widget.customProperties.zones.length -1){
- temp.customProperties.zones[this.state.selectedIndex + 1]['min'] = e.target.value
+ if(this.state.selectedIndex !== zones.length -1){
+ zones[this.state.selectedIndex + 1]['min'] = e.target.value
}
- this.setState({ widget: temp });
- }
-
- sendEvent(widget) {
- // create event
- const event = {
- target: {
- id: 'zones',
- value: widget.customProperties.zones
- }
- };
-
- this.props.handleChange(event);
+ this.setState({ colorZones: zones });
+ this.props.handleChange({target: { id: this.props.controlId, value: zones}})
}
-
render() {
-
const buttonStyle = {
marginBottom: '10px',
marginLeft: '120px',
@@ -187,9 +168,9 @@ class EditWidgetColorZonesControl extends React.Component {
let tempColor = 'FFFFFF';
let collapse = false;
- if(this.state.selectedZone !== null){
+ if(this.state.selectedIndex !== null){
collapse = true;
- tempColor = this.state.selectedZone.strokeStyle;
+ tempColor = this.state.colorZones[this.state.selectedIndex].strokeStyle;
}
let pickerStyle = {
@@ -213,7 +194,7 @@ class EditWidgetColorZonesControl extends React.Component {
{
- this.state.widget.customProperties.zones.map((zone, idx) => {
+ this.state.colorZones.map((zone, idx) => {
let color = zone.strokeStyle;
let width = (zone.max - zone.min)*(260/100);
let style = {
@@ -221,13 +202,24 @@ class EditWidgetColorZonesControl extends React.Component {
width: width,
height: '40px'
}
- return ( this.editColorZone(idx)} disabled={!this.props.widget.customProperties.colorZones}>
+ return (
+
+ Edit zone} >
+ this.editColorZone(idx)}
+ disabled={!this.props.widget.customProperties.colorZones}>
+
+
+
+
)
})
}
+ Edit selected color zone:
Change color} >
@@ -258,10 +250,22 @@ class EditWidgetColorZonesControl extends React.Component {
-
+ Remove zone} >
+
+
+
+
- this.closeEditModal(data)} widget={this.state.widget} zoneIndex={this.state.selectedIndex} controlId={'strokeStyle'} />
+ this.closeColorPickerEditModal(data)}
+ hexcolor={tempColor}
+ opacity={1}
+ disableOpacity={this.props.disableOpacity}
+ />
;
}
}
diff --git a/src/widget/edit-widget/edit-widget-control-creator.js b/src/widget/edit-widget/edit-widget-control-creator.js
index d2ee3fc..8eb6583 100644
--- a/src/widget/edit-widget/edit-widget-control-creator.js
+++ b/src/widget/edit-widget/edit-widget-control-creator.js
@@ -74,7 +74,8 @@ export default function CreateControls(widgetType = null, widget = null, session
handleChange(e)} direction={'out'}/>,
handleChange(e)} />,
handleChange(e)} />,
- handleChange(e)} />
+ handleChange(e)} />,
+ handleChange(e)} />
);
break;
case 'Table':
@@ -96,7 +97,7 @@ export default function CreateControls(widgetType = null, widget = null, session
handleChange(e)} />,
handleChange(e)} direction={'out'}/>,
handleChange(e)} />,
- handleChange(e)} />,
+ handleChange(e)} disableOpacity={true}/>,
handleChange(e)} />
);
break;
diff --git a/src/widget/edit-widget/edit-widget-plot-colors-control.js b/src/widget/edit-widget/edit-widget-plot-colors-control.js
index fbef75c..8dc6f9e 100644
--- a/src/widget/edit-widget/edit-widget-plot-colors-control.js
+++ b/src/widget/edit-widget/edit-widget-plot-colors-control.js
@@ -17,12 +17,9 @@
import React, { Component } from 'react';
import { OverlayTrigger, Tooltip , Button, Form } from 'react-bootstrap';
-import ColorPicker from './color-picker'
+import ColorPicker from '../../common/color-picker'
import Icon from "../../common/icon";
-import { scaleOrdinal } from "d3-scale";
-import { schemeCategory10 } from "d3-scale-chromatic";
-
-// schemeCategory20 no longer available in d3
+import {schemeCategory10} from "d3-scale-chromatic";
class EditWidgetPlotColorsControl extends Component {
@@ -30,51 +27,78 @@ class EditWidgetPlotColorsControl extends Component {
super(props);
this.state = {
- widget: {},
showColorPicker: false,
originalColor: null,
- selectedIndex: null
+ selectedIndex: null,
+ lineColors: [],
+ signalIDs: []
};
}
- static getDerivedStateFromProps(props, state){
-
- let widget = props.widget;
- if(widget.customProperties.lineColors === undefined || widget.customProperties.lineColors === null){
- // for backwards compatibility with old plots
- widget.customProperties.lineColors = []
-
- const newLineColor = scaleOrdinal(schemeCategory10);
- for (let signalID of widget.signalIDs){
- widget.customProperties.lineColors.push(newLineColor(signalID))
- }
- }
-
+ static getDerivedStateFromProps(props, state) {
return {
- widget: widget
+ lineColors: props.widget.customProperties.lineColors,
+ signalIDs: props.widget.signalIDs,
};
}
-//same here
+ componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: SS) {
- closeEditModal = (data) => {
+ let lineColorsChanged = false;
+
+ if (JSON.stringify(this.state.signalIDs) !== JSON.stringify(prevState.signalIDs)){
+ // if there was a change to the signal IDs
+ let tempLineColors = JSON.parse(JSON.stringify(this.state.lineColors));
+ let oldNoSignals = tempLineColors.length
+
+ if (this.state.signalIDs.length > prevState.signalIDs.length){
+ // more signals than before
+ let diff = this.state.signalIDs.length - prevState.signalIDs.length
+ for (let i = 0; i {
this.setState({showColorPicker: false})
+ let tempLineColors = JSON.parse(JSON.stringify(this.state.lineColors));
if(typeof data === 'undefined'){
-
- let temp = this.state.widget;
- temp.customProperties.lineColors[this.state.selectedIndex] = this.state.originalColor;
- this.setState({ widget: temp });
+ // Color picker canceled
+ tempLineColors[this.state.selectedIndex] = this.state.originalColor;
+ this.setState({lineColors: tempLineColors})
+ } else {
+ // color picker with result data {hexcolor, opacity}
+ tempLineColors[this.state.selectedIndex] = data.hexcolor
+ this.setState({lineColors: tempLineColors})
+ this.props.handleChange({target: { id: this.props.controlId, value: tempLineColors} })
}
}
editLineColor = (index) => {
if(this.state.selectedIndex !== index){
- let color = this.state.widget.customProperties.lineColors[index];
- this.setState({selectedIndex: index, showColorPicker: true, originalColor: color});
- }
- else{
- this.setState({selectedIndex: null});
- }
+ let color = typeof this.state.lineColors[index] === "undefined" ? schemeCategory10[index % 10] : this.state.lineColors[index];
+ this.setState({selectedIndex: index, showColorPicker: true, originalColor: color});
+ }
+ else{
+ this.setState({selectedIndex: null});
+ }
}
render() {
@@ -84,15 +108,15 @@ class EditWidgetPlotColorsControl extends Component {
Line Colors
{
- this.state.widget.signalIDs.map((signalID, idx) => {
- let color = this.state.widget.customProperties.lineColors[signalID];
- let width = 260 / this.state.widget.signalIDs.length;
+ this.props.widget.signalIDs.map((signalID, idx) => {
+
+ let color = typeof this.state.lineColors[idx] === "undefined" ? schemeCategory10[idx % 10] : this.state.lineColors[idx];
+ let width = 260 / this.props.widget.signalIDs.length;
let style = {
backgroundColor: color,
width: width,
height: '40px'
}
-
let signal = this.props.signals.find(signal => signal.id === signalID);
return this.editLineColor(signalID)}
+ onClick={i => this.editLineColor(idx)}
>
+
;
})
}
- this.closeEditModal(data)} widget={this.state.widget} lineIndex={this.state.selectedIndex} controlId={'lineColor'} disableOpacity={true}/>
+ this.closeColorPickerEditModal(data)}
+ hexcolor={this.state.lineColors[this.state.selectedIndex]}
+ opacity={1}
+ disableOpacity={true}
+ />
)
diff --git a/src/widget/widget-factory.js b/src/widget/widget-factory.js
index e3f4c2c..8a9baa8 100644
--- a/src/widget/widget-factory.js
+++ b/src/widget/widget-factory.js
@@ -90,6 +90,7 @@ class WidgetFactory {
widget.customProperties.yMax = 10;
widget.customProperties.yUseMinMax = false;
widget.customProperties.lineColors = [];
+ widget.customProperties.showUnit = false;
break;
case 'Table':
widget.minWidth = 200;
@@ -150,7 +151,7 @@ class WidgetFactory {
widget.customProperties.rangeMin = 0;
widget.customProperties.rangeMax = 200;
widget.customProperties.rangeUseMinMax = true;
- widget.customProperties.showUnit = true;
+ widget.customProperties.showUnit = false;
widget.customProperties.continous_update = false;
widget.customProperties.default_value = '0';
widget.customProperties.value = '';
diff --git a/src/widget/widget-plot/plot-legend.js b/src/widget/widget-plot/plot-legend.js
index 00d1b77..096c67b 100644
--- a/src/widget/widget-plot/plot-legend.js
+++ b/src/widget/widget-plot/plot-legend.js
@@ -24,26 +24,15 @@ function Legend(props){
const signal = props.sig;
const hasScalingFactor = (signal.scalingFactor !== 1);
- const newLineColor = scaleOrdinal(schemeCategory10);
+ let color = typeof props.lineColor === "undefined" ? schemeCategory10[props.index % 10] : props.lineColor;
- let color = typeof props.lineColor === "undefined" ? newLineColor(signal.id) : props.lineColor;
-
- if(hasScalingFactor){
- return (
-
- {signal.name}
- {signal.unit}
- {signal.scalingFactor}
-
- )
- } else {
- return (
-
- {signal.name}
- {signal.unit}
-
- )
- }
+ return (
+
+ {signal.name}
+ {props.showUnit ? {signal.unit} : <>> }
+ {hasScalingFactor ? {signal.scalingFactor} : <>>}
+
+ )
}
class PlotLegend extends React.Component {
@@ -52,11 +41,11 @@ class PlotLegend extends React.Component {
return
{ this.props.lineColors !== undefined && this.props.lineColors != null ? (
- this.props.signals.map( signal =>
-
+ this.props.signals.map( (signal, idx) =>
+
)) : (
- this.props.signals.map( signal =>
-
+ this.props.signals.map( (signal, idx) =>
+
))
}
diff --git a/src/widget/widget-plot/plot.js b/src/widget/widget-plot/plot.js
index 17e61fc..0c92bc1 100644
--- a/src/widget/widget-plot/plot.js
+++ b/src/widget/widget-plot/plot.js
@@ -199,7 +199,6 @@ class Plot extends React.Component {
// generate paths from data
const sparkLine = line().x(p => xScale(p.x)).y(p => yScale(p.y));
- const newLineColor = scaleOrdinal(schemeCategory10);
const lines = this.state.data.map((values, index) => {
let signalID = this.props.signalIDs[index];
@@ -208,10 +207,10 @@ class Plot extends React.Component {
this.props.lineColors = [] // for backwards compatibility
}
- if (typeof this.props.lineColors[signalID] === "undefined") {
- this.props.lineColors[signalID] = newLineColor(signalID);
+ if (typeof this.props.lineColors[index] === "undefined") {
+ this.props.lineColors[index] = schemeCategory10[index % 10];
}
- return
+ return
});
this.setState({ lines, xAxis, yAxis });
diff --git a/src/widget/widget.js b/src/widget/widget.js
index 1523545..8e093c9 100644
--- a/src/widget/widget.js
+++ b/src/widget/widget.js
@@ -94,18 +94,20 @@ class Widget extends React.Component {
};
}
- inputDataChanged(widget, data, controlID, controlValue) {
+ inputDataChanged(widget, data, controlID, controlValue, isFinalChange) {
// controlID is the path to the widget customProperty that is changed (for example 'value')
// modify the widget customProperty
if (controlID !== '') {
let updatedWidget = JSON.parse(JSON.stringify(widget));
updatedWidget.customProperties[controlID] = controlValue;
- AppDispatcher.dispatch({
- type: 'widgets/start-edit',
- token: this.state.sessionToken,
- data: updatedWidget
- });
+ if(isFinalChange) {
+ AppDispatcher.dispatch({
+ type: 'widgets/start-edit',
+ token: this.state.sessionToken,
+ data: updatedWidget
+ });
+ }
}
// The following assumes that a widget modifies/ uses exactly one signal
@@ -185,21 +187,21 @@ class Widget extends React.Component {
return
this.inputDataChanged(widget, value, controlID, controlValue)}
+ onInputChanged={(value, controlID, controlValue, isFinalChange) => this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
signals={this.state.signals}
/>
} else if (widget.type === 'NumberInput') {
return this.inputDataChanged(widget, value, controlID, controlValue)}
+ onInputChanged={(value, controlID, controlValue, isFinalChange) => this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
signals={this.state.signals}
/>
} else if (widget.type === 'Slider') {
return this.inputDataChanged(widget, value, controlID, controlValue)}
+ onInputChanged={(value, controlID, controlValue, isFinalChange) => this.inputDataChanged(widget, value, controlID, controlValue, isFinalChange)}
signals={this.state.signals}
/>
} else if (widget.type === 'Gauge') {
diff --git a/src/widget/widgets/button.js b/src/widget/widgets/button.js
index 897e1d9..f68144f 100644
--- a/src/widget/widgets/button.js
+++ b/src/widget/widgets/button.js
@@ -54,14 +54,14 @@ class WidgetButton extends Component {
valueChanged(newValue, pressed) {
if (this.props.onInputChanged) {
- this.props.onInputChanged(newValue, 'pressed', pressed);
+ this.props.onInputChanged(newValue, 'pressed', pressed, true);
}
}
render() {
const buttonStyle = {
- backgroundColor: this.props.widget.customProperties.background_color,
+ backgroundColor: this.props.widget.customProperties.background_color,
borderColor: this.props.widget.customProperties.border_color,
color: this.props.widget.customProperties.font_color,
opacity: this.props.widget.customProperties.background_color_opacity
diff --git a/src/widget/widgets/input.js b/src/widget/widgets/input.js
index 21c0421..8d99564 100644
--- a/src/widget/widgets/input.js
+++ b/src/widget/widgets/input.js
@@ -73,7 +73,7 @@ class WidgetInput extends Component {
valueChanged(newValue) {
if (this.props.onInputChanged) {
- this.props.onInputChanged(Number(newValue), 'value', Number(newValue));
+ this.props.onInputChanged(Number(newValue), 'value', Number(newValue), true);
}
}
diff --git a/src/widget/widgets/plot.js b/src/widget/widgets/plot.js
index 1479081..a9303ab 100644
--- a/src/widget/widgets/plot.js
+++ b/src/widget/widgets/plot.js
@@ -107,7 +107,10 @@ class WidgetPlot extends React.Component {
signalIDs={this.props.widget.signalIDs}
/>
-
+
;
}
}
diff --git a/src/widget/widgets/slider.js b/src/widget/widgets/slider.js
index 23360d4..82b6926 100644
--- a/src/widget/widgets/slider.js
+++ b/src/widget/widgets/slider.js
@@ -79,14 +79,14 @@ class WidgetSlider extends Component {
valueIsChanging(newValue) {
this.props.widget.customProperties.value = newValue;
if (this.props.widget.customProperties.continous_update)
- this.valueChanged(newValue);
+ this.valueChanged(newValue, false);
this.setState({ value: newValue });
}
- valueChanged(newValue) {
+ valueChanged(newValue, isFinalChange) {
if (this.props.onInputChanged) {
- this.props.onInputChanged(newValue, 'value', newValue);
+ this.props.onInputChanged(newValue, 'value', newValue, isFinalChange);
}
}
@@ -95,7 +95,7 @@ class WidgetSlider extends Component {
let isVertical = this.props.widget.customProperties.orientation === WidgetSlider.OrientationTypes.VERTICAL.value;
let fields = {
name: this.props.widget.name,
- control: this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>,
+ control: this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v, true) }/>,
value: { format('.2f')(Number.parseFloat(this.state.value)) } ,
unit: { this.state.unit }
}