mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Add simulation-model route and dialogs
Add inline edit to table
This commit is contained in:
parent
01488c4939
commit
04e7ea2184
7 changed files with 422 additions and 44 deletions
140
src/components/dialog/edit-simulation-model.js
Normal file
140
src/components/dialog/edit-simulation-model.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* File: edit-simulation-model.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 04.03.2017
|
||||
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
import Dialog from './dialog';
|
||||
|
||||
class EditSimulationModelDialog extends Component {
|
||||
static propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
simulators: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
valid: false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
simulator: '',
|
||||
length: 1
|
||||
}
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
} else if (column === 2) {
|
||||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({
|
||||
name: this.props.data.name,
|
||||
simulator: this.props.data.simulator,
|
||||
length: this.props.data.length,
|
||||
mapping: this.props.data.mapping
|
||||
});
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = 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.length)) {
|
||||
length = false;
|
||||
}
|
||||
|
||||
this.valid = name && length;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "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}>{simulator.name}</option>
|
||||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<Table data={this.state.mapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EditSimulationModelDialog;
|
92
src/components/dialog/edit-widget-value.js
Normal file
92
src/components/dialog/edit-widget-value.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* File: edit-widget-value.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 04.03.2017
|
||||
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap';
|
||||
|
||||
import Dialog from './dialog';
|
||||
|
||||
class EditWidgetValueDialog extends Component {
|
||||
static propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
valid: false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
simulator: '',
|
||||
signal: 0
|
||||
}
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '' });
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
|
||||
this.valid = name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
|
||||
return "success";
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="New Visualization" 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">
|
||||
<option value="RTDS">RTDS</option>
|
||||
<option value="Opal">Opal</option>
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="signal" validationState={this.validateForm('signal')}>
|
||||
<ControlLabel>Signal</ControlLabel>
|
||||
<FormControl type="text" placeholder="Enter signal" value={this.state.endpoint} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EditWidgetValueDialog;
|
136
src/components/dialog/new-simulation-model.js
Normal file
136
src/components/dialog/new-simulation-model.js
Normal file
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* File: new-simulation-model.js
|
||||
* Author: Markus Grigull <mgrigull@eonerc.rwth-aachen.de>
|
||||
* Date: 04.03.2017
|
||||
* Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC
|
||||
* This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential.
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap';
|
||||
|
||||
import Table from '../table';
|
||||
import TableColumn from '../table-column';
|
||||
import Dialog from './dialog';
|
||||
|
||||
class NewSimulationModelDialog extends Component {
|
||||
static propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
simulators: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
valid: false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: '',
|
||||
simulator: '',
|
||||
length: '1',
|
||||
mapping: [ { name: 'Signal', type: 'Type' } ]
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
if (canceled === false) {
|
||||
this.props.onClose(this.state);
|
||||
} else {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
if (e.target.id === 'length') {
|
||||
// change mapping size
|
||||
if (e.target.value > this.state.mapping.length) {
|
||||
// add missing signals
|
||||
while (this.state.mapping.length < e.target.value) {
|
||||
this.state.mapping.push({ name: 'Signal', type: 'Type' });
|
||||
}
|
||||
} else {
|
||||
// remove signals
|
||||
this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
handleMappingChange(event, row, column) {
|
||||
var mapping = this.state.mapping;
|
||||
|
||||
if (column === 1) {
|
||||
mapping[row].name = event.target.value;
|
||||
} else if (column === 2) {
|
||||
mapping[row].type = event.target.value;
|
||||
}
|
||||
|
||||
this.setState({ mapping: mapping });
|
||||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '', simulator: '', length: '1', mapping: [ { name: 'Signal', type: 'Type' } ] });
|
||||
}
|
||||
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
var name = true;
|
||||
var length = 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.length)) {
|
||||
length = false;
|
||||
}
|
||||
|
||||
this.valid = name && length;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
else if (target === 'length') return length ? "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" 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}>{simulator.name}</option>
|
||||
))}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup controlId="length" validationState={this.validateForm('length')}>
|
||||
<ControlLabel>Length</ControlLabel>
|
||||
<FormControl type="number" placeholder="Enter length" min="1" value={this.state.length} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="mapping">
|
||||
<ControlLabel>Mapping</ControlLabel>
|
||||
<HelpBlock>Click Name or Type cell to edit</HelpBlock>
|
||||
<Table data={this.state.mapping}>
|
||||
<TableColumn title='ID' width='60' dataIndex />
|
||||
<TableColumn title='Name' dataKey='name' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
<TableColumn title='Type' dataKey='type' inlineEditable onInlineChange={(event, row, column) => this.handleMappingChange(event, row, column)} />
|
||||
</Table>
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewSimulationModelDialog;
|
|
@ -17,7 +17,9 @@ class TableColumn extends Component {
|
|||
editButton: false,
|
||||
deleteButton: false,
|
||||
link: '/',
|
||||
linkKey: ''
|
||||
linkKey: '',
|
||||
dataIndex: false,
|
||||
inlineEditable: false
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -8,16 +8,28 @@
|
|||
**********************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Table, Button, Glyphicon } from 'react-bootstrap';
|
||||
import { Table, Button, Glyphicon, FormControl } from 'react-bootstrap';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import TableColumn from './table-column';
|
||||
|
||||
class CustomTable extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editCell: [ -1, -1 ]
|
||||
};
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
width: null
|
||||
};
|
||||
|
||||
onClick(event, row, column) {
|
||||
this.setState({ editCell: [ column, row ]}); // x, y
|
||||
}
|
||||
|
||||
render() {
|
||||
// create sorted data for rows
|
||||
var rows = [];
|
||||
|
@ -53,6 +65,10 @@ class CustomTable extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.props.children[i].props.dataIndex) {
|
||||
cell.push(index);
|
||||
}
|
||||
|
||||
// add buttons
|
||||
if (this.props.children[i].props.editButton) {
|
||||
const onEdit = this.props.children[i].props.onEdit;
|
||||
|
@ -80,13 +96,19 @@ class CustomTable extends Component {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={index}>
|
||||
{row.map((cell, index) => (
|
||||
<td key={index}>
|
||||
{cell.map((element, index) => (
|
||||
<span key={index}>{element}</span>
|
||||
))}
|
||||
{rows.map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td key={cellIndex} onClick={this.props.children[cellIndex].props.inlineEditable === true ? (event) => this.onClick(event, rowIndex, cellIndex) : () => {}}>
|
||||
{(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex ) ? (
|
||||
<FormControl type="text" value={cell} onChange={(event) => this.props.children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} />
|
||||
) : (
|
||||
<span>
|
||||
{cell.map((element, elementIndex) => (
|
||||
<span key={elementIndex}>{element}</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
|
|
|
@ -34,6 +34,7 @@ class Simulation extends Component {
|
|||
deleteModal: false,
|
||||
editModal: false,
|
||||
modalData: {},
|
||||
modalIndex: null,
|
||||
|
||||
simulation: {}
|
||||
}
|
||||
|
@ -79,24 +80,30 @@ class Simulation extends Component {
|
|||
}
|
||||
|
||||
confirmDeleteModal() {
|
||||
this.setState({ deleteModal: false });
|
||||
// remove model from simulation
|
||||
var simulation = this.state.simulation;
|
||||
simulation.models.splice(this.state.modalIndex, 1);
|
||||
|
||||
this.setState({ deleteModal: false, simulation: simulation });
|
||||
|
||||
|
||||
/*AppDispatcher.dispatch({
|
||||
type: 'visualizations/start-remove',
|
||||
data: this.state.modalVisualization
|
||||
});*/
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-edit',
|
||||
data: simulation
|
||||
});
|
||||
}
|
||||
|
||||
closeEditModal(data) {
|
||||
this.setState({ editModal : false });
|
||||
|
||||
if (data) {
|
||||
/*AppDispatcher.dispatch({
|
||||
type: 'visualizations/start-edit',
|
||||
data: data
|
||||
});*/
|
||||
var simulation = this.state.simulation;
|
||||
simulation.models[this.state.modalIndex] = data;
|
||||
this.setState({ simulation: simulation });
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'simulations/start-edit',
|
||||
data: simulation
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,14 +126,14 @@ class Simulation extends Component {
|
|||
<TableColumn title='Name' dataKey='name' />
|
||||
<TableColumn title='Simulator' dataKey='simulator' width='180' modifier={(id) => this.getSimulatorName(id)} />
|
||||
<TableColumn title='Length' dataKey='length' width='100' />
|
||||
<TableColumn title='' width='70' editButton deleteButton onEdit={(index) => this.setState({ editModal: true, modalData: this.state.simulation.models[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index] })} />
|
||||
<TableColumn title='' width='70' editButton deleteButton onEdit={(index) => this.setState({ editModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} />
|
||||
</Table>
|
||||
|
||||
<Button onClick={() => this.setState({ newModal: true })}><Glyphicon glyph="plus" /> Simulation Model</Button>
|
||||
|
||||
<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} />
|
||||
<EditSimulationModelDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} data={this.state.modalData} simulators={this.state.simulators} />
|
||||
|
||||
<Modal show={this.state.deleteModal}>
|
||||
<Modal.Header>
|
||||
|
|
|
@ -28,7 +28,7 @@ body {
|
|||
|
||||
padding: 10px 0 0 0;
|
||||
|
||||
color: #103B7D;
|
||||
color: #527984;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ body {
|
|||
}
|
||||
|
||||
.app-content {
|
||||
min-height: 200px;
|
||||
min-height: 400px;
|
||||
|
||||
margin: 20px 20px 20px 200px;
|
||||
padding: 15px 20px;
|
||||
|
@ -94,32 +94,11 @@ body {
|
|||
/**
|
||||
* Tables
|
||||
*/
|
||||
.table {
|
||||
margin-top: 20px;
|
||||
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.table th, td {
|
||||
padding: 5px;
|
||||
|
||||
text-align: left;
|
||||
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background-color: #527984;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.table tr:nth-child(even) {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buttons
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue