1
0
Fork 0
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:
Markus Grigull 2017-03-07 11:12:22 +01:00
parent 01488c4939
commit 04e7ea2184
7 changed files with 422 additions and 44 deletions

View 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;

View 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;

View 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;

View file

@ -17,7 +17,9 @@ class TableColumn extends Component {
editButton: false,
deleteButton: false,
link: '/',
linkKey: ''
linkKey: '',
dataIndex: false,
inlineEditable: false
};
render() {

View file

@ -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>

View file

@ -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>

View file

@ -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
*/