1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

merge example-dashboard

This commit is contained in:
Sonja Happ 2020-01-27 14:54:40 +01:00
commit 8066cbb19c
76 changed files with 5932 additions and 3842 deletions

7374
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,51 +3,54 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.19",
"@fortawesome/free-solid-svg-icons": "^5.9.0",
"@fortawesome/react-fontawesome": "^0.1.4",
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8",
"babel-runtime": "^6.26.0",
"bootstrap": "^4.3.1",
"bootstrap": "^4.4.1",
"classnames": "^2.2.6",
"d3-array": "^2.2.0",
"d3-array": "^2.4.0",
"d3-axis": "^1.0.12",
"d3-scale": "^3.0.0",
"d3-scale-chromatic": "^1.3.3",
"d3-selection": "^1.4.0",
"d3-shape": "^1.3.5",
"d3-time-format": "^2.1.3",
"d3-scale": "^3.2.1",
"d3-scale-chromatic": "^1.5.0",
"d3-selection": "^1.4.1",
"d3-shape": "^1.3.7",
"d3-time-format": "^2.2.3",
"es6-promise": "^4.2.8",
"fibers": "^4.0.2",
"file-saver": "^2.0.2",
"flux": "^3.1.3",
"frontend-collective-react-dnd-scrollzone": "^1.0.2",
"gaugeJS": "^1.3.7",
"handlebars": "^4.5.1",
"handlebars": "^4.7.1",
"immutable": "^4.0.0-rc.12",
"jquery": "^3.4.1",
"jszip": "^3.2.2",
"libcimsvg": "git+https://git.rwth-aachen.de/acs/public/cim/pintura-npm-package.git",
"lodash": "^4.17.15",
"node-sass": "^4.13.0",
"prop-types": "^15.7.2",
"rc-slider": "^8.6.13",
"react": "^16.8.6",
"react-bootstrap": "^1.0.0-beta.9",
"rc-slider": "^8.7.1",
"react": "^16.12.0",
"react-bootstrap": "^1.0.0-beta.16",
"react-contexify": "^4.1.1",
"react-d3": "^0.4.0",
"react-dnd": "^9.3.2",
"react-dnd-html5-backend": "^9.3.2",
"react-dom": "^16.8.6",
"react-dnd": "^9.5.1",
"react-dnd-html5-backend": "^9.5.1",
"react-dom": "^16.12.0",
"react-fullscreenable": "^2.5.1-0",
"react-grid-system": "^4.4.10",
"react-grid-system": "^4.4.11",
"react-json-view": "^1.19.1",
"react-notification-system": "^0.2.17",
"react-rnd": "^10.0.0",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-scripts": "^3.0.1",
"react-sortable-tree": "^2.6.2",
"react-svg-pan-zoom": "^3.1.0",
"superagent": "^5.1.0",
"typescript": "^3.5.3",
"react-rnd": "^10.1.4",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.3.0",
"react-sortable-tree": "^2.7.1",
"react-svg-pan-zoom": "^3.8.0",
"sass": "^1.24.4",
"superagent": "^5.2.1",
"typescript": "^3.7.4",
"validator": "^11.1.0"
},
"devDependencies": {

View file

@ -31,7 +31,7 @@ import { Hidden } from 'react-grid-system'
import AppDispatcher from './common/app-dispatcher';
import ScenarioStore from './scenario/scenario-store';
import SimulatorStore from './simulator/simulator-store';
import UserStore from './user/user-store';
import LoginStore from './user/login-store';
import NotificationsDataManager from './common/data-managers/notifications-data-manager';
import Home from './common/home';
@ -40,73 +40,60 @@ import Footer from './common/footer';
import SidebarMenu from './common/menu-sidebar';
import HeaderMenu from './common/header-menu';
//import Projects from './project/projects';
//import Project from './project/project';
import Simulators from './simulator/simulators';
import Dashboard from './dashboard/dashboard';
//import Simulations from './simulation/simulations';
//import Simulation from './simulation/simulation';
import Scenarios from './scenario/scenarios';
import Scenario from './scenario/scenario';
import SimulationModel from './simulationmodel/simulation-model';
import Users from './user/users';
import User from './user/user';
import FluxContainerConverter from "./common/FluxContainerConverter";
import './styles/app.css';
class App extends React.Component {
static getStores() {
return [ SimulatorStore, UserStore, ScenarioStore];
return [ SimulatorStore, LoginStore, ScenarioStore];
}
static calculateState(prevState) {
let currentUser = UserStore.getState().currentUser;
return {
simulators: SimulatorStore.getState(),
scenarios: ScenarioStore.getState(),
currentRole: currentUser ? currentUser.role : '',
currentUsername: currentUser ? currentUser.username: '',
currentUserID: UserStore.getState().userid,
token: UserStore.getState().token,
currentUser: LoginStore.getState().currentUser,
token: LoginStore.getState().token,
showSidebarMenu: false,
};
}
componentWillMount() {
componentDidMount() {
NotificationsDataManager.setSystem(this.refs.notificationSystem);
// if token stored locally, request user
const token = localStorage.getItem('token');
const userid = localStorage.getItem('userid');
let token = localStorage.getItem("token");
let currentUser = JSON.parse(localStorage.getItem("currentUser"));
if (token != null && token !== '') {
// save token so we dont logout
this.setState({ token });
AppDispatcher.dispatch({
type: 'users/logged-in',
token: token,
userid: userid
currentUser: currentUser
});
}
}
componentDidMount() {
// load all simulators and scenarios to fetch data
AppDispatcher.dispatch({
type: 'simulators/start-load',
token: this.state.token
});
// AppDispatcher.dispatch({
// type: 'simulators/start-load',
// token: this.state.token
// });
//
// AppDispatcher.dispatch({
// type: 'scenarios/start-load',
// token: this.state.token
// });
AppDispatcher.dispatch({
type: 'scenarios/start-load',
token: this.state.token
});
NotificationsDataManager.setSystem(this.refs.notificationSystem);
}
showSidebarMenu = () => {
@ -130,7 +117,7 @@ class App extends React.Component {
*/}
<Hidden sm md lg xl>
<Col style={{ width: this.state.showSidebarMenu ? '280px' : '0px' }} className="sidenav">
<HeaderMenu onClose={this.hideSidebarMenu} currentRole={this.state.currentRole} />
<HeaderMenu onClose={this.hideSidebarMenu} currentRole={this.state.currentUser.role} />
</Col>
</Hidden>
@ -141,7 +128,7 @@ class App extends React.Component {
<div className={`app-body app-body-spacing`} >
<Col xs={false}>
<SidebarMenu currentRole={this.state.currentRole} />
<SidebarMenu currentRole={this.state.currentUser.role} />
</Col>
<div className={`app-content app-content-margin-left`}>
@ -171,5 +158,6 @@ class App extends React.Component {
//<Route exact path="/simulations" component={Simulations} />
//<Route path="/simulations/:simulation" component={Simulation} />
export default Container.create(FluxContainerConverter.convert(App));
let fluxContainerConverter = require('./common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(App));
//DragDropContext(HTML5Backend)(Container.create(App));

View file

@ -1,7 +1,7 @@
/// FluxContainerConverter.js
/// This is an ugly workaround found here https://github.com/facebook/flux/issues/351 to make Flux Containers work with ES6
export default {
module.exports = {
convert: function(containerClass) {
const tmp = containerClass;
containerClass = function(...args) {

View file

@ -37,7 +37,7 @@ const REQUEST_TIMEOUT_NOTIFICATION = {
level: 'error'
};
// Check if the error was due to network failure, timeouts, etc.
// Check if the error was due to network failure, timeouts, etc.
// Can be used for the rest of requests
function isNetworkError(err) {
let result = false;
@ -45,7 +45,7 @@ function isNetworkError(err) {
// If not status nor response fields, it is a network error. TODO: Handle timeouts
if (err.status == null || err.response == null) {
result = true;
let notification = err.timeout? REQUEST_TIMEOUT_NOTIFICATION : SERVER_NOT_REACHABLE_NOTIFICATION;
NotificationsDataManager.addNotification(notification);
}
@ -79,7 +79,7 @@ class RestAPI {
if (token != null) {
req.set('Authorization', "Bearer " + token);
}
req.end(function (error, res) {
if (res == null || res.status !== 200) {

View file

@ -68,7 +68,6 @@ class ArrayStore extends ReduceStore {
reduce(state, action) {
switch (action.type) {
case this.type + '/start-load':
if (Array.isArray(action.data)) {
action.data.forEach((id) => {
this.dataManager.load(id, action.token,action.param);
@ -87,17 +86,17 @@ class ArrayStore extends ReduceStore {
case this.type + '/load-error':
if (action.error && !action.error.handled && action.error.response) {
const USER_LOAD_ERROR_NOTIFICATION = {
title: 'Failed to load',
message: action.error.response.body.message,
level: 'error'
};
NotificationsDataManager.addNotification(USER_LOAD_ERROR_NOTIFICATION);
}
return super.reduce(state, action);
case this.type + '/start-add':
this.dataManager.add(action.data, action.token,action.param);
return state;
@ -106,7 +105,7 @@ class ArrayStore extends ReduceStore {
return this.updateElements(state, [action.data]);
case this.type + '/add-error':
return state;
@ -128,30 +127,17 @@ class ArrayStore extends ReduceStore {
level: 'error'
};
NotificationsDataManager.addNotification(USER_REMOVE_ERROR_NOTIFICATION);
}
return super.reduce(state, action);
case this.type + '/start-edit':
this.dataManager.update(action.data, action.token,action.param);
return state;
case this.type + '/start-own-edit':
case this.type + '/start-edit':
this.dataManager.update(action.data, action.token,action.param);
return state;
case this.type + '/edited':
return this.updateElements(state, [action.data]);
case this.type + '/confirm-pw-doesnt-match':
const USER_PW_ERROR_NOTIFICATION = {
title: 'The new password does not match',
message: 'Try again',
level: 'error'
};
NotificationsDataManager.addNotification(USER_PW_ERROR_NOTIFICATION);
return state;
case this.type + '/edit-error':
return state;

View file

@ -65,10 +65,10 @@ class RestDataManager {
}
else{
if(id != null){
return this.makeURL(this.url + '/' + id + '?' + param);
return this.makeURL(this.url + '/' + id + param);
}
else {
return this.makeURL(this.url + '?' + param);
return this.makeURL(this.url + param)
}
}
case 'remove/update':
@ -76,7 +76,7 @@ class RestDataManager {
return this.makeURL(this.url + '/' + object.id);
}
else{
return this.makeURL(this.url + '/' + object.id + '?' + param);
return this.makeURL(this.url + '/' + object.id + param);
}
default:
console.log("something went wrong");

View file

@ -36,8 +36,10 @@ class EditableHeader extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
this.setState({ title: nextProps.title });
static getDerivedStateFromProps(props, state){
return {
title: props.title
};
}
edit = () => {

View file

@ -26,18 +26,15 @@ import React from 'react';
//import RestAPI from '../api/rest-api';
import config from '../config';
import UserStore from "../user/user-store";
import LoginStore from "../user/login-store";
class Home extends React.Component {
constructor(props) {
super(props);
let currentUser = UserStore.getState().currentUser;
this.state = {
currentRole: currentUser ? currentUser.role : '',
currentUsername: currentUser ? currentUser.username: '',
currentUserID: currentUser ? currentUser.id: 0,
token: UserStore.getState().token
currentUser: LoginStore.getState().currentUser,
token: LoginStore.getState().token
};
}
@ -48,12 +45,6 @@ class Home extends React.Component {
return '?';
}
componentDidMount() {
//RestAPI.get('/api/v1/counts').then(response => {
// this.setState({ counts: response });
//});
}
render() {
return (
<div className="home-container">
@ -64,7 +55,7 @@ class Home extends React.Component {
VILLASweb is a frontend for distributed real-time simulation hosted by <a href={"mailto:" + config.admin.mail}>{config.admin.name}</a>.
</p>
<p>
You are logged in as user <b>{this.state.currentUsername}</b> with <b>ID {this.state.currentUserID}</b> and role <b>{this.state.currentRole}</b>.
You are logged in as user <b>{this.state.currentUser.username}</b> with <b>ID {this.state.currentUser.id}</b> and role <b>{this.state.currentUser.role}</b>.
</p>
{/*
<p>

View file

@ -32,7 +32,7 @@ class CustomTable extends Component {
this.activeInput = null;
this.state = {
rows: this.getRows(props),
rows: CustomTable.getRows(props),
editCell: [ -1, -1 ]
};
}
@ -45,7 +45,7 @@ class CustomTable extends Component {
this.setState({ editCell: [ column, row ]}); // x, y
}
addCell(data, index, child) {
static addCell(data, index, child) {
// add data to cell
let content = null;
@ -112,7 +112,7 @@ class CustomTable extends Component {
}
if (child.props.checkbox) {
const checkboxKey = this.props.checkboxKey;
const checkboxKey = child.props.checkboxKey;
cell.push(<FormCheck className="table-control-checkbox" inline checked={checkboxKey ? data[checkboxKey] : null} onChange={e => child.props.onChecked(index, e)} />);
}
@ -122,12 +122,12 @@ class CustomTable extends Component {
}
return cell;
}
} // addCell
componentWillReceiveProps(nextProps) {
const rows = this.getRows(nextProps);
static getDerivedStateFromProps(props, state){
const rows = CustomTable.getRows(props);
this.setState({ rows });
return { rows };
}
componentDidUpdate() {
@ -147,7 +147,7 @@ class CustomTable extends Component {
this.setState({ editCell: [ -1, -1 ] });
}
getRows(props) {
static getRows(props) {
if (props.data == null) {
return [];
}
@ -156,13 +156,13 @@ class CustomTable extends Component {
// check if multiple columns
if (Array.isArray(props.children) === false) {
// table only has a single column
return [ this.addCell(data, index, props.children) ];
return [ CustomTable.addCell(data, index, props.children) ];
}
const row = [];
for (let child of props.children) {
row.push(this.addCell(data, index, child));
row.push(CustomTable.addCell(data, index, child));
}
return row;

View file

@ -22,6 +22,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';
import Icon from "../common/icon";
class DashboardButtonGroup extends React.Component {
render() {
@ -38,39 +39,39 @@ class DashboardButtonGroup extends React.Component {
if (this.props.editing) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onSave} style={buttonStyle}>
<span class="glyphicon glyphicon-floppy-disk"></span> Save
<Button key={key++} onClick={this.props.onSave} style={buttonStyle}>
<Icon icon="save" /> Save
</Button>,
<Button key={key++} bsStyle="info" onClick={this.props.onCancel} style={buttonStyle}>
<span class="glyphicon glyphicon-remove" ></span> Cancel
<Button key={key++} onClick={this.props.onCancel} style={buttonStyle}>
<Icon icon="times" /> Cancel
</Button>
);
} else {
if (this.props.fullscreen !== true) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onFullscreen} style={buttonStyle}>
<span className="glyphicon glyphicon-resize-full"></span> Fullscreen
<Button key={key++} onClick={this.props.onFullscreen} style={buttonStyle}>
<Icon icon="expand" /> Fullscreen
</Button>
);
}
if (this.props.paused) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onUnpause} style={buttonStyle}>
<span className="glyphicon glyphicon-play"></span> Live
<Button key={key++} onClick={this.props.onUnpause} style={buttonStyle}>
<Icon icon="play" /> Live
</Button>
);
} else {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onPause} style={buttonStyle}>
<span className="glyphicon glyphicon-pause"></span> Pause
<Button key={key++} onClick={this.props.onPause} style={buttonStyle}>
<Icon icon="pause" /> Pause
</Button>
);
}
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onEdit} style={buttonStyle}>
<span className="glyphicon glyphicon-pencil"></span> Pause
<Button key={key++} onClick={this.props.onEdit} style={buttonStyle}>
<Icon icon="pen" /> Edit
</Button>
);
}

View file

@ -19,7 +19,7 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import React, {Component} from 'react';
import { Container } from 'flux/utils';
import Fullscreenable from 'react-fullscreenable';
import classNames from 'classnames';
@ -34,82 +34,139 @@ import WidgetToolbox from './widget-toolbox';
import WidgetArea from './widget-area';
import DashboardButtonGroup from './dashboard-button-group';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import DashboardStore from './dashboard-store';
import ProjectStore from '../project/project-store';
import SimulationStore from '../simulation/simulation-store';
import SimulationModelStore from '../simulationmodel/simulation-model-store';
import FileStore from '../file/file-store';
import WidgetStore from '../widget/widget-store';
import AppDispatcher from '../common/app-dispatcher';
import FluxContainerConverter from "../common/FluxContainerConverter";
import 'react-contexify/dist/ReactContexify.min.css';
class Dashboard extends React.Component {
class Dashboard extends Component {
static lastWidgetKey = 0;
static getStores() {
return [ DashboardStore, ProjectStore, SimulationStore, SimulationModelStore, FileStore, UserStore ];
return [ DashboardStore, ProjectStore, SimulationStore, SimulationModelStore, FileStore, LoginStore, WidgetStore ];
}
static calculateState(prevState, props) {
if (prevState == null) {
prevState = {};
}
const sessionToken = LoginStore.getState().token;
let maxHeight = null;
let dashboard = Map();
let rawDashboard = DashboardStore.getState().find(v => v._id === props.match.params.dashboard);
let dashboards = DashboardStore.getState()
let rawDashboard = dashboards[props.match.params.dashboard - 1];
if (rawDashboard != null) {
if (rawDashboard) {
dashboard = Map(rawDashboard);
// convert widgets list to a dictionary to be able to reference widgets
const widgets = {};
//let widgets = {};
for (let widget of dashboard.get('widgets')) {
widgets[this.getNewWidgetKey()] = widget;
let rawWidgets = WidgetStore.getState();
if(rawWidgets.length === 0){
AppDispatcher.dispatch({
type: 'widgets/start-load',
token: sessionToken,
param: '?dashboardID=1'
});
}
dashboard = dashboard.set('widgets', widgets);
dashboard = dashboard.set('widgets', rawWidgets);
// this.computeHeightWithWidgets(widgets);
/* for(let widget of dashboard.get('widgets')){
console.log("load files got called")
console.log(widget);
AppDispatcher.dispatch({
type: 'files/start-load',
token: sessionToken,
param: '?objectID=' + widget.id + '&objectType=widget'
});
}
*/
// this.setState({ dashboard: selectedDashboards, project: null });
// AppDispatcher.dispatch({
// type: 'projects/start-load',
// data: selectedDashboard.get('project'),
// token: this.state.sessionToken
// });
/* if (this.state.dashboard.has('id') === false) {
AppDispatcher.dispatch({
type: 'dashboards/start-load',
data: this.props.match.params.dashboard,
token: this.state.sessionToken
});
}
*/
/*if(Object.keys(widgets).length !== 0 ){
this.computeHeightWithWidgets(widgets);
}
let selectedDashboards = dashboard;
/* this.setState({ dashboard: selectedDashboards, project: null });
AppDispatcher.dispatch({
type: 'projects/start-load',
data: selectedDashboards.get('project'),
token: this.state.sessionToken
});
*/
let widgets = {};
for (let widget of dashboard.get('widgets')) {
widgets[Dashboard.lastWidgetKey] = widget;
Dashboard.lastWidgetKey++;
}
maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => {
let thisWidget = widgets[widgetKey];
let thisWidgetHeight = thisWidget.y + thisWidget.height;
return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar;
}, 0);
}
let simulationModels = [];
if (prevState.simulation != null) {
simulationModels = SimulationModelStore.getState().filter(m => prevState.simulation.models.includes(m._id));
}
//if (prevState.simulation != null) {
// simulationModels = SimulationModelStore.getState().filter(m => prevState.simulation.models.includes(m._id));
//}
return {
rawDashboard,
dashboard,
sessionToken: UserStore.getState().token,
projects: ProjectStore.getState(),
simulations: SimulationStore.getState(),
files: FileStore.getState(),
project: prevState.project || null,
simulation: prevState.simulation || null,
sessionToken: sessionToken,
projects: null, //ProjectStore.getState(),
simulations: null, //SimulationStore.getState(),
files: null,
project: null,
simulation: null,
simulationModels,
editing: prevState.editing || false,
paused: prevState.paused || false,
editModal: prevState.editModal || false,
modalData: prevState.modalData || null,
modalIndex: prevState.modalIndex || null,
editModal: false,
modalData: null,
modalIndex: null,
widgetChangeData: [],
widgetAddData:[],
maxWidgetHeight: prevState.maxWidgetHeight || 0,
dropZoneHeight: prevState.dropZoneHeight || 0,
maxWidgetHeight: maxHeight || null,
dropZoneHeight: maxHeight +80 || null,
};
}
@ -121,20 +178,21 @@ class Dashboard extends React.Component {
return widgetKey;
}
//!!!won't work anymore
componentDidMount() {
//document.addEventListener('keydown', this.handleKeydown.bind(this));
if (this.state.dashboard.has('id') === false) {
AppDispatcher.dispatch({
type: 'dashboards/start-load',
data: this.props.match.params.dashboard,
token: this.state.sessionToken
token: this.state.sessionToken,
param: '?scenarioID=1',
});
}
}
/*
componentWillUnmount() {
//document.removeEventListener('keydown', this.handleKeydown.bind(this));
}
@ -169,9 +227,9 @@ class Dashboard extends React.Component {
}
});
}
}
} */
/*handleKeydown(e) {
handleKeydown(e) {
switch (e.key) {
case ' ':
case 'p':
@ -185,7 +243,8 @@ class Dashboard extends React.Component {
break;
default:
}
}*/
}
/*
* Adapt the area's height with the position of the new widget.
@ -236,17 +295,31 @@ class Dashboard extends React.Component {
});
}
handleDrop = widget => {
const widgets = this.state.dashboard.get('widgets') || [];
handleDrop(widget) {
widget.dashboardID = this.state.dashboard.get('id');
console.log(widget);
const widgetKey = this.getNewWidgetKey();
AppDispatcher.dispatch({
type: 'widgets/start-add',
token: this.state.sessionToken,
data: widget
});
let tempChanges = this.state.widgetAddData;
tempChanges.push(widget);
this.setState({ widgetAddData: tempChanges})
/*let widgets = [];
widgets = this.state.dashboard.get('widgets');
const widgetKey = Dashboard.getNewWidgetKey();
widgets[widgetKey] = widget;
const dashboard = this.state.dashboard.set('widgets');
const dashboard = this.state.dashboard.set('widgets',widgets);
// this.increaseHeightWithWidget(widget);
this.setState({ dashboard });
this.setState({ dashboard });*/
};
@ -255,25 +328,20 @@ class Dashboard extends React.Component {
this.widgetChange(updated_widget, key, this.saveChanges);
}
widgetChange = (widget, index, callback = null) => {
const widgets = this.state.dashboard.get('widgets');
widgets[index] = widget;
widgetChange(widget, index, callback = null){
const dashboard = this.state.dashboard.set('widgets');
let tempChanges = this.state.widgetChangeData;
tempChanges.push(widget);
// Check if the height needs to be increased, the section may have shrunk if not
if (!this.increaseHeightWithWidget(widget)) {
this.computeHeightWithWidgets(dashboard.widgets);
}
this.setState({ widgetChangeData: tempChanges})
this.setState({ dashboard }, callback);
}
/*
* Set the initial height state based on the existing widgets
*/
computeHeightWithWidgets(widgets) {
computeHeightWithWidgets(widgets) {
// Compute max height from widgets
let maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => {
let thisWidget = widgets[widgetKey];
@ -289,45 +357,61 @@ class Dashboard extends React.Component {
}
editWidget = (widget, index) => {
editWidget(widget, index){
this.setState({ editModal: true, modalData: widget, modalIndex: index });
}
};
closeEdit = data => {
closeEdit(data){
if (data == null) {
this.setState({ editModal: false });
return;
}
const widgets = this.state.dashboard.get('widgets');
widgets[this.state.modalIndex] = data;
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.state.sessionToken,
data: data
});
const dashboard = this.state.dashboard.set('widgets', widgets);
this.setState({ editModal: false, dashboard });
this.setState({ editModal: false });
};
deleteWidget = (widget, index) => {
const widgets = this.state.dashboard.get('widgets');
deleteWidget(widget, index) {
/*const widgets = this.state.dashboard.get('widgets');
delete widgets[index];
const dashboard = this.state.dashboard.set('widgets');
this.setState({ dashboard });
this.setState({ dashboard });*/
AppDispatcher.dispatch({
type: 'widgets/start-remove',
data: widget,
token: this.state.sessionToken
});
};
startEditing = () => {
startEditing(){
this.setState({ editing: true });
};
saveEditing = () => {
saveEditing() {
// Provide the callback so it can be called when state change is applied
// TODO: Check if callback is needed
this.setState({ editing: false }, this.saveChanges );
this.state.widgetChangeData.forEach( widget => {
AppDispatcher.dispatch({
type: 'widgets/start-edit',
token: this.state.sessionToken,
data: widget
});
});
this.setState({ editing: false });
};
saveChanges() {
@ -343,23 +427,36 @@ class Dashboard extends React.Component {
});
}
cancelEditing = () => {
this.setState({ editing: false, dasboard: {} });
cancelEditing() {
console.log("cancelEditing the add data: ");
console.log(this.state.widgetAddData);
this.state.widgetAddData.forEach( widget => {
AppDispatcher.dispatch({
type: 'widgets/start-remove',
data: widget,
token: this.state.sessionToken
});
});
AppDispatcher.dispatch({
type: 'widgets/start-load',
token: this.state.sessionToken,
param: '?dashboardID=1'
});
this.setState({ editing: false, widgetChangeData: [], widgetAddData: [] });
this.reloadDashboard();
};
setGrid = value => {
setGrid(value) {
const dashboard = this.state.dashboard.set('grid', value);
this.setState({ dashboard });
};
pauseData = () => {
pauseData(){
this.setState({ paused: true });
};
unpauseData = () => {
unpauseData() {
this.setState({ paused: false });
};
@ -367,9 +464,8 @@ class Dashboard extends React.Component {
render() {
const widgets = this.state.dashboard.get('widgets');
const grid = this.state.dashboard.get('grid');
const boxClasses = classNames('section', 'box', { 'fullscreen-padding': this.props.isFullscreen });
let draggable = this.state.editing;
return <div className={boxClasses} >
<div className='section-header box-header'>
<div className="section-title">
@ -378,47 +474,86 @@ class Dashboard extends React.Component {
<DashboardButtonGroup
editing={this.state.editing}
onEdit={this.startEditing.bind(this)}
fullscreen={this.props.isFullscreen}
paused={this.state.paused}
onEdit={this.startEditing}
onSave={this.saveEditing}
onCancel={this.cancelEditing}
onSave={this.saveEditing.bind(this)}
onCancel={this.cancelEditing.bind(this)}
onFullscreen={this.props.toggleFullscreen}
onPause={this.pauseData}
onUnpause={this.unpauseData}
onPause={this.pauseData.bind(this)}
onUnpause={this.unpauseData.bind(this)}
/>
</div>
<div className="box box-content" onContextMenu={ (e) => e.preventDefault() }>
{this.state.editing &&
<WidgetToolbox grid={grid} onGridChange={this.setGrid} widgets={widgets} />
<WidgetToolbox grid={grid} onGridChange={this.setGrid.bind(this)} widgets={widgets} />
}
{!draggable?(
<WidgetArea widgets={widgets} editing={this.state.editing} grid={grid} onWidgetAdded={this.handleDrop.bind(this)}>
{widgets != null && Object.keys(widgets).map(widgetKey => (
<WidgetContextMenu
key={widgetKey}
index={parseInt(widgetKey,10)}
widget={widgets[widgetKey]}
onEdit={this.editWidget.bind(this)}
onDelete={this.deleteWidget.bind(this)}
onChange={this.widgetChange.bind(this)}
<WidgetArea widgets={widgets} editing={this.state.editing} grid={grid} onWidgetAdded={this.handleDrop}>
simulation={this.state.simulation}
onWidgetChange={this.widgetChange.bind(this)}
onWidgetStatusChange={this.widgetStatusChange.bind(this)}
editing={this.state.editing}
grid={grid}
paused={this.state.paused}
/>
))}
</WidgetArea>
) : (
<WidgetArea widgets={widgets} editing={this.state.editing} grid={grid} onWidgetAdded={this.handleDrop.bind(this)}>
{widgets != null && Object.keys(widgets).map(widgetKey => (
<Widget
key={widgetKey}
data={widgets[widgetKey]}
simulation={this.state.simulation}
onWidgetChange={(w, k) => this.widgetChange(w, k)}
onWidgetStatusChange={(w, k) => this.widgetStatusChange(w, k)}
onWidgetChange={this.widgetChange.bind(this)}
onWidgetStatusChange={this.widgetStatusChange.bind(this)}
editing={this.state.editing}
index={widgetKey}
index={parseInt(widgetKey,10)}
grid={grid}
paused={this.state.paused}
/>
))}
</WidgetArea>
{/* TODO: Create only one context menu for all widgets */}
{widgets != null && Object.keys(widgets).map(widgetKey => (
<WidgetContextMenu key={widgetKey} index={widgetKey} widget={widgets[widgetKey]} onEdit={this.editWidget} onDelete={this.deleteWidget} onChange={this.widgetChange} />
))}
)}
<EditWidget sessionToken={this.state.sessionToken} show={this.state.editModal} onClose={this.closeEdit.bind(this)} widget={this.state.modalData} simulationModels={this.state.simulationModels} files={this.state.files} />
<EditWidget sessionToken={this.state.sessionToken} show={this.state.editModal} onClose={this.closeEdit} widget={this.state.modalData} simulationModels={this.state.simulationModels} files={this.state.files} />
</div>
</div>;
}
}
/*
onWidgetChange={(w, k) => this.widgetChange(w, k)}
onWidgetStatusChange={(w, k) => this.widgetStatusChange(w, k)}
export default Fullscreenable()(Container.create(FluxContainerConverter.convert(Dashboard), { withProps: true }));
const widgets = this.state.dashboard.get('widgets');
widgets[index] = widget;
const dashboard = this.state.dashboard.set('widgets');
// Check if the height needs to be increased, the section may have shrunk if not
if (!this.increaseHeightWithWidget(widget)) {
this.computeHeightWithWidgets(dashboard.widgets);
}
this.setState({ dashboard }, callback);
*/
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Fullscreenable()(Container.create(fluxContainerConverter.convert(Dashboard), { withProps: true }));

View file

@ -52,6 +52,7 @@ const dropzoneTarget = {
};
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
@ -61,6 +62,7 @@ function collect(connect, monitor) {
class Dropzone extends React.Component {
render() {
var toolboxClass = classNames({
'box-content': true,
'toolbox-dropzone': true,

View file

@ -24,7 +24,7 @@ import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
class NewVisualzationDialog extends React.Component {
class NewDashboardDialog extends React.Component {
valid: false;
constructor(props) {
@ -84,4 +84,4 @@ class NewVisualzationDialog extends React.Component {
}
}
export default NewVisualzationDialog;
export default NewDashboardDialog;

View file

@ -48,7 +48,7 @@ class WidgetArea extends React.Component {
}
render() {
const maxHeight = Object.values(this.props.widgets).reduce((currentHeight, widget) => {
const maxHeight = Object.values(this.props.widgets).reduce((currentHeight, widget) => {
const absolutHeight = widget.y + widget.height;
return absolutHeight > currentHeight ? absolutHeight : currentHeight;
@ -67,7 +67,7 @@ WidgetArea.propTypes = {
editing: PropTypes.bool,
grid: PropTypes.number,
defaultSimulationModel: PropTypes.string,
widgets: PropTypes.object,
//widgets: PropTypes.array,
onWidgetAdded: PropTypes.func
};

View file

@ -21,7 +21,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { contextMenu, Item, Separator } from 'react-contexify';
import { Menu, Item, Separator, MenuProvider } from 'react-contexify';
import Widget from '../widget/widget';
class WidgetContextMenu extends React.Component {
editWidget = event => {
@ -92,8 +93,8 @@ class WidgetContextMenu extends React.Component {
render() {
const isLocked = this.props.widget.locked;
return <contextMenu id={'widgetMenu'+ this.props.index}>
const ContextMenu = () => (
<Menu id={'widgetMenu'+ this.props.index}>
<Item disabled={isLocked} onClick={this.editWidget}>Edit</Item>
<Item disabled={isLocked} onClick={this.deleteWidget}>Delete</Item>
@ -108,14 +109,31 @@ class WidgetContextMenu extends React.Component {
<Item disabled={isLocked} onClick={this.lockWidget}>Lock</Item>
<Item disabled={isLocked === false} onClick={this.unlockWidget}>Unlock</Item>
</contextMenu>;
</Menu>
);
return <div>
<MenuProvider id={'widgetMenu'+ this.props.index} style={{ border: '1px solid purple', display: 'inline-block' }}>
<Widget
data={this.props.widget}
simulation={this.props.simulation}
onWidgetChange={this.props.onWidgetChange}
onWidgetStatusChange={this.props.onWidgetStatusChange}
editing={this.props.editing}
index={this.props.index}
grid={this.props.grid}
paused={this.props.paused}
/>
</MenuProvider>
<ContextMenu />
</div>
}
}
WidgetContextMenu.propTypes = {
index: PropTypes.number.isRequired,
widget: PropTypes.object.isRequired,
onEdit: PropTypes.func,
onEdit: PropTypes.func.isRequired,
onDelete: PropTypes.func,
onChange: PropTypes.func
};

View file

@ -24,130 +24,130 @@ import { Container } from 'flux/utils';
import { FormGroup, FormControl, FormLabel, Button, ProgressBar, Col } from 'react-bootstrap';
import FileStore from './file-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher';
import FluxContainerConverter from "../common/FluxContainerConverter";
class SelectFile extends React.Component {
static getStores() {
return [ FileStore, UserStore ];
static getStores() {
return [ FileStore, LoginStore ];
}
static calculateState() {
return {
files: FileStore.getState(),
sessionToken: LoginStore.getState().token,
selectedFile: '',
uploadFile: null,
uploadProgress: 0
};
}
componentDidMount() {
AppDispatcher.dispatch({
type: 'files/start-load',
token: this.state.sessionToken
});
}
static getDerivedStateFromProps(props, state){
if (props.value === state.selectedSimulator) {
return null;
}
static calculateState() {
return {
files: FileStore.getState(),
sessionToken: UserStore.getState().token,
selectedFile: '',
uploadFile: null,
uploadProgress: 0
};
let selectedSimulator = props.value;
if (selectedSimulator == null) {
if (state.simulators.length > 0) {
selectedSimulator = state.simulators[0]._id;
} else {
selectedSimulator = '';
}
}
componentDidMount() {
AppDispatcher.dispatch({
type: 'files/start-load',
token: this.state.sessionToken
});
}
return {selectedSimulator};
}
componentWillReceiveProps(nextProps) {
if (nextProps.value === this.state.selectedSimulator) {
return;
}
handleChange = event => {
this.setState({ selectedFile: event.target.value });
let selectedSimulator = nextProps.value;
if (selectedSimulator == null) {
if (this.state.simulators.length > 0) {
selectedSimulator = this.state.simulators[0]._id;
} else {
selectedSimulator = '';
}
}
// send file to callback
if (this.props.onChange != null) {
const file = this.state.files.find(f => f._id === event.target.value);
this.setState({ selectedSimulator });
}
this.props.onChange(file);
}
};
handleChange = event => {
this.setState({ selectedFile: event.target.value });
selectUploadFile = event => {
this.setState({ uploadFile: event.target.files[0] });
};
// send file to callback
if (this.props.onChange != null) {
const file = this.state.files.find(f => f._id === event.target.value);
startFileUpload = () => {
// upload file
const formData = new FormData();
formData.append(0, this.state.uploadFile);
this.props.onChange(file);
}
}
AppDispatcher.dispatch({
type: 'files/start-upload',
data: formData,
token: this.state.sessionToken,
progressCallback: this.updateUploadProgress,
finishedCallback: this.clearProgress
});
};
selectUploadFile = event => {
this.setState({ uploadFile: event.target.files[0] });
}
updateUploadProgress = event => {
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
};
startFileUpload = () => {
// upload file
const formData = new FormData();
formData.append(0, this.state.uploadFile);
clearProgress = () => {
// select uploaded file
const selectedFile = this.state.files[this.state.files.length - 1]._id;
this.setState({ selectedFile, uploadProgress: 0 });
};
AppDispatcher.dispatch({
type: 'files/start-upload',
data: formData,
token: this.state.sessionToken,
progressCallback: this.updateUploadProgress,
finishedCallback: this.clearProgress
});
}
render() {
const fileOptions = this.state.files.map(f =>
<option key={f._id} value={f._id}>{f.name}</option>
);
updateUploadProgress = event => {
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
}
const progressBarStyle = {
marginLeft: '100px',
marginTop: '-25px'
};
clearProgress = () => {
// select uploaded file
const selectedFile = this.state.files[this.state.files.length - 1]._id;
this.setState({ selectedFile, uploadProgress: 0 });
}
return <div>
<FormGroup>
<Col componentClass={FormLabel} sm={3} md={2}>
{this.props.name}
</Col>
render() {
const fileOptions = this.state.files.map(f =>
<option key={f._id} value={f._id}>{f.name}</option>
);
<Col sm={9} md={10}>
<FormControl disabled={this.props.disabled} componentClass='select' placeholder='Select file' onChange={this.handleChange}>
{fileOptions}
</FormControl>
</Col>
</FormGroup>
const progressBarStyle = {
marginLeft: '100px',
marginTop: '-25px'
};
<FormGroup>
<Col sm={9} md={10} smOffset={3} mdOffset={2}>
<FormControl disabled={this.props.disabled} type='file' onChange={this.selectUploadFile} />
</Col>
</FormGroup>
return <div>
<FormGroup>
<Col componentClass={FormLabel} sm={3} md={2}>
{this.props.name}
</Col>
<FormGroup>
<Col sm={9} md={10} smOffset={3} mdOffset={2}>
<Button disabled={this.props.disabled} bsSize='small' onClick={this.startFileUpload}>
Upload file
</Button>
<Col sm={9} md={10}>
<FormControl disabled={this.props.disabled} componentClass='select' placeholder='Select file' onChange={this.handleChange}>
{fileOptions}
</FormControl>
</Col>
</FormGroup>
<FormGroup>
<Col sm={9} md={10} smOffset={3} mdOffset={2}>
<FormControl disabled={this.props.disabled} type='file' onChange={this.selectUploadFile} />
</Col>
</FormGroup>
<FormGroup>
<Col sm={9} md={10} smOffset={3} mdOffset={2}>
<Button disabled={this.props.disabled} bsSize='small' onClick={this.startFileUpload}>
Upload file
</Button>
<ProgressBar striped active now={this.state.uploadProgress} label={this.state.uploadProgress + '%'} style={progressBarStyle} />
</Col>
</FormGroup>
</div>;
}
<ProgressBar striped active now={this.state.uploadProgress} label={this.state.uploadProgress + '%'} style={progressBarStyle} />
</Col>
</FormGroup>
</div>;
}
}
export default Container.create(FluxContainerConverter.convert(SelectFile));

View file

@ -26,7 +26,7 @@ import FileSaver from 'file-saver';
import AppDispatcher from '../common/app-dispatcher';
import ProjectStore from './project-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import DashboardStore from '../dashboard/dashboard-store';
import SimulationStore from '../simulation/simulation-store';
@ -41,14 +41,14 @@ import DeleteDialog from '../common/dialogs/delete-dialog';
class Dashboards extends Component {
static getStores() {
return [ ProjectStore, DashboardStore, UserStore, SimulationStore ];
return [ ProjectStore, DashboardStore, LoginStore, SimulationStore ];
}
static calculateState(prevState, props) {
prevState = prevState || {};
// load project
const sessionToken = UserStore.getState().token;
const sessionToken = LoginStore.getState().token;
let project = ProjectStore.getState().find(project => project._id === props.match.params.project);
if (project == null) {

View file

@ -25,7 +25,7 @@ import { Button } from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
import ProjectStore from './project-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import SimulationStore from '../simulation/simulation-store';
import Icon from '../common/icon';
@ -38,14 +38,14 @@ import DeleteDialog from '../common/dialogs/delete-dialog';
class Projects extends React.Component {
static getStores() {
return [ ProjectStore, SimulationStore, UserStore ];
return [ ProjectStore, SimulationStore, LoginStore ];
}
static calculateState() {
return {
projects: ProjectStore.getState(),
simulations: SimulationStore.getState(),
sessionToken: UserStore.getState().token,
sessionToken: LoginStore.getState().token,
newModal: false,
editModal: false,

View file

@ -22,42 +22,5 @@
import ScenariosDataManager from './scenarios-data-manager';
import ArrayStore from '../common/array-store';
//import UsersDataManager from "../user/users-data-manager";
//import SimulatorDataDataManager from "../simulator/simulator-data-data-manager";
//import AppDispatcher from "../common/app-dispatcher";
export default new ArrayStore('scenarios', ScenariosDataManager);
// class ScenariosStore extends ReduceStore {
// constructor() {
// super('scenarios', ScenariosDataManager);
// }
//
// getInitialState() {
// return {
// scenarios: [],
//
// };
// }
//
// reduce(state, action) {
// switch (action.type) {
// case 'scenarios/load-models':
// // request simulation model data of scenario
// ScenariosDataManager.getSimulationModels(action.token, action.scenarioID);
//
// return Object.assign({}, state, { token: action.token, simulationmodels: action.simulationmodels});
//
// case 'scenarios/load-dashboards':
// // request dashboard data of scenario
// ScenariosDataManager.getDashboards(action.token, action.scenarioID);
//
// return Object.assign({}, state, { token: action.token, dashboards: action.dashboards});
// default:
// return state;
// }
// }
// }
//
// export default new ScenariosStore();

View file

@ -27,84 +27,92 @@ import _ from 'lodash';
import ScenarioStore from './scenario-store';
import SimulatorStore from '../simulator/simulator-store';
import DashboardStore from '../dashboard/dashboard-store';
import SimulationModelStore from '../simulationmodel/simulation-model-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher';
import Icon from '../common/icon';
import Table from '../common/table';
import TableColumn from '../common/table-column';
import ImportSimulationModelDialog from '../simulationmodel/import-simulation-model';
import ImportDashboardDialog from "../dashboard/import-dashboard";
import NewDashboardDialog from "../dashboard/new-dashboard";
import SimulatorAction from '../simulator/simulator-action';
import DeleteDialog from '../common/dialogs/delete-dialog';
import FluxContainerConverter from "../common/FluxContainerConverter";
class Scenario extends React.Component {
static getStores() {
return [ ScenarioStore, SimulatorStore, SimulationModelStore, UserStore ];
return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore];
}
static calculateState(prevState, props) {
// get selected scenario
const sessionToken = UserStore.getState().token;
const sessionToken = LoginStore.getState().token;
let scenario = ScenarioStore.getState().find(s => s.id === props.match.params.scenario);
const scenario = ScenarioStore.getState().find(s => s.id === parseInt(props.match.params.scenario, 10));
if (scenario == null) {
AppDispatcher.dispatch({
type: 'scenarios/start-load',
data: props.match.params.scenario,
token: sessionToken
});
scenario = {};
}
// load models
let simulationModels = [];
if (scenario.simulationModels != null) {
simulationModels = SimulationModelStore.getState().filter(m => m != null && scenario.simulationModels.includes(m.id));
}
// obtain all dashboards of a scenario
let dashboards = DashboardStore.getState().filter(dashb => dashb.scenarioID === parseInt(props.match.params.scenario, 10));
// obtain all simulation models of a scenario
let simulationmodels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10));
return {
simulationModels,
scenario,
//simulators: SimulatorStore.getState(),
sessionToken,
simulationModels: simulationmodels,
dashboards: dashboards,
simulators: SimulatorStore.getState(),
deleteModal: false,
importModal: false,
modalData: {},
deleteSimulationModelModal: false,
importSimulationModelModal: false,
modalSimulationModelData: {},
selectedSimulationModels: [],
selectedSimulationModels: []
newDashboardModal: false,
deleteDashboardModal: false,
importDashboardModal: false,
modalDashboardData: {},
}
}
componentDidMount() {
//load selected scenario
AppDispatcher.dispatch({
type: 'scenarios/start-load',
data: this.state.scenario.id,
token: this.state.sessionToken
});
// load simulation models for selected scenario
AppDispatcher.dispatch({
type: 'simulationModels/start-load',
token: this.state.sessionToken
token: this.state.sessionToken,
param: '?scenarioID='+this.state.scenario.id,
});
// load dashboards of selected scenario
AppDispatcher.dispatch({
type: 'dashboards/start-load',
token: this.state.sessionToken,
param: '?scenarioID='+this.state.scenario.id,
});
// load simulators to enable that simulation models work with them
AppDispatcher.dispatch({
type: 'simulators/start-load',
token: this.state.sessionToken
token: this.state.sessionToken,
});
//TODO users
//TODO dashboards
}
addSimulationModel = () => {
@ -133,8 +141,8 @@ class Scenario extends React.Component {
});
}
closeDeleteModal = confirmDelete => {
this.setState({ deleteModal: false });
closeDeleteSimulationModelModal = confirmDelete => {
this.setState({ deleteSimulationModelModal: false });
if (confirmDelete === false) {
return;
@ -142,13 +150,13 @@ class Scenario extends React.Component {
AppDispatcher.dispatch({
type: 'simulationModels/start-remove',
data: this.state.modalData,
data: this.state.modalSimulationModelData,
token: this.state.sessionToken
});
}
importSimulationModel = simulationModel => {
this.setState({ importModal: false });
this.setState({ importSimulationModelModal: false });
if (simulationModel == null) {
return;
@ -156,8 +164,6 @@ class Scenario extends React.Component {
simulationModel.scenario = this.state.scenario.id;
console.log(simulationModel);
AppDispatcher.dispatch({
type: 'simulationModels/start-add',
data: simulationModel,
@ -173,6 +179,44 @@ class Scenario extends React.Component {
});
}
closeNewDashboardModal(data) {
this.setState({ newDashboardModal : false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
closeDeleteDashboardModal(confirmDelete){
this.setState({ deleteDashboardModal: false });
if (confirmDelete === false) {
return;
}
AppDispatcher.dispatch({
type: 'dashboards/start-remove',
data: this.state.modalDashboardData,
token: this.state.sessionToken,
});
}
closeImportDashboardModal(data) {
this.setState({ importDashboardModal: false });
if (data) {
AppDispatcher.dispatch({
type: 'dashboards/start-add',
data,
token: this.state.sessionToken,
});
}
}
getSimulatorName(simulatorId) {
for (let simulator of this.state.simulators) {
if (simulator.id === simulatorId) {
@ -185,14 +229,26 @@ class Scenario extends React.Component {
// filter properties
const model = Object.assign({}, this.state.simulationModels[index]);
delete model.simulator;
delete model.scenario;
//delete model.simulator;
//delete model.scenario;
// TODO get elements recursively
// show save dialog
const blob = new Blob([JSON.stringify(model, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'simulation model - ' + model.name + '.json');
}
exportDashboard(index) {
// filter properties
const dashboard = Object.assign({}, this.state.dashboards[index]);
// TODO get elements recursively
// show save dialog
const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
}
onSimulationModelChecked(index, event) {
const selectedSimulationModels = Object.assign([], this.state.selectedSimulationModels);
for (let key in selectedSimulationModels) {
@ -251,20 +307,22 @@ class Scenario extends React.Component {
};
return <div className='section'>
<h1>{this.state.simulation.name}</h1>
<h1>{this.state.scenario.name}</h1>
{/*Simulation Model table*/}
<h2>Simulation Models</h2>
<Table data={this.state.simulationModels}>
<TableColumn checkbox onChecked={(index, event) => this.onSimulationModelChecked(index, event)} width='30' />
<TableColumn title='Name' dataKey='name' link='/simulationModel/' linkKey='_id' />
<TableColumn title='Simulator' dataKey='simulator' modifier={(simulator) => this.getSimulatorName(simulator)} />
<TableColumn title='Output' dataKey='outputLength' width='100' />
<TableColumn title='Input' dataKey='inputLength' width='100' />
<TableColumn title='Name' dataKey='name' link='/simulationModel/' linkKey='id' />
<TableColumn title='Simulator' dataKey='simulatorID' modifier={(simulatorID) => this.getSimulatorName(simulatorID)} />
<TableColumn title='Outputs' dataKey='outputLength' width='100' />
<TableColumn title='Inputs' dataKey='inputLength' width='100' />
<TableColumn
title=''
width='70'
width='200'
deleteButton
exportButton
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulationModels[index], modalIndex: index })}
onDelete={(index) => this.setState({ deleteSimulationModelModal: true, modalSimulationModelData: this.state.simulationModels[index], modalSimulationModelIndex: index })}
onExport={index => this.exportModel(index)}
/>
</Table>
@ -283,16 +341,46 @@ class Scenario extends React.Component {
<div style={{ float: 'right' }}>
<Button onClick={this.addSimulationModel} style={buttonStyle}><Icon icon="plus" /> Simulation Model</Button>
<Button onClick={() => this.setState({ importModal: true })} style={buttonStyle}><Icon icon="upload" /> Import</Button>
<Button onClick={() => this.setState({ importSimulationModelModal: true })} style={buttonStyle}><Icon icon="upload" /> Import</Button>
</div>
<div style={{ clear: 'both' }} />
<ImportSimulationModelDialog show={this.state.importModal} onClose={this.importSimulationModel} simulators={this.state.simulators} />
<ImportSimulationModelDialog show={this.state.importSimulationModelModal} onClose={this.importSimulationModel} simulators={this.state.simulators} />
<DeleteDialog title="simulation model" name={this.state.modalSimulationModelData.name} show={this.state.deleteSimulationModelModal} onClose={this.closeDeleteSimulationModelModal} />
{/*Dashboard table*/}
<h2>Dashboards</h2>
<Table data={this.state.dashboards}>
<TableColumn title='Name' dataKey='name' link='/dashboards/' linkKey='id' />
<TableColumn title='Grid' dataKey='grid' />
<TableColumn
title=''
width='200'
deleteButton
exportButton
onDelete={(index) => this.setState({ deleteDashboardModal: true, modalDashboardData: this.state.dashboards[index], modalDashboardIndex: index })}
onExport={index => this.exportDashboard(index)}
/>
</Table>
<div style={{ float: 'right' }}>
<Button onClick={() => this.setState({ newDashboardModal: true })} style={buttonStyle}><Icon icon="plus" /> Dashboard</Button>
<Button onClick={() => this.setState({ importDashboardModal: true })} style={buttonStyle}><Icon icon="upload" /> Import</Button>
</div>
<div style={{ clear: 'both' }} />
<NewDashboardDialog show={this.state.newDashboardModal} onClose={data => this.closeNewDashboardModal(data)}/>
<ImportDashboardDialog show={this.state.importDashboardModal} onClose={data => this.closeImportDashboardModal(data)} />
<DeleteDialog title="dashboard" name={this.state.modalDashboardData.name} show={this.state.deleteDashboardModal} onClose={(e) => this.closeDeleteDashboardModal(e)}/>
<DeleteDialog title="simulation model" name={this.state.modalData.name} show={this.state.deleteModal} onClose={this.closeDeleteModal} />
</div>;
}
}
export default Container.create(FluxContainerConverter.convert(Scenario), { withProps: true });
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(Scenario), { withProps: true });

View file

@ -26,7 +26,7 @@ import FileSaver from 'file-saver';
import AppDispatcher from '../common/app-dispatcher';
import ScenarioStore from './scenario-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import Icon from '../common/icon';
import Table from '../common/table';
@ -41,13 +41,12 @@ import FluxContainerConverter from "../common/FluxContainerConverter";
class Scenarios extends Component {
static getStores() {
return [ ScenarioStore, UserStore ];
return [ ScenarioStore, LoginStore ];
}
static calculateState() {
const scenarios = ScenarioStore.getState();
const sessionToken = UserStore.getState().token;
console.log(scenarios);
const sessionToken = LoginStore.getState().token;
return {
scenarios,
@ -180,8 +179,8 @@ class Scenarios extends Component {
<Table data={this.state.scenarios}>
<TableColumn title='Name' dataKey='name' link='/scenarios/' linkKey='id' />
<TableColumn title='ID' dataKey='id' link='/scenarios/' linkKey='id' />
<TableColumn title='Running' dataKey='running' link='/scenarios/' linkKey='id' />
<TableColumn title='ID' dataKey='id' />
<TableColumn title='Running' dataKey='running' />
<TableColumn
width='200'
editButton

View file

@ -28,7 +28,7 @@ import _ from 'lodash';
import SimulationStore from './simulation-store';
import SimulatorStore from '../simulator/simulator-store';
import SimulationModelStore from '../simulationmodel/simulation-model-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher';
import Icon from '../common/icon';
@ -41,12 +41,12 @@ import DeleteDialog from '../common/dialogs/delete-dialog';
class Simulation extends React.Component {
static getStores() {
return [ SimulationStore, SimulatorStore, SimulationModelStore, UserStore ];
return [ SimulationStore, SimulatorStore, SimulationModelStore, LoginStore ];
}
static calculateState(prevState, props) {
// get selected simulation
const sessionToken = UserStore.getState().token;
const sessionToken = LoginStore.getState().token;
let simulation = SimulationStore.getState().find(s => s._id === props.match.params.simulation);
if (simulation == null) {

View file

@ -26,7 +26,7 @@ import FileSaver from 'file-saver';
import AppDispatcher from '../common/app-dispatcher';
import SimulationStore from './simulation-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import SimulatorStore from '../simulator/simulator-store';
import SimulationModelStore from '../simulationmodel/simulation-model-store';
@ -42,7 +42,7 @@ import DeleteDialog from '../common/dialogs/delete-dialog';
class Simulations extends Component {
static getStores() {
return [ SimulationStore, UserStore, SimulatorStore, SimulationModelStore ];
return [ SimulationStore, LoginStore, SimulatorStore, SimulationModelStore ];
}
static calculateState() {
@ -50,7 +50,7 @@ class Simulations extends Component {
const simulationModels = SimulationModelStore.getState();
const simulators = SimulatorStore.getState();
const sessionToken = UserStore.getState().token;
const sessionToken = LoginStore.getState().token;
return {
simulations,

View file

@ -41,12 +41,15 @@ class SignalMapping extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.length === this.state.length && nextProps.signals === this.state.signals) {
return;
}
static getDerivedStateFromProps(props, state){
if (props.length === state.length && props.signals === state.signals) {
return null
}
this.setState({ length: nextProps.length, signals: nextProps.signals });
return{
length: props.length,
signals: props.signals
};
}
validateLength(){

View file

@ -24,7 +24,7 @@ import { Container } from 'flux/utils';
import { Button, Col, Form, FormLabel } from 'react-bootstrap';
import SimulationModelStore from './simulation-model-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import AppDispatcher from '../common/app-dispatcher';
import SelectSimulator from '../simulator/select-simulator';
@ -37,7 +37,7 @@ import FluxContainerConverter from "../common/FluxContainerConverter";
class SimulationModel extends React.Component {
static getStores() {
return [ SimulationModelStore, UserStore ];
return [ SimulationModelStore, LoginStore ];
}
static calculateState(prevState, props) {
@ -45,7 +45,7 @@ class SimulationModel extends React.Component {
return {
simulationModel: simulationModel || {},
sessionToken: UserStore.getState().token
sessionToken: LoginStore.getState().token
};
}

View file

@ -40,21 +40,23 @@ class SelectSimulator extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.value === this.state.selectedSimulator) {
return;
}
static getDerivedStateFromProps(props, state){
if (props.value === state.selectedSimulator) {
return null; // no change
}
let selectedSimulator = nextProps.value;
if (selectedSimulator == null) {
if (this.state.simulators.length > 0) {
selectedSimulator = this.state.simulators[0].id;
} else {
selectedSimulator = '';
}
let selectedSimulator = props.value;
if (selectedSimulator == null) {
if (state.simulators.length > 0) {
selectedSimulator = state.simulators[0].id;
} else {
selectedSimulator = '';
}
}
this.setState({ selectedSimulator });
return {
selectedSimulator
};
}
handleChange = event => {
@ -66,7 +68,7 @@ class SelectSimulator extends React.Component {
this.props.onChange(simulator);
}
}
};
render() {
const simulatorOptions = this.state.simulators.map(s =>

View file

@ -20,7 +20,7 @@
******************************************************************************/
import React from 'react';
import { Button, DropdownButton, DropdownItem } from 'react-bootstrap';
import { Button, ButtonToolbar, DropdownButton, DropdownItem } from 'react-bootstrap';
class SimulatorAction extends React.Component {
constructor(props) {
@ -31,14 +31,17 @@ class SimulatorAction extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
if (this.state.selectedAction == null) {
if (nextProps.actions != null && nextProps.actions.length > 0) {
this.setState({ selectedAction: nextProps.actions[0] });
}
static getDerivedStateFromProps(props, state){
if (state.selectedAction == null) {
if (props.actions != null && props.actions.length > 0) {
return{
selectedAction: props.actions[0]
};
}
}
return null
}
setAction = id => {
// search action
for (let action of this.props.actions) {
@ -46,7 +49,7 @@ class SimulatorAction extends React.Component {
this.setState({ selectedAction: action });
}
}
}
};
render() {
const actionList = this.props.actions.map(action => (
@ -56,11 +59,13 @@ class SimulatorAction extends React.Component {
));
return <div>
<ButtonToolbar>
<DropdownButton title={this.state.selectedAction != null ? this.state.selectedAction.title : ''} id="action-dropdown" onSelect={this.setAction}>
{actionList}
{actionList}
</DropdownButton>
<Button style={{ marginLeft: '5px' }} disabled={this.props.runDisabled} onClick={() => this.props.runAction(this.state.selectedAction)}>Run</Button>
</ButtonToolbar>
</div>;
}
}

View file

@ -27,7 +27,7 @@ import _ from 'lodash';
import AppDispatcher from '../common/app-dispatcher';
import SimulatorStore from './simulator-store';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import Icon from '../common/icon';
import Table from '../common/table';
@ -43,7 +43,7 @@ import FluxContainerConverter from "../common/FluxContainerConverter";
class Simulators extends Component {
static getStores() {
return [ UserStore, SimulatorStore ];
return [ LoginStore, SimulatorStore ];
}
static statePrio(state) {
@ -80,8 +80,7 @@ class Simulators extends Component {
});
return {
sessionToken: UserStore.getState().token,
sessionUserID: UserStore.getState().userid,
sessionToken: LoginStore.getState().token,
simulators,
modalSimulator: {},
deleteModal: false,
@ -94,7 +93,6 @@ class Simulators extends Component {
AppDispatcher.dispatch({
type: 'simulators/start-load',
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
// Start timer for periodic refresh
@ -114,7 +112,6 @@ class Simulators extends Component {
AppDispatcher.dispatch({
type: 'simulators/start-load',
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
}
@ -128,7 +125,6 @@ class Simulators extends Component {
type: 'simulators/start-add',
data,
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
}
@ -148,7 +144,6 @@ class Simulators extends Component {
type: 'simulators/start-edit',
data: simulator,
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
}
@ -164,7 +159,6 @@ class Simulators extends Component {
type: 'simulators/start-remove',
data: this.state.modalSimulator,
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
@ -186,7 +180,6 @@ class Simulators extends Component {
type: 'simulators/start-add',
data,
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
}
@ -223,7 +216,6 @@ class Simulators extends Component {
simulator: this.state.simulators[index],
data: action.data,
token: this.state.sessionToken,
userid: this.state.sessionUserID
});
}
}

View file

@ -23,7 +23,7 @@ import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import UserStore from './user-store';
//import LoginStore from './login-store';
class EditOwnUserDialog extends React.Component {
@ -34,17 +34,15 @@ class EditOwnUserDialog extends React.Component {
this.state = {
username: '',
mail: '',
role: '',
id: '',
mail: '',
password: '',
oldPassword: '',
active: '',
confirmpassword: ''
}
}
onClose(canceled) {
if (canceled === false) {
@ -56,26 +54,22 @@ class EditOwnUserDialog extends React.Component {
}
}
handleChange(e) {
let user = UserStore.getState().currentUser;
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 oldPassword = true;
var confirmpassword = true;
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;
}
@ -84,10 +78,6 @@ class EditOwnUserDialog extends React.Component {
pw = false;
}
if(this.state.active === ''){
active = false;
}
if(this.state.oldPassword === ''){
oldPassword = false;
}
@ -96,23 +86,26 @@ class EditOwnUserDialog extends React.Component {
confirmpassword = false;
}
this.setState({
role: user.role,
id: user.id
});
// form is valid if any of the fields contain somethig
this.valid = username || role || active || oldPassword || mail || pw || confirmpassword;
/*this.setState({
role: user.role,
id: user.id,
active: user.active
});*/
// form is valid if the following condition is met
this.valid = username || mail || (oldPassword && pw && confirmpassword);
}
resetState() {
this.setState({
//username: this.props.user.username,
//mail: this.props.user.mail,
role: this.props.user.role,
id: this.props.user.id
username: '',
mail: '',
oldPassword: '',
confirmpassword: '',
password: '',
id: this.props.user.id,
});
}
@ -122,12 +115,12 @@ class EditOwnUserDialog extends React.Component {
<form>
<FormGroup as={Col} controlId="username">
<FormLabel>Username</FormLabel>
<FormControl type="text" placeholder="Enter username" value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl type="text" placeholder={this.props.user.username} value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="mail">
<FormLabel>E-mail</FormLabel>
<FormControl type="text" placeholder="Enter e-mail" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
<FormControl type="text" placeholder={this.props.user.mail} value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="oldPassword">
<FormLabel>Old Password</FormLabel>

View file

@ -113,12 +113,12 @@ class EditUserDialog extends React.Component {
<form>
<FormGroup as={Col} controlId="username">
<FormLabel>Username</FormLabel>
<FormControl type="text" placeholder="Enter username" value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl type="text" placeholder={this.props.user.username} value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="mail">
<FormLabel>E-mail</FormLabel>
<FormControl type="text" placeholder="Enter e-mail" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
<FormControl type="text" placeholder={this.props.user.mail} value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="oldPassword">
<FormLabel>Admin Password</FormLabel>

View file

@ -25,17 +25,15 @@ import AppDispatcher from '../common/app-dispatcher';
import UsersDataManager from './users-data-manager';
import SimulatorDataDataManager from '../simulator/simulator-data-data-manager';
class UserStore extends ReduceStore {
class LoginStore extends ReduceStore {
constructor() {
super(AppDispatcher);
}
getInitialState() {
return {
users: [],
currentUser: null,
token: null,
userid: 0,
loginMessage: null
};
}
@ -49,19 +47,29 @@ class UserStore extends ReduceStore {
case 'users/logout':
// disconnect from all simulators
SimulatorDataDataManager.closeAll();
//remove token and current user from local storage
localStorage.clear();
// delete user and token
return Object.assign({}, state, { token: null, currentUser: null });
case 'users/logged-in':
// // request logged-in user data
UsersDataManager.getCurrentUser(action.token, action.userid);
return Object.assign({}, state, { token: action.token, userid: action.userid});
UsersDataManager.getCurrentUser(action.token, action.currentUser.id);
return Object.assign({}, state, { token: action.token, currentUser: action.currentUser});
case 'users/current-user':
// // save logged-in user
return Object.assign({}, state, { currentUser: action.user});
return Object.assign({}, state, { currentUser: action.currentUser});
case 'users/start-edit-own-user':
// update the current user
UsersDataManager.updateCurrentUser(action.token, action.data);
return Object.assign({}, state, { token: action.token, currentUser: action.data});
case 'users/reload-current-user':
UsersDataManager.getCurrentUser(action.token, action.currentUser.id);
return Object.assign({}, state, { token: action.token, currentUser: action.currentUser});
case 'users/current-user-error':
// discard user token
@ -81,4 +89,4 @@ class UserStore extends ReduceStore {
}
}
export default new UserStore();
export default new LoginStore();

View file

@ -31,21 +31,20 @@ import Footer from '../common/footer';
import NotificationsDataManager from '../common/data-managers/notifications-data-manager';
import AppDispatcher from '../common/app-dispatcher';
import UserStore from './user-store';
import LoginStore from './login-store';
import FluxContainerConverter from "../common/FluxContainerConverter";
class Login extends Component {
static getStores() {
return [ UserStore ];
return [ LoginStore ];
}
static calculateState() {
return {
currentUser: UserStore.getState().currentUser,
token: UserStore.getState().token,
loginMessage: UserStore.getState().loginMessage,
userid: UserStore.getState().userid
currentUser: LoginStore.getState().currentUser,
token: LoginStore.getState().token,
loginMessage: LoginStore.getState().loginMessage,
};
}
@ -53,27 +52,29 @@ class Login extends Component {
NotificationsDataManager.setSystem(this.refs.notificationSystem);
}
componentWillUpdate(nextProps, nextState) {
// if token stored locally, request user
if (nextState.token == null) {
const token = localStorage.getItem('token');
const userid = localStorage.getItem('userid');
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
if (token != null && token !== '' && nextState.currentUser == null) {
// if token stored locally, request user
if (this.state.token == null) {
const token = localStorage.getItem('token');
const currentUser = localStorage.getItem('currentUser');
if (token != null && token !== '' && this.state.currentUser == null) {
AppDispatcher.dispatch({
type: 'users/logged-in',
token: token,
userid: userid
currentUser: currentUser
});
}
} else {
// check if logged in
if (nextState.currentUser != null) {
if (this.state.currentUser != null) {
// save login in local storage
localStorage.setItem('token', nextState.token);
localStorage.setItem('userid', nextState.userid);
localStorage.setItem('token', this.state.token);
localStorage.setItem('currentUser', JSON.stringify(this.state.currentUser));
}
}
}
render() {

View file

@ -24,75 +24,79 @@ import { Container } from 'flux/utils';
import {Button, Col, Row} from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
import UserStore from './user-store';
import LoginStore from './login-store';
import UsersStore from './users-store';
import Icon from '../common/icon';
import EditOwnUserDialog from './edit-own-user'
import FluxContainerConverter from "../common/FluxContainerConverter";
import NotificationsDataManager from "../common/data-managers/notifications-data-manager"
class User extends Component {
static getStores() {
return [ UserStore, UsersStore ];
return [ LoginStore, UsersStore ];
}
static calculateState(prevState, props) {
//prevState = prevState || {};
prevState = prevState || {};
let sessionToken = UserStore.getState().token;
let user = UserStore.getState().currentUser;
if(user === null) {
AppDispatcher.dispatch({
type: 'users/start-load',
data: UserStore.getState().userid,
token: sessionToken
});
user = {};
}
console.log("extracted user 2: " + user.username);
let user = LoginStore.getState().currentUser;
return {
user,
token: sessionToken,
newModal: false,
currentUser: user,
token: LoginStore.getState().token,
editModal: false,
update: false,
modalData: {}
};
}
closeEditModal(data) {
this.setState({ editModal: false });
//this.setState({currentUser: data});
let updatedData = {};
if (data) {
if(data.password === data.confirmpassword){
if(data){
if (data.username !== ''){
updatedData["id"] = data.id;
updatedData["username"] = data.username;
}
if (data.mail !== ''){
updatedData["id"] = data.id;
updatedData["mail"] = data.mail;
}
if (data.password !== '' && data.oldPassword !== '' && data.password === data.confirmpassword ){
updatedData["id"] = data.id;
updatedData["password"] = data.password;
updatedData["oldPassword"] = data.oldPassword;
} else if (data.password !== '' && data.password !== data.confirmpassword) {
const USER_UPDATE_ERROR_NOTIFICATION = {
title: 'Update Error ',
message: 'New password not correctly confirmed',
level: 'error'
};
NotificationsDataManager.addNotification(USER_UPDATE_ERROR_NOTIFICATION);
return
}
if (updatedData !== {}) {
let requestData = {};
requestData["user"] = updatedData;
AppDispatcher.dispatch({
type: 'users/start-own-edit',
data: data,
type: 'users/start-edit-own-user',
data: requestData,
token: this.state.token
});
} else {
const USER_UPDATE_WARNING_NOTIFICATION = {
title: 'Update Warning ',
message: 'No update requested, no input data',
level: 'warning'
};
NotificationsDataManager.addNotification(USER_UPDATE_WARNING_NOTIFICATION);
}
else{
AppDispatcher.dispatch({
type: 'users/confirm-pw-doesnt-match',
data: data,
token: this.state.token
});
}
}
}
}
@ -112,24 +116,24 @@ class User extends Component {
<form>
<Row>
<Col xs={3}>Username: </Col>
<Col xs={3}> {this.state.user.username} </Col>
<Col xs={3}> {this.state.currentUser.username} </Col>
</Row>
<Row as={Col} >
<Col xs={3}>E-mail: </Col>
<Col xs={3}> {this.state.user.mail} </Col>
<Col xs={3}> {this.state.currentUser.mail} </Col>
</Row>
<Row as={Col} >
<Col xs={3}>Role: </Col>
<Col xs={3}> {this.state.user.role} </Col>
<Col xs={3}> {this.state.currentUser.role} </Col>
</Row>
<Button onClick={() => this.setState({ editModal: true })}><Icon icon='edit' /> Edit</Button>
<EditOwnUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} user={this.state.modalData} />
<EditOwnUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} user={this.state.currentUser} />
</form>
@ -138,4 +142,8 @@ class User extends Component {
}
}
export default Container.create(FluxContainerConverter.convert(User));
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(User));

View file

@ -33,8 +33,7 @@ class UsersDataManager extends RestDataManager {
AppDispatcher.dispatch({
type: 'users/logged-in',
token: response.token,
user: response.user,
userid: response.user.id
currentUser: response.user,
});
}).catch(error => {
AppDispatcher.dispatch({
@ -48,7 +47,21 @@ class UsersDataManager extends RestDataManager {
RestAPI.get(this.makeURL('/users/' + id), token).then(response => {
AppDispatcher.dispatch({
type: 'users/current-user',
user: response.user
currentUser: response.user
});
}).catch(error => {
AppDispatcher.dispatch({
type: 'users/current-user-error',
error: error
});
});
}
updateCurrentUser(token, userUpdate){
RestAPI.put(this.makeURL('/users/' + userUpdate.user.id), userUpdate, token).then( response => {
AppDispatcher.dispatch({
type: 'users/current-user',
currentUser: response.user
});
}).catch(error => {
AppDispatcher.dispatch({

View file

@ -24,7 +24,7 @@ import { Container } from 'flux/utils';
import { Button } from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
import UserStore from './user-store';
import LoginStore from './login-store';
import UsersStore from './users-store';
import Icon from '../common/icon';
@ -34,17 +34,16 @@ import NewUserDialog from './new-user';
import EditUserDialog from './edit-user';
import DeleteDialog from '../common/dialogs/delete-dialog';
import FluxContainerConverter from "../common/FluxContainerConverter";
import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
class Users extends Component {
static getStores() {
return [ UserStore, UsersStore ];
return [ LoginStore, UsersStore ];
}
static calculateState(prevState, props) {
let tokenState = UserStore.getState().token;
let tokenState = LoginStore.getState().token;
// If there is a token available and this method was called as a result of loading users
if (!prevState && tokenState) {
@ -95,22 +94,21 @@ class Users extends Component {
this.setState({ editModal: false });
if (data) {
if(data.password === data.confirmpassword){
if(data.password === data.confirmpassword) {
AppDispatcher.dispatch({
type: 'users/start-edit',
data: data,
token: this.state.token
});
}
else{
AppDispatcher.dispatch({
type: 'users/confirm-pw-doesnt-match',
data: data,
token: this.state.token
});
}
AppDispatcher.dispatch({
type: 'users/start-edit',
data: data,
token: this.state.token
});
} else{
const USER_UPDATE_ERROR_NOTIFICATION = {
title: 'Update Error ',
message: 'New password not correctly confirmed',
level: 'error'
};
NotificationsDataManager.addNotification(USER_UPDATE_ERROR_NOTIFICATION)
}
}
}
@ -154,4 +152,5 @@ class Users extends Component {
}
}
export default Container.create(FluxContainerConverter.convert(Users));
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(Users));

View file

@ -28,19 +28,23 @@ class EditWidgetAspectControl extends React.Component {
this.state = {
widget: {
lockAspect: true
customProperties:{
isLocked: true
}
}
};
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {
return (
<FormGroup>
<FormCheck id="lockAspect" checked={this.state.widget.lockAspect} onChange={e => this.props.handleChange(e)}>Lock Aspect</FormCheck>
<FormCheck id="lockAspect" checked={this.state.widget.customProperties.isLocked} onChange={e => this.props.handleChange(e)}>Lock Aspect</FormCheck>
</FormGroup>
);
}

View file

@ -27,17 +27,23 @@ class EditWidgetCheckboxControl extends React.Component {
super(props);
this.state = {
widget: {}
widget: {
customProperties:{
}
}
};
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {
return <FormGroup>
<FormCheck id={this.props.controlId} checked={this.state.widget[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)}>{this.props.text}</FormCheck>
<FormCheck id={this.props.controlId} label={this.props.controlId} checked={this.state.widget[this.props.controlId] || this.state.widget.customProperties[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)}></FormCheck>
</FormGroup>;
}
}

View file

@ -43,24 +43,26 @@ class EditWidgetColorControl extends Component {
super(props);
this.state = {
widget: {}
widget: {
customProperties:{}
}
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
render() {
return (
<FormGroup bsClass="color-control">
<FormGroup bsclass="color-control">
<Row>
<Col componentClass={FormLabel} style={{whiteSpace: 'nowrap' }} sm={2}>
<Col className={FormLabel} style={{whiteSpace: 'nowrap' }} sm={3}>
{ this.props.label }
</Col>
<Col sm={10} bsClass='colors-column'>
<Col sm={10} bsclass='colors-column'>
{
EditWidgetColorControl.ColorPalette.map( (color, idx ) => {
let colorStyle = {
@ -69,7 +71,7 @@ class EditWidgetColorControl extends Component {
};
let checkedClass = classNames({
'checked': idx === this.state.widget[this.props.controlId]
'checked': idx === this.state.widget[this.props.controlId] || this.state.widget.customProperties[this.props.controlId]
});
return (<FormCheck type='radio' key={idx} name={this.props.controlId} style={colorStyle} className={checkedClass} value={idx} inline onChange={(e) => this.props.handleChange({target: { id: this.props.controlId, value: idx}})} />)

View file

@ -33,20 +33,24 @@ class EditWidgetColorZonesControl extends React.Component {
this.state = {
widget: {
customProperties:{
zones: []
}
},
selectedZones: []
};
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
addZone = () => {
// add row
const widget = this.state.widget;
widget.zones.push({ strokeStyle: 'ffffff', min: 0, max: 100 });
widget.customProperties.zones.push({ strokeStyle: 'ffffff', min: 0, max: 100 });
this.setState({ widget });
@ -58,7 +62,7 @@ class EditWidgetColorZonesControl extends React.Component {
const widget = this.state.widget;
this.state.selectedZones.forEach(row => {
widget.zones.splice(row, 1);
widget.customProperties.zones.splice(row, 1);
});
this.setState({ selectedZones: [], widget });
@ -71,11 +75,11 @@ class EditWidgetColorZonesControl extends React.Component {
const widget = this.state.widget;
if (column === 1) {
widget.zones[row].strokeStyle = event.target.value;
widget.customProperties.zones[row].strokeStyle = event.target.value;
} else if (column === 2) {
widget.zones[row].min = event.target.value;
widget.customProperties.zones[row].min = event.target.value;
} else if (column === 3) {
widget.zones[row].max = event.target.value;
widget.customProperties.zones[row].max = event.target.value;
}
this.setState({ widget });
@ -88,7 +92,7 @@ class EditWidgetColorZonesControl extends React.Component {
const event = {
target: {
id: 'zones',
value: widget.zones
value: widget.customProperties.zones
}
};
@ -117,15 +121,15 @@ class EditWidgetColorZonesControl extends React.Component {
return <FormGroup>
<FormLabel>Color zones</FormLabel>
<Table data={this.state.widget.zones}>
<Table data={this.state.widget.customProperties.zones}>
<TableColumn width="20" checkbox onChecked={this.checkedCell} />
<TableColumn title="Color" dataKey="strokeStyle" inlineEditable onInlineChange={this.changeCell} />
<TableColumn title="Minimum" dataKey="min" inlineEditable onInlineChange={this.changeCell} />
<TableColumn title="Maximum" dataKey="max" inlineEditable onInlineChange={this.changeCell} />
</Table>
<Button onClick={this.addZone} disabled={!this.props.widget.colorZones}><Icon icon="plus" /> Add</Button>
<Button onClick={this.removeZones} disabled={!this.props.widget.colorZones}><Icon icon="minus" /> Remove</Button>
<Button onClick={this.addZone} disabled={!this.props.widget.customProperties.colorZones}><Icon icon="plus" /> Add</Button>
<Button onClick={this.removeZones} disabled={!this.props.widget.customProperties.colorZones}><Icon icon="minus" /> Remove</Button>
</FormGroup>;
}
}

View file

@ -38,13 +38,13 @@ import EditWidgetMinMaxControl from './edit-widget-min-max-control';
import EditWidgetHTMLContent from './edit-widget-html-content';
import EditWidgetParametersControl from './edit-widget-parameters-control';
export default function createControls(widgetType = null, widget = null, sessionToken = null, files = null, validateForm, simulationModels, handleChange) {
export default function CreateControls(widgetType = null, widget = null, sessionToken = null, files = null, validateForm, simulationModels, handleChange) {
// Use a list to concatenate the controls according to the widget type
var dialogControls = [];
var DialogControls = [];
switch(widgetType) {
case 'CustomAction':
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
<EditWidgetTextControl key={1} widget={widget} controlId={'icon'} label={'Icon'} placeholder={'Enter an awesome font icon name'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
<EditWidgetSimulationControl key={2} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -52,7 +52,7 @@ export default function createControls(widgetType = null, widget = null, session
)
break;
case 'Action':
dialogControls.push(
DialogControls.push(
<EditWidgetSimulationControl key={0} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />
)
break;
@ -60,7 +60,7 @@ export default function createControls(widgetType = null, widget = null, session
let valueBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signal', value: 0}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
<EditWidgetSimulationControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => valueBoundOnChange(e)} />,
<EditWidgetSignalControl key={2} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -72,7 +72,7 @@ export default function createControls(widgetType = null, widget = null, session
let lampBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signal', value: 0}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetSimulationControl key={0} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => lampBoundOnChange(e)} />,
<EditWidgetSignalControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetTextControl key={2} widget={widget} controlId={'threshold'} label={'Threshold'} placeholder={'0.5'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
@ -84,7 +84,7 @@ export default function createControls(widgetType = null, widget = null, session
let plotBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signals', value: []}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetTimeControl key={0} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetSimulationControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => plotBoundOnChange(e)} />,
<EditWidgetSignalsControl key={2} controlId={'signals'} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -93,7 +93,7 @@ export default function createControls(widgetType = null, widget = null, session
);
break;
case 'Table':
dialogControls.push(
DialogControls.push(
<EditWidgetSimulationControl key={0} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetCheckboxControl key={1} widget={widget} controlId={'showUnit'} text="Show unit" handleChange={e => handleChange(e)} />
);
@ -101,7 +101,7 @@ export default function createControls(widgetType = null, widget = null, session
case 'Image':
// Restrict to only image file types (MIME)
let imageControlFiles = files == null? [] : files.filter(file => file.type.includes('image'));
dialogControls.push(
DialogControls.push(
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={imageControlFiles} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetAspectControl key={1} widget={widget} handleChange={e => handleChange(e)} />
);
@ -110,7 +110,7 @@ export default function createControls(widgetType = null, widget = null, session
let gaugeBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signal', value: ''}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
<EditWidgetSimulationControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => gaugeBoundOnChange(e) } />,
<EditWidgetSignalControl key={2} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -123,7 +123,7 @@ export default function createControls(widgetType = null, widget = null, session
let plotTableBoundOnChange = (e) => {
handleChange([e, {target: {id: 'preselectedSignals', value: []}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetSimulationControl key={0} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => plotTableBoundOnChange(e)} />,
<EditWidgetSignalsControl key={1} controlId={'preselectedSignals'} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetTextControl key={2} controlId={'ylabel'} label={'Y-Axis'} placeholder={'Enter a name for the Y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />,
@ -132,7 +132,7 @@ export default function createControls(widgetType = null, widget = null, session
);
break;
case 'Slider':
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} handleChange={e => handleChange(e)} validate={id => validateForm(id)} />,
<EditWidgetOrientation key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
<EditWidgetSimulationControl key={2} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -148,7 +148,7 @@ export default function createControls(widgetType = null, widget = null, session
let buttonBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signal', value: 0}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} handleChange={e => handleChange(e)} validate={id => validateForm(id)} />,
<EditWidgetSimulationControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => buttonBoundOnChange(e)} />,
<EditWidgetSignalControl key={2} widget={widget} controlId={'signal'} input validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />,
@ -158,27 +158,27 @@ export default function createControls(widgetType = null, widget = null, session
);
break;
case 'Box':
dialogControls.push(
DialogControls.push(
<EditWidgetColorControl key={0} widget={widget} controlId={'border_color'} label={'Border color'} validate={(id) => validateForm(id)} handleChange={(e) => handleChange(e)} />,
<EditWidgetColorControl key={1} widget={widget} controlId={'background_color'} label={'Background color'} handleChange={e => handleChange(e)} />
);
break;
case 'Label':
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} handleChange={e => handleChange(e)} validate={id => validateForm(id)} />,
<EditWidgetTextSizeControl key={1} widget={widget} handleChange={e => handleChange(e)} />,
<EditWidgetColorControl key={2} widget={widget} controlId={'fontColor'} label={'Text color'} handleChange={e => handleChange(e)} />
);
break;
case 'HTML':
dialogControls.push(
DialogControls.push(
<EditWidgetHTMLContent key={0} widget={widget} placeholder='HTML Code' controlId='content' handleChange={e => handleChange(e)} />
);
break;
case 'Topology':
// Restrict to only xml files (MIME)
let topologyControlFiles = files == null? [] : files.filter( file => file.type.includes('xml'));
dialogControls.push(
DialogControls.push(
<EditImageWidgetControl key={0} sessionToken={sessionToken} widget={widget} files={topologyControlFiles} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />
);
break;
@ -187,7 +187,7 @@ export default function createControls(widgetType = null, widget = null, session
let inputBoundOnChange = (e) => {
handleChange([e, {target: {id: 'signal', value: 0}}]);
}
dialogControls.push(
DialogControls.push(
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} validate={id => validateForm(id)} handleChange={e => handleChange(e)} />,
<EditWidgetSimulationControl key={1} widget={widget} validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => inputBoundOnChange(e)} />,
<EditWidgetSignalControl key={2} widget={widget} controlId={'signal'} input validate={(id) => validateForm(id)} simulationModels={simulationModels} handleChange={(e) => handleChange(e)} />
@ -198,5 +198,5 @@ export default function createControls(widgetType = null, widget = null, session
console.log('Non-valid widget type: ' + widgetType);
}
return dialogControls;
return DialogControls;
}

View file

@ -36,15 +36,16 @@ class EditWidgetHTMLContent extends React.Component {
event.stopPropagation();
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
render() {
return <FormGroup controlId={this.props.controlId}>
<FormLabel>HTML Content</FormLabel>
<FormControl onKeyPress={this.handleKeyIgnore} componentClass="textarea" style={{ height: 200 }} placeholder={this.props.placeholder} value={this.state.widget[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)} />
<FormControl onKeyPress={this.handleKeyIgnore} componentClass="textarea" style={{ height: 200 }} placeholder={this.props.placeholder} value={this.state.widget[this.props.controlId] || this.state.widget.customProperties[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)} />
</FormGroup>;
}
}

View file

@ -30,15 +30,19 @@ class EditImageWidgetControl extends React.Component {
this.state = {
widget: {
customProperties:{
file: ''
}
},
fileList: null,
progress: 0
};
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
startFileUpload = () => {
@ -73,7 +77,7 @@ class EditImageWidgetControl extends React.Component {
return <div>
<FormGroup controlId="file">
<FormLabel>Image</FormLabel>
<FormControl componentClass="select" value={this.state.widget.file} onChange={(e) => this.props.handleChange(e)}>
<FormControl componentClass="select" value={this.state.widget.customProperties.file} onChange={(e) => this.props.handleChange(e)}>
{this.props.files.length === 0 ? (
<option disabled value style={{ display: 'none' }}>No images found, please upload one first.</option>
) : (

View file

@ -26,33 +26,34 @@ class EditWidgetMinMaxControl extends React.Component {
constructor(props) {
super(props);
const widget = {};
widget[props.controlID + "UseMinMax"] = false;
widget[props.controlId + "Min"] = 0;
widget[props.controlId + "Max"] = 100;
this.state = {
widget
};
widget: {
customProperties:{
}
}
}
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {
return <FormGroup>
<FormLabel>{this.props.label}</FormLabel>
<FormCheck id={this.props.controlId + "UseMinMax"} checked={this.state.widget[this.props.controlId + "UseMinMax"] || ''} onChange={e => this.props.handleChange(e)}>Enable min-max</FormCheck>
<FormCheck id={this.props.controlId + "UseMinMax"} label= {"UseMinMax"} checked={this.state.widget.customProperties[this.props.controlId + "UseMinMax"] || ''} onChange={e => this.props.handleChange(e)}></FormCheck>
<Table>
<tbody>
<tr>
<td>
Min: <FormControl type="number" step="any" id={this.props.controlId + "Min"} disabled={!this.state.widget[this.props.controlId + "UseMinMax"]} placeholder="Minimum value" value={this.state.widget[this.props.controlId + 'Min']} onChange={e => this.props.handleChange(e)} />
Min: <FormControl type="number" step="any" id={this.props.controlId + "Min"} disabled={!this.state.widget.customProperties[this.props.controlId + "UseMinMax"]} placeholder="Minimum value" value={this.state.widget.customProperties[this.props.controlId + 'Min']} onChange={e => this.props.handleChange(e)} />
</td>
<td>
Max: <FormControl type="number" step="any" id={this.props.controlId + "Max"} disabled={!this.state.widget[this.props.controlId + "UseMinMax"]} placeholder="Maximum value" value={this.state.widget[this.props.controlId + 'Max']} onChange={e => this.props.handleChange(e)} />
Max: <FormControl type="number" step="any" id={this.props.controlId + "Max"} disabled={!this.state.widget.customProperties[this.props.controlId + "UseMinMax"]} placeholder="Maximum value" value={this.state.widget.customProperties[this.props.controlId + 'Max']} onChange={e => this.props.handleChange(e)} />
</td>
</tr>
</tbody>

View file

@ -27,20 +27,23 @@ class EditWidgetNumberControl extends Component {
super(props);
this.state = {
widget: {}
widget: {
customProperties:{}
}
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {
return (
<FormGroup controlId={this.props.controlId}>
<FormLabel>{this.props.label}</FormLabel>
<FormControl type="number" step="any" defaultValue={this.props.defaultValue} value={this.state.widget[this.props.controlId] || 0} onChange={e => this.props.handleChange(e)} />
<FormControl type="number" step="any" value={this.state.widget.customProperties[this.props.controlId] || 0} onChange={e => this.props.handleChange(e)} />
</FormGroup>
);
}

View file

@ -29,13 +29,16 @@ class EditWidgetOrientation extends Component {
super(props);
this.state = {
widget: {}
widget: {
customProperties:{}
}
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
handleOrientationChange(orientation) {
@ -48,7 +51,7 @@ class EditWidgetOrientation extends Component {
return (
<FormGroup controlId="orientation">
<Row>
<Col componentClass={FormLabel} sm={2}>
<Col className={FormLabel} sm={3}>
Orientation
</Col>
<Col sm={10}>
@ -58,8 +61,7 @@ class EditWidgetOrientation extends Component {
let name = WidgetSlider.OrientationTypes[type].name;
return (
<FormCheck inline key={value} type='radio' name='orientation' checked={ value === this.state.widget.orientation } onChange={(e) => this.handleOrientationChange(value)}>
{ name }
<FormCheck inline label={name} key={value} id={value} type='radio' title='orientation' checked={ value === this.state.widget.customProperties.orientation } onChange={(e) => this.handleOrientationChange(value)}>
</FormCheck>)
})
}

View file

@ -32,9 +32,10 @@ class EditWidgetParametersControl extends Component {
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
handleChange(value) {

View file

@ -33,9 +33,10 @@ class EditWidgetSignalControl extends Component {
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
render() {
@ -54,7 +55,7 @@ class EditWidgetSignalControl extends Component {
return (
<FormGroup controlId="signal">
<FormLabel>Signal</FormLabel>
<FormControl componentClass="select" placeholder="Select signal" value={this.state.widget.signal} onChange={(e) => this.props.handleChange(e)}>
<FormControl as="select" placeholder="Select signal" value={this.state.widget.signal} onChange={(e) => this.props.handleChange(e)}>
{
signalsToRender.length === 0 ? (
<option disabled value style={{ display: 'none' }}>No signals available.</option>

View file

@ -20,7 +20,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormCheck, FormLabel, FormControl } from 'react-bootstrap';
import { FormGroup, FormCheck, FormLabel } from 'react-bootstrap';
class EditWidgetSignalsControl extends Component {
constructor(props) {
@ -33,9 +33,10 @@ class EditWidgetSignalsControl extends Component {
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
handleSignalChange(checked, index) {
@ -68,7 +69,7 @@ class EditWidgetSignalsControl extends Component {
<FormLabel>Signals</FormLabel>
{
signalsToRender.length === 0 || !this.state.widget.hasOwnProperty(this.props.controlId)? (
<FormControl.Static>No signals available.</FormControl.Static>
<FormLabel>No signals available</FormLabel>
) : (
signalsToRender.map((signal, index) => (
<FormCheck key={index} checked={this.state.widget[this.props.controlId].indexOf(index) !== -1} onChange={(e) => this.handleSignalChange(e.target.checked, index)}>{signal.name}</FormCheck>

View file

@ -28,21 +28,25 @@ class EditWidgetSimulationControl extends Component {
this.state = {
widget: {
simulationModel: ''
customProperties: {
simulationModel: ''
}
}
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {
return (
<FormGroup controlId="simulationModel">
<FormLabel>Simulation Model</FormLabel>
<FormControl componentClass="select" placeholder="Select simulation model" value={this.state.widget.simulationModel || '' } onChange={(e) => this.props.handleChange(e)}>
<FormControl as="select" placeholder="Select simulation model" value={this.state.widget.simulationModel || '' } onChange={(e) => this.props.handleChange(e)}>
{
this.props.simulationModels.length === 0 ? (
<option disabled value style={{ display: 'none' }}> No simulation models available. </option>

View file

@ -31,14 +31,15 @@ class EditWidgetTextControl extends Component {
};
}
componentWillReceiveProps(nextProps) {
// Update state's widget with props
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return {
widget: props.widget
};
}
render() {
return (
<FormGroup controlId={this.props.controlId} validationState={this.props.validate ? this.props.validate(this.props.controlId) : null}>
<FormGroup controlId={this.props.controlId} valid={this.props.validate ? this.props.validate(this.props.controlId) : null}>
<FormLabel>{this.props.label}</FormLabel>
<FormControl type="text" placeholder={this.props.placeholder} value={this.state.widget[this.props.controlId] || ''} onChange={e => this.props.handleChange(e)} />
<FormControl.Feedback />

View file

@ -29,7 +29,7 @@ class EditWidgetTextSizeControl extends React.Component {
return (
<FormGroup controlId="textSize">
<FormLabel>Text size</FormLabel>
<FormControl componentClass="select" value={this.props.widget.textSize} onChange={e => this.props.handleChange(e)}>
<FormControl as="select" value={this.props.widget.textSize} onChange={e => this.props.handleChange(e)}>
{sizes.map((size, index) => (
<option key={index} value={size}>{size}</option>
))}

View file

@ -33,8 +33,10 @@ class EditWidgetTimeControl extends Component {
};
}
componentWillReceiveProps(nextProps) {
this.setState({ widget: nextProps.widget });
static getDerivedStateFromProps(props, state){
return{
widget: props.widget
};
}
render() {

View file

@ -24,10 +24,11 @@ import React from 'react';
import Dialog from '../common/dialogs/dialog';
import createControls from './edit-widget-control-creator';
import CreateControls from './edit-widget-control-creator';
class EditWidgetDialog extends React.Component {
valid = true;
constructor(props) {
super(props);
@ -37,10 +38,13 @@ class EditWidgetDialog extends React.Component {
name: '',
simulationModel: '',
signal: 0
}
};
}
onClose(canceled) {
if (canceled === false) {
if (this.valid) {
@ -63,7 +67,8 @@ class EditWidgetDialog extends React.Component {
}
handleChange(e) {
if (e.constructor === Array) {
if (e.constructor === Array) {
// Every property in the array will be updated
let changes = e.reduce( (changesObject, event) => {
changesObject[event.target.id] = event.target.value;
@ -72,7 +77,9 @@ class EditWidgetDialog extends React.Component {
}, {});
this.setState({ temporal: Object.assign({}, this.state.temporal, changes ) });
} else {
}
if(e.target.type !== 'text'){
let changeObject = {};
if (e.target.id === 'lockAspect') {
changeObject[e.target.id] = e.target.checked;
@ -92,12 +99,29 @@ class EditWidgetDialog extends React.Component {
changeObject[e.target.id] = e.target.checked;
} else if (e.target.type === 'number') {
changeObject[e.target.id] = Number(e.target.value);
} else {
}
else {
changeObject[e.target.id] = e.target.value;
}
this.setState({ temporal: Object.assign({}, this.state.temporal, changeObject ) });
}
let finalChange = this.state.temporal;
finalChange.customProperties[e.target.id] = changeObject[e.target.id];
this.setState({ temporal: finalChange});
}
else{
if(this.state.temporal[e.target.id]){
let finalChange = this.state.temporal;
finalChange[e.target.id] = e.target.value;
this.setState({ temporal: finalChange});
}
}
}
resetState() {
@ -114,7 +138,7 @@ class EditWidgetDialog extends React.Component {
}
//this.valid = name;
this.valid = true;
this.valid = name;
// return state to control
if (target === 'name') return name ? "success" : "error";
@ -123,7 +147,7 @@ class EditWidgetDialog extends React.Component {
render() {
let controls = null;
if (this.props.widget) {
controls = createControls(
controls = CreateControls(
this.props.widget.type,
this.state.temporal,
this.props.sessionToken,

View file

@ -23,12 +23,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Rnd } from 'react-rnd';
import { contextMenu } from 'react-contexify';
class EditableWidgetContainer extends React.Component {
constructor(props) {
super(props);
this.rnd = null;
}
@ -66,7 +65,8 @@ class EditableWidgetContainer extends React.Component {
}
};
resizeStop = (direction, delta, ref, event) => {
resizeStop = (event, direction, ref,delta, position) => {
const widget = this.props.widget;
// resize depends on direction
@ -90,21 +90,21 @@ class EditableWidgetContainer extends React.Component {
const widget = this.props.widget;
const resizing = {
bottom: !widget.locked,
bottomLeft: !widget.locked,
bottomRight: !widget.locked,
left: !widget.locked,
right: !widget.locked,
top: !widget.locked,
topLeft: !widget.locked,
topRight: !widget.locked
bottom: !widget.isLocked,
bottomLeft: !widget.isLocked,
bottomRight: !widget.isLocked,
left: !widget.isLocked,
right: !widget.idLocked,
top: !widget.isLocked,
topLeft: !widget.isLocked,
topRight: !widget.isLocked
};
const gridArray = [ this.props.grid, this.props.grid ];
const widgetClasses = classNames({
'editing-widget': true,
'locked': widget.locked
'locked': widget.isLocked
});
return <Rnd
@ -112,7 +112,7 @@ class EditableWidgetContainer extends React.Component {
default={{ x: Number(widget.x), y: Number(widget.y), width: widget.width, height: widget.height }}
minWidth={widget.minWidth}
minHeight={widget.minHeight}
lockAspectRatio={Boolean(widget.lockAspect)}
lockAspectRatio={Boolean(widget.isLocked)}
bounds={'parent'}
className={widgetClasses}
onResizeStart={this.borderWasClicked}
@ -121,13 +121,13 @@ class EditableWidgetContainer extends React.Component {
onDragStop={this.dragStop}
dragGrid={gridArray}
resizeGrid={gridArray}
zIndex={widget.z}
zindex={widget.z}
enableResizing={resizing}
disableDragging={widget.locked}
disableDragging={widget.isLocked}
>
<contextMenu id={'widgetMenu' + this.props.index}>
{this.props.children}
</contextMenu>
</Rnd>;
}
}

View file

@ -24,12 +24,13 @@ import PropTypes from 'prop-types';
class WidgetContainer extends React.Component {
render() {
const containerStyle = {
width: Number(this.props.widget.width),
height: Number(this.props.widget.height),
left: Number(this.props.widget.x),
top: Number(this.props.widget.y),
zIndex: Number(this.props.widget.z),
zindex: Number(this.props.widget.z),
position: 'absolute'
};

View file

@ -35,13 +35,14 @@ class WidgetFactory {
x: position.x,
y: position.y,
z: position.z,
locked: false
locked: false,
customProperties: {}
};
// set type specific properties
switch(type) {
case 'CustomAction':
widget.actions = [
widget.customProperties.actions = [
{
action: 'stop'
},
@ -56,51 +57,51 @@ class WidgetFactory {
}
];
widget.name = 'Action';
widget.icon = 'star';
widget.customProperties.icon = 'star';
widget.width = 100;
widget.height = 50;
widget.simulationModel = defaultSimulationModel;
widget.customProperties.simulationModel = defaultSimulationModel;
break;
case 'Action':
widget.simulationModel = defaultSimulationModel;
widget.customProperties.simulationModel = defaultSimulationModel;
break;
case 'Lamp':
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
widget.minWidth = 5;
widget.minHeight = 5;
widget.width = 20;
widget.height = 20;
widget.on_color = 6;
widget.off_color = 8;
widget.threshold = 0.5;
widget.customProperties.on_color = 6;
widget.customProperties.off_color = 8;
widget.customProperties.threshold = 0.5;
break;
case 'Value':
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
widget.minWidth = 70;
widget.minHeight = 20;
widget.width = 120;
widget.height = 30;
widget.textSize = 16;
widget.customProperties.textSize = 16;
widget.name = 'Value';
widget.showUnit = false;
widget.customProperties.showUnit = false;
break;
case 'Plot':
widget.simulationModel = defaultSimulationModel;
widget.signals = [ 0 ];
widget.ylabel = '';
widget.time = 60;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signals = [ 0 ];
widget.customProperties.ylabel = '';
widget.customProperties.time = 60;
widget.minWidth = 400;
widget.minHeight = 200;
widget.width = 400;
widget.height = 200;
widget.yMin = 0;
widget.yMax = 10;
widget.yUseMinMax = false;
widget.customProperties.yMin = 0;
widget.customProperties.yMax = 10;
widget.customProperties.yUseMinMax = false;
break;
case 'Table':
widget.simulationModel = defaultSimulationModel;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.minWidth = 200;
widget.width = 300;
widget.height = 200;
@ -111,80 +112,85 @@ class WidgetFactory {
widget.width = 100;
widget.height = 35;
widget.name = 'Label';
widget.textSize = 32;
widget.fontColor = 0;
widget.customProperties.textSize = 32;
widget.customProperties.fontColor = 0;
break;
case 'PlotTable':
widget.simulationModel = defaultSimulationModel;
widget.preselectedSignals = [];
widget.signals = []; // initialize selected signals
widget.ylabel = '';
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.preselectedSignals = [];
widget.customProperties.signals = []; // initialize selected signals
widget.customProperties.ylabel = '';
widget.minWidth = 200;
widget.minHeight = 100;
widget.width = 600;
widget.height = 300;
widget.time = 60;
widget.yMin = 0;
widget.yMax = 10;
widget.yUseMinMax = false;
widget.customProperties.time = 60;
widget.customProperties.yMin = 0;
widget.customProperties.yMax = 10;
widget.customProperties.yUseMinMax = false;
break;
case 'Image':
widget.minWidth = 20;
widget.minHeight = 20;
widget.width = 200;
widget.height = 200;
widget.lockAspect = true;
widget.customProperties.lockAspect = true;
break;
case 'Button':
widget.minWidth = 100;
widget.minHeight = 50;
widget.width = 100;
widget.height = 100;
widget.background_color = 1;
widget.font_color = 0;
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.background_color = 1;
widget.customProperties.font_color = 0;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
break;
case 'Input':
widget.minWidth = 200;
widget.minHeight = 50;
widget.width = 200;
widget.height = 50;
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
break;
case 'Slider':
widget.minWidth = 380;
widget.minHeight = 30;
widget.width = 400;
widget.height = 50;
widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
widget.customProperties.rangeMin = 0;
widget.customProperties.rangeMax = 200;
widget.customProperties.rangeUseMinMax = true;
widget.customProperties.showUnit = true
break;
case 'Gauge':
widget.simulationModel = defaultSimulationModel;
widget.signal = 0;
widget.customProperties.simulationModel = defaultSimulationModel;
widget.customProperties.signal = 0;
widget.minWidth = 100;
widget.minHeight = 150;
widget.width = 150;
widget.height = 150;
widget.colorZones = false;
widget.zones = [];
widget.valueMin = 0;
widget.valueMax = 1;
widget.valueUseMinMax = false;
widget.customProperties.colorZones = false;
widget.customProperties.zones = [];
widget.customProperties.valueMin = 0;
widget.customProperties.valueMax = 1;
widget.customProperties.valueUseMinMax = false;
break;
case 'Box':
widget.minWidth = 50;
widget.minHeight = 50;
widget.width = 100;
widget.height = 100;
widget.border_color = 0;
widget.customProperties.border_color = 0;
widget.z = 0;
break;
case 'HTML':
widget.content = '<i>Hello World</i>';
widget.customProperties.content = '<i>Hello World</i>';
break;
case 'Topology':
widget.width = 600;

View file

@ -77,26 +77,26 @@ class Plot extends React.Component {
this.removeInterval();
}
componentWillReceiveProps(nextProps) {
if (nextProps.time !== this.props.time) {
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
if (prevProps.time !== this.props.time) {
this.createInterval();
}
let labelMargin = 0;
if (nextProps.yLabel !== '') {
if (this.props.yLabel !== '') {
labelMargin = 30;
}
// check if data is invalid
if (nextProps.data == null || nextProps.data.length === 0 || nextProps.data[0].length === 0) {
if (this.props.data == null || this.props.data.length === 0 || this.props.data[0].length === 0) {
// create empty plot axes
const xScale = scaleTime().domain([Date.now() - nextProps.time * 1000, Date.now()]).range([0, nextProps.width - leftMargin - labelMargin - rightMargin]);
const xScale = scaleTime().domain([Date.now() - this.props.time * 1000, Date.now()]).range([0, this.props.width - leftMargin - labelMargin - rightMargin]);
let yScale;
if (nextProps.yUseMinMax) {
yScale = scaleLinear().domain([nextProps.yMin, nextProps.yMax]).range([nextProps.height + topMargin - bottomMargin, topMargin]);
if (this.props.yUseMinMax) {
yScale = scaleLinear().domain([this.props.yMin, this.props.yMax]).range([this.props.height + topMargin - bottomMargin, topMargin]);
} else {
yScale = scaleLinear().domain([0, 10]).range([nextProps.height + topMargin - bottomMargin, topMargin]);
yScale = scaleLinear().domain([0, 10]).range([this.props.height + topMargin - bottomMargin, topMargin]);
}
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
@ -106,17 +106,20 @@ class Plot extends React.Component {
return;
}
// only show data in requested time
let data = nextProps.data;
// check if requested time frame has changed
if(this.props.time !== prevProps.time) {
// only show data in requested time
let data = this.props.data;
const firstTimestamp = data[0][data[0].length - 1].x - (nextProps.time + 1) * 1000;
if (data[0][0].x < firstTimestamp) {
// only show data in range (+100 ms)
const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
data = data.map(values => values.slice(index));
const firstTimestamp = data[0][data[0].length - 1].x - (this.props.time + 1) * 1000;
if (data[0][0].x < firstTimestamp) {
// only show data in range (+100 ms)
const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
data = data.map(values => values.slice(index));
}
this.setState({data, labelMargin});
}
this.setState({ data, labelMargin });
}
createInterval() {

View file

@ -0,0 +1,4 @@
import ArrayStore from '../common/array-store';
import WidgetsDataManager from './widgets-data-manager';
export default new ArrayStore('widgets', WidgetsDataManager);

View file

@ -23,7 +23,7 @@ import React from 'react';
import { Container } from 'flux/utils';
import AppDispatcher from '../common/app-dispatcher';
import UserStore from '../user/user-store';
import LoginStore from '../user/login-store';
import SimulatorDataStore from '../simulator/simulator-data-store';
import SimulationModelStore from '../simulationmodel/simulation-model-store';
import FileStore from '../file/file-store';
@ -54,10 +54,11 @@ import '../styles/widgets.css';
class Widget extends React.Component {
static getStores() {
return [ SimulatorDataStore, SimulationModelStore, FileStore, UserStore ];
return [ SimulatorDataStore, SimulationModelStore, FileStore, LoginStore ];
}
static calculateState(prevState, props) {
let simulatorData = {};
if (props.paused) {
@ -75,7 +76,7 @@ class Widget extends React.Component {
sequence: prevState != null ? prevState.sequence + 1 : 0,
sessionToken: UserStore.getState().token
sessionToken: LoginStore.getState().token
};
}
@ -84,14 +85,16 @@ class Widget extends React.Component {
return;
}
AppDispatcher.dispatch({
/*AppDispatcher.dispatch({
type: 'files/start-load',
token: this.state.sessionToken
});
token: this.state.sessionToken,
param: '?objectID=1&objectType=widget'
});*/
AppDispatcher.dispatch({
type: 'simulationModels/start-load',
token: this.state.sessionToken
token: this.state.sessionToken,
param: '?scenarioID=1'
});
}
@ -162,13 +165,13 @@ class Widget extends React.Component {
return null;
}
rn
render() {
const element = this.createWidget(this.props.data);
if (this.props.editing) {
return <EditableWidgetContainer widget={this.props.data} grid={this.props.grid} index={this.props.index}>
return <EditableWidgetContainer widget={this.props.data} grid={this.props.grid} index={this.props.index} onWidgetChange={this.props.onWidgetChange}>
{element}
</EditableWidgetContainer>;
}

View file

@ -0,0 +1,3 @@
import RestDataManager from '../common/data-managers/rest-data-manager';
export default new RestDataManager('widget', '/widgets');

View file

@ -30,9 +30,9 @@ class WidgetBox extends Component {
let colors = EditWidgetColorControl.ColorPalette;
let colorStyle = {
borderColor: colors[this.props.widget.border_color],
backgroundColor: colors[this.props.widget.background_color],
opacity: this.props.widget.background_color_opacity
borderColor: colors[this.props.widget.customProperties.border_color],
backgroundColor: colors[this.props.widget.customProperties.background_color],
opacity: this.props.widget.customProperties.background_color_opacity
}
return (

View file

@ -34,20 +34,22 @@ class WidgetButton extends Component {
}
onPress(e) {
if (!this.props.widget.toggle) {
console.log("button was pressed!");
if (!this.props.widget.customProperties.toggle) {
this.setState({ pressed: true });
this.valueChanged(this.props.widget.on_value);
this.valueChanged(this.props.widget.customProperties.on_value);
}
}
onRelease(e) {
console.log("button was released!");
let nextState = false;
if (this.props.widget.toggle) {
if (this.props.widget.customProperties.toggle) {
nextState = !this.state.pressed;
}
this.setState({ pressed: nextState });
this.valueChanged(nextState ? this.props.widget.on_value : this.props.widget.off_value);
this.valueChanged(nextState ? this.props.widget.customProperties.on_value : this.props.widget.customProperties.off_value);
}
valueChanged(newValue) {

View file

@ -23,7 +23,7 @@
import React, { Component } from 'react';
import { Button } from 'react-bootstrap';
import Icon from '../../common/icon';
import UserStore from '../../user/user-store';
import LoginStore from '../../user/login-store';
import SimulatorStore from '../../simulator/simulator-store';
import AppDispatcher from '../../common/app-dispatcher';
@ -37,24 +37,24 @@ class WidgetCustomAction extends Component {
}
static getStores() {
return [ SimulatorStore ];
return [ SimulatorStore, LoginStore ];
}
componentWillReceiveProps(props) {
static getDerivedStateFromProps(props, state){
if (props.simulationModel === null)
return;
return null; //no change
this.setState({
return{
simulator: SimulatorStore.getState().find(s => s._id === props.simulationModel.simulator),
sessionToken: UserStore.getState().token
});
sessionToken: LoginStore.getState().token
};
}
onClick() {
AppDispatcher.dispatch({
type: 'simulators/start-action',
simulator: this.state.simulator,
data: this.props.widget.actions,
data: this.props.widget.customProperties.actions,
token: this.state.sessionToken
});
}
@ -62,7 +62,7 @@ class WidgetCustomAction extends Component {
render() {
return <div className="widget-custom-action full">
<Button className="full" disabled={this.state.simulator === null} onClick={(e) => this.onClick()}>
<Icon icon={this.props.widget.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
<Icon icon={this.props.widget.customProperties.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
</Button>
</div>;
}

View file

@ -22,6 +22,7 @@
import React, { Component } from 'react';
import { Gauge } from 'gaugeJS';
//import {update} from "immutable";
class WidgetGauge extends Component {
constructor(props) {
@ -33,7 +34,7 @@ class WidgetGauge extends Component {
this.state = {
value: 0,
minValue: null,
maxValue: null
maxValue: null,
};
}
@ -47,99 +48,122 @@ class WidgetGauge extends Component {
//this.updateLabels(this.state.minValue, this.state.maxValue);
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
this.setState({ value: 0 });
return;
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
if(prevState.minValue !== this.state.minValue){
this.gauge.setMinValue(this.state.minValue);
}
if(prevState.maxValue !== this.state.maxValue){
this.gauge.maxValue = this.state.maxValue
}
const simulator = nextProps.simulationModel.simulator;
// update gauge's value
if(prevState.value !== this.state.value){
this.gauge.set(this.state.value)
}
// update labels
if(prevState.minValue !== this.state.minValue || prevState.maxValue !== this.state.maxValue){
this.updateLabels(this.state.minValue, this.state.maxValue)
}
}
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return{value:0};
}
const simulator = props.simulationModel.simulator;
// update value
if (nextProps.data == null || nextProps.data[simulator] == null
|| nextProps.data[simulator].output == null
|| nextProps.data[simulator].output.values == null
|| nextProps.data[simulator].output.values.length === 0
|| nextProps.data[simulator].output.values[0].length === 0) {
this.setState({ value: 0 });
return;
if (props.data == null
|| props.data[simulator] == null
|| props.data[simulator].output == null
|| props.data[simulator].output.values == null
|| props.data[simulator].output.values.length === 0
|| props.data[simulator].output.values[0].length === 0) {
return{value:0};
}
// memorize if min or max value is updated
let updateValue = false;
let updateMinValue = false;
let updateMaxValue = false;
// check if value has changed
const signal = nextProps.data[simulator].output.values[nextProps.widget.signal];
const signal = props.data[simulator].output.values[props.widget.customProperties.signal];
// Take just 3 decimal positions
// Note: Favor this method over Number.toFixed(n) in order to avoid a type conversion, since it returns a String
if (signal != null) {
const value = Math.round(signal[signal.length - 1].y * 1e3) / 1e3;
if (this.state.value !== value && value != null) {
this.setState({ value });
let minValue = null;
let maxValue = null;
if (state.value !== value && value != null) {
//value has changed
updateValue = true;
// update min-max if needed
let updateLabels = false;
let minValue = this.state.minValue;
let maxValue = this.state.maxValue;
minValue = state.minValue;
maxValue = state.maxValue;
if (minValue == null) {
minValue = value - 0.5;
updateLabels = true;
this.setState({ minValue });
this.gauge.setMinValue(minValue);
updateMinValue = true;
}
if (maxValue == null) {
maxValue = value + 0.5;
updateLabels = true;
this.setState({ maxValue });
this.gauge.maxValue = maxValue;
updateMaxValue = true;
}
if (nextProps.widget.valueUseMinMax) {
if (this.state.minValue > nextProps.widget.valueMin) {
minValue = nextProps.widget.valueMin;
this.setState({ minValue });
this.gauge.setMinValue(minValue);
if (props.widget.customProperties.valueUseMinMax) {
if (state.minValue > props.widget.customProperties.valueMin) {
minValue = props.widget.customProperties.valueMin;
updateMinValue = true;
updateLabels = true;
}
if (this.state.maxValue < nextProps.widget.valueMax) {
maxValue = nextProps.widget.valueMax;
this.setState({ maxValue });
this.gauge.maxValue = maxValue;
if (state.maxValue < props.widget.customProperties.valueMax) {
maxValue = props.widget.customProperties.valueMax;
updateMaxValue = true;
updateLabels = true;
}
}
if (updateLabels === false) {
// check if min/max changed
if (minValue > this.gauge.minValue) {
minValue = this.gauge.minValue;
updateLabels = true;
this.setState({ minValue });
if (minValue > state.gauge.minValue) {
minValue = state.gauge.minValue;
updateMinValue = true;
}
if (maxValue < this.gauge.maxValue) {
maxValue = this.gauge.maxValue;
updateLabels = true;
this.setState({ maxValue });
if (maxValue < state.gauge.maxValue) {
maxValue = state.gauge.maxValue;
updateMaxValue = true;
}
}
if (updateLabels) {
this.updateLabels(minValue, maxValue);
}
// update gauge's value
this.gauge.set(value);
}
}
// prepare returned state
let returnState = null;
if(updateValue === true){
returnState["value"] = value;
}
if(updateMinValue === true){
returnState["minValue"] = minValue;
}
if(updateMaxValue === true){
returnState["maxValue"] = maxValue;
}
return returnState
} // if there is a signal
}
updateLabels(minValue, maxValue, force) {
@ -153,7 +177,7 @@ class WidgetGauge extends Component {
}
// calculate zones
let zones = this.props.widget.colorZones ? this.props.widget.zones : null;
let zones = this.props.widget.customProperties.colorZones ? this.props.widget.customProperties.zones : null;
if (zones != null) {
// adapt range 0-100 to actual min-max
const step = (maxValue - minValue) / 100;
@ -197,7 +221,7 @@ class WidgetGauge extends Component {
let signalType = null;
if (this.props.simulationModel != null) {
signalType = (this.props.simulationModel != null && this.props.simulationModel.outputLength > 0 && this.props.widget.signal < this.props.simulationModel.outputLength) ? this.props.simulationModel.outputMapping[this.props.widget.signal].type : '';
signalType = (this.props.simulationModel != null && this.props.simulationModel.outputLength > 0 && this.props.widget.customProperties.signal < this.props.simulationModel.outputLength) ? this.props.simulationModel.outputMapping[this.props.widget.customProperties.signal].type : '';
}
return (

View file

@ -23,7 +23,7 @@ import React from 'react';
class WidgetHTML extends React.Component {
render() {
return <div dangerouslySetInnerHTML={{__html: this.props.widget.content }} />
return <div dangerouslySetInnerHTML={{__html: this.props.widget.customProperties.content }} />
}
}

View file

@ -25,20 +25,21 @@ import AppDispatcher from '../../common/app-dispatcher';
import config from '../../config';
class WidgetImage extends React.Component {
componentWillReceiveProps(nextProps) {
componentDidMount() {
// Query the image referenced by the widget
let widgetFile = nextProps.widget.file;
if (widgetFile && !nextProps.files.find(file => file._id === widgetFile)) {
let widgetFile = this.props.widget.customProperties.file;
if (widgetFile && !this.props.files.find(file => file.id === widgetFile)) {
AppDispatcher.dispatch({
type: 'files/start-load',
data: widgetFile,
token: nextProps.token
token: this.props.token
});
}
}
render() {
const file = this.props.files.find(file => file._id === this.props.widget.file);
const file = this.props.files.find(file => file._id === this.props.widget.customProperties.file);
return (
<div className="full">

View file

@ -34,24 +34,25 @@ class WidgetInput extends Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return null;
}
let returnState = null;
// Update value
if (nextProps.widget.default_value && this.state.value === undefined) {
this.setState({
value: nextProps.widget.default_value
});
if (props.widget.customProperties.default_value && this.state.value === undefined) {
returnState["value"] = props.widget.customProperties.default_value;
}
// Update unit
if (nextProps.widget.simulationModel && nextProps.simulationModel.inputMapping && this.state.unit !== nextProps.simulationModel.inputMapping[nextProps.widget.signal].type) {
this.setState({
unit: nextProps.simulationModel.inputMapping[nextProps.widget.signal].type
});
if (props.widget.customProperties.simulationModel
&& props.simulationModel.inputMapping
&& state.unit !== props.simulationModel.inputMapping[props.widget.customProperties.signal].type) {
returnState["unit"] = props.simulationModel.inputMapping[props.widget.customProperties.signal].type;
}
return returnState;
}
valueIsChanging(newValue) {
@ -75,7 +76,7 @@ class WidgetInput extends Component {
<div className="number-input-widget full">
<Form componentClass="fieldset" horizontal>
<FormGroup>
<Col componentClass={FormLabel} xs={3}>
<Col as={FormLabel} xs={3}>
{this.props.widget.name}
</Col>
<Col xs={9}>

View file

@ -25,9 +25,9 @@ import EditWidgetColorControl from '../edit-widget-color-control';
class WidgetLabel extends Component {
render() {
const style = {
fontSize: this.props.widget.textSize + 'px',
color: EditWidgetColorControl.ColorPalette[this.props.widget.fontColor]
const style = {
fontSize: this.props.widget.customProperties.textSize + 'px',
color: EditWidgetColorControl.ColorPalette[this.props.widget.customProperties.fontColor]
};
return (

View file

@ -33,35 +33,38 @@ class WidgetLamp extends Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
this.setState({ value: '' });
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return{ value: ''};
}
const simulator = nextProps.simulationModel.simulator;
const simulator = props.simulationModel.simulator;
// update value
if (nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].output == null || nextProps.data[simulator].output.values == null) {
this.setState({ value: '' });
return;
if (props.data == null
|| props.data[simulator] == null
|| props.data[simulator].output == null
|| props.data[simulator].output.values == null) {
return{value:''};
}
// check if value has changed
const signal = nextProps.data[simulator].output.values[nextProps.widget.signal];
if (signal != null && this.state.value !== signal[signal.length - 1].y) {
this.setState({ value: signal[signal.length - 1].y });
const signal = props.data[simulator].output.values[props.widget.customProperties.signal];
if (signal != null && state.value !== signal[signal.length - 1].y) {
return { value: signal[signal.length - 1].y };
}
return null;
}
render() {
let colors = EditWidgetColorControl.ColorPalette;
let color;
if (Number(this.state.value) > Number(this.props.widget.threshold))
color = colors[this.props.widget.on_color];
if (Number(this.state.value) > Number(this.props.widget.customProperties.threshold))
color = colors[this.props.widget.customProperties.on_color];
else
color = colors[this.props.widget.off_color];
color = colors[this.props.widget.customProperties.off_color];
let style = {
backgroundColor: color,

View file

@ -36,25 +36,25 @@ class WidgetPlotTable extends Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
if (this.props.simulationModel == null) {
return;
}
// Update internal selected signals state with props (different array objects)
if (this.props.widget.signals !== nextProps.widget.signals) {
this.setState( {signals: nextProps.widget.signals});
if (prevProps.widget.customProperties.signals !== this.props.widget.customProperties.signals) {
this.setState( {signals: this.props.widget.customProperties.signals});
}
// Identify if there was a change in the preselected signals
if (JSON.stringify(nextProps.widget.preselectedSignals) !== JSON.stringify(this.props.widget.preselectedSignals) || this.state.preselectedSignals.length === 0) {
// Update the currently selected signals by intersecting with the preselected signals
if (JSON.stringify(prevProps.widget.customProperties.preselectedSignals) !== JSON.stringify(this.props.widget.customProperties.preselectedSignals)
|| this.state.preselectedSignals.length === 0) {
// Update the currently selected signals by intersecting with the preselected signalsWidget
// Do the same with the plot values
var intersection = this.computeIntersection(nextProps.widget.preselectedSignals, nextProps.widget.signals);
var intersection = this.computeIntersection(this.props.widget.customProperties.preselectedSignals, this.props.widget.customProperties.signals);
this.setState({ signals: intersection });
this.updatePreselectedSignalsState(nextProps);
return;
this.updatePreselectedSignalsState(this.props);
}
}
@ -63,13 +63,14 @@ class WidgetPlotTable extends Component {
return preselectedSignals.filter( s => selectedSignals.includes(s));
}
updatePreselectedSignalsState(nextProps) {
updatePreselectedSignalsState(props) {
// Create checkboxes using the signal indices from simulation model
const preselectedSignals = nextProps.simulationModel.outputMapping.reduce(
if(props.simulationModel.outputMapping){
const preselectedSignals = props.simulationModel.outputMapping.reduce(
// Loop through simulation model signals
(accum, model_signal, signal_index) => {
// Append them if they belong to the current selected type
if (nextProps.widget.preselectedSignals.indexOf(signal_index) > -1) {
if (props.widget.customProperties.preselectedSignals.indexOf(signal_index) > -1) {
accum.push(
{
index: signal_index,
@ -82,6 +83,7 @@ class WidgetPlotTable extends Component {
}, []);
this.setState({ preselectedSignals });
}
}
updateSignalSelection(signal_index, checked) {
@ -105,7 +107,7 @@ class WidgetPlotTable extends Component {
if (this.props.data[simulator] != null && this.props.data[simulator].output != null && this.props.data[simulator].output.values != null) {
simulatorData = this.props.data[simulator].output.values.filter((values, index) => (
this.props.widget.signals.findIndex(value => value === index) !== -1
this.props.widget.customProperties.signals.findIndex(value => value === index) !== -1
));
}
@ -150,14 +152,14 @@ class WidgetPlotTable extends Component {
<div className="widget-plot">
<Plot
data={simulatorData}
time={this.props.widget.time}
time={this.props.widget.customProperties.time}
width={this.props.widget.width - 100}
height={this.props.widget.height - 55}
yMin={this.props.widget.yMin}
yMax={this.props.widget.yMax}
yUseMinMax={this.props.widget.yUseMinMax}
yMin={this.props.widget.customProperties.yMin}
yMax={this.props.widget.customProperties.yMax}
yUseMinMax={this.props.widget.customProperties.yUseMinMax}
paused={this.props.paused}
yLabel={this.props.widget.ylabel}
yLabel={this.props.widget.customProperties.ylabel}
/>
</div>
</div>

View file

@ -34,24 +34,28 @@ class WidgetPlot extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
this.setState({ data: [], legend: [] });
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return{
data: [],
legend: [],
};
}
const simulator = nextProps.simulationModel.simulator;
const simulator = props.simulationModel.simulator;
// Proceed if a simulation with models and a simulator are available
if (simulator && nextProps.data[simulator] != null && nextProps.data[simulator] != null && nextProps.data[simulator].output != null && nextProps.data[simulator].output.values != null) {
const chosenSignals = nextProps.widget.signals;
if (simulator && props.data[simulator] != null && props.data[simulator] != null && props.data[simulator].output != null && props.data[simulator].output.values != null) {
const chosenSignals = props.widget.customProperties.signals;
const data = nextProps.data[simulator].output.values.filter((values, index) => (
nextProps.widget.signals.findIndex(value => value === index) !== -1
const data = props.data[simulator].output.values.filter((values, index) => (
props.widget.customProperties.signals.findIndex(value => value === index) !== -1
));
// Query the signals that will be displayed in the legend
const legend = nextProps.simulationModel.outputMapping.reduce( (accum, model_signal, signal_index) => {
const legend = props.simulationModel.outputMapping.reduce( (accum, model_signal, signal_index) => {
if (chosenSignals.includes(signal_index)) {
accum.push({ index: signal_index, name: model_signal.name, type: model_signal.type });
}
@ -59,10 +63,17 @@ class WidgetPlot extends React.Component {
return accum;
}, []);
this.setState({ data, legend });
return{
data: data,
legend: legend,
};
} else {
this.setState({ data: [], legend: [] });
return{
data: [],
legend: [],
};
}
}
render() {
@ -72,12 +83,12 @@ class WidgetPlot extends React.Component {
data={this.state.data}
height={this.props.widget.height - 55}
width={this.props.widget.width - 20}
time={this.props.widget.time}
yMin={this.props.widget.yMin}
yMax={this.props.widget.yMax}
yUseMinMax={this.props.widget.yUseMinMax}
time={this.props.widget.customProperties.time}
yMin={this.props.widget.customProperties.yMin}
yMax={this.props.widget.customProperties.yMax}
yUseMinMax={this.props.widget.customProperties.yUseMinMax}
paused={this.props.paused}
yLabel={this.props.widget.ylabel}
yLabel={this.props.widget.customProperties.ylabel}
/>
</div>
<PlotLegend signals={this.state.legend} />

View file

@ -39,32 +39,43 @@ class WidgetSlider extends Component {
super(props);
this.state = {
unit: ''
unit: 'bla',
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return null;
}
let returnState = {};
// Update value
if (nextProps.widget.default_value && this.state.value === undefined) {
this.setState({
value: nextProps.widget.default_value,
});
if (props.widget.customProperties.default_value && state.value === undefined) {
returnState["value"] = props.widget.customProperties.default_value;
}
// Update unit
if (nextProps.widget.simulationModel && nextProps.simulationModel.inputMapping && this.state.unit !== nextProps.simulationModel.inputMapping[nextProps.widget.signal].type) {
this.setState({
unit: nextProps.simulationModel.inputMapping[nextProps.widget.signal].type
});
if (props.widget.customProperties.simulationModel
&& props.simulationModel.inputMapping &&
state.unit !== props.simulationModel.inputMapping[props.widget.customProperties.signal].type) {
returnState["unit"] = props.simulationModel.inputMapping[props.widget.customProperties.signal].type;
}
if (returnState !== {}){
return returnState;
}
else{
return null;
}
}
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
// Check if the orientation changed, update the size if it did
if (this.props.widget.orientation !== nextProps.widget.orientation) {
let baseWidget = nextProps.widget;
if (this.props.widget.customProperties.orientation !== prevProps.widget.customProperties.orientation) {
let baseWidget = this.props.widget;
// Exchange dimensions and constraints
let newWidget = Object.assign({}, baseWidget, {
@ -72,12 +83,13 @@ class WidgetSlider extends Component {
height: baseWidget.width,
minWidth: baseWidget.minHeight,
minHeight: baseWidget.minWidth,
maxWidth: baseWidget.maxHeight,
maxHeight: baseWidget.maxWidth
maxWidth: baseWidget.customProperties.maxHeight,
maxHeight: baseWidget.customProperties.maxWidth
});
nextProps.onWidgetChange(newWidget);
this.props.onWidgetChange(newWidget);
}
}
valueIsChanging(newValue) {
@ -94,11 +106,11 @@ class WidgetSlider extends Component {
}
render() {
let isVertical = this.props.widget.orientation === WidgetSlider.OrientationTypes.VERTICAL.value;
let isVertical = this.props.widget.customProperties.orientation === WidgetSlider.OrientationTypes.VERTICAL.value;
let fields = {
name: this.props.widget.name,
control: <Slider min={ this.props.widget.rangeMin } max={ this.props.widget.rangeMax } step={ this.props.widget.step } value={ this.state.value } disabled={ this.props.editing } vertical={ isVertical } onChange={ (v) => this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>,
control: <Slider min={ this.props.widget.customProperties.rangeMin } max={ this.props.widget.customProperties.rangeMax } step={ this.props.widget.customProperties.step } value={ this.state.value } disabled={ this.props.editing } vertical={ isVertical } onChange={ (v) => this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>,
value: <span>{ format('.3s')(Number.parseFloat(this.state.value)) }</span>,
unit: <span className="signal-unit">{ this.state.unit }</span>
}
@ -111,7 +123,7 @@ class WidgetSlider extends Component {
});
return (
this.props.widget.orientation === WidgetSlider.OrientationTypes.HORIZONTAL.value? (
!isVertical? (
<div className={widgetClasses}>
<label>{ fields.name }</label>
<div className='slider'>{ fields.control }</div>
@ -119,10 +131,7 @@ class WidgetSlider extends Component {
</div>
) : (
<div className={widgetClasses}>
<label>{ fields.name }</label>
{ fields.control }
{ fields.value }
{ this.props.widget.showUnit && fields.unit }
<Slider vertical min={ this.props.widget.customProperties.rangeMin } max={ this.props.widget.customProperties.rangeMax } step={ this.props.widget.customProperties.step } value={ this.state.value } disabled={ this.props.editing } onChange={ (v) => this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>,
</div>
)
);
@ -130,3 +139,17 @@ class WidgetSlider extends Component {
}
export default WidgetSlider;
/*!isVertical? (
<div className={widgetClasses}>
<label>{ fields.name }</label>
<div className='slider'>{ fields.control }</div>
<span>{ fields.value }</span>
</div>
) : (
<div className={widgetClasses}>
<label>{ fields.name }</label>
{ fields.control }
{ fields.value }
{ this.props.widget.customProperties.showUnit && fields.unit }
</div>
)*/

View file

@ -36,45 +36,55 @@ class WidgetTable extends Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
this.setState({ rows: [], sequence: null });
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return{
rows: [],
sequence: null,
};
}
const simulator = nextProps.simulationModel.simulator;
const simulator = props.simulationModel.simulator;
// check data
if (nextProps.data == null
|| nextProps.data[simulator] == null
|| nextProps.data[simulator].output == null
|| nextProps.data[simulator].output.values.length === 0
|| nextProps.data[simulator].output.values[0].length === 0) {
if (props.data == null
|| props.data[simulator] == null
|| props.data[simulator].output == null
|| props.data[simulator].output.values.length === 0
|| props.data[simulator].output.values[0].length === 0) {
// clear values
this.setState({ rows: [], sequence: null, showUnit: false });
return;
return{
rows: [],
sequence: null,
showUnit: false,
};
}
// check if new data, otherwise skip
/*if (this.state.sequence >= nextProps.data[simulator.node][simulator.simulator].sequence) {
/*if (state.sequence >= props.data[simulator.node][simulator.simulator].sequence) {
return;
}*/
// get rows
const rows = [];
nextProps.data[simulator].output.values.forEach((signal, index) => {
if (index < nextProps.simulationModel.outputMapping.length) {
props.data[simulator].output.values.forEach((signal, index) => {
if (index < props.simulationModel.outputMapping.length) {
rows.push({
name: nextProps.simulationModel.outputMapping[index].name,
unit: nextProps.simulationModel.outputMapping[index].type,
name: props.simulationModel.outputMapping[index].name,
unit: props.simulationModel.outputMapping[index].type,
value: signal[signal.length - 1].y
});
}
});
this.setState({ showUnit: nextProps.showUnit, rows: rows, sequence: nextProps.data[simulator].output.sequence });
return {
showUnit: props.showUnit,
rows: rows,
sequence: props.data[simulator].output.sequence
};
}
render() {
@ -83,7 +93,7 @@ class WidgetTable extends Component {
<TableColumn key={2} title="Value" dataKey="value" modifier={format('.4s')} />
];
if (this.props.widget.showUnit)
if (this.props.widget.customProperties.showUnit)
columns.push(<TableColumn key={3} title="Unit" dataKey="unit" />)
return (

View file

@ -28,12 +28,12 @@ import { cimsvg } from 'libcimsvg';
// Do not show Pintura's grid
const pinturaGridStyle = {
display: 'none'
}
};
// Avoid another color in the frontend
const pinturaBackingStyle = {
fill: 'transparent'
}
};
// Center spinner
const spinnerContainerStyle = {
@ -42,16 +42,16 @@ const spinnerContainerStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
};
// Topology failed message
const msgContainerStyle = Object.assign({
backgroundColor: '#ececec'
},spinnerContainerStyle)
},spinnerContainerStyle);
const msgStyle = {
fontWeight: 'bold'
}
};
// Initialize functions
function attachComponentEvents() {
@ -105,10 +105,12 @@ class WidgetTopology extends React.Component {
detachComponentEvents();
}
componentWillReceiveProps(nextProps) {
const file = nextProps.files.find(file => file._id === nextProps.widget.file);
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
const file = this.props.files.find(file => file._id === this.props.widget.customProperties.file);
// Ensure model is requested only once or a different was selected
if (this.props.widget.file !== nextProps.widget.file || (this.state.dashboardState === 'initial' && file)) {
if (prevProps.widget.customProperties.file !== this.props.widget.customProperties.file
|| (prevState.dashboardState === 'initial' && file)) {
this.setState({'dashboardState': 'loading' });
if (file) {
fetch(new Request('/' + config.publicPathBase + file.path))
@ -141,7 +143,7 @@ class WidgetTopology extends React.Component {
}
} else {
// No file has been selected
if (!nextProps.widget.file) {
if (!this.props.widget.customProperties.file) {
this.setState({
'dashboardState': 'show_message',
'message': 'Select a topology model first.'});

View file

@ -32,37 +32,41 @@ class WidgetValue extends Component {
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.simulationModel == null) {
this.setState({ value: '' });
return;
static getDerivedStateFromProps(props, state){
if (props.simulationModel == null) {
return{ value: '' };
}
const simulator = nextProps.simulationModel.simulator;
const simulator = props.simulationModel.simulator;
// update value
if (nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].output == null || nextProps.data[simulator].output.values == null) {
this.setState({ value: '' });
return;
if (props.data == null || props.data[simulator] == null || props.data[simulator].output == null || props.data[simulator].output.values == null) {
return{ value: '' };
}
const unit = nextProps.simulationModel.outputMapping[nextProps.widget.signal].type;
// TODO fixme (unit)
//const unit = props.simulationModel.outputMapping[props.widget.customProperties.signal].type;
const unit = 42;
// check if value has changed
const signal = nextProps.data[simulator].output.values[nextProps.widget.signal];
if (signal != null && this.state.value !== signal[signal.length - 1].y) {
this.setState({ value: signal[signal.length - 1].y, unit });
const signal = props.data[simulator].output.values[props.widget.customProperties.signal];
if (signal != null && state.value !== signal[signal.length - 1].y) {
return {
value: signal[signal.length - 1].y,
unit: unit,
};
}
}
render() {
var value_to_render = Number(this.state.value);
return (
<div className="single-value-widget">
<strong style={{ fontSize: this.props.widget.textSize + 'px' }}>{this.props.widget.name}</strong>
<span style={{ fontSize: this.props.widget.textSize + 'px' }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
{this.props.widget.showUnit &&
<span style={{ fontSize: this.props.widget.textSize + 'px' }}>[{this.state.unit}]</span>
<strong style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>{this.props.widget.name}</strong>
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
{this.props.widget.customProperties.showUnit &&
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>[{this.state.unit}]</span>
}
</div>
);