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 branch '168-add-simulation-model-edit-view' into 'develop'

Resolve "Add simulation model edit view"

Closes #168

See merge request acs/public/villas/VILLASweb!34
This commit is contained in:
Markus Grigull 2018-05-30 19:11:32 +02:00
commit b6fa751869
16 changed files with 799 additions and 624 deletions

31
package-lock.json generated
View file

@ -8322,11 +8322,29 @@
}
},
"prop-types": {
"version": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz",
"integrity": "sha512-vCFzoUFaZkVNeFkhK1KbSq4cn97GDrpfBt9K2qLkGnPAEFhEv3M61Lk5t+B7c0QfMLWo0fPkowk/4SuXerh26Q==",
"version": "15.6.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz",
"integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==",
"requires": {
"fbjs": "^0.8.9",
"loose-envify": "^1.3.1"
"fbjs": "^0.8.16",
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
},
"dependencies": {
"fbjs": {
"version": "0.8.16",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
"integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.9"
}
}
}
},
"proxy-addr": {
@ -12309,6 +12327,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
"validator": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-10.2.0.tgz",
"integrity": "sha512-gz/uknWtNfZTj1BLUzYHDxOoiQ7A4wZ6xPuuE6RpxswR4cNyT4I5kN9jmU0AQr7IBEap9vfYChI2TpssTN6Itg=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View file

@ -18,6 +18,7 @@
"gaugeJS": "^1.3.2",
"immutable": "^3.8.1",
"lodash": "^4.17.5",
"prop-types": "^15.6.1",
"rc-slider": "^8.3.0",
"react": "^15.4.2",
"react-bootstrap": "^0.31.1",
@ -34,7 +35,8 @@
"react-scripts": "1.0.10",
"react-sortable-tree": "^0.1.19",
"react-svg-pan-zoom": "^2.14.1",
"superagent": "^3.5.0"
"superagent": "^3.5.0",
"validator": "^10.2.0"
},
"devDependencies": {
"chai": "^4.1.0"

View file

@ -31,7 +31,7 @@ describe('edit widget control creator', () => {
{ args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditImageWidgetControl, EditWidgetAspectControl] } },
{ args: { widgetType: 'Gauge' }, result: { controlNumber: 6, controlTypes: [EditWidgetTextControl, EditWidgetSimulatorControl, EditWidgetSignalControl, EditWidgetCheckboxControl, EditWidgetColorZonesControl, EditWidgetMinMaxControl] } },
{ args: { widgetType: 'PlotTable' }, result: { controlNumber: 5, controlTypes: [EditWidgetSimulatorControl, EditWidgetSignalsControl, EditWidgetTextControl, EditWidgetTimeControl, EditWidgetMinMaxControl] } },
{ args: { widgetType: 'Slider' }, result: { controlNumber: 3, controlTypes: [EditWidgetOrientation, EditWidgetSimulatorControl, EditWidgetSignalControl] } },
{ args: { widgetType: 'Slider' }, result: { controlNumber: 4, controlTypes: [EditWidgetTextControl, EditWidgetOrientation, EditWidgetSimulatorControl, EditWidgetSignalControl] } },
{ args: { widgetType: 'Button' }, result: { controlNumber: 4, controlTypes: [EditWidgetColorControl, EditWidgetSimulatorControl, EditWidgetSignalControl] } },
{ args: { widgetType: 'Box' }, result: { controlNumber: 1, controlTypes: [EditWidgetColorControl] } },
{ args: { widgetType: 'Label' }, result: { controlNumber: 3, controlTypes: [EditWidgetTextControl, EditWidgetTextSizeControl, EditWidgetColorControl] } },

View file

@ -1,186 +0,0 @@
/**
* File: edit-simulation-model.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 04.03.2017
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
import _ from 'lodash';
import Table from '../table';
import TableColumn from '../table-column';
import Dialog from './dialog';
class EditSimulationModelDialog extends React.Component {
valid = false;
constructor(props) {
super(props);
this.state = {
_id: '',
name: '',
simulator: '',
simulation: '',
outputLength: 1,
inputLength: 1,
outputMapping: [{ name: 'Signal', type: 'Type' }],
inputMapping: [{ name: 'Signal', type: 'Type' }]
}
}
onClose(canceled) {
if (canceled === false) {
if (this.valid) {
this.props.onClose(this.state);
}
} else {
this.props.onClose();
}
}
handleChange(e) {
let mapping = null;
if (e.target.id === 'outputLength') {
mapping = this.state.outputMapping;
} else if (e.target.id === 'inputLength') {
mapping = this.state.inputMapping;
}
if (mapping != null) {
// change mapping size
if (e.target.value > mapping.length) {
// add missing signals
while (mapping.length < e.target.value) {
mapping.push({ name: 'Signal', type: 'Type' });
}
} else {
// remove signals
mapping.splice(e.target.value, mapping.length - e.target.value);
}
}
this.setState({ [e.target.id]: e.target.value });
}
handleMappingChange(key, event, row, column) {
const mapping = this.state[key];
if (column === 1) {
mapping[row].name = event.target.value;
} else if (column === 2) {
mapping[row].type = event.target.value;
}
this.setState({ [key]: mapping });
}
resetState() {
this.setState({
_id: this.props.data._id,
simulation: this.props.data.simulation,
name: this.props.data.name,
simulator: this.props.data.simulator,
outputLength: this.props.data.outputLength,
inputLength: this.props.data.inputLength,
outputMapping: this.props.data.outputMapping,
inputMapping: this.props.data.inputMapping
});
}
validateForm(target) {
// check all controls
var name = true;
let inputLength = true;
let outputLength = true;
if (this.state.name === '') {
name = false;
}
// test if simulatorid is a number (in a string, not type of number)
if (!/^\d+$/.test(this.state.outputLength)) {
outputLength = false;
}
if (!/^\d+$/.test(this.state.inputLength)) {
inputLength = false;
}
this.valid = name && inputLength && outputLength;
// return state to control
if (target === 'name') return name ? "success" : "error";
else if (target === 'outputLength') return outputLength ? "success" : "error";
else if (target === 'inputLength') return inputLength ? "success" : "error";
}
render() {
return (
<Dialog show={this.props.show} title="New Simulation Model" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="name" validationState={this.validateForm('name')}>
<ControlLabel>Name</ControlLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="simulator" validationState={this.validateForm('simulator')}>
<ControlLabel>Simulator</ControlLabel>
<FormControl componentClass="select" placeholder="Select simulator" value={this.state.simulator} onChange={(e) => this.handleChange(e)}>
{this.props.simulators.map(simulator => (
<option key={simulator._id} value={simulator._id}>{_.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name')}</option>
))}
</FormControl>
</FormGroup>
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
<ControlLabel>Output Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="outputMapping">
<ControlLabel>Output Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.outputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
</Table>
</FormGroup>
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
<ControlLabel>Input Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="inputMapping">
<ControlLabel>Input Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.inputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
</Table>
</FormGroup>
</form>
</Dialog>
);
}
}
export default EditSimulationModelDialog;

View file

@ -20,194 +20,89 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
import _ from 'lodash';
import Table from '../table';
import TableColumn from '../table-column';
import Dialog from './dialog';
class ImportSimulationModelDialog extends React.Component {
valid = false;
imported = false;
constructor(props) {
super(props);
this.state = {
name: '',
simulator: '',
outputLength: '1',
inputLength: '1',
outputMapping: [ { name: 'Signal', type: 'Type' } ],
inputMapping: [{ name: 'Signal', type: 'Type' }]
model: {}
};
}
onClose(canceled) {
if (canceled === false) {
this.props.onClose(this.state);
} else {
onClose = canceled => {
if (canceled) {
this.props.onClose();
return;
}
this.props.onClose(this.state.model);
}
resetState() {
resetState = () => {
this.setState({
name: '',
simulator: '',
outputLength: '1',
inputLength: '1',
outputMapping: [{ name: 'Signal', type: 'Type' }],
inputMapping: [{ name: 'Signal', type: 'Type' }]
model: {}
});
this.imported = false;
}
handleChange(e) {
let mapping = null;
if (e.target.id === 'outputLength') {
mapping = this.state.outputMapping;
} else if (e.target.id === 'inputLength') {
mapping = this.state.inputMapping;
}
if (mapping != null) {
// change mapping size
if (e.target.value > mapping.length) {
// add missing signals
while (mapping.length < e.target.value) {
mapping.push({ name: 'Signal', type: 'Type' });
}
} else {
// remove signals
mapping.splice(e.target.value, mapping.length - e.target.value);
}
}
this.setState({ [e.target.id]: e.target.value });
}
handleMappingChange(key, event, row, column) {
const mapping = this.state[key];
if (column === 1) {
mapping[row].name = event.target.value;
} else if (column === 2) {
mapping[row].type = event.target.value;
}
this.setState({ [key]: mapping });
}
loadFile(fileList) {
loadFile = event => {
// get file
const file = fileList[0];
if (!file.type.match('application/json')) {
const file = event.target.files[0];
if (file.type.match('application/json') === false) {
return;
}
// create file reader
var reader = new FileReader();
var self = this;
const reader = new FileReader();
const self = this;
reader.onload = function(event) {
// read simulator
reader.onload = event => {
const model = JSON.parse(event.target.result);
model.simulator = this.props.simulators.length > 0 ? this.props.simulators[0]._id : null;
self.imported = true;
self.valid = true;
self.setState({ name: model.name, mapping: model.mapping, length: model.length, simulator: { node: self.props.nodes[0]._id, simulator: 0 } });
this.setState({ model });
};
reader.readAsText(file);
}
validateForm(target) {
// check all controls
var name = true;
let inputLength = true;
let outputLength = true;
var simulator = true;
handleSimulatorChange = event => {
const model = this.state.model;
if (this.state.name === '') {
name = false;
}
model.simulator = event.target.value;
if (this.state.simulator === '') {
simulator = false;
}
// test if simulatorid is a number (in a string, not type of number)
if (!/^\d+$/.test(this.state.outputLength)) {
outputLength = false;
}
if (!/^\d+$/.test(this.state.inputLength)) {
inputLength = false;
}
this.valid = name && inputLength && outputLength && simulator;
// return state to control
if (target === 'name') return name ? "success" : "error";
else if (target === 'outputLength') return outputLength ? "success" : "error";
else if (target === 'inputLength') return inputLength ? "success" : "error";
else if (target === 'simulator') return simulator ? "success" : "error";
this.setState({ model });
}
render() {
return (
<Dialog show={this.props.show} title="Import Simulation Model" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<Dialog show={this.props.show} title="Import Simulation Model" buttonTitle="Import" onClose={this.onClose} onReset={this.resetState} valid={this.imported}>
<form>
<FormGroup controlId="file">
<FormGroup controlId='file'>
<ControlLabel>Simulation Model File</ControlLabel>
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
<FormControl type='file' onChange={this.loadFile} />
</FormGroup>
<FormGroup controlId="name" validationState={this.validateForm('name')}>
<ControlLabel>Name</ControlLabel>
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="simulator">
<FormGroup controlId='simulator'>
<ControlLabel>Simulator</ControlLabel>
<FormControl readOnly={!this.imported} componentClass="select" placeholder="Select simulator" value={this.state.simulator} onChange={(e) => this.handleChange(e)}>
<FormControl disabled={this.imported === false} componentClass='select' placeholder='Select simulator' value={this.state.model.simulator} onChange={this.handleSimulatorChange}>
{this.props.simulators.map(simulator => (
<option key={simulator._id} value={simulator}>{_.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name')}</option>
<option key={simulator._id} value={simulator._id}>{_.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name')}</option>
))}
</FormControl>
</FormGroup>
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
<ControlLabel>Output Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="outputMapping">
<ControlLabel>Output Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.outputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
</Table>
</FormGroup>
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
<ControlLabel>Input Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="inputMapping">
<ControlLabel>Input Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.inputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
</Table>
</FormGroup>
</form>
</Dialog>
);

View file

@ -1,182 +0,0 @@
/**
* File: new-simulation-model.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 04.03.2017
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
import _ from 'lodash';
import Table from '../table';
import TableColumn from '../table-column';
import Dialog from './dialog';
class NewSimulationModelDialog extends React.Component {
valid = false;
constructor(props) {
super(props);
this.state = {
name: '',
simulator: '',
outputLength: '1',
inputLength: '1',
outputMapping: [ { name: 'Signal', type: 'Type' } ],
inputMapping: [ { name: 'Signal', type: 'Type' } ]
};
}
onClose(canceled) {
if (canceled === false) {
if (this.valid) {
this.props.onClose(this.state);
}
} else {
this.props.onClose();
}
}
handleChange(e) {
let mapping = null;
if (e.target.id === 'outputLength') {
mapping = this.state.outputMapping;
} else if (e.target.id === 'inputLength') {
mapping = this.state.inputMapping;
}
if (mapping != null) {
// change mapping size
if (e.target.value > mapping.length) {
// add missing signals
while (mapping.length < e.target.value) {
mapping.push({ name: 'Signal', type: 'Type' });
}
} else {
// remove signals
mapping.splice(e.target.value, mapping.length - e.target.value);
}
}
this.setState({ [e.target.id]: e.target.value });
}
handleMappingChange(key, event, row, column) {
const mapping = this.state[key];
if (column === 1) {
mapping[row].name = event.target.value;
} else if (column === 2) {
mapping[row].type = event.target.value;
}
this.setState({ [key]: mapping });
}
resetState() {
this.setState({
name: '',
simulator: this.props.simulators[0]._id || '',
outputLength: '1',
inputLength: '1',
outputMapping: [{ name: 'Signal', type: 'Type' }],
inputMapping: [{ name: 'Signal', type: 'Type' }]
});
}
validateForm(target) {
// check all controls
let name = true;
let inputLength = true;
let outputLength = true;
if (this.state.name === '') {
name = false;
}
// test if simulatorid is a number (in a string, not type of number)
if (!/^\d+$/.test(this.state.outputLength)) {
outputLength = false;
}
if (!/^\d+$/.test(this.state.inputLength)) {
inputLength = false;
}
this.valid = name && inputLength && outputLength;
// return state to control
if (target === 'name') return name ? "success" : "error";
else if (target === 'outputLength') return outputLength ? "success" : "error";
else if (target === 'inputLength') return inputLength ? "success" : "error";
}
render() {
return (
<Dialog show={this.props.show} title="New Simulation Model" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="name" validationState={this.validateForm('name')}>
<ControlLabel>Name</ControlLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="simulator">
<ControlLabel>Simulator</ControlLabel>
<FormControl componentClass="select" placeholder="Select simulator" value={this.state.simulator} onChange={(e) => this.handleChange(e)}>
{this.props.simulators.map(simulator => (
<option key={simulator._id} value={simulator._id}>{_.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name')}</option>
))}
</FormControl>
</FormGroup>
<FormGroup controlId="outputLength" validationState={this.validateForm('outputLength')}>
<ControlLabel>Output Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.outputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="outputMapping">
<ControlLabel>Output Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.outputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('outputMapping', event, row, column)} />
</Table>
</FormGroup>
<FormGroup controlId="inputLength" validationState={this.validateForm('inputLength')}>
<ControlLabel>Input Length</ControlLabel>
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.inputLength} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="inputMapping">
<ControlLabel>Input Mapping</ControlLabel>
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
<Table data={this.state.inputMapping}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange('inputMapping', event, row, column)} />
</Table>
</FormGroup>
</form>
</Dialog>
);
}
}
export default NewSimulationModelDialog;

View file

@ -0,0 +1,107 @@
/**
* File: header.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 25.05.2018
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import PropTypes from 'prop-types';
import { Glyphicon, FormControl } from 'react-bootstrap';
class EditableHeader extends React.Component {
titleInput = null;
constructor(props) {
super(props);
this.state = {
editing: false,
title: props.title
};
}
componentWillReceiveProps(nextProps) {
this.setState({ title: nextProps.title });
}
edit = () => {
this.setState({ editing: true });
}
save = () => {
this.setState({ editing: false });
if (this.props.onChange != null) {
this.props.onChange(this.state.title);
}
}
cancel = () => {
this.setState({ editing: false, title: this.props.title });
}
onChange = event => {
this.setState({ title: event.target.value });
}
render() {
const wrapperStyle= {
float: 'left'
};
const glyphStyle = {
float: 'left',
marginLeft: '10px',
marginTop: '25px',
marginBottom: '20px'
};
if (this.state.editing) {
const editStyle = {
maxWidth: '500px',
marginTop: '10px'
};
return <div>
<form style={wrapperStyle}>
<FormControl type='text' bsSize='large' value={this.state.title} onChange={this.onChange} style={editStyle} autoFocus />
</form>
<a onClick={this.save}><Glyphicon glyph='ok' style={glyphStyle} /></a>
<a onClick={this.cancel}><Glyphicon glyph='remove' style={glyphStyle} /></a>
</div>;
}
return <div>
<h1 style={wrapperStyle}>
{this.state.title}
</h1>
<a onClick={this.edit}><Glyphicon glyph='pencil' style={glyphStyle} /></a>
</div>;
}
}
EditableHeader.PropTypes = {
title: PropTypes.string.isRequired,
onChange: PropTypes.func
};
export default EditableHeader;

View file

@ -0,0 +1,125 @@
/**
* File: signalMapping.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 10.08.2018
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import PropTypes from 'prop-types';
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
import validator from 'validator';
import Table from './table';
import TableColumn from './table-column';
class SignalMapping extends React.Component {
constructor(props) {
super(props);
this.state = {
length: props.length,
signals: props.signals
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.length === this.state.length && nextProps.signals === this.state.signals) {
return;
}
this.setState({ length: nextProps.length, signals: nextProps.signals });
}
validateLength(){
const valid = validator.isInt(this.state.length + '', { min: 1, max: 100 });
return valid ? 'success' : 'error';
}
handleLengthChange = event => {
const length = event.target.value;
// update signals to represent length
const signals = this.state.signals;
if (this.state.length < length) {
while (signals.length < length) {
signals.push({ name: 'Signal', type: 'Type' });
}
} else {
signals.splice(length, signals.length - length);
}
// save updated state
this.setState({ length, signals });
if (this.props.onChange != null) {
this.props.onChange(length, signals);
}
}
handleMappingChange = (event, row, column) => {
const signals = this.state.signals;
if (column === 1) {
signals[row].name = event.target.value;
} else if (column === 2) {
signals[row].type = event.target.value;
}
this.setState({ signals });
if (this.props.onChange != null) {
this.props.onChange(this.state.length, signals);
}
}
render() {
return <div>
<FormGroup validationState={this.validateLength()}>
<ControlLabel>{this.props.name} Length</ControlLabel>
<FormControl type='number' placeholder='Enter length' min='1' value={this.state.length} onChange={this.handleLengthChange} />
<FormControl.Feedback />
</FormGroup>
<FormGroup>
<ControlLabel>{this.props.name} Mapping</ControlLabel>
<HelpBlock>Click <i>name</i> or <i>type</i> cell to edit</HelpBlock>
<Table data={this.props.signals}>
<TableColumn title='ID' width='60' dataIndex />
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={this.handleMappingChange} />
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={this.handleMappingChange} />
</Table>
</FormGroup>
</div>;
}
}
SignalMapping.propTypes = {
name: PropTypes.string,
length: PropTypes.number,
signals: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired
})
),
onChange: PropTypes.func
};
export default SignalMapping;

View file

@ -24,15 +24,14 @@ import _ from 'lodash';
import { Table, Button, Glyphicon, FormControl, Label, Checkbox } from 'react-bootstrap';
import { Link } from 'react-router-dom';
//import TableColumn from './table-column';
class CustomTable extends Component {
constructor(props) {
super(props);
this.activeInput = null;
this.state = {
rows: [],
rows: this.getRows(props),
editCell: [ -1, -1 ]
};
}
@ -119,30 +118,9 @@ class CustomTable extends Component {
}
componentWillReceiveProps(nextProps) {
// check if data exists
if (nextProps.data == null) {
this.setState({ rows: [] });
return;
}
const rows = this.getRows(nextProps);
// create row data
var rows = nextProps.data.map((data, index) => {
// check if multiple columns
if (Array.isArray(nextProps.children)) {
var row = [];
nextProps.children.forEach(child => {
row.push(this.addCell(data, index, child));
});
return row;
} else {
// table only has a single column
return [ this.addCell(data, index, nextProps.children) ];
}
});
this.setState({ rows: rows });
this.setState({ rows });
}
componentDidUpdate() {
@ -162,6 +140,28 @@ class CustomTable extends Component {
this.setState({ editCell: [ -1, -1 ] });
}
getRows(props) {
if (props.data == null) {
return [];
}
return props.data.map((data, index) => {
// check if multiple columns
if (Array.isArray(props.children) === false) {
// table only has a single column
return [ this.addCell(data, index, props.children) ];
}
const row = [];
for (let child of props.children) {
row.push(this.addCell(data, index, child));
}
return row;
});
}
render() {
// get children
let children = this.props.children;

View file

@ -45,6 +45,7 @@ import Simulators from './simulators';
import Visualization from './visualization';
import Simulations from './simulations';
import Simulation from './simulation';
import SimulationModel from './simulation-model';
import Users from './users';
import '../styles/app.css';
@ -134,6 +135,7 @@ class App extends React.Component {
<Route path="/visualizations/:visualization" component={Visualization} />
<Route exact path="/simulations" component={Simulations} />
<Route path="/simulations/:simulation" component={Simulation} />
<Route path="/simulationModel/:simulationModel" component={SimulationModel} />
<Route path="/simulators" component={Simulators} />
<Route path="/users" component={Users} />
</div>

View file

@ -0,0 +1,151 @@
/**
* File: selectFile.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 10.05.2018
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { Container } from 'flux/utils';
import { FormGroup, FormControl, ControlLabel, Button, ProgressBar, Col } from 'react-bootstrap';
import FileStore from '../stores/file-store';
import UserStore from '../stores/user-store';
import AppDispatcher from '../app-dispatcher';
class SelectFile extends React.Component {
static getStores() {
return [ FileStore, UserStore ];
}
static calculateState() {
return {
files: FileStore.getState(),
sessionToken: UserStore.getState().token,
selectedFile: '',
uploadFile: null,
uploadProgress: 0
};
}
componentDidMount() {
AppDispatcher.dispatch({
type: 'files/start-load',
token: this.state.sessionToken
});
}
componentWillReceiveProps(nextProps) {
if (nextProps.value === this.state.selectedSimulator) {
return;
}
let selectedSimulator = nextProps.value;
if (selectedSimulator == null) {
if (this.state.simulators.length > 0) {
selectedSimulator = this.state.simulators[0]._id;
} else {
selectedSimulator = '';
}
}
this.setState({ selectedSimulator });
}
handleChange = event => {
this.setState({ selectedFile: event.target.value });
// send file to callback
if (this.props.onChange != null) {
const file = this.state.files.find(f => f._id === event.target.value);
this.props.onChange(file);
}
}
selectUploadFile = event => {
this.setState({ uploadFile: event.target.files[0] });
}
startFileUpload = () => {
// upload file
const formData = new FormData();
formData.append(0, this.state.uploadFile);
AppDispatcher.dispatch({
type: 'files/start-upload',
data: formData,
token: this.state.sessionToken,
progressCallback: this.updateUploadProgress,
finishedCallback: this.clearProgress
});
}
updateUploadProgress = event => {
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
}
clearProgress = () => {
// select uploaded file
const selectedFile = this.state.files[this.state.files.length - 1]._id;
this.setState({ selectedFile, uploadProgress: 0 });
}
render() {
const fileOptions = this.state.files.map(f =>
<option key={f._id} value={f._id}>{f.name}</option>
);
const progressBarStyle = {
marginLeft: '100px',
marginTop: '-25px'
};
return <div>
<FormGroup>
<Col componentClass={ControlLabel} sm={3} md={2}>
{this.props.name}
</Col>
<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>;
}
}
export default Container.create(SelectFile);

View file

@ -0,0 +1,88 @@
/**
* File: selectSimulator.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 10.05.2018
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { Container } from 'flux/utils';
import { FormGroup, FormControl, ControlLabel, Col } from 'react-bootstrap';
import _ from 'lodash';
import SimulatorStore from '../stores/simulator-store';
class SelectSimulator extends React.Component {
static getStores() {
return [ SimulatorStore ];
}
static calculateState() {
return {
simulators: SimulatorStore.getState(),
selectedSimulator: ''
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.value === this.state.selectedSimulator) {
return;
}
let selectedSimulator = nextProps.value;
if (selectedSimulator == null) {
if (this.state.simulators.length > 0) {
selectedSimulator = this.state.simulators[0]._id;
} else {
selectedSimulator = '';
}
}
this.setState({ selectedSimulator });
}
handleChange = event => {
this.setState({ selectedSimulator: event.target.value });
// send complete simulator to callback
if (this.props.onChange != null) {
const simulator = this.state.simulators.find(s => s._id === event.target.value);
this.props.onChange(simulator);
}
}
render() {
const simulatorOptions = this.state.simulators.map(s =>
<option key={s._id} value={s._id}>{_.get(s, 'properties.name') || _.get(s, 'rawProperties.name') || s.uuid}</option>
);
return <FormGroup>
<Col componentClass={ControlLabel} sm={3} md={2}>
Simulator
</Col>
<Col sm={9} md={10}>
<FormControl componentClass='select' placeholder='Select simulator' value={this.state.selectedSimulator} onChange={this.handleChange}>
{simulatorOptions}
</FormControl>
</Col>
</FormGroup>;
}
}
export default Container.create(SelectSimulator);

View file

@ -0,0 +1,151 @@
/**
* File: simulationModel.js
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
* Date: 10.05.2018
*
* This file is part of VILLASweb.
*
* VILLASweb is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VILLASweb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { Container } from 'flux/utils';
import { Button, Col, Form } from 'react-bootstrap';
import SimulationModelStore from '../stores/simulation-model-store';
import UserStore from '../stores/user-store';
import AppDispatcher from '../app-dispatcher';
import SelectSimulator from './select-simulator';
import SelectFile from './select-file';
import SignalMapping from '../components/signal-mapping';
import EditableHeader from '../components/editable-header';
class SimulationModel extends React.Component {
static getStores() {
return [ SimulationModelStore, UserStore ];
}
static calculateState(prevState, props) {
const simulationModel = SimulationModelStore.getState().find(m => m._id === props.match.params.simulationModel);
return {
simulationModel: simulationModel || {},
sessionToken: UserStore.getState().token
};
}
componentWillMount() {
AppDispatcher.dispatch({
type: 'simulationModels/start-load',
data: this.props.match.params.simulationModel,
token: this.state.sessionToken
});
}
submitForm = event => {
event.preventDefault();
}
saveChanges = () => {
AppDispatcher.dispatch({
type: 'simulationModels/start-edit',
data: this.state.simulationModel,
token: this.state.sessionToken
});
this.props.history.push('/simulations/' + this.state.simulationModel.simulation);
}
discardChanges = () => {
this.props.history.push('/simulations/' + this.state.simulationModel.simulation);
}
handleSimulatorChange = simulator => {
const simulationModel = this.state.simulationModel;
simulationModel.simulator = simulator;
this.setState({ simulationModel });
}
handleModelChange = file => {
console.log(file);
}
handleConfigurationChange = file => {
console.log(file);
}
handleOutputMappingChange = (length, signals) => {
const simulationModel = this.state.simulationModel;
simulationModel.outputMapping = signals;
simulationModel.outputLength = length;
this.setState({ simulationModel });
}
handleInputMappingChange = (length, signals) => {
const simulationModel = this.state.simulationModel;
simulationModel.inputMapping = signals;
simulationModel.inputLength = length;
this.setState({ simulationModel });
}
handleTitleChange = title => {
const simulationModel = this.state.simulationModel;
simulationModel.name = title;
this.setState({ simulationModel });
}
render() {
const buttonStyle = {
marginRight: '10px'
};
return <div className='section'>
<EditableHeader title={this.state.simulationModel.name} onChange={this.handleTitleChange} />
<Form horizontal onSubmit={this.submitForm}>
<Col xs={12} sm={12}>
<SelectSimulator onChange={this.handleSimulatorChange} value={this.state.simulationModel.simulator} />
<SelectFile disabled type='model' name='Model' onChange={this.handleModelChange} value={this.state.simulationModel.model} />
<SelectFile disabled type='configuration' name='Configuration' onChange={this.handleConfigurationChange} value={this.state.simulationModel.configuration} />
</Col>
<Col xs={12} sm={6}>
<SignalMapping name='Output' length={this.state.simulationModel.outputLength} signals={this.state.simulationModel.outputMapping} onChange={this.handleOutputMappingChange} />
</Col>
<Col xs={12} sm={6}>
<SignalMapping name='Input' length={this.state.simulationModel.inputLength} signals={this.state.simulationModel.inputMapping} onChange={this.handleInputMappingChange} />
</Col>
<div style={{ clear: 'both' }}></div>
<Button onClick={this.discardChanges} style={buttonStyle}>Cancel</Button>
<Button bsStyle='primary' onClick={this.saveChanges} style={buttonStyle}>Save</Button>
</Form>
</div>;
}
}
export default Container.create(SimulationModel, { withProps: true });

View file

@ -33,8 +33,6 @@ import AppDispatcher from '../app-dispatcher';
import Table from '../components/table';
import TableColumn from '../components/table-column';
import NewSimulationModelDialog from '../components/dialog/new-simulation-model';
import EditSimulationModelDialog from '../components/dialog/edit-simulation-model';
import ImportSimulationModelDialog from '../components/dialog/import-simulation-model';
import SimulatorAction from '../components/simulator-action';
@ -73,12 +71,9 @@ class Simulation extends React.Component {
simulators: SimulatorStore.getState(),
sessionToken,
newModal: false,
deleteModal: false,
editModal: false,
importModal: false,
modalData: {},
modalIndex: null,
selectedSimulationModels: []
}
@ -101,26 +96,30 @@ class Simulation extends React.Component {
});
}
closeNewModal(data) {
this.setState({ newModal : false });
addSimulationModel = () => {
const simulationModel = {
simulation: this.state.simulation._id,
name: 'New Simulation Model',
simulator: this.state.simulators.length > 0 ? this.state.simulators[0]._id : null,
outputLength: 1,
outputMapping: [{ name: 'Signal', type: 'Type' }],
inputLength: 1,
inputMapping: [{ name: 'Signal', type: 'Type' }]
};
if (data) {
data.simulation = this.state.simulation._id;
AppDispatcher.dispatch({
type: 'simulationModels/start-add',
data: simulationModel,
token: this.state.sessionToken
});
this.setState({ simulation: {} }, () => {
AppDispatcher.dispatch({
type: 'simulationModels/start-add',
data,
type: 'simulations/start-load',
data: this.props.match.params.simulation,
token: this.state.sessionToken
});
this.setState({ simulation: {} }, () => {
AppDispatcher.dispatch({
type: 'simulations/start-load',
data: this.props.match.params.simulation,
token: this.state.sessionToken
});
});
}
});
}
closeDeleteModal = confirmDelete => {
@ -137,38 +136,30 @@ class Simulation extends React.Component {
});
}
closeEditModal(data) {
this.setState({ editModal : false });
if (data) {
AppDispatcher.dispatch({
type: 'simulationModels/start-edit',
data,
token: this.state.sessionToken
});
}
}
closeImportModal(data) {
importSimulationModel = simulationModel => {
this.setState({ importModal: false });
if (data) {
data.simulation = this.state.simulation._id;
if (simulationModel == null) {
return;
}
simulationModel.simulation = this.state.simulation._id;
console.log(simulationModel);
AppDispatcher.dispatch({
type: 'simulationModels/start-add',
data: simulationModel,
token: this.state.sessionToken
});
this.setState({ simulation: {} }, () => {
AppDispatcher.dispatch({
type: 'simulationModels/start-add',
data,
type: 'simulations/start-load',
data: this.props.match.params.simulation,
token: this.state.sessionToken
});
this.setState({ simulation: {} }, () => {
AppDispatcher.dispatch({
type: 'simulations/start-load',
data: this.props.match.params.simulation,
token: this.state.sessionToken
});
});
}
});
}
getSimulatorName(simulatorId) {
@ -182,6 +173,7 @@ class Simulation extends React.Component {
exportModel(index) {
// filter properties
const model = Object.assign({}, this.state.simulationModels[index]);
delete model.simulator;
delete model.simulation;
@ -239,52 +231,52 @@ class Simulation extends React.Component {
}
render() {
return (
<div className='section'>
<h1>{this.state.simulation.name}</h1>
const buttonStyle = {
marginLeft: '10px'
};
<Table data={this.state.simulationModels}>
<TableColumn checkbox onChecked={(index, event) => this.onSimulationModelChecked(index, event)} width='30' />
<TableColumn title='Name' dataKey='name' />
<TableColumn title='Simulator' dataKey='simulator' width='180' modifier={(simulator) => this.getSimulatorName(simulator)} />
<TableColumn title='Output' dataKey='outputLength' width='100' />
<TableColumn title='Input' dataKey='inputLength' width='100' />
<TableColumn
title=''
width='100'
editButton
deleteButton
exportButton
onEdit={(index) => this.setState({ editModal: true, modalData: this.state.simulationModels[index], modalIndex: index })}
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulationModels[index], modalIndex: index })}
onExport={index => this.exportModel(index)}
/>
</Table>
return <div className='section'>
<h1>{this.state.simulation.name}</h1>
<div style={{ float: 'left' }}>
<SimulatorAction
runDisabled={this.state.selectedSimulationModels.length === 0}
runAction={this.runAction}
actions={[
{ id: '0', title: 'Start', data: { action: 'start' } },
{ id: '1', title: 'Stop', data: { action: 'stop' } },
{ id: '2', title: 'Pause', data: { action: 'pause' } },
{ id: '3', title: 'Resume', data: { action: 'resume' } }
]}/>
</div>
<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=''
width='70'
deleteButton
exportButton
onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulationModels[index], modalIndex: index })}
onExport={index => this.exportModel(index)}
/>
</Table>
<div style={{ float: 'right' }}>
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Simulation Model</Button>
<Button onClick={() => this.setState({ importModal: true })}><Glyphicon glyph="import" /> Import</Button>
</div>
<NewSimulationModelDialog show={this.state.newModal} onClose={data => this.closeNewModal(data)} simulators={this.state.simulators} />
<EditSimulationModelDialog show={this.state.editModal} onClose={data => this.closeEditModal(data)} data={this.state.modalData} simulators={this.state.simulators} />
<ImportSimulationModelDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} simulators={this.state.simulators} />
<DeleteDialog title="simulation model" name={this.state.modalData.name} show={this.state.deleteModal} onClose={this.closeDeleteModal} />
<div style={{ float: 'left' }}>
<SimulatorAction
runDisabled={this.state.selectedSimulationModels.length === 0}
runAction={this.runAction}
actions={[
{ id: '0', title: 'Start', data: { action: 'start' } },
{ id: '1', title: 'Stop', data: { action: 'stop' } },
{ id: '2', title: 'Pause', data: { action: 'pause' } },
{ id: '3', title: 'Resume', data: { action: 'resume' } }
]}/>
</div>
);
<div style={{ float: 'right' }}>
<Button onClick={this.addSimulationModel} style={buttonStyle}><Glyphicon glyph="plus" /> Simulation Model</Button>
<Button onClick={() => this.setState({ importModal: true })} style={buttonStyle}><Glyphicon glyph="import" /> Import</Button>
</div>
<div style={{ clear: 'both' }} />
<ImportSimulationModelDialog show={this.state.importModal} onClose={this.importSimulationModel} simulators={this.state.simulators} />
<DeleteDialog title="simulation model" name={this.state.modalData.name} show={this.state.deleteModal} onClose={this.closeDeleteModal} />
</div>;
}
}

View file

@ -30,8 +30,10 @@ class FilesDataManager extends RestDataManager {
upload(file, token = null, progressCallback = null, finishedCallback = null) {
RestAPI.upload(this.makeURL('/upload'), file, token, progressCallback).then(response => {
console.log(response);
AppDispatcher.dispatch({
type: 'files/uploaded'
type: 'files/uploaded',
});
// Trigger a files reload

View file

@ -386,3 +386,8 @@ body {
.section-buttons-group-right .rc-slider {
margin-left: 12px;
}
.form-horizontal .form-group {
margin-left: 0 !important;
margin-right: 0 !important;
}