mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
move IC dialog to new page
This commit is contained in:
parent
468b4c66f8
commit
c5420cc4a5
6 changed files with 248 additions and 219 deletions
|
@ -31,6 +31,7 @@ import Footer from './common/footer';
|
|||
import Menu from './common/menu';
|
||||
|
||||
import InfrastructureComponents from './ic/ics';
|
||||
import InfrastructureComponent from './ic/ic';
|
||||
import Dashboard from './dashboard/dashboard';
|
||||
import Scenarios from './scenario/scenarios';
|
||||
import Scenario from './scenario/scenario';
|
||||
|
@ -117,7 +118,8 @@ class App extends React.Component {
|
|||
<Route exact path="/scenarios" component={Scenarios} />
|
||||
<Route path="/scenarios/:scenario" component={Scenario} />
|
||||
<Route path="/dashboards/:dashboard" component={Dashboard} />
|
||||
<Route path="/infrastructure" component={InfrastructureComponents} />
|
||||
<Route exact path="/infrastructure" component={InfrastructureComponents} />
|
||||
<Route path="/infrastructure/:ic" component={InfrastructureComponent} />
|
||||
<Route path="/account" component={User} />
|
||||
<Route path="/users" component={Users} />
|
||||
<Route path="/api" component={APIBrowser} />
|
||||
|
|
|
@ -291,7 +291,7 @@ class CustomTable extends Component {
|
|||
|
||||
for (let child of props.children) {
|
||||
// check whether empty <></> object has been given
|
||||
if (Object.keys(child.props).length != 0) {
|
||||
if (Object.keys(child.props).length !== 0) {
|
||||
row.push(CustomTable.addCell(data, index, child));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Button, Row, Col } from 'react-bootstrap';
|
||||
import Dialog from '../common/dialogs/dialog';
|
||||
import Icon from "../common/icon";
|
||||
import ConfirmCommand from './confirm-command';
|
||||
import ReactJson from 'react-json-view';
|
||||
import FileSaver from 'file-saver';
|
||||
import moment from 'moment';
|
||||
|
||||
class ICDialog extends React.Component {
|
||||
valid = true;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
confirmCommand: false,
|
||||
command: '',
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
|
||||
}
|
||||
|
||||
showFurtherInfo(key){
|
||||
if(typeof this.state[key] === 'undefined') this.setState({[key]: false});
|
||||
this.setState({[key]: !this.state[key]});
|
||||
}
|
||||
|
||||
closeConfirmModal(canceled){
|
||||
if(!canceled){
|
||||
this.props.sendControlCommand(this.state.command,this.props.ic);
|
||||
}
|
||||
|
||||
this.setState({confirmCommand: false, command: ''});
|
||||
}
|
||||
|
||||
async downloadGraph(url) {
|
||||
|
||||
let blob = await fetch(url).then(r => r.blob())
|
||||
FileSaver.saveAs(blob, this.props.ic.name + ".svg");
|
||||
}
|
||||
|
||||
isJSON(data){
|
||||
if (data === undefined || data === null){
|
||||
return false;
|
||||
}
|
||||
let str = JSON.stringify(data);
|
||||
try
|
||||
{
|
||||
JSON.parse(str)
|
||||
}
|
||||
catch(ex){
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
let graphURL = ""
|
||||
if (this.props.ic.apiurl !== ""){
|
||||
graphURL = this.props.ic.apiurl + "/graph.svg"
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
show={this.props.show}
|
||||
title={this.props.ic.name}
|
||||
buttonTitle="Close"
|
||||
onClose={(c) => this.onClose(c)}
|
||||
valid={true}
|
||||
size='xl'
|
||||
blendOutCancel={true}
|
||||
>
|
||||
<form>
|
||||
<Row>
|
||||
<Col>
|
||||
<Row>
|
||||
<Col><b>Name</b></Col>
|
||||
<Col>{this.props.ic.name}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>UUID</b></Col>
|
||||
<Col>{this.props.ic.uuid}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>State</b></Col>
|
||||
<Col>{this.props.ic.state}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Category</b></Col>
|
||||
<Col>{this.props.ic.category}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Type</b></Col>
|
||||
<Col>{this.props.ic.type}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Uptime</b></Col>
|
||||
<Col>{moment.duration(this.props.ic.uptime, "seconds").humanize()}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Location</b></Col>
|
||||
<Col>{this.props.ic.location}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Description</b></Col>
|
||||
<Col>{this.props.ic.description}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Websocket URL</b></Col>
|
||||
<Col>{this.props.ic.websocketurl}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>API URL</b></Col>
|
||||
<Col>{this.props.ic.apiurl}</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col><b>Start parameter schema</b></Col>
|
||||
<Col>
|
||||
<ReactJson
|
||||
src={this.props.ic.startParameterSchema}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
enableClipboard={false}
|
||||
collapsed={0}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<h5>Raw Status:</h5>
|
||||
{this.isJSON(this.props.ic.statusupdateraw) ?
|
||||
<ReactJson
|
||||
src={this.props.ic.statusupdateraw}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
enableClipboard={false}
|
||||
collapsed={2}
|
||||
/>
|
||||
:
|
||||
<div>No valid JSON raw data available.</div>
|
||||
}
|
||||
|
||||
|
||||
{this.props.ic.type === "villas-node" ?
|
||||
<>
|
||||
<div className='section-buttons-group-right'>
|
||||
<Button style={{margin: '5px'}} size='sm' onClick={() => this.downloadGraph(graphURL)}><Icon
|
||||
icon="download"/></Button>
|
||||
</div>
|
||||
<h5>Graph:</h5>
|
||||
<div>
|
||||
<img alt={"Graph image download failed and/or incorrect image API URL"} src={graphURL}/>
|
||||
</div>
|
||||
|
||||
{this.props.user.role === "Admin" ?
|
||||
<div>
|
||||
<h5>Controls:</h5>
|
||||
<div className='solid-button'>
|
||||
<Button variant='secondary' style={{margin: '5px'}} size='lg'
|
||||
onClick={() => this.setState({confirmCommand: true, command: 'restart'})}>Restart</Button>
|
||||
<Button variant='secondary' style={{margin: '5px'}} size='lg' onClick={() => this.setState({
|
||||
confirmCommand: true,
|
||||
command: 'shutdown'
|
||||
})}>Shutdown</Button>
|
||||
</div>
|
||||
</div>
|
||||
: <div/>
|
||||
}
|
||||
<ConfirmCommand show={this.state.confirmCommand} command={this.state.command} name={this.props.ic.name}
|
||||
onClose={c => this.closeConfirmModal(c)}/>
|
||||
</>
|
||||
: <div/>
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ICDialog;
|
240
src/ic/ic.js
Normal file
240
src/ic/ic.js
Normal file
|
@ -0,0 +1,240 @@
|
|||
/**
|
||||
* 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 InfrastructureComponentStore from './ic-store';
|
||||
import { Container as FluxContainer } from 'flux/utils';
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
import { Container, Col, Row, Table, Button } from 'react-bootstrap';
|
||||
import moment from 'moment';
|
||||
import ReactJson from 'react-json-view';
|
||||
import ConfirmCommand from './confirm-command';
|
||||
import Icon from "../common/icon";
|
||||
|
||||
|
||||
|
||||
class InfrastructureComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
confirmCommand: false,
|
||||
command: '',
|
||||
};
|
||||
}
|
||||
|
||||
static getStores() {
|
||||
return [InfrastructureComponentStore];
|
||||
}
|
||||
|
||||
static calculateState(prevState, props) {
|
||||
if (prevState == null) {
|
||||
prevState = {};
|
||||
}
|
||||
|
||||
return {
|
||||
sessionToken: localStorage.getItem("token"),
|
||||
currentUser: JSON.parse(localStorage.getItem("currentUser")),
|
||||
ic: InfrastructureComponentStore.getState().find(ic => ic.id === parseInt(props.match.params.ic, 10))
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let icID = parseInt(this.props.match.params.ic, 10);
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-load',
|
||||
data: icID,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
|
||||
isJSON(data) {
|
||||
if (data === undefined || data === null) {
|
||||
return false;
|
||||
}
|
||||
let str = JSON.stringify(data);
|
||||
try {
|
||||
JSON.parse(str)
|
||||
}
|
||||
catch (ex) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async downloadGraph(url) {
|
||||
let blob = await fetch(url).then(r => r.blob())
|
||||
FileSaver.saveAs(blob, this.state.ic.name + ".svg");
|
||||
}
|
||||
|
||||
sendControlCommand() {
|
||||
if (this.state.command === "restart") {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/restart',
|
||||
url: this.state.ic.apiurl + "/restart",
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
} else if (this.state.command === "shutdown") {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/shutdown',
|
||||
url: this.state.ic.apiurl + "/shutdown",
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
confirmCommand(canceled){
|
||||
if(!canceled){
|
||||
this.sendControlCommand();
|
||||
}
|
||||
|
||||
this.setState({confirmCommand: false, command: ''});
|
||||
}
|
||||
|
||||
async downloadGraph(url) {
|
||||
|
||||
let blob = await fetch(url).then(r => r.blob())
|
||||
FileSaver.saveAs(blob, this.props.ic.name + ".svg");
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.ic === undefined) {
|
||||
return <h1>Loading Infrastructure Component...</h1>;
|
||||
}
|
||||
|
||||
let graphURL = ""
|
||||
if (this.state.ic.apiurl !== "") {
|
||||
graphURL = this.state.ic.apiurl + "/graph.svg"
|
||||
}
|
||||
|
||||
return <div className='section'>
|
||||
<h1>{this.state.ic.name}</h1>
|
||||
<Container>
|
||||
<Row>
|
||||
<Col>
|
||||
<Table striped size="sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{this.state.ic.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{this.state.ic.description}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UUID</td>
|
||||
<td>{this.state.ic.uuid}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>State</td>
|
||||
<td>{this.state.ic.state}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Category</td>
|
||||
<td>{this.state.ic.category}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>{this.state.ic.type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime</td>
|
||||
<td>{moment.duration(this.state.ic.uptime, "seconds").humanize()}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Location</td>
|
||||
<td>{this.state.ic.location}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Websocket URL</td>
|
||||
<td>{this.state.ic.websocketurl}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API URL</td>
|
||||
<td>{this.state.ic.apiurl}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start parameter schema</td>
|
||||
<td>
|
||||
<ReactJson
|
||||
src={this.state.ic.startparameterschema}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
enableClipboard={false}
|
||||
collapsed={0}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Col>
|
||||
<Col><b>Raw Status</b>
|
||||
{this.isJSON(this.state.ic.statusupdateraw) ?
|
||||
<ReactJson
|
||||
src={this.state.ic.statusupdateraw}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
enableClipboard={false}
|
||||
collapsed={0}
|
||||
/> : <div>No valid JSON raw data available.</div>}
|
||||
|
||||
{this.state.ic.type === "villas-node" ?
|
||||
<>
|
||||
<div className='section-buttons-group-right'>
|
||||
<Button style={{ margin: '5px' }} size='sm' onClick={() => this.downloadGraph(graphURL)}><Icon
|
||||
icon="download" /></Button>
|
||||
</div>
|
||||
<hr></hr>
|
||||
<b>Graph:</b>
|
||||
<div>
|
||||
<img alt={"Graph image download failed and/or incorrect image API URL"} src={graphURL} />
|
||||
</div>
|
||||
|
||||
{this.state.currentUser.role === "Admin" ?
|
||||
<div>
|
||||
<hr></hr>
|
||||
<b>Controls:</b>
|
||||
<div className='solid-button'>
|
||||
<Button variant='secondary' style={{ margin: '5px' }} size='lg'
|
||||
onClick={() => this.setState({ confirmCommand: true, command: 'restart' })}>Restart</Button>
|
||||
<Button variant='secondary' style={{ margin: '5px' }} size='lg' onClick={() => this.setState({
|
||||
confirmCommand: true,
|
||||
command: 'shutdown'
|
||||
})}>Shutdown</Button>
|
||||
</div>
|
||||
</div>
|
||||
: <div />
|
||||
}
|
||||
<ConfirmCommand show={this.state.confirmCommand} command={this.state.command} name={this.state.ic.name}
|
||||
onClose={c => this.confirmCommand(c)} />
|
||||
</>
|
||||
: <div />}
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let fluxContainerConverter = require('../common/FluxContainerConverter');
|
||||
export default FluxContainer.create(fluxContainerConverter.convert(InfrastructureComponent), { withProps: true });
|
|
@ -367,32 +367,11 @@ class InfrastructureComponents extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
modifyNameColumn(name, component){
|
||||
let index = this.state.ics.indexOf(component);
|
||||
return <Button variant="link" onClick={() => this.openICStatus(component)}>{name}</Button>
|
||||
}
|
||||
|
||||
openICStatus(ic){
|
||||
let index = this.state.ics.indexOf(ic);
|
||||
this.setState({ icModal: true, modalIC: ic, modalIndex: index })
|
||||
}
|
||||
|
||||
sendControlCommand(command,ic){
|
||||
if(command === "restart"){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/restart',
|
||||
url: ic.apiurl + "/restart",
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}else if(command === "shutdown"){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/shutdown',
|
||||
url: ic.apiurl + "/shutdown",
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isLocalIC(index, ics){
|
||||
let ic = ics[index]
|
||||
return !ic.managedexternally
|
||||
|
@ -419,7 +398,8 @@ class InfrastructureComponents extends Component {
|
|||
<TableColumn
|
||||
title='Name'
|
||||
dataKeys={['name']}
|
||||
modifier={(name, component) => this.modifyNameColumn(name, component)}
|
||||
link='/infrastructure/'
|
||||
linkKey='id'
|
||||
/>
|
||||
<TableColumn
|
||||
title='State'
|
||||
|
|
|
@ -26,6 +26,7 @@ import Scenarios from './scenario/scenarios';
|
|||
import Scenario from './scenario/scenario';
|
||||
import Dashboard from './dashboard/dashboard'
|
||||
import InfrastructureComponents from './ic/ics';
|
||||
import InfrastructureComponent from './ic/ic';
|
||||
import Users from './user/users';
|
||||
import User from "./user/user";
|
||||
import LoginComplete from './user/login-complete'
|
||||
|
@ -45,6 +46,7 @@ class Root extends React.Component {
|
|||
<Route path='/scenarios/:scenario' component={Scenario} />
|
||||
<Route path='/dashboards/:dashboard' component={Dashboard} />
|
||||
<Route path='/infrastructure' component={InfrastructureComponents} />
|
||||
<Route path='/infrastructure/:ic' component={InfrastructureComponent} />
|
||||
<Route path='/users' component={Users} />
|
||||
<Route path='/account' component={User} />
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue