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

manual merge with master (other layout changes)

This commit is contained in:
irismarie 2021-03-09 17:17:15 +01:00
commit 5f18a9841a
77 changed files with 2070 additions and 1862 deletions

1
.gitignore vendored
View file

@ -18,3 +18,4 @@ yarn-error.log*
.vscode/
*.code-workspace
package-lock.json
.eslintcache

View file

@ -76,7 +76,7 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:4000",
"proxy": "https://villas.k8s.eonerc.rwth-aachen.de",
"browserslist": {
"production": [
">0.2%",

View file

@ -20,7 +20,6 @@ import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import NotificationSystem from 'react-notification-system';
import { Redirect, Route } from 'react-router-dom';
import { Col } from 'react-bootstrap';
import { Hidden } from 'react-grid-system'
import AppDispatcher from './common/app-dispatcher';
@ -29,8 +28,7 @@ import NotificationsDataManager from './common/data-managers/notifications-data-
import Home from './common/home';
import Header from './common/header';
import Footer from './common/footer';
import SidebarMenu from './common/menu-sidebar';
import HeaderMenu from './common/header-menu';
import Menu from './common/menu';
import InfrastructureComponents from './ic/ics';
import Dashboard from './dashboard/dashboard';
@ -55,11 +53,8 @@ class App extends React.Component {
type: 'config/load',
});
this.state = {
showSidebarMenu: false,
}
this.setBrandingStyle();
this.state = {}
}
changeHead() {
@ -128,14 +123,6 @@ class App extends React.Component {
}
}
showSidebarMenu = () => {
this.setState({ showSidebarMenu: true });
};
hideSidebarMenu = () => {
this.setState({ showSidebarMenu: false });
};
render() {
let token = localStorage.getItem("token");
@ -148,46 +135,32 @@ class App extends React.Component {
let currentUser = JSON.parse(currentUserRaw);
return (
<DndProvider backend={HTML5Backend} >
<div>
{/*
<Col style={{ width: this.state.showSidebarMenu ? '280px' : '0px' }} smHidden mdHidden lgHidden className="sidenav">
*/}
<Hidden sm md lg xl xxl>
<Col style={{ width: this.state.showSidebarMenu ? '280px' : '0px' }} className="sidenav">
<HeaderMenu onClose={this.hideSidebarMenu} currentRole={currentUser.role} />
</Col>
</Hidden>
return <DndProvider backend={HTML5Backend} >
<div className="app">
<NotificationSystem
ref="notificationSystem"
/>
<Header />
<div className="app">
<NotificationSystem ref="notificationSystem" />
<div className='app-body app-body-spacing'>
<Menu currentRole={currentUser.role} />
<Header onMenuButton={this.showSidebarMenu} showMenuButton={false} />
<div className={`app-body app-body-spacing`} >
<Col xs={false}>
<SidebarMenu currentRole={currentUser.role} />
</Col>
<div className={`app-content app-content-margin-left`}>
<Route exact path="/" component={Home} />
<Route path="/home" component={Home} />
<Route exact path="/scenarios" component={Scenarios} />
<Route path="/scenarios/:scenario" component={Scenario} />
<Route path="/dashboards/:dashboard" component={Dashboard} />
<Route path="/infrastructure" component={InfrastructureComponents} />
<Route path="/account" component={User} />
<Route path="/users" component={Users} />
<Route path="/api" component={APIBrowser} />
</div>
<div className='app-content app-content-margin-left'>
<Route exact path="/" component={Home} />
<Route path="/home" component={Home} />
<Route exact path="/scenarios" component={Scenarios} />
<Route path="/scenarios/:scenario" component={Scenario} />
<Route path="/dashboards/:dashboard" component={Dashboard} />
<Route path="/infrastructure" component={InfrastructureComponents} />
<Route path="/account" component={User} />
<Route path="/users" component={Users} />
<Route path="/api" component={APIBrowser} />
</div>
<Footer />
</div>
<Footer />
</div>
</DndProvider>
)
</DndProvider>
}
}

View file

@ -25,12 +25,15 @@ import { slew_home } from './slew/slew_home';
class Branding {
constructor(chosenbrand) {
// TODO: simplify; error only for "wrong" brand, not for missing brand
var brand = _.get(brands, [chosenbrand]);
if (!brand) {
console.error("Branding '" + chosenbrand + "' not available, will use 'villasweb' branding");
brand = _.get(brands, ['villasweb']);
chosenbrand = 'villasweb';
this.default = true;
} else if (chosenbrand === 'villasweb') {
this.default = true;
} else {
this.default = false;
}

View file

@ -18,51 +18,51 @@ import React from 'react';
import { NavLink } from 'react-router-dom';
export function villasweb_home(title, username, userid, role) {
return (
<div className="home-container">
<img style={{ height: 120, float: 'right' }} src={require('./img/villas_web.svg').default} alt="Logo VILLASweb" />
<h1>Home</h1>
<p>
Welcome to <b>{title}</b>!
</p>
<p>
You are logged in as user <b>{username}</b> with <b>ID {userid}</b> and role <b>{role}</b>.
</p>
<h3>Credits</h3>
<p>VILLASweb is an open source project developed by the <a href="http://acs.eonerc.rwth-aachen.de">Institute for Automation of Complex Power Systems</a> at <a href="https;//www.rwth-aachen.de">RWTH Aachen University</a>.</p>
<img height={60} src={require('./img/eonerc_rwth.svg').default} alt="Logo ACS" />
<ul>
<li><a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a></li>
<li><a href="mailto:sonja.happ@eonerc.rwth-aachen.de">Sonja Happ</a></li>
</ul>
<h3>Links</h3>
<ul>
<li><NavLink to="/api">VILLASweb API browser</NavLink></li>
<li><a href="http://fein-aachen.org/projects/villas-framework/">FEIN Aachen e.V. project page of VILLASframework</a></li>
<li><a href="https://villas.fein-aachen.org/doc/web.html">Documentation of VILLASweb</a></li>
<li><a href="https://git.rwth-aachen.de/acs/public/villas/web">Source Code of VILLASweb frontend</a></li>
<li><a href="https://git.rwth-aachen.de/acs/public/villas/web-backend-go">Source Code of VILLASweb backend</a></li>
</ul>
<h3>Funding</h3>
<p>The development of <a href="http://fein-aachen.org/projects/villas-framework/">VILLASframework</a> projects has received funding from</p>
<ul>
<p>SLEW: Second Life for Energiewende, an Exploratory Teaching Space project funded by RWTH Aachen University</p>
<p><a href="https://erigrid2.eu/"> ERIgrid 2.0: </a> An EU Horizon 2020 research and innovation action project for connecting European Smart Grid Infrastructures (grant agreement No 870620)</p>
<p>
<img height={100} src={require('./img/european_commission.svg').default} alt="Logo EU" />
<img height={70} src={require('./img/erigrid2.png').default} alt="Logo ERIgrid 2.0" />
</p>
<p><a href="http://www.uel4-0.de/">Urban Energy Lab 4.0:</a> A project funded by EFRE.NRW (European Regional Development Fund) for the setup of a novel energy research infrastructure.</p>
<p>
<img height={70} src={require('./img/uel_efre.jpeg').default} alt="Logo UEL OP EFRE NRW" />
<img height={70} src={require('./img/uel.png').default} alt="Logo UEL" />
</p>
<p><a href="http://www.re-serve.eu">RESERVE:</a> An EU Horizon 2020 research and innovation project (grant agreement No 727481)</p>
<p>
<img height={100} src={require('./img/european_commission.svg').default} alt="Logo EU" />
<img height={70} src={require('./img/reserve.svg').default} alt="Logo RESERVE" />
</p>
<p><a href="http://www.jara.org/en/research/energy">JARA-ENERGY:</a> Jülich-Aachen Research Alliance (JARA) is an initiative of RWTH Aachen University and Forschungszentrum Jülich.</p>
</ul>
</div>)
return (
<div className="home-container">
<img style={{ height: 120, float: 'right' }} src={require('./img/villas_web.svg').default} alt="Logo VILLASweb" />
<h1>Home</h1>
<p>
Welcome to <b>{title}</b>!
</p>
<p>
You are logged in as user <b>{username}</b> with <b>ID {userid}</b> and role <b>{role}</b>.
</p>
<h3>Credits</h3>
<p>VILLASweb is an open source project developed by the <a href="http://acs.eonerc.rwth-aachen.de">Institute for Automation of Complex Power Systems</a> at <a href="https;//www.rwth-aachen.de">RWTH Aachen University</a>.</p>
<img height={60} src={require('./img/eonerc_rwth.svg').default} alt="Logo ACS" />
<ul>
<li><a href="mailto:stvogel@eonerc.rwth-aachen.de">Steffen Vogel</a></li>
<li><a href="mailto:sonja.happ@eonerc.rwth-aachen.de">Sonja Happ</a></li>
</ul>
<h3>Links</h3>
<ul>
<li><NavLink to="/api">VILLASweb API browser</NavLink></li>
<li><a href="http://fein-aachen.org/projects/villas-framework/">FEIN Aachen e.V. project page of VILLASframework</a></li>
<li><a href="https://villas.fein-aachen.org/doc/web.html">Documentation of VILLASweb</a></li>
<li><a href="https://git.rwth-aachen.de/acs/public/villas/web">Source Code of VILLASweb frontend</a></li>
<li><a href="https://git.rwth-aachen.de/acs/public/villas/web-backend-go">Source Code of VILLASweb backend</a></li>
</ul>
<h3>Funding</h3>
<p>The development of <a href="http://fein-aachen.org/projects/villas-framework/">VILLASframework</a> projects has received funding from</p>
<ul>
<p><a href="https://www.acs.eonerc.rwth-aachen.de/cms/E-ON-ERC-ACS/Forschung/Forschungsprojekte/Bildungsprojekte/~mikmu/SLEW-SECOND-LIFE-FOR-ENERGIEWENDE/">SLEW:</a> Second Life for Energiewende, an Exploratory Teaching Space project funded by RWTH Aachen University</p>
<p><a href="https://erigrid2.eu/">ERIgrid 2.0:</a> An EU Horizon 2020 research and innovation action project for connecting European Smart Grid Infrastructures (grant agreement No 870620)</p>
<p>
<img height={100} src={require('./img/european_commission.svg').default} alt="Logo EU" />
<img height={70} src={require('./img/erigrid2.png').default} alt="Logo ERIgrid 2.0" />
</p>
<p><a href="http://www.uel4-0.de/">Urban Energy Lab 4.0:</a> A project funded by EFRE.NRW (European Regional Development Fund) for the setup of a novel energy research infrastructure.</p>
<p>
<img height={70} src={require('./img/uel_efre.jpeg').default} alt="Logo UEL OP EFRE NRW" />
<img height={70} src={require('./img/uel.png').default} alt="Logo UEL" />
</p>
<p><a href="http://www.re-serve.eu">RESERVE:</a> An EU Horizon 2020 research and innovation project (grant agreement No 727481)</p>
<p>
<img height={100} src={require('./img/european_commission.svg').default} alt="Logo EU" />
<img height={70} src={require('./img/reserve.svg').default} alt="Logo RESERVE" />
</p>
<p><a href="http://www.jara.org/en/research/energy">JARA-ENERGY:</a> Jülich-Aachen Research Alliance (JARA) is an initiative of RWTH Aachen University and Forschungszentrum Jülich.</p>
</ul>
</div>)
}

View file

@ -16,8 +16,8 @@
******************************************************************************/
import React from 'react';
import { Button, Modal, FormLabel } from 'react-bootstrap';
import {Collapse} from 'react-collapse';
import { Button, Modal, Form } from 'react-bootstrap';
import { Collapse } from 'react-collapse';
class DeleteDialog extends React.Component {
onModalKeyPress = (event) => {
@ -37,7 +37,7 @@ class DeleteDialog extends React.Component {
<Modal.Body>
Are you sure you want to delete the {this.props.title} <strong>'{this.props.name}'</strong>?
<Collapse isOpened={this.props.managedexternally} >
<FormLabel size="sm">The IC will be deleted if the respective manager sends "gone" state and no component config is using the IC anymore</FormLabel>
<Form.Label size="sm">The IC will be deleted if the respective manager sends "gone" state and no component config is using the IC anymore</Form.Label>
</Collapse>
</Modal.Body>

View file

@ -51,7 +51,7 @@ class Dialog extends React.Component {
};
return (
<Modal size={this.props.size || 'sm'} keyboard show={this.props.show} onEnter={this.props.onReset} onHide={this.cancelModal} onKeyPress={this.onKeyPress}>
<Modal size={this.props.size || 'lg'} keyboard show={this.props.show} onEnter={this.props.onReset} onHide={this.cancelModal} onKeyPress={this.onKeyPress}>
<Modal.Header>
<Modal.Title>{this.props.title}</Modal.Title>
</Modal.Header>

View file

@ -17,7 +17,7 @@
// TODO remove this file (not used!)
import React from 'react';
import PropTypes from 'prop-types';
import { FormControl, Button } from 'react-bootstrap';
import { Form, Button } from 'react-bootstrap';
import Icon from './icon';
class EditableHeader extends React.Component {
@ -79,12 +79,25 @@ class EditableHeader extends React.Component {
};
return <div>
<form style={wrapperStyle}>
<FormControl type='text' size='large' value={this.state.title} onChange={this.onChange} style={editStyle} autoFocus />
</form>
<Form style={wrapperStyle}>
<Form.Control
type='text'
size='large'
value={this.state.title}
onChange={this.onChange}
style={editStyle}
autoFocus
/>
</Form>
<Button onClick={this.save}><Icon icon='check' style={iconStyle} /></Button>
<Button onClick={this.cancel}><Icon icon='times' style={iconStyle} /></Button>
<Button
onClick={this.save}>
<Icon icon='check' style={iconStyle} />
</Button>
<Button
onClick={this.cancel}>
<Icon icon='times' style={iconStyle} />
</Button>
</div>;
}

View file

@ -1,40 +0,0 @@
/**
* 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 { Button } from 'react-bootstrap';
import { NavLink } from 'react-router-dom';
export default class HeaderMenu extends React.Component {
render() {
return <div>
<Button className="closeButton" variant="link" onClick={this.props.onClose}>&times;</Button>
<ul>
<li><NavLink to="/home" activeClassName="active" title="Home" onClick={this.props.onClose}>Home</NavLink></li>
<li><NavLink to="/scenarios" activeClassName="active" title="Scenarios" onClick={this.props.onClose}>Scenarios</NavLink></li>
<li><NavLink to="/infrastructure" activeClassName="active" title="Infrastructure Components" onClick={this.props.onClose}>Infrastructure Components</NavLink></li>
{ this.props.currentRole === 'Admin' ?
<li><NavLink to="/users" activeClassName="active" title="User Management" onClick={this.props.onClose}>User Management</NavLink></li> : ''
}
<li><NavLink to="/account" title="Account">Account</NavLink></li>
<li><NavLink to="/logout" title="Logout" onClick={this.props.onClose}>Logout</NavLink></li>
</ul>
</div>;
}
}

View file

@ -16,30 +16,16 @@
******************************************************************************/
import React from 'react';
import { Col, Button } from 'react-bootstrap';
import { Hidden } from 'react-grid-system'
import Icon from './icon';
import Branding from '../branding/branding';
class Header extends React.Component {
render() {
return (
<header className="app-header">
<Col xs={{span: 10}} sm={{span: 8, offset: 2}}>
<h1>{Branding.instance.brand.title} - {Branding.instance.brand.subtitle}</h1>
</Col>
<Hidden sm md lg xl>
<Col xs={2} style={{ paddingLeft: 'auto', paddingRight: 0 }}>
{this.props.showMenuButton &&
<Button variant="link" onClick={this.props.onMenuButton} style={{ float: 'right', marginRight: '10px' }}>
<Icon size="3x" icon="bars" className="menu-icon" />
</Button>
}
</Col>
</Hidden>
<h1>{Branding.instance.brand.title} - {Branding.instance.brand.subtitle}</h1>
</header>
);
}
}
export default Header;
export default Header;

56
src/common/icon-button.js Normal file
View file

@ -0,0 +1,56 @@
/**
* 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 { Button, Tooltip, OverlayTrigger } from 'react-bootstrap';
import Icon from '../common/icon';
class IconButton extends React.Component {
render() {
const altButtonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return <OverlayTrigger
key={this.props.key}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}>{this.props.tooltip}</Tooltip>} >
<Button
variant='light'
onClick={this.props.onClick}
style={altButtonStyle}
>
<Icon
icon={this.props.icon}
classname={'icon-color'}
style={iconStyle}
/>
</Button>
</OverlayTrigger>
}
}
export default IconButton;

View file

@ -21,7 +21,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
//import '@fortawesome/free-regular-svg-icons';
library.add(fas);

View file

@ -74,49 +74,34 @@ class SidebarMenu extends React.Component {
})
}
if (this.state.externalAuth) {
return (
<div className="menu-sidebar">
<h2>Menu</h2>
return (
<div className="menu">
<h2>Menu</h2>
{this.state.externalAuth ?
<ul>
<li hidden={!brand.pages.home}><NavLink to="/home" activeClassName="active" title="Home">Home</NavLink></li>
<li hidden={!brand.pages.scenarios}><NavLink to="/scenarios" activeClassName="active" title="Scenarios">Scenarios</NavLink></li>
<li hidden={!brand.pages.infrastructure}><NavLink to="/infrastructure" activeClassName="active" title="Infrastructure Components">Infrastructure Components</NavLink></li>
{ this.props.currentRole === 'Admin' ?
<li><NavLink to="/users" activeClassName="active" title="User Management">User Management</NavLink></li> : ''
<li hidden={!brand.pages.infrastructure}><NavLink to="/infrastructure" activeClassName="active" title="Infrastructure">Infrastructure</NavLink></li>
{this.props.currentRole === 'Admin' ?
<li><NavLink to="/users" activeClassName="active" title="Users">Users</NavLink></li> : ''
}
<li hidden={!brand.pages.account}><NavLink to="/account" title="Account">Account</NavLink></li>
<a onClick={this.logout.bind(this)} href={this.state.logoutLink}>Logout</a>
<li hidden={!brand.pages.api}><NavLink to="/api" title="API Browser">API Browser</NavLink></li>
</ul>
{
links.length > 0 ?
<div>
<br></br>
<h4> Links</h4>
<ul> {links} </ul>
</div>
: ''
}
</div>
);
}
: <ul>
<li hidden={!brand.pages.home}><NavLink to="/home" activeClassName="active" title="Home">Home</NavLink></li>
<li hidden={!brand.pages.scenarios}><NavLink to="/scenarios" activeClassName="active" title="Scenarios">Scenarios</NavLink></li>
<li hidden={!brand.pages.infrastructure}><NavLink to="/infrastructure" activeClassName="active" title="Infrastructure">Infrastructure</NavLink></li>
{this.props.currentRole === 'Admin' ?
<li><NavLink to="/users" activeClassName="active" title="Users">Users</NavLink></li> : ''
}
<li hidden={!brand.pages.account}><NavLink to="/account" title="Account">Account</NavLink></li>
<li><NavLink to={this.state.logoutLink} title="Logout">Logout</NavLink></li>
<li hidden={!brand.pages.api}> <NavLink to="/api" title="API Browser">API Browser</NavLink></li >
</ul >}
return (
<div className="menu-sidebar">
<h2>Menu</h2>
<ul>
<li hidden={!brand.pages.home}><NavLink to="/home" activeClassName="active" title="Home">Home</NavLink></li>
<li hidden={!brand.pages.scenarios}><NavLink to="/scenarios" activeClassName="active" title="Scenarios">Scenarios</NavLink></li>
<li hidden={!brand.pages.infrastructure}><NavLink to="/infrastructure" activeClassName="active" title="Infrastructure Components">Infrastructure Components</NavLink></li>
{this.props.currentRole === 'Admin' ?
<li><NavLink to="/users" activeClassName="active" title="User Management">User Management</NavLink></li> : ''
}
<li hidden={!brand.pages.account}><NavLink to="/account" title="Account">Account</NavLink></li>
<li><NavLink to={this.state.logoutLink} title="Logout">Logout</NavLink></li>
<li hidden={!brand.pages.api}><NavLink to="/api" title="API Browser">API Browser</NavLink></li>
</ul>
{
links.length > 0 ?
<div>
@ -132,4 +117,4 @@ class SidebarMenu extends React.Component {
}
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(SidebarMenu));
export default Container.create(fluxContainerConverter.convert(SidebarMenu));

View file

@ -39,16 +39,19 @@ class TableColumn extends Component {
checkboxKey: '',
checkboxDisabled: null,
labelStyle: null,
labelModifier: null
labelModifier: null,
align: 'left'
};
render() {
return (
<th width={this.props.width}>
{this.props.title}
</th>
);
let style = {
textAlign: this.props.align,
width: this.props.width
};
return <th style={style}>
{this.props.title}
</th>;
}
}

View file

@ -17,7 +17,7 @@
import React, { Component } from 'react';
import _ from 'lodash';
import { Table, Button, FormControl, FormLabel, FormCheck, Tooltip, OverlayTrigger } from 'react-bootstrap';
import { Table, Button, Form, Tooltip, OverlayTrigger } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import Icon from './icon';
@ -39,7 +39,7 @@ class CustomTable extends Component {
};
onClick(event, row, column) {
this.setState({ editCell: [column, row] }); // x, y
this.setState({ editCell: [column, row] }); // x, y
}
static addCell(data, index, child) {
@ -83,12 +83,13 @@ class CustomTable extends Component {
<OverlayTrigger
key={contentkey}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"export"}`}>Download {contentvalue}</Tooltip>} >
overlay={<Tooltip id={`tooltip-${"export"}`}>Download {contentvalue}</Tooltip>}
>
<Button
variant='table-control-button'
onClick={() => child.props.onDownload(contentkey)}
disabled={child.props.onDownload == null}>
{contentkey + ' ' }
{contentkey + ' '}
<Icon icon='file-download' />
</Button>
</OverlayTrigger>);
@ -111,56 +112,56 @@ class CustomTable extends Component {
cell.push(<span>
&nbsp;
<FormLabel column={false} className={labelStyle}>
<Form.Label
column={false}
className={labelStyle}>
{labelContent}
</FormLabel>
</Form.Label>
</span>
);
}
if (child.props.dataIndex) {
cell.push(index);
}
// add buttons
let showEditButton = true
if (child.props.showEditButton !== null)
{
showEditButton = child.props.showEditButton(index)
}
if(showEditButton){
if (child.props.editButton) {
cell.push(
<OverlayTrigger
key={0}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"edit"}`}> Edit </Tooltip>}>
<Button
variant='table-control-button'
onClick={() => child.props.onEdit(index)}
disabled={child.props.onEdit == null} >
<Icon icon='edit' />
</Button>
</OverlayTrigger>);
}
}
let showEditButton = child.props.showEditButton !== null
? child.props.showEditButton(index)
: true;
if (child.props.editButton && showEditButton) {
cell.push(
<OverlayTrigger
key={0}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"edit"}`}> Edit </Tooltip>}
>
<Button
variant='table-control-button'
onClick={() => child.props.onEdit(index)}
disabled={child.props.onEdit == null} >
<Icon icon='edit' />
</Button>
</OverlayTrigger>
);
}
if (child.props.checkbox) {
const checkboxKey = child.props.checkboxKey;
let isDisabled = false;
if (child.props.checkboxDisabled != null){
isDisabled = child.props.checkboxDisabled(index)
}
let isDisabled = child.props.checkboxDisabled != null
? child.props.checkboxDisabled(index)
: false;
cell.push(
<FormCheck
<Form.Check
className="table-control-checkbox"
inline
disabled = {isDisabled}
disabled={isDisabled}
checked={checkboxKey ? data[checkboxKey] : null}
onChange={e => child.props.onChecked(data, e)}
/>);
/>
);
}
if (child.props.exportButton) {
@ -168,14 +169,16 @@ class CustomTable extends Component {
<OverlayTrigger
key={1}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"export"}`}> Export </Tooltip>} >
overlay={<Tooltip id={`tooltip-${"export"}`}> Export </Tooltip>}
>
<Button
variant='table-control-button'
onClick={() => child.props.onExport(index)}
disabled={child.props.onExport == null}>
<Icon icon='download' />
</Button>
</OverlayTrigger>);
</OverlayTrigger>
);
}
if (child.props.duplicateButton) {
@ -190,7 +193,8 @@ class CustomTable extends Component {
disabled={child.props.onDuplicate == null}>
<Icon icon='copy' />
</Button>
</OverlayTrigger>);
</OverlayTrigger>
);
}
if (child.props.addRemoveFilesButton) {
@ -205,7 +209,8 @@ class CustomTable extends Component {
disabled={child.props.onAddRemove == null}>
<Icon icon='file' />
</Button>
</OverlayTrigger>);
</OverlayTrigger>
);
}
if (child.props.downloadAllButton) {
@ -220,35 +225,32 @@ class CustomTable extends Component {
disabled={child.props.onDownloadAll == null}>
<Icon icon='file-download' />
</Button>
</OverlayTrigger>);
</OverlayTrigger>
);
}
let showDeleteButton = true;
if (child.props.showDeleteButton !== null){
showDeleteButton = child.props.showDeleteButton(index)
let showDeleteButton = child.props.showDeleteButton !== null
? child.props.showDeleteButton(index)
: true;
if (child.props.deleteButton && showDeleteButton) {
cell.push(
<OverlayTrigger
key={5}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"delete"}`}> Delete </Tooltip>} >
<Button
variant='table-control-button'
onClick={() => child.props.onDelete(index)}
disabled={child.props.onDelete == null}>
<Icon icon='trash' />
</Button>
</OverlayTrigger>
);
}
if (showDeleteButton){
if (child.props.deleteButton) {
cell.push(
<OverlayTrigger
key={5}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"delete"}`}> Delete </Tooltip>} >
<Button
variant='table-control-button'
onClick={() => child.props.onDelete(index)}
disabled={child.props.onDelete == null}>
<Icon icon='trash' />
</Button>
</OverlayTrigger>);
}
}
return cell;
} // addCell
}
static getDerivedStateFromProps(props, state) {
const rows = CustomTable.getRows(props);
@ -311,49 +313,53 @@ class CustomTable extends Component {
</thead>
<tbody>
{
this.state.rows.map((row, rowIndex) => (
this.state.rows.map((row, rowIndex) =>
<tr key={rowIndex}>
{
row.map((cell, cellIndex) => {
let isCellInlineEditable = children[cellIndex].props.inlineEditable === true;
let tabIndex = isCellInlineEditable ? 0 : -1;
let evtHdls = isCellInlineEditable ? {
onCellClick: (event) => this.onClick(event, rowIndex, cellIndex),
onCellFocus: () => this.onCellFocus({ cell: cellIndex, row: rowIndex }),
onCellBlur: () => this.cellLostFocus()
} : {
onCellClick: () => { },
onCellFocus: () => { },
onCellBlur: () => { }
};
onCellClick: () => { },
onCellFocus: () => { },
onCellBlur: () => { }
};
let cellStyle = {
textAlign: children[cellIndex].props.align
};
return (<td
return <td
key={cellIndex}
style={cellStyle}
tabIndex={tabIndex}
onClick={evtHdls.onCellClick}
onFocus={evtHdls.onCellFocus}
onBlur={evtHdls.onCellBlur}>
{(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex) ? (
<FormControl
onBlur={evtHdls.onCellBlur}
>
{(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex) ?
<Form.Control
as='input'
type={children[cellIndex].props.inputType}
value={cell}
onChange={(event) => children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)}
ref={ref => { this.activeInput = ref; }} />
) : (
<span>
{cell.map((element, elementIndex) => (
: <span>
{
cell.map((element, elementIndex) =>
<span key={elementIndex}>{element}</span>
))}
</span>
)}
</td>)
)
}
</span>
}
</td>
})
}
</tr>))
</tr>)
}
</tbody>
</Table>

View file

@ -16,8 +16,8 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel} from 'react-bootstrap';
import { Multiselect } from 'multiselect-react-dropdown'
import { Form } from 'react-bootstrap';
import { Multiselect } from 'multiselect-react-dropdown'
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
@ -141,29 +141,29 @@ class EditConfigDialog extends React.Component {
onReset={() => this.resetState()}
valid={this.valid}
>
<form>
<FormGroup controlId="name">
<FormLabel column={false}>Name</FormLabel>
<FormControl
<Form>
<Form.Group controlId="name">
<Form.Label column={false}>Name</Form.Label>
<Form.Control
type="text"
placeholder={this.props.config.name}
value={this.state.name}
onChange={(e) => this.handleChange(e)}
/>
<FormControl.Feedback />
</FormGroup>
<Form.Control.Feedback />
</Form.Group>
<FormGroup controlId="icID">
<FormLabel column={false}> Infrastructure Component </FormLabel>
<FormControl
<Form.Group controlId="icID">
<Form.Label column={false}> Infrastructure Component </Form.Label>
<Form.Control
as="select"
placeholder='Select infrastructure component'
value={this.state.icID}
onChange={(e) => this.handleChange(e)}
>
{ICOptions}
</FormControl>
</FormGroup>
</Form.Control>
</Form.Group>
<Multiselect
options={configFileOptions}
@ -175,14 +175,14 @@ class EditConfigDialog extends React.Component {
placeholder={'Select file(s)...'}
/>
<FormGroup controlId='startParameters'>
<FormLabel> Start Parameters </FormLabel>
<Form.Group controlId='startParameters'>
<Form.Label> Start Parameters </Form.Label>
<ParametersEditor
content={this.state.startParameters}
onChange={(data) => this.handleParameterChange(data)}
/>
</FormGroup>
</form>
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -102,15 +102,15 @@ class ImportConfigDialog extends React.Component {
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid} >
<form>
<FormGroup controlId='file'>
<FormLabel>Component Configuration File</FormLabel>
<FormControl type='file' onChange={this.loadFile} />
</FormGroup>
<Form>
<Form.Group controlId='file'>
<Form.Label>Component Configuration File</Form.Label>
<Form.Control type='file' onChange={this.loadFile} />
</Form.Group>
<FormGroup controlId="name" >
<FormLabel>Name</FormLabel>
<FormControl
<Form.Group controlId="name" >
<Form.Label>Name</Form.Label>
<Form.Control
readOnly={!this.imported}
isValid={this.validateForm('name')}
type="text"
@ -118,9 +118,9 @@ class ImportConfigDialog extends React.Component {
value={this.state.name}
onChange={(e) => this.handleChange(e)}
/>
<FormControl.Feedback />
</FormGroup>
</form>
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -22,7 +22,7 @@ import classNames from 'classnames';
import EditWidget from '../widget/edit-widget/edit-widget';
import EditFiles from '../file/edit-files';
import EditSignalMapping from "../signal/edit-signal-mapping";
import EditSignalMappingDialog from "../signal/edit-signal-mapping";
import WidgetContextMenu from '../widget/widget-context-menu';
import WidgetToolbox from '../widget/widget-toolbox';
import WidgetArea from '../widget/widget-area';
@ -569,7 +569,7 @@ class Dashboard extends Component {
scenarioID={this.state.dashboard.scenarioID}
/>
<EditSignalMapping
<EditSignalMappingDialog
show={this.state.editOutputSignalsModal}
onCloseEdit={(direction) => this.closeEditSignalsModal(direction)}
direction="Output"
@ -578,7 +578,7 @@ class Dashboard extends Component {
configs={this.state.configs}
sessionToken={this.state.sessionToken}
/>
<EditSignalMapping
<EditSignalMappingDialog
show={this.state.editInputSignalsModal}
onCloseEdit={(direction) => this.closeEditSignalsModal(direction)}
direction="Input"
@ -587,8 +587,6 @@ class Dashboard extends Component {
configs={this.state.configs}
sessionToken={this.state.sessionToken}
/>
</div>
</div>;
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -71,14 +71,21 @@ class EditDashboardDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="Edit Dashboard" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="name" valid={this.validateForm('name')}>
<FormLabel>Name</FormLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
<Dialog
show={this.props.show}
title="Edit Dashboard"
buttonTitle="Save"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form>
<Form.Group controlId="name" valid={this.validateForm('name')}>
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -100,15 +100,15 @@ class ImportDashboardDialog extends React.Component {
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}>
<form>
<FormGroup controlId="file">
<FormLabel>Dashboard File</FormLabel>
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
</FormGroup>
<Form>
<Form.Group controlId="file">
<Form.Label>Dashboard File</Form.Label>
<Form.Control type="file" onChange={(e) => this.loadFile(e.target.files)} />
</Form.Group>
<FormGroup controlId="name" >
<FormLabel>Name</FormLabel>
<FormControl
<Form.Group controlId="name" >
<Form.Label>Name</Form.Label>
<Form.Control
readOnly={!this.imported}
isValid={this.validateForm('name')}
type="text"
@ -116,9 +116,9 @@ class ImportDashboardDialog extends React.Component {
value={this.state.name}
onChange={(e) => this.handleChange(e)}
/>
<FormControl.Feedback />
</FormGroup>
</form>
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -67,14 +67,21 @@ class NewDashboardDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="New Dashboard" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="name" valid={this.validateForm('name')}>
<FormLabel>Name</FormLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
<Dialog
show={this.props.show}
title="New Dashboard"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form>
<Form.Group controlId="name" valid={this.validateForm('name')}>
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, Button, Col} from 'react-bootstrap';
import { Form, Button, Col } from 'react-bootstrap';
import AppDispatcher from "../common/app-dispatcher";
import Dialog from '../common/dialogs/dialog';
@ -28,7 +28,6 @@ class EditFileContent extends React.Component {
this.state = {
uploadFile: null,
};
}
@ -50,31 +49,33 @@ class EditFileContent extends React.Component {
this.setState({ uploadFile: null });
};
onClose = () => {
this.props.onClose();
};
render() {
return <Dialog show={this.props.show} title='Edit File Content' buttonTitle='Close' onClose={() => this.onClose()} blendOutCancel = {true} valid={true}>
<FormGroup as={Col} >
<FormControl
return <Dialog
show={this.props.show}
title='Edit File Content'
buttonTitle='Close'
onClose={() => this.onClose()}
blendOutCancel = {true}
valid={true}
>
<Form.Group as={Col} >
<Form.Control
disabled={false}
type='file'
onChange={(event) => this.selectUploadFile(event)} />
</FormGroup>
</Form.Group>
<FormGroup as={Col} >
<Form.Group as={Col} >
<Button
disabled={this.state.uploadFile === null}
onClick={() => this.startEditContent()}>
Upload
</Button>
</FormGroup>
</Form.Group>
</Dialog>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, Button, Col, ProgressBar} from 'react-bootstrap';
import { Form, Button, Col, ProgressBar } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import AppDispatcher from "../common/app-dispatcher";
import Table from "../common/table";
@ -27,7 +27,6 @@ import EditFileContent from "./edit-file-content";
class EditFilesDialog extends React.Component {
valid = true;
constructor(props) {
super(props);
@ -40,7 +39,6 @@ class EditFilesDialog extends React.Component {
}
onClose() {
this.props.onClose();
}
@ -71,29 +69,17 @@ class EditFilesDialog extends React.Component {
} else {
this.setState({ uploadProgress: 0 });
}
};
clearProgress = (newFileID) => {
/*if (this.props.onChange != null) {
let event = {}
event["target"] = {}
event.target["value"] = newFileID
this.props.onChange(event);
}
*/
this.setState({ uploadProgress: 0 });
};
closeEditModal(){
this.setState({editModal: false});
closeEditModal() {
this.setState({editModal: false});
}
deleteFile(index){
let file = this.props.files[index]
AppDispatcher.dispatch({
type: 'files/start-remove',
@ -102,9 +88,7 @@ class EditFilesDialog extends React.Component {
});
}
render() {
let fileOptions = [];
if (this.props.files.length > 0){
fileOptions.push(
@ -122,33 +106,51 @@ class EditFilesDialog extends React.Component {
marginTop: '-40px'
};
return (
<Dialog show={this.props.show} title="Edit Files of scenario" buttonTitle="Close" onClose={() => this.onClose()} blendOutCancel = {true} valid={true} size = 'lg'>
<div>
<Dialog
show={this.props.show}
title="Edit Files of Scenario"
buttonTitle="Close"
onClose={() => this.onClose()}
blendOutCancel = {true}
valid={true}
>
<Table data={this.props.files}>
<TableColumn
title='ID'
dataKey='id'
/>
<TableColumn
title='Name'
dataKey='name'
/>
<TableColumn
title='Size (bytes)'
dataKey='size'
/>
<TableColumn
title='Type'
dataKey='type'
/>
<TableColumn
title=''
align='right'
deleteButton
onDelete={(index) => this.deleteFile(index)}
editButton
onEdit={index => this.setState({ editModal: true, modalFile: this.props.files[index] })}
/>
</Table>
<Table data={this.props.files}>
<TableColumn title='ID' dataKey='id'/>
<TableColumn title='Name' dataKey='name'/>
<TableColumn title='Size (bytes)' dataKey='size'/>
<TableColumn title='Type' dataKey='type'/>
<TableColumn
title=''
deleteButton
onDelete={(index) => this.deleteFile(index)}
editButton
onEdit={index => this.setState({ editModal: true, modalFile: this.props.files[index] })}
/>
</Table>
<Form.Group as={Col} >
<Form.Control
disabled={this.props.disabled}
type='file'
onChange={(event) => this.selectUploadFile(event)}
/>
</Form.Group>
<FormGroup as={Col} >
<FormControl
disabled={this.props.disabled}
type='file'
onChange={(event) => this.selectUploadFile(event)} />
</FormGroup>
<FormGroup as={Col} >
<Form.Group as={Col} >
<span className='solid-button'>
<Button
variant='secondary'
@ -157,25 +159,27 @@ class EditFilesDialog extends React.Component {
Upload
</Button>
</span>
</FormGroup>
</Form.Group>
<FormGroup as={Col} >
<ProgressBar
striped={true}
animated={true}
now={this.state.uploadProgress}
label={this.state.uploadProgress + '%'}
style={progressBarStyle}
/>
</FormGroup>
<div style={{ clear: 'both' }} />
<Form.Group as={Col} >
<ProgressBar
striped={true}
animated={true}
now={this.state.uploadProgress}
label={this.state.uploadProgress + '%'}
style={progressBarStyle}
/>
</Form.Group>
<EditFileContent show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} sessionToken={this.props.sessionToken} file={this.state.modalFile} />
<div style={{ clear: 'both' }} />
</div>
<EditFileContent
show={this.state.editModal}
onClose={(data) => this.closeEditModal(data)}
sessionToken={this.props.sessionToken}
file={this.state.modalFile}
/>
</Dialog>
);
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, Button, Col, ProgressBar } from 'react-bootstrap';
import { Form, Button, Col, ProgressBar } from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
class SelectFile extends React.Component {
@ -88,39 +88,39 @@ class SelectFile extends React.Component {
};
return <div>
<FormGroup>
<FormLabel sm={3} md={2}>
<Form.Group>
<Form.Label sm={3} md={2}>
{this.props.name}
</FormLabel>
</For.m.Label>
<FormGroup as={Col} sm={9} md={10}>
<FormControl
<Form.Group as={Col} sm={9} md={10}>
<Form.Control
as="select"
value={this.props.value}
disabled={this.props.disabled}
placeholder='Select file'
onChange={(event) => this.props.onChange(event)}>
{fileOptions}
</FormControl>
</FormGroup>
</FormGroup>
</Form.Control>
</Form.Group>
</Form.Group>
<FormGroup as={Col} >
<FormControl
<Form.Group as={Col} >
<Form.Control
disabled={this.props.disabled}
type='file'
onChange={(event) => this.selectUploadFile(event)} />
</FormGroup>
</Form.Group>
<FormGroup as={Col} >
<Form.Group as={Col} >
<Button
disabled={this.state.uploadFile === null}
onClick={() => this.startFileUpload()}>
Upload
</Button>
</FormGroup>
</Form.Group>
<FormGroup as={Col} >
<Form.Group as={Col} >
<ProgressBar
striped={true}
animated={true}
@ -128,7 +128,7 @@ class SelectFile extends React.Component {
label={this.state.uploadProgress + '%'}
style={progressBarStyle}
/>
</FormGroup>
</Form.Group>
</div>;
}

View file

@ -16,9 +16,8 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, FormCheck } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import _ from 'lodash';
import {Collapse} from 'react-collapse';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
@ -37,7 +36,8 @@ class EditICDialog extends React.Component {
type: '',
category: '',
managedexternally: false,
startParameterScheme: {},
startParameterSchema: {},
properties: {}
};
}
@ -50,14 +50,9 @@ class EditICDialog extends React.Component {
data.name = this.state.name;
}
data.websocketurl = this.state.websocketurl;
data.apiurl = this.state.apiurl;
if (this.state.location != null && this.state.location !== this.props.ic.location) {
data.location = this.state.location;
}
@ -73,13 +68,17 @@ class EditICDialog extends React.Component {
if (this.state.category != null && this.state.category !== "" && this.state.category !== this.props.ic.category) {
data.category = this.state.category;
}
if (this.state.startParameterScheme !== {}) {
data.startParameterScheme = this.state.startParameterScheme
if (this.state.startParameterSchema !== {}) {
data.startParameterSchema = this.state.startParameterSchema;
}
if (this.state.properties !== {}) {
data.properties = this.state.properties;
}
data.managedexternally = this.state.managedexternally;
this.props.onClose(data);
this.setState({managedexternally: false});
}
@ -98,8 +97,12 @@ class EditICDialog extends React.Component {
}
}
handleStartParameterSchemeChange(data) {
this.setState({ startParameterScheme: data });
handleStartParameterSchemaChange(data) {
this.setState({ startParameterSchema: data });
}
handlePropertiesChange(data) {
this.setState({ properties: data });
}
resetState() {
@ -112,7 +115,8 @@ class EditICDialog extends React.Component {
description: this.props.ic.description,
category: this.props.ic.category,
managedexternally: false,
startParameterScheme: this.props.ic.startParameterScheme,
startParameterSchema: this.props.ic.startParameterSchema,
properties: this.props.ic.properties,
});
}
@ -120,7 +124,7 @@ class EditICDialog extends React.Component {
let typeOptions = [];
switch(this.state.category){
case "simulator":
typeOptions = ["dummy","generic","dpsim","rtlab","rscad", "opalrt"];
typeOptions = ["dummy","generic","dpsim","rtlab","rscad","rtlab","kubernetes"];
break;
case "manager":
typeOptions = ["villas-node","villas-relay","generic"];
@ -145,63 +149,70 @@ class EditICDialog extends React.Component {
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
size='lg'
>
<form>
<FormLabel column={false}>UUID: {this.props.ic.uuid}</FormLabel>
<FormGroup controlId="name">
<FormLabel column={false}>Name</FormLabel>
<FormControl type="text" placeholder={this.props.ic.name} value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="category">
<FormLabel column={false}>Category</FormLabel>
<FormControl as="select" value={this.state.category} onChange={(e) => this.handleChange(e)}>
<Form>
<Form.Label column={false}>UUID: {this.props.ic.uuid}</Form.Label>
<Form.Group controlId="name">
<Form.Label column={false}>Name</Form.Label>
<Form.Control type="text" placeholder={this.props.ic.name} value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="category">
<Form.Label column={false}>Category</Form.Label>
<Form.Control as="select" value={this.state.category} onChange={(e) => this.handleChange(e)}>
<option>simulator</option>
<option>service</option>
<option>gateway</option>
<option>equipment</option>
<option>manager</option>
</FormControl>
</FormGroup>
<FormGroup controlId="type">
<FormLabel column={false}>Type</FormLabel>
<FormControl as="select" value={this.state.type} onChange={(e) => this.handleChange(e)}>
</Form.Control>
</Form.Group>
<Form.Group controlId="type">
<Form.Label column={false}>Type</Form.Label>
<Form.Control as="select" value={this.state.type} onChange={(e) => this.handleChange(e)}>
<option default>Select type</option>
{typeOptions.map((name,index) => (
<option key={index}>{name}</option>
))}
</FormControl>
</FormGroup>
<FormGroup controlId="websocketurl">
<FormLabel column={false}>Websocket URL</FormLabel>
<FormControl type="text" placeholder={this.props.ic.websocketurl} value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="apiurl">
<FormLabel column={false}>API URL</FormLabel>
<FormControl type="text" placeholder={this.props.ic.apiurl} value={this.state.apiurl} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="location">
<FormLabel column={false}>Location</FormLabel>
<FormControl type="text" placeholder={this.props.ic.location} value={this.state.location || '' } onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="description">
<FormLabel column={false}>Description</FormLabel>
<FormControl type="text" placeholder={this.props.ic.description} value={this.state.description || '' } onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId='startParameterScheme'>
<FormLabel column={false}>Start parameter scheme of IC</FormLabel>
</Form.Control>
</Form.Group>
<Form.Group controlId="websocketurl">
<Form.Label column={false}>Websocket URL</Form.Label>
<Form.Control type="text" placeholder={this.props.ic.websocketurl} value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="apiurl">
<Form.Label column={false}>API URL</Form.Label>
<Form.Control type="text" placeholder={this.props.ic.apiurl} value={this.state.apiurl} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="location">
<Form.Label column={false}>Location</Form.Label>
<Form.Control type="text" placeholder={this.props.ic.location} value={this.state.location || '' } onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="description">
<Form.Label column={false}>Description</Form.Label>
<Form.Control type="text" placeholder={this.props.ic.description} value={this.state.description || '' } onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId='startParameterSchema'>
<Form.Label column={false}>Start parameter schema of IC</Form.Label>
<ParametersEditor
content={this.state.startParameterScheme}
content={this.state.startParameterSchema}
disabled={false}
onChange={(data) => this.handleStartParameterSchemeChange(data)}
onChange={(data) => this.handleStartParameterSchemaChange(data)}
/>
</FormGroup>
</form>
</Form.Group>
<Form.Group controlId='properties'>
<Form.Label column={false}>Properties</Form.Label>
<ParametersEditor
content={this.state.properties}
disabled={true}
onChange={(data) => this.handlePropertiesChange(data)}
/>
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,23 +16,24 @@
******************************************************************************/
import React from 'react';
import { Button, DropdownButton, Dropdown, InputGroup, FormControl } from 'react-bootstrap';
import { Form, SplitButton, Dropdown } from 'react-bootstrap';
import AppDispatcher from "../common/app-dispatcher";
import NotificationsFactory from "../common/data-managers/notifications-factory";
import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
Number.prototype.pad = function(size) {
var s = String(this);
while (s.length < (size || 2)) {
s = "0" + s;
}
return s;
}
class ICAction extends React.Component {
constructor(props) {
super(props);
let t = new Date()
Number.prototype.pad = function(size) {
var s = String(this);
while (s.length < (size || 2)) {s = "0" + s;}
return s;
}
let time = new Date();
time.setMinutes(5 * Math.round(time.getMinutes() / 5 + 1))
@ -55,13 +56,12 @@ class ICAction extends React.Component {
}
runAction(action, when) {
if (action.data.action === 'none') {
console.warn("No command selected. Nothing was sent.");
return;
}
if (!this.props.hasConfigs){
if (!this.props.configs) {
let newAction = {};
newAction["action"] = action.data.action
newAction["when"] = when
@ -71,38 +71,29 @@ class ICAction extends React.Component {
let icID = ic.id;
/* VILLAScontroller protocol
see: https://villas.fein-aachen.org/doc/controller-protocol.html
* see: https://villas.fein-aachen.org/doc/controller-protocol.html
*/
RESET SHUTDOWN
{
"action": "reset/shutdown/stop/pause/resume"
"when": "1234567"
}
DELETE
{
"action": "delete"
"parameters":{
"uuid": "uuid-of-the-manager-for-this-IC"
}
"when": "1234567"
}
CREATE is not possible within ICAction (see add IC)
*/
if (newAction.action === "delete"){
if (newAction.action == "create" || newAction.action === "delete") {
// prepare parameters for delete incl. correct IC id
newAction["parameters"] = {};
newAction.parameters["uuid"] = ic.uuid;
if (newAction.action == "delete") {
newAction.parameters["uuid"] = ic.uuid;
}
else if (newAction.action == "create") {
newAction.parameters = ic.statusupdateraw.properties;
}
// get the ID of the manager IC
let managerIC = null;
for (let i of this.props.ics){
if (i.uuid === ic.manager){
for (let i of this.props.ics) {
if (i.uuid === ic.manager) {
managerIC = i;
}
}
if (managerIC == null){
if (managerIC == null) {
NotificationsDataManager.addNotification(NotificationsFactory.DELETE_ERROR("Could not find manager IC with UUID " + ic.manager));
continue;
}
@ -120,38 +111,9 @@ class ICAction extends React.Component {
} // end for loop over selected ICs
} else {
/*VILLAScontoller protocol
see: https://villas.fein-aachen.org/doc/controller-protocol.html
*
* STOP PAUSE RESUME
{
"action": "reset/shutdown/stop/pause/resume"
"when": "1234567"
}
*
* START
{
"action": "start"
"when": 1234567
"parameters": {
Start parameters for this IC as configured in the component config
}
"model": {
"type": "url"
"url": "https://villas.k8s.eonerc.rwth-aachen.de/api/v2/files/{fileID}" where fileID is the model file configured in the component config
"token": "asessiontoken"
}
"results":{
"type": "url"
"url" : "https://villas.k8s.eonerc.rwth-aachen.de/api/v2/results/{resultID}/file" where resultID is the ID of the result created for this run
"token": "asessiontoken"
}
}
*
*
* */
/* VILLAScontoller protocol
* see: https://villas.fein-aachen.org/doc/controller-protocol.html
*/
let newActions = [];
for (let config of this.props.selectedConfigs) {
@ -177,14 +139,13 @@ class ICAction extends React.Component {
if (newAction.action === 'start') {
newAction["parameters"] = config.startParameters;
if (config.fileIDs.length > 0){
if (config.fileIDs && config.fileIDs.length > 0) {
newAction["model"] = {}
newAction.model["type"] = "url"
newAction.model["type"] = "url-list"
newAction.model["token"] = this.props.token
let fileURLs = []
for (let fileID of config.fileIDs){
for (let fileID of config.fileIDs) {
fileURLs.push("/files/" + fileID.toString())
}
newAction.model["url"] = fileURLs
@ -194,7 +155,6 @@ class ICAction extends React.Component {
newAction.results["type"] = "url"
newAction.results["token"] = this.props.token
newAction.results["url"] = "/results/RESULTID/file" // RESULTID serves as placeholder and is replaced later
}
// add the new action
@ -202,11 +162,10 @@ class ICAction extends React.Component {
} // end for loop over selected configs
let newResult = {}
newResult["result"] = {}
if (action.data.action === 'start') {
if (action.data.action === 'start') {
let configSnapshots = [];
// create config snapshots in case action is start
for (let config of this.props.selectedConfigs) {
@ -220,7 +179,6 @@ class ICAction extends React.Component {
newResult.result["configSnapshots"] = configSnapshots
}
console.log("Dispatching actions for configs", newActions, newResult)
AppDispatcher.dispatch({
type: 'ics/start-action',
@ -246,13 +204,15 @@ class ICAction extends React.Component {
render() {
let sendCommandDisabled = false;
if (!this.props.hasConfigs && this.props.selectedICs.length === 0 || this.state.selectedAction == null || this.state.selectedAction.id === "-1"){
sendCommandDisabled = true;
}
if (this.props.hasConfigs && this.props.selectedConfigs.length === 0|| this.state.selectedAction == null || this.state.selectedAction.id === "-1"){
sendCommandDisabled = true;
}
let disabled = this.state.selectedAction == null ||
(this.props.configs
? this.props.selectedConfigs.length === 0
: this.props.selectedICs.length === 0
);
let splitButtonStyle = {
marginLeft: '10px'
};
let time = this.state.time.getFullYear().pad(4) + '-' +
this.state.time.getMonth().pad(2) + '-' +
@ -266,29 +226,25 @@ class ICAction extends React.Component {
</Dropdown.Item>
));
return <div className='solid-button'>
<InputGroup>
<InputGroup.Prepend>
<DropdownButton
variant="secondary"
title={this.state.selectedAction != null ? this.state.selectedAction.title : ''}
id="action-dropdown"
onSelect={this.setAction}>
{actionList}
</DropdownButton>
<FormControl
type="datetime-local"
variant="outline-secondary"
value={time}
onChange={this.setTimeForAction} />
</InputGroup.Prepend>
<Button
variant="secondary"
disabled={sendCommandDisabled}
onClick={() => this.runAction(this.state.selectedAction, this.state.time)}>Run</Button>
</InputGroup>
<small className="text-muted">Select time for synced command execution</small>
</div>;
return <div>
<Form inline>
<Form.Control
type="datetime-local"
value={time}
onChange={this.setTimeForAction}
/>
<SplitButton
style={splitButtonStyle}
title={this.state.selectedAction != null ? this.state.selectedAction.title : ''}
id="action-dropdown"
onSelect={this.setAction}
disabled={disabled}
onClick={() => this.runAction(this.state.selectedAction, this.state.time)}>
{actionList}
</SplitButton>
</Form>
<small className="text-muted">Select time for synced command execution</small>
</div>;
}
}

View file

@ -1,11 +1,11 @@
import React from 'react';
import {Button, Row, Col} from 'react-bootstrap';
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;
@ -46,7 +46,6 @@ class ICDialog extends React.Component {
FileSaver.saveAs(blob, this.props.ic.name + ".svg");
}
render() {
let graphURL = ""
@ -54,11 +53,10 @@ class ICDialog extends React.Component {
graphURL = this.props.ic.apiurl + "/graph.svg"
}
return (
<Dialog
show={this.props.show}
title={this.props.ic.name + " ( " + this.props.ic.uuid + " )"}
title={this.props.ic.name}
buttonTitle="Close"
onClose={(c) => this.onClose(c)}
valid={true}
@ -68,27 +66,62 @@ class ICDialog extends React.Component {
<form>
<Row>
<Col>
<h5>State: {this.props.ic.state}</h5>
<h5>Category: {this.props.ic.category}</h5>
<h5>Type: {this.props.ic.type}</h5>
<h5>Uptime: {this.props.ic.uptime}</h5>
<h5>Location: {this.props.ic.location}</h5>
<h5>Description: {this.props.ic.description}</h5>
<h5>Websocket URL: {this.props.ic.websocketurl}</h5>
<h5>API URL: {this.props.ic.apiurl}</h5>
<h5>Start parameter scheme:</h5>
<ReactJson
src={this.props.ic.startParameterScheme}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={0}
/>
<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>
<ReactJson
src={this.props.ic.statusupdateraw}
@ -99,7 +132,7 @@ class ICDialog extends React.Component {
collapsed={1}
/>
{this.props.ic.type === "villas-node" || this.props.ic.type === "villas-relay" ? (
{this.props.ic.type === "villas-node" ?
<>
<div className='section-buttons-group-right'>
<Button style={{margin: '5px'}} size='sm' onClick={() => this.downloadGraph(graphURL)}><Icon
@ -110,29 +143,29 @@ class ICDialog extends React.Component {
<img alt={"Graph image download failed and/or incorrect image API URL"} src={graphURL}/>
</div>
{this.props.userRole === "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/>)}
{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/>)}
</>
: <div/>
}
</Col>
</Row>
</form>
</Dialog>
);
}
}

View file

@ -117,12 +117,7 @@ class InfrastructureComponentStore extends ArrayStore {
if(!tempIC.managedexternally){
tempIC.state = action.data.state;
tempIC.uptime = action.data.time_now - action.data.time_started;
if (tempIC.statusupdateraw === null || tempIC.statusupdateraw === undefined){
tempIC.statusupdateraw = {};
tempIC.statusupdateraw["status"] = action.data;
} else {
tempIC.statusupdateraw["status"] = action.data;
}
tempIC.statusupdateraw = action.data
AppDispatcher.dispatch({
type: 'ics/start-edit',
data: tempIC,
@ -138,12 +133,10 @@ class InfrastructureComponentStore extends ArrayStore {
case 'ics/nodestats-received':
let tempIC2 = action.ic;
if(!tempIC2.managedexternally){
if (tempIC2.statusupdateraw === null || tempIC2.statusupdateraw === undefined){
if (tempIC2.statusupdateraw === null || tempIC2.statusupdateraw === undefined) {
tempIC2.statusupdateraw = {};
tempIC2.statusupdateraw["statistics"] = action.data;
} else {
tempIC2.statusupdateraw["statistics"] = action.data;
}
tempIC2.statusupdateraw["statistics"] = action.data;
AppDispatcher.dispatch({
type: 'ics/start-edit',
data: tempIC2,

View file

@ -17,7 +17,7 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import {Button, Badge, Tooltip, OverlayTrigger} from 'react-bootstrap';
import { Button, Badge } from 'react-bootstrap';
import FileSaver from 'file-saver';
import _ from 'lodash';
import moment from 'moment'
@ -38,6 +38,7 @@ import ICAction from './ic-action';
import DeleteDialog from '../common/dialogs/delete-dialog';
import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
import NotificationsFactory from "../common/data-managers/notifications-factory";
import IconButton from '../common/icon-button';
class InfrastructureComponents extends Component {
static getStores() {
@ -64,7 +65,11 @@ class InfrastructureComponents extends Component {
}
}
static calculateState() {
static calculateState(prevState, props) {
if (prevState == null) {
prevState = {};
}
const ics = InfrastructureComponentStore.getState().sort((a, b) => {
if (a.state !== b.state) {
return InfrastructureComponents.statePrio(a.state) > InfrastructureComponents.statePrio(b.state);
@ -88,7 +93,6 @@ class InfrastructureComponents extends Component {
let services = ics.filter(ic => ic.category === "service")
let equipment = ics.filter(ic => ic.category === "equipment")
return {
sessionToken: localStorage.getItem("token"),
ics: ics,
@ -101,7 +105,7 @@ class InfrastructureComponents extends Component {
modalIC: {},
deleteModal: false,
icModal: false,
selectedICs: [],
selectedICs: prevState.selectedICs || [],
currentUser: JSON.parse(localStorage.getItem("currentUser"))
};
}
@ -112,7 +116,7 @@ class InfrastructureComponents extends Component {
token: this.state.sessionToken,
});
// Start timer for periodic refresh
// Start timer for periodic refresh
this.timer = window.setInterval(() => this.refresh(), 10000);
}
@ -121,7 +125,6 @@ class InfrastructureComponents extends Component {
}
refresh() {
if (this.state.editModal || this.state.deleteModal || this.state.icModal){
// do nothing since a dialog is open at the moment
}
@ -160,8 +163,9 @@ class InfrastructureComponents extends Component {
} else {
// externally managed IC: dispatch create action to selected manager
let newAction = {};
newAction["action"] = "create";
newAction["parameters"] = data;
newAction["parameters"] = data.parameters;
newAction["when"] = new Date()
// find the manager IC
@ -178,7 +182,6 @@ class InfrastructureComponents extends Component {
result: null,
token: this.state.sessionToken
});
}
}
}
@ -240,7 +243,6 @@ class InfrastructureComponents extends Component {
}
onICChecked(ic, event) {
let index = this.state.ics.indexOf(ic);
const selectedICs = Object.assign([], this.state.selectedICs);
for (let key in selectedICs) {
@ -266,8 +268,6 @@ class InfrastructureComponents extends Component {
this.setState({ selectedICs: selectedICs });
}
static isICOutdated(component) {
if (!component.stateUpdateAt)
return true;
@ -323,18 +323,18 @@ class InfrastructureComponents extends Component {
style.push('badge-default');
/* Possible states of ICs
* 'error': ['resetting', 'error'],
'idle': ['resetting', 'error', 'idle', 'starting', 'shuttingdown'],
'starting': ['resetting', 'error', 'running'],
'running': ['resetting', 'error', 'pausing', 'stopping'],
'pausing': ['resetting', 'error', 'paused'],
'paused': ['resetting', 'error', 'resuming', 'stopping'],
'resuming': ['resetting', 'error', 'running'],
'stopping': ['resetting', 'error', 'idle'],
'resetting': ['resetting', 'error', 'idle'],
'shuttingdown': ['shutdown', 'error'],
'shutdown': ['starting', 'error']
* */
* 'error': ['resetting', 'error'],
* 'idle': ['resetting', 'error', 'idle', 'starting', 'shuttingdown'],
* 'starting': ['resetting', 'error', 'running'],
* 'running': ['resetting', 'error', 'pausing', 'stopping'],
* 'pausing': ['resetting', 'error', 'paused'],
* 'paused': ['resetting', 'error', 'resuming', 'stopping'],
* 'resuming': ['resetting', 'error', 'running'],
* 'stopping': ['resetting', 'error', 'idle'],
* 'resetting': ['resetting', 'error', 'idle'],
* 'shuttingdown': ['shutdown', 'error'],
* 'shutdown': ['starting', 'error']
*/
}
return style.join(' ')
@ -409,6 +409,13 @@ class InfrastructureComponents extends Component {
onChecked={(ic, event) => this.onICChecked(ic, event)}
width='30'
/>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Name'
dataKeys={['name']}
@ -438,6 +445,7 @@ class InfrastructureComponents extends Component {
{this.state.currentUser.role === "Admin" ?
<TableColumn
width='150'
align='right'
editButton
showEditButton ={(index) => this.isLocalIC(index, ics)}
exportButton
@ -481,26 +489,23 @@ class InfrastructureComponents extends Component {
return (
<div className='section'>
<h1>Infrastructure Components
<h1>Infrastructure
{this.state.currentUser.role === "Admin" ?
(<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Infrastructure Component </Tooltip>} >
<Button variant='light' onClick={() => this.setState({newModal: true})} style={buttonStyle}><Icon icon="plus" classname='icon-color' style={iconStyle}
/></Button>
</OverlayTrigger>
<OverlayTrigger
key={2}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"import"}`}> Import Infrastructure Component </Tooltip>} >
<Button variant='light' onClick={() => this.setState({importModal: true})} style={buttonStyle}><Icon icon="upload" classname='icon-color' style={iconStyle}
/></Button>
</OverlayTrigger>
</span>)
:
(<span> </span>)
<span className='icon-button'>
<IconButton
alt={1}
tooltip='Add Infrastructure Component'
onClick={() => this.setState({newModal: true})}
icon='plus'
/>
<IconButton
alt={1}
tooltip='Import Infrastructure Component'
onClick={() => this.setState({importModal: true})}
icon='upload'
/>
</span>
: <span/>
}
</h1>
@ -513,20 +518,18 @@ class InfrastructureComponents extends Component {
{this.state.currentUser.role === "Admin" && this.state.numberOfExternalICs > 0 ?
<div style={{float: 'left'}}>
<ICAction
hasConfigs = {false}
ics={this.state.ics}
selectedICs={this.state.selectedICs}
token={this.state.sessionToken}
actions={[
{id: '-1', title: 'Action', data: {action: 'none'}},
{id: '0', title: 'Reset', data: {action: 'reset'}},
{id: '1', title: 'Shutdown', data: {action: 'shutdown'}},
{id: '2', title: 'Delete', data: {action: 'delete'}}
{id: '2', title: 'Delete', data: {action: 'delete'}},
{id: '3', title: 'Recreate', data: {action: 'create'}},
]}
/>
</div>
:
<div/>
: <div/>
}
<div style={{ clear: 'both' }} />
@ -540,9 +543,8 @@ class InfrastructureComponents extends Component {
onClose={data => this.closeICModal(data)}
ic={this.state.modalIC}
token={this.state.sessionToken}
userRole={this.state.currentUser.role}
user={this.state.currentUser}
sendControlCommand={(command, ic) => this.sendControlCommand(command, ic)}/>
</div>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import _ from 'lodash';
import Dialog from '../common/dialogs/dialog';
@ -111,29 +111,36 @@ class ImportICDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="Import Infrastructure Component" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup controlId="file">
<FormLabel>Infrastructure Component File</FormLabel>
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
</FormGroup>
<Dialog
show={this.props.show}
title="Import Infrastructure Component"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form>
<Form.Group controlId="file">
<Form.Label>Infrastructure Component File</Form.Label>
<Form.Control type="file" onChange={(e) => this.loadFile(e.target.files)} />
</Form.Group>
<FormGroup controlId="name" valid={this.validateForm('name')}>
<FormLabel>Name</FormLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="websocketurl">
<FormLabel>Websocket URL</FormLabel>
<FormControl type="text" placeholder="Enter websocketurl" value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="uuid" valid={this.validateForm('uuid')}>
<FormLabel>UUID</FormLabel>
<FormControl type="text" placeholder="Enter uuid" value={this.state.uuid} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
<Form.Group controlId="name" valid={this.validateForm('name')}>
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="websocketurl">
<Form.Label>Websocket URL</Form.Label>
<Form.Control type="text" placeholder="Enter websocketurl" value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="uuid" valid={this.validateForm('uuid')}>
<Form.Label>UUID</Form.Label>
<Form.Control type="text" placeholder="Enter uuid" value={this.state.uuid} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,8 +16,9 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, FormCheck, OverlayTrigger, Tooltip} from 'react-bootstrap';
import { Form, OverlayTrigger, Tooltip} from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
class NewICDialog extends React.Component {
valid = false;
@ -35,30 +36,39 @@ class NewICDialog extends React.Component {
managedexternally: false,
description: '',
location: '',
manager: ''
manager: '',
properties: {}
};
}
onClose(canceled) {
if (canceled === false) {
if (this.valid) {
const data = {
const parameters = {
name: this.state.name,
type: this.state.type,
category: this.state.category,
uuid: this.state.uuid,
managedexternally: this.state.managedexternally,
location: this.state.location,
description: this.state.description,
manager: this.state.manager
};
if (this.state.websocketurl != null && this.state.websocketurl !== "" && this.state.websocketurl !== 'http://') {
data.websocketurl = this.state.websocketurl;
}
if (this.state.apiurl != null && this.state.apiurl !== "" && this.state.apiurl !== 'http://') {
data.apiurl = this.state.apiurl;
const data = {
managedexternally: this.state.managedexternally,
manager: this.state.manager,
parameters: parameters
};
// Add custom properties
if (this.state.managedexternally)
Object.assign(parameters, this.state.properties);
if (this.state.websocketurl != null && this.state.websocketurl !== "") {
parameters.websocketurl = this.state.websocketurl;
}
if (this.state.apiurl != null && this.state.apiurl !== "") {
parameters.apiurl = this.state.apiurl;
}
this.props.onClose(data);
@ -75,12 +85,29 @@ class NewICDialog extends React.Component {
this.setState({ managedexternally : !this.state.managedexternally});
}
else{
this.setState({ [e.target.id]: e.target.value });
this.setState({ [e.target.id]: e.target.value });
}
}
handlePropertiesChange = properties => {
this.setState({
properties: properties
});
};
resetState() {
this.setState({ name: '', websocketurl: 'http://', apiurl: 'http://', uuid: this.uuidv4(), type: '', category: '', managedexternally: false, description: '', location: ''});
this.setState({
name: '',
websocketurl: '',
apiurl: '',
uuid: this.uuidv4(),
type: '',
category: '',
managedexternally: false,
description: '',
location: '',
properties: {}
});
}
validateForm(target) {
@ -137,10 +164,10 @@ class NewICDialog extends React.Component {
let typeOptions = [];
switch(this.state.category){
case "simulator":
typeOptions = ["dummy","generic","dpsim","rtlab","rscad","opalrt"];
typeOptions = ["dummy","generic","dpsim","rtlab","rscad","rtlab","kubernetes"];
break;
case "manager":
typeOptions = ["villas-node","villas-relay","generic"];
typeOptions = ["villas-node","villas-relay","generic","kubernetes"];
break;
case "gateway":
typeOptions = ["villas-node","villas-relay"];
@ -163,94 +190,109 @@ class NewICDialog extends React.Component {
);
}
return (
<Dialog show={this.props.show} title="New Infrastructure Component" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.validateForm()}>
<form>
<Dialog
show={this.props.show}
title="New Infrastructure Component"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.validateForm()}
>
<Form>
{this.props.managers.length > 0 ?
<>
<FormGroup controlId="managedexternally">
<Form.Group controlId="managedexternally">
<OverlayTrigger key="-1" placement={'left'} overlay={<Tooltip id={`tooltip-${"me"}`}>An externally managed component is created and managed by an IC manager via AMQP</Tooltip>} >
<FormCheck type={"checkbox"} label={"Managed externally"} defaultChecked={this.state.managedexternally} onChange={e => this.handleChange(e)}>
</FormCheck>
<Form.Check type={"checkbox"} label={"Managed externally"} defaultChecked={this.state.managedexternally} onChange={e => this.handleChange(e)}>
</Form.Check>
</OverlayTrigger>
</FormGroup>
</Form.Group>
{this.state.managedexternally === true ?
<FormGroup controlId="manager" valid={this.validateForm('manager')}>
<Form.Group controlId="manager" valid={this.validateForm('manager')}>
<OverlayTrigger key="0" placement={'right'} overlay={<Tooltip id={`tooltip-${"required"}`}> Required field </Tooltip>} >
<FormLabel>Manager to create new IC *</FormLabel>
<Form.Label>Manager to create new IC *</Form.Label>
</OverlayTrigger>
<FormControl as="select" value={this.state.manager} onChange={(e) => this.handleChange(e)}>
<Form.Control as="select" value={this.state.manager} onChange={(e) => this.handleChange(e)}>
{managerOptions}
</FormControl>
</FormGroup>
</Form.Control>
</Form.Group>
: <div/>
}
</>
: <div/>
}
<FormGroup controlId="name" valid={this.validateForm('name')}>
<OverlayTrigger key="1" placement={'right'} overlay={<Tooltip id={`tooltip-${"required"}`}> Required field </Tooltip>} >
<FormLabel>Name *</FormLabel>
</OverlayTrigger>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="category" valid={this.validateForm('category')}>
<Form.Group controlId="name" valid={this.validateForm('name')}>
<OverlayTrigger key="1" placement={'right'} overlay={<Tooltip id={`tooltip-${"required"}`}> Required field </Tooltip>} >
<Form.Label>Name *</Form.Label>
</OverlayTrigger>
<Form.Control type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
{this.state.managedexternally === false ?
<Form.Group controlId="uuid" valid={this.validateForm('uuid')}>
<Form.Label>UUID</Form.Label>
<Form.Control type="text" placeholder="Enter uuid" value={this.state.uuid}
onChange={(e) => this.handleChange(e)}/>
<Form.Control.Feedback/>
</Form.Group>
: <div/>
}
<Form.Group controlId="location">
<Form.Label>Location</Form.Label>
<Form.Control type="text" placeholder="Enter Location" value={this.state.location} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="description">
<Form.Label>Description</Form.Label>
<Form.Control type="text" placeholder="Enter Description" value={this.state.description} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="category" valid={this.validateForm('category')}>
<OverlayTrigger key="2" placement={'right'} overlay={<Tooltip id={`tooltip-${"required"}`}> Required field </Tooltip>} >
<FormLabel>Category of component *</FormLabel>
<Form.Label>Category of component *</Form.Label>
</OverlayTrigger>
<FormControl as="select" value={this.state.category} onChange={(e) => this.handleChange(e)}>
<Form.Control as="select" value={this.state.category} onChange={(e) => this.handleChange(e)}>
<option default>Select category</option>
<option>simulator</option>
<option>service</option>
<option>gateway</option>
<option>equipment</option>
<option>manager</option>
</FormControl>
</FormGroup>
<FormGroup controlId="type" valid={this.validateForm('type')}>
<option value="simulator">Simulator</option>
<option value="service">Service</option>
<option value="gateway">Gateway</option>
<option value="equipment">Equipment</option>
<option value="manager">Manager</option>
</Form.Control>
</Form.Group>
<Form.Group controlId="type" valid={this.validateForm('type')}>
<OverlayTrigger key="3" placement={'right'} overlay={<Tooltip id={`tooltip-${"required"}`}> Required field </Tooltip>} >
<FormLabel>Type of component *</FormLabel>
<Form.Label>Type of component *</Form.Label>
</OverlayTrigger>
<FormControl as="select" value={this.state.type} onChange={(e) => this.handleChange(e)}>
<Form.Control as="select" value={this.state.type} onChange={(e) => this.handleChange(e)}>
<option default>Select type</option>
{typeOptions.map((name,index) => (
<option key={index}>{name}</option>
))}
</FormControl>
</FormGroup>
<FormGroup controlId="websocketurl">
<FormLabel>Websocket URL</FormLabel>
<FormControl type="text" placeholder="Enter Websocket URL" value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="apiurl">
<FormLabel>API URL</FormLabel>
<FormControl type="text" placeholder="Enter API URL" value={this.state.apiurl} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="location">
<FormLabel>Location</FormLabel>
<FormControl type="text" placeholder="Enter Location" value={this.state.location} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup controlId="description">
<FormLabel>Description</FormLabel>
<FormControl type="text" placeholder="Enter Description" value={this.state.description} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
{this.state.managedexternally === false ?
<FormGroup controlId="uuid" valid={this.validateForm('uuid')}>
<FormLabel>UUID</FormLabel>
<FormControl type="text" placeholder="Enter uuid" value={this.state.uuid}
onChange={(e) => this.handleChange(e)}/>
<FormControl.Feedback/>
</FormGroup>
</Form.Control>
</Form.Group>
<Form.Group controlId="websocketurl">
<Form.Label>Websocket URL</Form.Label>
<Form.Control type="text" placeholder="https://" value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group controlId="apiurl">
<Form.Label>API URL</Form.Label>
<Form.Control type="text" placeholder="https://" value={this.state.apiurl} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
{this.state.managedexternally === true ?
<Form.Group controlId='properties'>
<Form.Label> Properties </Form.Label>
<ParametersEditor
content={this.state.properties}
onChange={(data) => this.handlePropertiesChange(data)}
/>
</Form.Group>
: <div/>
}
</form>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, Col, Row, Button, ProgressBar } from 'react-bootstrap';
import { Form, Col, Row, Button, ProgressBar } from 'react-bootstrap';
import AppDispatcher from "../common/app-dispatcher";
import FileStore from "../file/file-store"
@ -131,78 +131,90 @@ class EditResultDialog extends React.Component {
}
render() {
return <Dialog show={this.props.show}
return <Dialog
show={this.props.show}
title={'Edit Result No. ' + this.state.id}
buttonTitle='Close'
onClose={() => this.onClose()}
blendOutCancel={true}
valid={true}
size='lg'>
>
<Form.Group as={Col} controlId='description'>
<Row style={{ float: 'center' }} >
<Col xs lg="2">
<Form.Label>Description</Form.Label>
</Col>
<Col xs lg="4">
<Form.Control
type='text'
placeholder={this.state.description}
value={this.state.description}
onChange={this.handleChange}
/>
<Form.Control.Feedback />
</Col>
<Col xs lg="2">
<Button
type="submit"
onClick={() => this.submitDescription()}>
Save
</Button>
</Col>
</Row>
</Form.Group>
<Table data={this.state.files}>
<TableColumn
title='ID'
dataKey='id'
/>
<TableColumn
title='Name'
dataKey='name'
/>
<TableColumn
title='Size (bytes)'
dataKey='size'
/>
<TableColumn
title='Type'
dataKey='type'
/>
<TableColumn
title=''
deleteButton
onDelete={(index) => this.deleteFile(index)}
/>
</Table>
<div>
<FormGroup as={Col} controlId='description'>
<Row style={{ float: 'center' }} >
<Col xs lg="2">
<FormLabel>Description</FormLabel>
</Col>
<Col xs lg="4">
<FormControl type='text' placeholder={this.state.description} value={this.state.description} onChange={this.handleChange} />
<FormControl.Feedback />
</Col>
<Col xs lg="2">
<Button
type="submit"
onClick={() => this.submitDescription()}>
Save
</Button>
</Col>
</Row>
</FormGroup>
<Table data={this.state.files}>
<TableColumn title='ID' dataKey='id' />
<TableColumn title='Name' dataKey='name' />
<TableColumn title='Size (bytes)' dataKey='size' />
<TableColumn title='Type' dataKey='type' />
<TableColumn
title=''
deleteButton
onDelete={(index) => this.deleteFile(index)}
/>
</Table>
<div style={{ float: 'center' }}>
<h5>Add result file</h5>
<Row>
<Col xs lg="4">
<FormControl type='file' onChange={(event) => this.selectUploadFile(event)} />
</Col>
<Col xs lg="2">
<Button
disabled={this.state.uploadFile === null}
onClick={() => this.startFileUpload()}>
Upload
</Button>
</Col>
</Row>
</div>
<br></br>
<FormGroup as={Col} >
<ProgressBar
striped={true}
animated={true}
now={this.state.uploadProgress}
label={this.state.uploadProgress + '%'}
/>
</FormGroup>
<div style={{ float: 'center' }}>
<h5>Add result file</h5>
<Row>
<Col xs lg="4">
<Form.Control type='file' onChange={(event) => this.selectUploadFile(event)} />
</Col>
<Col xs lg="2">
<Button
disabled={this.state.uploadFile === null}
onClick={() => this.startFileUpload()}>
Upload
</Button>
</Col>
</Row>
</div>
<br />
<Form.Group as={Col} >
<ProgressBar
striped={true}
animated={true}
now={this.state.uploadProgress}
label={this.state.uploadProgress + '%'}
/>
</Form.Group>
</Dialog>;
}
}
export default EditResultDialog;
export default EditResultDialog;

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -45,7 +45,7 @@ class NewResultDialog extends React.Component {
}
resetState() {
this.setState({
this.setState({
ConfigSnapshots: '',
Description: '',
ResultFileIDs: [],
@ -54,14 +54,21 @@ class NewResultDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="New Result" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={true}>
<form>
<FormGroup controlId="Description">
<FormLabel>Description</FormLabel>
<FormControl type="text" placeholder="Enter description" value={this.state.Description} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
</form>
<Dialog
show={this.props.show}
title="New Result"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={true}
>
<Form>
<Form.Group controlId="Description">
<Form.Label>Description</Form.Label>
<Form.Control type="text" placeholder="Enter description" value={this.state.Description} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -15,6 +15,7 @@
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import React from 'react';
import { Form } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import ReactJson from 'react-json-view';
@ -46,16 +47,16 @@ class ResultConfigDialog extends React.Component {
size="lg"
blendOutCancel={true}
>
<form>
<ReactJson
src={this.props.configs}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={false}
/>
</form>
<Form>
<ReactJson
src={this.props.configs}
name={false}
displayDataTypes={false}
displayObjectSize={false}
enableClipboard={false}
collapsed={false}
/>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
@ -53,13 +53,11 @@ class EditScenarioDialog extends React.Component {
this.setState({ [event.target.id]: event.target.value });
let name = true;
if (this.state.name === '') {
name = false;
}
this.valid = name;
};
resetState = () => {
@ -76,20 +74,26 @@ class EditScenarioDialog extends React.Component {
};
render() {
return <Dialog show={this.props.show} title='Edit Scenario' buttonTitle='Save' onClose={this.onClose} onReset={this.resetState} valid={true}>
<form>
<FormGroup as={Col} controlId='name'>
<FormLabel column={false}>Name</FormLabel>
<FormControl type='text' placeholder='Enter name' value={this.state.name} onChange={this.handleChange} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId='startParameters'>
<FormLabel column={false}>Start Parameters</FormLabel>
return <Dialog
show={this.props.show}
title='Edit Scenario'
buttonTitle='Save'
onClose={this.onClose}
onReset={this.resetState}
valid={true}
>
<Form>
<Form.Group as={Col} controlId='name'>
<Form.Label column={false}>Name</Form.Label>
<Form.Control type='text' placeholder='Enter name' value={this.state.name} onChange={this.handleChange} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId='startParameters'>
<Form.Label column={false}>Start Parameters</Form.Label>
<ParametersEditor content={this.state.startParameters} onChange={this.handleStartParametersChange} />
</FormGroup>
</form>
</Form.Group>
</Form>
</Dialog>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
@ -93,26 +93,32 @@ class ImportScenarioDialog extends React.Component {
}
render() {
return <Dialog show={this.props.show} title="Import Scenario" buttonTitle="Import" onClose={this.onClose} onReset={this.resetState} valid={this.valid}>
<form>
<FormGroup as={Col} controlId="file">
<FormLabel>Scenario File</FormLabel>
<FormControl type="file" onChange={this.loadFile} />
</FormGroup>
return <Dialog
show={this.props.show}
title="Import Scenario"
buttonTitle="Import"
onClose={this.onClose}
onReset={this.resetState}
valid={this.valid}
>
<Form>
<Form.Group as={Col} controlId="file">
<Form.Label>Scenario File</Form.Label>
<Form.Control type="file" onChange={this.loadFile} />
</Form.Group>
<FormGroup as={Col} controlId="name">
<FormLabel>Name</FormLabel>
<FormControl readOnly={this.imported === false} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<Form.Group as={Col} controlId="name">
<Form.Label>Name</Form.Label>
<Form.Control readOnly={this.imported === false} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<FormGroup as={Col}>
<FormLabel>Start Parameters</FormLabel>
<Form.Group as={Col}>
<Form.Label>Start Parameters</Form.Label>
<ParametersEditor content={this.state.startParameters} onChange={this.handleStartParametersChange} disabled={this.imported === false} />
</FormGroup>
</form>
</Form.Group>
</Form>
</Dialog>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import { Form, Col} from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
import ParametersEditor from '../common/parameters-editor';
@ -70,20 +70,26 @@ class NewScenarioDialog extends React.Component {
}
render() {
return <Dialog show={this.props.show} title="New Scenario" buttonTitle="Add" onClose={this.onClose} onReset={this.resetState} valid={this.valid}>
<form>
<FormGroup as={Col} controlId="name">
<FormLabel>Name</FormLabel>
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={this.handleChange} />
<FormControl.Feedback />
</FormGroup>
return <Dialog
show={this.props.show}
title="New Scenario"
buttonTitle="Add"
onClose={this.onClose}
onReset={this.resetState}
valid={this.valid}>
<Form>
<Form.Group as={Col} controlId="name">
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Enter name" value={this.state.name} onChange={this.handleChange} />
<Form.Control.Feedback />
</Form.Group>
<FormGroup as={Col}>
<FormLabel>Start Parameters</FormLabel>
<Form.Group as={Col}>
<Form.Label>Start Parameters</Form.Label>
<ParametersEditor content={this.state.startParameters} onChange={this.handleStartParametersChange} />
</FormGroup>
</form>
</Form.Group>
</Form>
</Dialog>;
}
}

View file

@ -17,7 +17,7 @@
import React from 'react';
import { Container } from 'flux/utils';
import { Button, InputGroup, FormControl, Tooltip, OverlayTrigger } from 'react-bootstrap';
import { Button, InputGroup, Form } from 'react-bootstrap';
import FileSaver from 'file-saver';
@ -31,6 +31,7 @@ import AppDispatcher from '../common/app-dispatcher';
import Icon from '../common/icon';
import Table from '../common/table';
import TableColumn from '../common/table-column';
import IconButton from '../common/icon-button';
import ImportConfigDialog from '../componentconfig/import-config';
import ImportDashboardDialog from "../dashboard/import-dashboard";
import NewDashboardDialog from "../dashboard/new-dashboard";
@ -44,7 +45,7 @@ import ResultConfigDialog from '../result/result-configs-dialog';
import ICAction from '../ic/ic-action';
import DeleteDialog from '../common/dialogs/delete-dialog';
import EditConfigDialog from "../componentconfig/edit-config";
import EditSignalMapping from "../signal/edit-signal-mapping";
import EditSignalMappingDialog from "../signal/edit-signal-mapping";
import FileStore from "../file/file-store"
import WidgetStore from "../widget/widget-store";
import ResultStore from "../result/result-store"
@ -63,6 +64,7 @@ class Scenario extends React.Component {
if (prevState == null) {
prevState = {};
}
// get selected scenario
const sessionToken = localStorage.getItem("token");
@ -109,7 +111,7 @@ class Scenario extends React.Component {
deleteConfigModal: false,
importConfigModal: false,
newConfig: prevState.newConfig || false,
selectedConfigs: [],
selectedConfigs: prevState.selectedConfigs || [],
filesEditModal: prevState.filesEditModal || false,
filesEditSaveState: prevState.filesEditSaveState || [],
@ -376,7 +378,7 @@ class Scenario extends React.Component {
let ic = null;
for (let component of this.state.ics) {
if (component.id === this.state.configs[index].icID) {
if (component.id === icID) {
ic = component;
}
}
@ -391,7 +393,6 @@ class Scenario extends React.Component {
}
return false
}
getICName(icID) {
@ -581,22 +582,18 @@ class Scenario extends React.Component {
* File modification methods
############################################## */
getListOfFiles(fileIDs, types) {
let fileList = '';
getListOfFiles(files, fileIDs) {
let fileList = [];
for (let id of fileIDs) {
for (let file of this.state.files) {
if (file.id === id && types.some(e => file.type.includes(e))) {
if (fileList === '') {
fileList = file.name
} else {
fileList = fileList + ';' + file.name;
}
for (let file of files) {
if (file.id === id) {
fileList.push(file.name)
}
}
}
return fileList;
return fileList.join(';');
}
/* ##############################################
@ -738,63 +735,14 @@ class Scenario extends React.Component {
return <h1>Loading Scenario...</h1>;
}
let resulttable;
if (this.state.results && this.state.results.length > 0) {
resulttable = <div>
<Table data={this.state.results}>
<TableColumn
title='Result No.'
dataKey='id'
modifier={(id, result) => this.modifyResultNoColumn(id, result)}
/>
<TableColumn title='Description' dataKey='description' />
<TableColumn title='Created at' dataKey='createdAt' />
<TableColumn title='Last update' dataKey='updatedAt' />
<TableColumn
title='Files/Data'
dataKey='resultFileIDs'
linkKey='filebuttons'
data={this.state.files}
width='300'
onDownload={(index) => this.downloadResultData(index)}
/>
<TableColumn
title='Options'
width='300'
editButton
downloadAllButton
deleteButton
onEdit={index => this.setState({ editResultsModal: true, modalResultsIndex: index })}
onDownloadAll={(index) => this.downloadResultData(this.state.results[index])}
onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })}
/>
</Table>
<EditResultDialog
sessionToken={this.state.sessionToken}
show={this.state.editResultsModal}
files={this.state.files}
results={this.state.results}
resultId={this.state.modalResultsIndex}
scenarioID={this.state.scenario.id}
onClose={this.closeEditResultsModal.bind(this)} />
<DeleteDialog title="result" name={this.state.modalResultsData.id} show={this.state.deleteResultsModal} onClose={(e) => this.closeDeleteResultsModal(e)} />
<ResultConfigDialog
show={this.state.resultConfigsModal}
configs={this.state.modalResultConfigs}
resultNo={this.state.modalResultConfigsIndex}
onClose={this.closeResultConfigSnapshots.bind(this)}
/>
</div>
}
return <div className='section'>
<div className='section-buttons-group-right'>
<OverlayTrigger key={0} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"file"}`}> Add, edit or delete files of scenario </Tooltip>} >
<Button variant='light' key={0} size="lg" onClick={this.onEditFiles.bind(this)}>
<Icon icon="file" classname={'icon-color'} style={iconStyle}/>
</Button>
</OverlayTrigger>
<IconButton
key="0"
tooltip="Add, edit or delete files of scenario"
onClick={this.onEditFiles.bind(this)}
icon="file"
/>
</div>
<h1>{this.state.scenario.name}</h1>
@ -810,34 +758,37 @@ class Scenario extends React.Component {
{/*Component Configurations table*/}
<h2 style={tableHeadingStyle}>Component Configurations
<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Component Configuration </Tooltip>} >
<Button variant='light' onClick={() => this.addConfig()} style={altButtonStyle}><Icon icon="plus" classname={'icon-color'} style={iconStyle} /></Button>
</OverlayTrigger>
<OverlayTrigger
key={2}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"import"}`}> Import Component Configuration </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ importConfigModal: true })} style={altButtonStyle}><Icon icon="upload" classname={'icon-color'} style={iconStyle}/></Button>
</OverlayTrigger>
</span>
<IconButton
key={0}
tooltip='Add Component Configuration'
onClick={() => this.addConfig()}
icon='plus'
/>
<IconButton
key={1}
tooltip='Import Component Configuration'
onClick={() => this.setState({ importConfigModal: true })}
icon='upload'
/>
</span>
</h2>
<Table data={this.state.configs}>
<TableColumn
checkbox
checkboxDisabled={(index) => this.usesExternalIC(index)}
checkboxDisabled={(index) => !this.usesExternalIC(index)}
onChecked={(index, event) => this.onConfigChecked(index, event)}
width='30' />
<TableColumn title='Name' dataKey='name' />
<TableColumn title='Configuration file(s)' dataKey='fileIDs' modifier={(fileIDs) => this.getListOfFiles(fileIDs, ['json', 'JSON'])} />
width='30'
/>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Model file(s)'
dataKey='fileIDs'
modifier={(fileIDs) => this.getListOfFiles(fileIDs, ['xml'])}
editButton
onEdit={(index) => this.startPintura(index)}
title='Name'
dataKey='name'
/>
<TableColumn
title='# Output Signals'
@ -852,14 +803,19 @@ class Scenario extends React.Component {
onEdit={index => this.setState({ editInputSignalsModal: true, modalConfigData: this.state.configs[index], modalConfigIndex: index })}
/>
<TableColumn
title='Signal AutoConf'
title='Import Signals'
exportButton
onExport={(index) => this.signalsAutoConf(index)}
/>
<TableColumn title='Infrastructure Component' dataKey='icID' modifier={(icID) => this.getICName(icID)} />
<TableColumn
title='Infrastructure Component'
dataKey='icID'
modifier={(icID) => this.getICName(icID)}
/>
<TableColumn
title=''
width='200'
align='right'
editButton
deleteButton
exportButton
@ -871,27 +827,25 @@ class Scenario extends React.Component {
/>
</Table>
{this.state.ExternalICInUse ? (
{this.state.ExternalICInUse ?
<div style={{ float: 'left' }}>
<ICAction
hasConfigs={true}
ics={this.state.ics}
configs={this.state.configs}
selectedConfigs = {this.state.selectedConfigs}
snapshotConfig = {(index) => this.copyConfig(index)}
token = {this.state.sessionToken}
actions={[
{ id: '-1', title: 'Action', data: { action: 'none' } },
{ 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 />)
: <div />
}
< div style={{ clear: 'both' }} />
<div style={{ clear: 'both' }} />
<EditConfigDialog
show={this.state.editConfigModal}
@ -901,11 +855,18 @@ class Scenario extends React.Component {
files={this.state.files}
sessionToken={this.state.sessionToken}
/>
<ImportConfigDialog show={this.state.importConfigModal} onClose={data => this.importConfig(data)} ics={this.state.ics} />
<DeleteDialog title="component configuration" name={this.state.modalConfigData.name} show={this.state.deleteConfigModal} onClose={(c) => this.closeDeleteConfigModal(c)} />
<EditSignalMapping
<ImportConfigDialog
show={this.state.importConfigModal}
onClose={data => this.importConfig(data)}
ics={this.state.ics}
/>
<DeleteDialog
title="component configuration"
name={this.state.modalConfigData.name}
show={this.state.deleteConfigModal}
onClose={(c) => this.closeDeleteConfigModal(c)}
/>
<EditSignalMappingDialog
show={this.state.editOutputSignalsModal}
onCloseEdit={(direction) => this.closeEditSignalsModal(direction)}
direction="Output"
@ -913,7 +874,7 @@ class Scenario extends React.Component {
configID={this.state.modalConfigData.id}
sessionToken={this.state.sessionToken}
/>
<EditSignalMapping
<EditSignalMappingDialog
show={this.state.editInputSignalsModal}
onCloseEdit={(direction) => this.closeEditSignalsModal(direction)}
direction="Input"
@ -925,26 +886,41 @@ class Scenario extends React.Component {
{/*Dashboard table*/}
<h2 style={tableHeadingStyle}>Dashboards
<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Dashboard </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ newDashboardModal: true })} style={altButtonStyle}><Icon icon="plus" classname={'icon-color'} style={iconStyle} /></Button>
</OverlayTrigger>
<OverlayTrigger
key={2}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"import"}`}> Import Dashboard </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ importDashboardModal: true })} style={altButtonStyle}><Icon icon="upload" classname={'icon-color'} style={iconStyle} /></Button>
</OverlayTrigger>
</span>
<IconButton
key={0}
tooltip='Add Dashboard'
onClick={() => this.setState({ newDashboardModal: true })}
icon='plus'
/>
<IconButton
key={1}
tooltip='Import Dashboard'
onClick={() => this.setState({ importDashboardModal: true })}
icon='upload'
/>
</span>
</h2>
<Table data={this.state.dashboards}>
<TableColumn title='Name' dataKey='name' link='/dashboards/' linkKey='id' />
<TableColumn title='Grid' dataKey='grid' />
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Name'
dataKey='name'
link='/dashboards/'
linkKey='id'
/>
<TableColumn
title='Grid'
dataKey='grid' />
<TableColumn
title=''
width='200'
align='right'
editButton
deleteButton
exportButton
@ -956,48 +932,145 @@ class Scenario extends React.Component {
/>
</Table>
<NewDashboardDialog show={this.state.newDashboardModal} onClose={data => this.closeNewDashboardModal(data)} />
<EditDashboardDialog show={this.state.dashboardEditModal} dashboard={this.state.modalDashboardData} onClose={data => this.closeEditDashboardModal(data)} />
<ImportDashboardDialog show={this.state.importDashboardModal} onClose={data => this.closeImportDashboardModal(data)} />
<DeleteDialog title="dashboard" name={this.state.modalDashboardData.name} show={this.state.deleteDashboardModal} onClose={(e) => this.closeDeleteDashboardModal(e)} />
<NewDashboardDialog
show={this.state.newDashboardModal}
onClose={data => this.closeNewDashboardModal(data)}
/>
<EditDashboardDialog
show={this.state.dashboardEditModal}
dashboard={this.state.modalDashboardData}
onClose={data => this.closeEditDashboardModal(data)}
/>
<ImportDashboardDialog
show={this.state.importDashboardModal}
onClose={data => this.closeImportDashboardModal(data)}
/>
<DeleteDialog
title="dashboard"
name={this.state.modalDashboardData.name}
show={this.state.deleteDashboardModal}
onClose={(e) => this.closeDeleteDashboardModal(e)}
/>
{/*Result table*/}
<h2 style={tableHeadingStyle}>Results
<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Result </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ newResultModal: true })} style={altButtonStyle}><Icon icon="plus" classname={'icon-color'} style={iconStyle} /></Button>
</OverlayTrigger>
</span>
<IconButton
key={1}
tooltip='Add Result'
onClick={() => this.setState({ newResultModal: true })}
icon='plus'
/>
</span>
</h2>
{resulttable}
<NewResultDialog show={this.state.newResultModal} onClose={data => this.closeNewResultModal(data)} />
<Table data={this.state.results}>
<TableColumn
title='ID'
dataKey='id'
modifier={(id, result) => this.modifyResultNoColumn(id, result)}
/>
<TableColumn
title='Description'
dataKey='description'
/>
<TableColumn
title='Created at'
dataKey='createdAt'
/>
<TableColumn
title='Last update'
dataKey='updatedAt'
/>
<TableColumn
title='Files'
dataKey='resultFileIDs'
linkKey='filebuttons'
data={this.state.files}
onDownload={(index) => this.downloadResultData(index)}
/>
<TableColumn
width='200'
align='right'
editButton
downloadAllButton
deleteButton
onEdit={index => this.setState({ editResultsModal: true, modalResultsIndex: index })}
onDownloadAll={(index) => this.downloadResultData(this.state.results[index])}
onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })}
/>
</Table>
<EditResultDialog
sessionToken={this.state.sessionToken}
show={this.state.editResultsModal}
files={this.state.files}
results={this.state.results}
resultId={this.state.modalResultsIndex}
scenarioID={this.state.scenario.id}
onClose={this.closeEditResultsModal.bind(this)}
/>
<DeleteDialog
title="result"
name={this.state.modalResultsData.id}
show={this.state.deleteResultsModal}
onClose={(e) => this.closeDeleteResultsModal(e)}
/>
<ResultConfigDialog
show={this.state.resultConfigsModal}
configs={this.state.modalResultConfigs}
resultNo={this.state.modalResultConfigsIndex}
onClose={this.closeResultConfigSnapshots.bind(this)}
/>
<NewResultDialog
show={this.state.newResultModal}
onClose={data => this.closeNewResultModal(data)}
/>
{/*Scenario Users table*/}
<h2 style={tableHeadingStyle}>Users sharing this scenario</h2>
<div>
<Table data={this.state.scenario.users}>
<TableColumn title='Name' dataKey='username' />
<TableColumn title='Mail' dataKey='mail' />
<Table data={this.state.scenario.users}>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title=''
width='200'
deleteButton
onDelete={(index) => this.setState({ deleteUserModal: true, deleteUserName: this.state.scenario.users[index].username, modalUserIndex: index })}
title='ID'
dataKey='id'
/>
</Table>
: <></>
}
<TableColumn
title='Name'
dataKey='username'
/>
<TableColumn
title='Role'
dataKey='role'
/>
<TableColumn
title=''
width='200'
align='right'
deleteButton
onDelete={(index) => this.setState({
deleteUserModal: true,
deleteUserName: this.state.scenario.users[index].username,
modalUserIndex: index
})}
/>
</Table>
<InputGroup style={{ width: 400, float: 'right' }}>
<FormControl
placeholder="Username"
onChange={(e) => this.onUserInputChange(e)}
value={this.state.userToAdd}
type="text"
/>
<InputGroup.Append>
<InputGroup
style={{
width: 400,
float: 'right'
}}
>
<Form.Control
placeholder="Username"
onChange={(e) => this.onUserInputChange(e)}
value={this.state.userToAdd}
type="text"
/>
<InputGroup.Append>
<span className='icon-button'>
<Button
variant='light'
@ -1006,14 +1079,18 @@ class Scenario extends React.Component {
onClick={() => this.addUser()}>
<Icon icon="plus" classname={'icon-color'} style={iconStyle} />
</Button>
</span>
</InputGroup.Append>
</InputGroup><br /><br />
</div>
<DeleteDialog title="user from scenario:" name={this.state.deleteUserName} show={this.state.deleteUserModal} onClose={(c) => this.closeDeleteUserModal(c)} />
</span>
</InputGroup.Append>
</InputGroup>
<br />
<br />
<DeleteDialog
title="Delete user from scenario"
name={this.state.deleteUserName}
show={this.state.deleteUserModal}
onClose={(c) => this.closeDeleteUserModal(c)}
/>
</div>;
}
}

View file

@ -17,7 +17,6 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import {Button, OverlayTrigger, Tooltip} from 'react-bootstrap';
import FileSaver from 'file-saver';
import AppDispatcher from '../common/app-dispatcher';
@ -35,6 +34,7 @@ import EditScenarioDialog from './edit-scenario';
import ImportScenarioDialog from './import-scenario';
import DeleteDialog from '../common/dialogs/delete-dialog';
import IconButton from '../common/icon-button';
class Scenarios extends Component {
@ -43,7 +43,10 @@ class Scenarios extends Component {
return [ScenarioStore, DashboardStore, WidgetStore, ConfigStore, SignalStore];
}
static calculateState() {
static calculateState(prevState, props) {
if (prevState == null) {
prevState = {};
}
return {
scenarios: ScenarioStore.getState(),
@ -57,8 +60,8 @@ class Scenarios extends Component {
editModal: false,
importModal: false,
modalScenario: {},
selectedScenarios: []
selectedScenarios: prevState.selectedScenarios || [],
currentUser: JSON.parse(localStorage.getItem("currentUser"))
};
}
@ -225,50 +228,50 @@ class Scenarios extends Component {
}
modifyRunningColumn(running){
if(running){
return <Icon icon='check' />
} else {
return <Icon icon='times' />
}
return <Icon icon={ running ? 'check' : 'times' } />
}
render() {
const buttonStyle = {
marginLeft: '10px',
};
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div className='section'>
return <div className='section'>
<h1>Scenarios
<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Scenario </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ newModal: true })} style={buttonStyle}><Icon icon="plus" classname='icon-color' style={iconStyle} /></Button>
</OverlayTrigger>
<OverlayTrigger
key={2}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"import"}`}> Import Scenario </Tooltip>} >
<Button variant='light' onClick={() => this.setState({ importModal: true })} style={buttonStyle}><Icon icon="upload" classname='icon-color' style={iconStyle} /></Button>
</OverlayTrigger>
</span>
<IconButton
key={0}
tooltip='Add Scenario'
onClick={() => this.setState({ newModal: true })}
icon='plus'
/>
<IconButton
key={1}
tooltip='Import Scenario'
onClick={() => this.setState({ importModal: true })}
icon='upload'
/>
</span>
</h1>
<Table data={this.state.scenarios}>
<TableColumn title='Name' dataKey='name' link='/scenarios/' linkKey='id' />
<TableColumn title='ID' dataKey='id' />
<TableColumn title='Running' dataKey='running' modifier={(running) => this.modifyRunningColumn(running)}/>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Name'
dataKey='name'
link='/scenarios/'
linkKey='id'
/>
<TableColumn
title='Running'
dataKey='running'
modifier={(running) => this.modifyRunningColumn(running)}
/>
<TableColumn
width='200'
align='right'
editButton
deleteButton
exportButton
@ -285,8 +288,7 @@ class Scenarios extends Component {
<ImportScenarioDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} nodes={this.state.nodes} />
<DeleteDialog title="scenario" name={this.state.modalScenario.name} show={this.state.deleteModal} onClose={(e) => this.closeDeleteModal(e)} />
</div>
);
</div>;
}
}

View file

@ -17,27 +17,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Button, FormGroup, FormLabel, FormText, OverlayTrigger, Tooltip} from 'react-bootstrap';
import {Collapse} from 'react-collapse';
import { Button, Form, OverlayTrigger, Tooltip } from 'react-bootstrap';
import { Collapse } from 'react-collapse';
import Table from '../common/table';
import TableColumn from '../common/table-column';
import Dialog from "../common/dialogs/dialog";
import Icon from "../common/icon";
import AppDispatcher from "../common/app-dispatcher";
class EditSignalMapping extends React.Component {
class EditSignalMappingDialog extends React.Component {
constructor(props) {
super(props);
let dir = "";
if ( this.props.direction === "Output"){
if ( this.props.direction === "Output") {
dir = "out";
} else if ( this.props.direction === "Input" ){
} else if ( this.props.direction === "Input" ) {
dir = "in";
}
this.state = {
dir,
signals: [],
@ -46,38 +45,37 @@ class EditSignalMapping extends React.Component {
};
}
static getDerivedStateFromProps(props, state){
static getDerivedStateFromProps(props, state) {
// filter all signals by configID and direction
let signals = [];
if(props.signalID != null || typeof props.configs === "undefined"){
if (props.signalID != null || typeof props.configs === "undefined") {
signals = props.signals.filter((sig) => {
return (sig.configID === props.configID) && (sig.direction === state.dir);
});
}
else{
for(let i = 0; i < props.configs.length; i++){
else {
for (let i = 0; i < props.configs.length; i++) {
let temp = props.signals.filter((sig) => {
return (sig.configID === props.configs[i].id) && (sig.direction === state.dir);
})
signals = signals.concat(temp);
signals = signals.concat(temp);
}
}
}
signals.forEach(signal => {
if(signal.checked === undefined) signal.checked = false
});
signals.forEach(signal => {
if (signal.checked === undefined) {
signal.checked = false;
}
});
return {
signals: signals,
};
}
onClose(canceled) {
for (let signalID of this.state.modifiedSignalIDs){
for (let signalID of this.state.modifiedSignalIDs) {
let sig = this.state.signals.find(s => s.id === signalID);
@ -100,23 +98,23 @@ class EditSignalMapping extends React.Component {
console.log("HandleMappingChange", row, column)
if (column === 2) { // Name change
signals[row].name = event.target.value;
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
if (modifiedSignals.find(id => id === signals[row].id) === undefined) {
modifiedSignals.push(signals[row].id);
}
} else if (column === 3) { // unit change
signals[row].unit = event.target.value;
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
if (modifiedSignals.find(id => id === signals[row].id) === undefined) {
modifiedSignals.push(signals[row].id);
}
} else if (column === 4) { // scaling factor change
signals[row].scalingFactor = parseFloat(event.target.value);
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
if (modifiedSignals.find(id => id === signals[row].id) === undefined) {
modifiedSignals.push(signals[row].id);
}
} else if (column === 1) { //index change
console.log("Index change")
signals[row].index =parseInt(event.target.value, 10);
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
if (modifiedSignals.find(id => id === signals[row].id) === undefined) {
modifiedSignals.push(signals[row].id);
}
}
@ -125,11 +123,9 @@ class EditSignalMapping extends React.Component {
signals: signals,
modifiedSignalIDs: modifiedSignals
})
};
handleDelete = (index) => {
let data = this.state.signals[index]
AppDispatcher.dispatch({
@ -137,11 +133,9 @@ class EditSignalMapping extends React.Component {
data: data,
token: this.props.sessionToken
});
};
handleRemove = () => {
let checkedSignals = this.state.signals.filter(signal => signal.checked === true);
checkedSignals.forEach(signal => {
@ -150,23 +144,20 @@ class EditSignalMapping extends React.Component {
data: signal,
token: this.props.sessionToken
});
})
}
handleAdd = (configID = null) => {
if(typeof this.props.configs !== "undefined"){
if(configID === null){
if (typeof this.props.configs !== "undefined") {
if (configID === null) {
this.setState({openCollapse: true});
return
}
}
else{
configID = this.props.configID;
return;
}
}
else {
configID = this.props.configID;
}
let newSignal = {
configID: configID,
direction: this.state.dir,
@ -186,7 +177,6 @@ class EditSignalMapping extends React.Component {
};
resetState() {
let signals = this.props.signals.filter((sig) => {
return (sig.configID === this.props.configID) && (sig.direction === this.state.dir);
});
@ -201,83 +191,136 @@ class EditSignalMapping extends React.Component {
tempSignals[index].checked = !tempSignals[index].checked;
this.setState({signals: tempSignals});
}
checkAll(){
checkAll() {
let tempSignals = this.state.signals;
let allChecked = true;
tempSignals.forEach(signal =>
{
if(signal.checked === false){
tempSignals.forEach(signal => {
if (signal.checked === false) {
signal.checked = true;
allChecked = false;
}
});
});
if(allChecked){
if (allChecked) {
tempSignals.forEach(signal => signal.checked = false);
}
this.setState({signals: tempSignals});
}
render() {
const buttonStyle = {
marginLeft: '10px',
};
return(
return <Dialog
show={this.props.show}
title="Edit Signal Mapping"
buttonTitle="Save"
blendOutCancel = {false}
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={true}
>
<Form.Group>
<Form.Label>{this.props.direction} Mapping</Form.Label>
<Form.Text>Click <i>Index</i>, <i>Name</i> or <i>Unit</i> cell to edit</Form.Text>
<Table checkbox onChecked={(signal) => this.onSignalChecked(signal)} data={this.state.signals}>
<TableColumn
checkbox
onChecked={(index, event) => this.onSignalChecked(index, event)}
checkboxKey='checked'
width='30'
/>
<TableColumn
title='Index'
dataKey='index'
inlineEditable
inputType='number'
onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)}
/>
<TableColumn
title='Name'
dataKey='name'
inlineEditable
inputType='text'
onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)}
/>
<TableColumn
title='Unit'
dataKey='unit'
inlineEditable
inputType='text'
onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)}
/>
<TableColumn
title='Scaling Factor'
dataKey='scalingFactor'
inlineEditable
inputType='number'
onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)}
/>
<TableColumn
title='Remove'
deleteButton
onDelete={(index) => this.handleDelete(index)}
/>
</Table>
<Dialog
show={this.props.show}
title="Edit Signal Mapping"
buttonTitle="Save"
blendOutCancel = {false}
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={true}
size='lg'>
<FormGroup>
<FormLabel>{this.props.direction} Mapping</FormLabel>
<FormText>Click <i>Index</i>, <i>Name</i> or <i>Unit</i> cell to edit</FormText>
<Table checkbox onChecked={(signal) => this.onSignalChecked(signal)} data={this.state.signals}>
<TableColumn checkbox onChecked={(index, event) => this.onSignalChecked(index, event)} checkboxKey='checked' width='30' />
<TableColumn title='Index' dataKey='index' inlineEditable inputType='number' onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)} />
<TableColumn title='Name' dataKey='name' inlineEditable inputType='text' onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)} />
<TableColumn title='Unit' dataKey='unit' inlineEditable inputType='text' onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)} />
<TableColumn title='Scaling Factor' dataKey='scalingFactor' inlineEditable inputType='number' onInlineChange={(e, row, column) => this.handleMappingChange(e, row, column)} />
<TableColumn title='Remove' deleteButton onDelete={(index) => this.handleDelete(index)} />
</Table>
<div className='solid-button' style={{ float: 'right' }}>
<OverlayTrigger key={0} placement={'left'} overlay={<Tooltip id={`tooltip-${"check"}`}> Check/Uncheck All </Tooltip>} >
<Button variant='secondary' key={50} onClick={() => this.checkAll()} style={buttonStyle}> <Icon icon="check" /> </Button>
</OverlayTrigger>
<Button variant='secondary' key={51} onClick={() => this.handleRemove()} style={buttonStyle}> Remove </Button>
<Button variant='secondary' key={52} onClick={() => this.handleAdd()} style={buttonStyle}><Icon icon="plus" /> Signal </Button>
<div className='solid-button' style={{ float: 'right' }}>
<OverlayTrigger
key={0}
placement='left'
overlay={<Tooltip id={`tooltip-${"check"}`}> Check/Uncheck All </Tooltip>}
>
<Button
variant='secondary'
key={50}
onClick={() => this.checkAll()}
style={buttonStyle}
>
<Icon icon="check" />
</Button>
</OverlayTrigger>
<Button
variant='secondary'
key={51}
onClick={() => this.handleRemove()} style={buttonStyle}>
<Icon icon="minus" /> Remove
</Button>
<Button
variant='secondary'
key={52}
onClick={() => this.handleAdd()} style={buttonStyle}>
<Icon icon="plus" /> Signal
</Button>
</div>
<div>
<Collapse isOpened={this.state.openCollapse}>
<h6>Choose a Component Configuration to add the signal to: </h6>
<div className='solid-button'>
{typeof this.props.configs !== "undefined" && this.props.configs.map(config => (
<Button
variant='secondary'
key={config.id}
onClick={() => this.handleAdd(config.id)}
style={buttonStyle}
>
{config.name}
</Button>
))}
</div>
<div>
<Collapse isOpened={this.state.openCollapse}>
<h6>Choose a Component Configuration to add the signal to: </h6>
<div className='solid-button'>
{typeof this.props.configs !== "undefined" && this.props.configs.map(config => (
<Button variant='secondary' key ={config.id} onClick={() => this.handleAdd(config.id)} style={buttonStyle}>{config.name}</Button>
))}
</div>
</Collapse>
</div>
</FormGroup>
</Dialog>
);
</Collapse>
</div>
</Form.Group>
</Dialog>;
}
}
EditSignalMapping.propTypes = {
EditSignalMappingDialog.propTypes = {
name: PropTypes.string,
length: PropTypes.number,
signals: PropTypes.arrayOf(
@ -293,4 +336,4 @@ EditSignalMapping.propTypes = {
onChange: PropTypes.func
};
export default EditSignalMapping;
export default EditSignalMappingDialog;

View file

@ -39,38 +39,18 @@ body {
hyphens: auto;
}
.app-body {
/*height: 100%;*/
}
.app-header {
width: 100%;
height: 60px;
padding: 10px 0 0 0;
color: var(--highlights);
background-color: #fff;
}
.app-header h1 {
width: 100%;
padding: 5px 0;
margin: 0;
text-align: left;
}
@media (min-width: 768px) {
.app-header h1 {
text-align: center !important;
}
}
/* can be deleted when header-menu gets removed */
.app-header .menu-icon {
color: var(--secondarytext);
right: 5px;
padding: 6px 0 0 0;
width: 100%;
text-align: center;
}
.btn-link {
@ -93,12 +73,7 @@ body {
}
.app-body-spacing {
padding: 15px 5px 0px 5px;
}
.app-body-spacing > div {
margin-left: 7px;
margin-right: 7px;
padding: 20px;
}
.app-content-fullscreen {
@ -106,7 +81,7 @@ body {
}
.app-content {
padding: 15px 20px;
padding: 15px 20px 20px 20px;
width: auto;
min-height: 300px;
@ -116,10 +91,8 @@ body {
0 9px 18px 0 rgba(0, 0, 0, 0.1);
}
@media (min-width: 768px) {
.app-content-margin-left {
margin-left: 275px !important;
}
.app-content-margin-left {
margin-left: 180px !important;
}
.verticalhorizontal {
@ -132,10 +105,9 @@ body {
/**
* Menus
*/
.menu-sidebar {
/*display: inline-table;*/
padding: 20px 25px 20px 25px;
width: 250px;
.menu {
padding: 20px;
width: 160px;
float: left;
background-color: #fff;
@ -143,12 +115,12 @@ body {
0 9px 18px 0 rgba(0, 0, 0, 0.1);
}
.menu-sidebar a {
.menu a {
color: var(--primarytext);
text-decoration:none;
}
.menu-sidebar a:hover, .menu-sidebar a:focus {
.menu a:hover, .menu a:focus {
text-decoration:none;
}
@ -156,13 +128,17 @@ body {
font-weight: bold;
}
.menu-sidebar ul {
.menu ul {
padding-top: 10px;
margin-bottom: 0;
list-style: none;
white-space: nowrap;
}
.menu li {
margin: 5px 0;
}
.menu-sidebar a::after {
.menu a::after {
/* Trick to make menu items to be as wide as in bold */
display: block;
content: attr(title);
@ -260,6 +236,10 @@ hr {
/**
* Tables
*/
.table th,td {
padding: 0.5em 0.5em !important;
}
.table th {
background-color: var(--highlights);
color: #fff;
@ -268,16 +248,13 @@ hr {
/**
* Buttons
*/
.table-control-button {
border: none;
background: none;
padding: 0 5px;
.btn-table-control-button {
padding: 0 0.5em !important;
line-height: 1em !important;
}
.table-control-button:hover {
color: #888;
.btn-table-control-button:hover {
color: #888 !important;
}
.table-control-checkbox input {
@ -474,3 +451,15 @@ hr {
.badge-outdated {
opacity: 0.4;
}
/**
* Swagger UI
*/
.swagger-ui .wrapper {
padding: 0 !important;
}
.swagger-ui .info {
margin-top: 10px;
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -28,17 +28,15 @@ class EditOwnUserDialog extends React.Component {
super(props);
this.state = {
username: '',
id: '',
mail: '',
username: this.props.user.username,
id: this.props.user.id,
mail: this.props.user.mail,
password: '',
oldPassword: '',
confirmpassword: ''
confirmPassword: ''
}
}
onClose(canceled) {
if (canceled === false) {
if (this.valid) {
@ -49,8 +47,6 @@ class EditOwnUserDialog extends React.Component {
}
}
handleChange(e) {
this.setState({ [e.target.id]: e.target.value });
@ -59,73 +55,69 @@ class EditOwnUserDialog extends React.Component {
let mail = true;
let pw = true;
let oldPassword = true;
let confirmpassword = true;
let confirmPassword = true;
if (this.state.username === '') {
username = false;
}
if(this.state.mail === ''){
if (this.state.mail === '') {
mail = false;
}
if(this.state.password === ''){
if (this.state.password === '') {
pw = false;
}
if(this.state.oldPassword === ''){
if (this.state.oldPassword === '') {
oldPassword = false;
}
if(this.state.confirmpassword === ''){
confirmpassword = false;
if (this.state.confirmPassword === '') {
confirmPassword = false;
}
// form is valid if the following condition is met
this.valid = username || mail || (oldPassword && pw && confirmpassword);
this.valid = username || mail || (oldPassword && pw && confirmPassword);
}
resetState() {
this.setState({
username: '',
mail: '',
oldPassword: '',
confirmpassword: '',
password: '',
username: this.props.user.username,
id: this.props.user.id,
mail: this.props.user.mail,
oldPassword: '',
confirmPassword: '',
password: '',
});
}
render() {
return (
<Dialog show={this.props.show} title="Edit user" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup as={Col} controlId="username">
<FormLabel>Username</FormLabel>
<FormControl type="text" placeholder={this.props.user.username} value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="mail">
<FormLabel>E-mail</FormLabel>
<FormControl type="text" placeholder={this.props.user.mail} value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="oldPassword">
<FormLabel>Old Password</FormLabel>
<FormControl type="password" placeholder="Enter current password" value={this.state.oldPassword} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="password">
<FormLabel>New Password</FormLabel>
<FormControl type="password" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="confirmpassword">
<FormLabel>Confirm New Password</FormLabel>
<FormControl type="password" placeholder="Enter password" value={this.state.confirmpassword} onChange={(e) => this.handleChange(e)} />
</FormGroup>
</form>
<Form>
<Form.Group as={Col} controlId="username">
<Form.Label>Username</Form.Label>
<Form.Control type="text" value={this.state.username} onChange={(e) => this.handleChange(e)} autocomplete="username" />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="mail">
<Form.Label>E-mail</Form.Label>
<Form.Control type="text" value={this.state.mail} onChange={(e) => this.handleChange(e)} autocomplete="email" />
</Form.Group>
<Form.Group as={Col} controlId="oldPassword">
<Form.Label>Old Password</Form.Label>
<Form.Control type="password" placeholder="Enter current password" value={this.state.oldPassword} onChange={(e) => this.handleChange(e)} autocomplete="current-password" />
</Form.Group>
<Form.Group as={Col} controlId="password">
<Form.Label>New Password</Form.Label>
<Form.Control type="password" placeholder="Enter new password" value={this.state.password} onChange={(e) => this.handleChange(e)} autocomplete="new-password" />
</Form.Group>
<Form.Group as={Col} controlId="confirmPassword">
<Form.Label>Confirm New Password</Form.Label>
<Form.Control type="password" placeholder="Repeat new password" value={this.state.confirmPassword} onChange={(e) => this.handleChange(e)} autocomplete="new-password" />
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -27,13 +27,13 @@ class EditUserDialog extends React.Component {
super(props);
this.state = {
username: '',
mail: '',
role: '',
id: '',
username: props.user.username,
mail: props.user.mail,
role: props.user.role,
id: props.user.id,
active: props.user.active,
password: '',
active: '',
confirmpassword: '',
confirmPassword: '',
oldPassword: ''
}
}
@ -57,47 +57,45 @@ class EditUserDialog extends React.Component {
var mail = true;
var pw = true;
var active = true;
var confirmpassword = true;
var oldPW = true;
var confirmPassword = true;
var oldPassword = true;
if (this.state.username === '') {
username = false;
}
if (this.state.role === ''){
if (this.state.role === '') {
role = false;
}
if(this.state.mail === ''){
if (this.state.mail === '') {
mail = false;
}
if(this.state.password === ''){
if (this.state.password === '') {
pw = false;
}
if(this.state.active === ''){
if (this.state.active === '') {
active = false;
}
if(this.state.confirmpassword === ''){
confirmpassword = false;
if (this.state.confirmPassword === '') {
confirmPassword = false;
}
if(this.state.oldPassword === ''){
oldPW = false;
if (this.state.oldPassword === '') {
oldPassword = false;
}
// form is valid if any of the fields contain somethig
this.valid = username || role || mail || pw || active || confirmpassword || oldPW;
this.valid = username || role || mail || pw || active || confirmPassword || oldPassword;
}
resetState() {
this.setState({
//username: this.props.user.username,
//mail: this.props.user.mail,
username: this.props.user.username,
mail: this.props.user.mail,
role: this.props.user.role,
id: this.props.user.id
});
@ -106,46 +104,44 @@ class EditUserDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="Edit user" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup as={Col} controlId="username">
<FormLabel>Username</FormLabel>
<FormControl type="text" placeholder={this.props.user.username} value={this.state.username} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="mail">
<FormLabel>E-mail</FormLabel>
<FormControl type="text" placeholder={this.props.user.mail} value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="oldPassword">
<FormLabel>Admin Password</FormLabel>
<FormControl type="password" placeholder="Enter admin password" value={this.state.oldPassword} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="password">
<FormLabel>Password</FormLabel>
<FormControl type="password" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="confirmpassword">
<FormLabel>Confirm New Password</FormLabel>
<FormControl type="password" placeholder="Enter password" value={this.state.confirmpassword} onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup as={Col} controlId="role">
<FormLabel>Role</FormLabel>
<FormControl as="select" placeholder="Select role" value={this.state.role} onChange={(e) => this.handleChange(e)}>
<Form>
<Form.Group as={Col} controlId="username">
<Form.Label>Username</Form.Label>
<Form.Control type="text" value={this.state.username} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="mail">
<Form.Label>E-mail</Form.Label>
<Form.Control type="text" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="oldPassword">
<Form.Label>Admin Password</Form.Label>
<Form.Control type="password" placeholder="Enter admin password" value={this.state.oldPassword} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="confirmPassword">
<Form.Label>Confirm New Password</Form.Label>
<Form.Control type="password" placeholder="Enter password" value={this.state.confirmPassword} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="role">
<Form.Label>Role</Form.Label>
<Form.Control as="select" placeholder="Select role" value={this.state.role} onChange={(e) => this.handleChange(e)}>
<option key='1' value='Admin'>Administrator</option>
<option key='2' value='User'>User</option>
<option key='3' value='Guest'>Guest</option>
</FormControl>
</FormGroup>
<FormGroup as={Col} controlId="active">
<FormLabel>Active</FormLabel>
<FormControl as="select" placeholder="Select Active state" value={this.state.active} onChange={(e) => this.handleChange(e)}>
</Form.Control>
</Form.Group>
<Form.Group as={Col} controlId="active">
<Form.Label>Active</Form.Label>
<Form.Control as="select" placeholder="Select Active state" value={this.state.active} onChange={(e) => this.handleChange(e)}>
<option key='1' value='yes'>Yes</option>
<option key='2' value='no'>No</option>
</FormControl>
</FormGroup>
</form>
</Form.Control>
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -26,7 +26,7 @@ class LoginComplete extends React.Component {
constructor(props) {
console.log("LoginComplete constructor");
super(props);
AppDispatcher.dispatch({
type: 'users/extlogin',
});
@ -42,8 +42,6 @@ class LoginComplete extends React.Component {
this.startTimer = this.startTimer.bind(this);
this.countDown = this.countDown.bind(this);
this.stopTimer = this.stopTimer.bind(this);
}
componentDidMount() {
@ -51,7 +49,6 @@ class LoginComplete extends React.Component {
this.setState({secondsToWait: 5});
}
static getStores(){
return [LoginStore]
}
@ -97,11 +94,15 @@ class LoginComplete extends React.Component {
this.stopTimer();
return (<Redirect to="/login" />);
} else {
return (<div class="verticalhorizontal">
<img style={{height: 300}}src={require('../img/dog-waiting-bw.jpg').default} alt="Waiting Dog" /></div>);
return <div class="verticalhorizontal">
<img
style={{height: 300}}
src={require('../img/dog-waiting-bw.jpg').default}
alt="Waiting Dog" />
</div>;
}
}
}
let fluxContainerConverter = require('../common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(LoginComplete));
export default Container.create(fluxContainerConverter.convert(LoginComplete));

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React, { Component } from 'react';
import { Form, Button, FormGroup, FormControl, FormLabel, Col } from 'react-bootstrap';
import { Form, Button, Col } from 'react-bootstrap';
import RecoverPassword from './recover-password'
import AppDispatcher from '../common/app-dispatcher';
import _ from 'lodash';
@ -69,19 +69,19 @@ class LoginForm extends Component {
villaslogin() {
return (
<Form key="login_a">
<FormGroup controlId="username">
<FormLabel column={true}>Username</FormLabel>
<Form.Group controlId="username">
<Form.Label column={true}>Username</Form.Label>
<Col>
<FormControl type="text" placeholder="Username" autoComplete="username" onChange={(e) => this.handleChange(e)} />
<Form.Control type="text" placeholder="Username" autoComplete="username" onChange={(e) => this.handleChange(e)} />
</Col>
</FormGroup>
</Form.Group>
<FormGroup controlId="password">
<FormLabel column={true}>Password</FormLabel>
<Form.Group controlId="password">
<Form.Label column={true}>Password</Form.Label>
<Col >
<FormControl type="password" placeholder="Password" autoComplete="current-password" onChange={(e) => this.handleChange(e)} />
<Form.Control type="password" placeholder="Password" autoComplete="current-password" onChange={(e) => this.handleChange(e)} />
</Col>
</FormGroup>
</Form.Group>
{this.props.loginMessage &&
<div style={{ marginBottom: '20px', color: 'red', fontSize: 'small' }}>
@ -91,17 +91,16 @@ class LoginForm extends Component {
</div>
}
<FormGroup style={{ paddingTop: 15, paddingBottom: 5 }}>
<Form.Group style={{ paddingTop: 15, paddingBottom: 5 }}>
<Col>
<span className='solid-button'>
<Button variant='secondary' style={{width: 90}} type="submit" disabled={this.state.disableLogin} onClick={e => this.login(e)}>Login</Button>
</span>
<Button variant="link" size="sm" style={{marginLeft: 85}} onClick={() => this.openRecoverPassword()}>Forgot your password?</Button>
</Col>
</FormGroup>
</Form.Group>
<RecoverPassword show={this.state.forgottenPassword} onClose={() => this.closeRecoverPassword()} sessionToken={this.props.sessionToken} />
</Form>
);
}

View file

@ -32,7 +32,7 @@ class Login extends Component {
constructor(props) {
super(props);
// load config in case the user goes directly to /login
// Load config in case the user goes directly to /login
// otherwise it will be loaded in app constructor
AppDispatcher.dispatch({
type: 'config/load',

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, FormText, Col } from 'react-bootstrap';
import { Form, Col } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
@ -54,7 +54,6 @@ class NewUserDialog extends React.Component {
let mail = this.state.mail !== '';
this.valid = username && password && role && mail;
}
resetState() {
@ -68,33 +67,40 @@ class NewUserDialog extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="New user" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form>
<FormGroup as={Col} controlId="username">
<FormLabel>Username</FormLabel>
<FormControl type="text" placeholder="Enter username" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
<FormText>Min 3 characters.</FormText>
</FormGroup>
<FormGroup as={Col} controlId="mail">
<FormLabel>E-mail</FormLabel>
<FormControl type="text" placeholder="Enter e-mail" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="password">
<FormLabel>Password</FormLabel>
<FormControl type="text" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<FormGroup as={Col} controlId="role">
<FormLabel>Role</FormLabel>
<FormControl as="select" placeholder="Select role" value={this.state.role} onChange={(e) => this.handleChange(e)}>
<Dialog
show={this.props.show}
title="New User"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form>
<Form.Group as={Col} controlId="username">
<Form.Label>Username</Form.Label>
<Form.Control type="text" placeholder="Enter username" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
<Form.Text>Min 3 characters.</Form.Text>
</Form.Group>
<Form.Group as={Col} controlId="mail">
<Form.Label>E-mail</Form.Label>
<Form.Control type="text" placeholder="Enter e-mail" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control type="text" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="role">
<Form.Label>Role</Form.Label>
<Form.Control as="select" placeholder="Select role" value={this.state.role} onChange={(e) => this.handleChange(e)}>
<option key='1' value='Admin'>Administrator</option>
<option key='2' value='User'>User</option>
<option key='3' value='Guest'>Guest</option>
</FormControl>
</FormGroup>
</form>
</Form.Control>
</Form.Group>
</Form>
</Dialog>
);
}

View file

@ -35,13 +35,20 @@ class RecoverPassword extends React.Component {
render() {
return (
<Dialog show={this.props.show} title="Recover password" buttonTitle="Close" onClose={(c) => this.onClose(c)} blendOutCancel = {true} valid={true} size = 'lg'>
<Dialog
show={this.props.show}
title="Recover password"
buttonTitle="Close"
onClose={(c) => this.onClose(c)}
blendOutCancel={true}
valid={true}
>
<div>
<div>Please contact:</div>
<div>{this.state.admin.name}</div>
<div>E-Mail:</div>
<a href={`mailto:${this.state.admin.mail}`}>{this.state.admin.mail}</a>
</div>
<div>Please contact:</div>
<div>{this.state.admin.name}</div>
<div>E-Mail:</div>
<a href={`mailto:${this.state.admin.mail}`}>{this.state.admin.mail}</a>
</div>
</Dialog>
);
}

View file

@ -17,7 +17,7 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import {Button, Col, Row, FormGroup} from 'react-bootstrap';
import { Button, Form, Row, Col } from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
import UsersStore from './users-store';
@ -46,7 +46,6 @@ class User extends Component {
}
componentDidMount() {
let currentUserID = JSON.parse(localStorage.getItem("currentUser")).id;
AppDispatcher.dispatch({
@ -54,93 +53,102 @@ class User extends Component {
data: parseInt(currentUserID, 10),
token: this.state.token
});
}
closeEditModal(data) {
this.setState({ editModal: false });
let updatedData = {}
updatedData.id = this.state.currentUser.id;
let updatedUser = this.state.currentUser;
let hasChanged = false;
let pwChanged = false;
if(data){
if (data.username !== ''){
hasChanged = true;
updatedData.username = data.username;
updatedUser.username = data.username
}
if (data.mail !== ''){
hasChanged = true;
updatedData.mail = data.mail;
updatedUser.mail = data.mail;
}
if (data.password !== '' && data.oldPassword !== '' && data.password === data.confirmpassword ){
pwChanged = true;
updatedData.password = data.password;
updatedData.oldPassword = data.oldPassword;
} else if (data.password !== '' && data.password !== data.confirmpassword) {
NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR('New password not correctly confirmed'));
return
}
updatedData.id = this.state.currentUser.id;
if (hasChanged || pwChanged) {
if(hasChanged){
this.setState({currentUser: updatedUser})
if (data) {
if (data.username !== this.state.currentUser.username) {
hasChanged = true;
updatedData.username = data.username;
updatedUser.username = data.username
}
AppDispatcher.dispatch({
type: 'users/start-edit',
data: updatedData,
token: this.state.token
});
} else {
NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_WARNING('No update requested, no input data'));
if (data.mail !== this.state.currentUser.mail) {
hasChanged = true;
updatedData.mail = data.mail;
updatedUser.mail = data.mail;
}
if (data.password !== '' && data.oldPassword !== '' && data.password === data.confirmPassword ) {
pwChanged = true;
updatedData.password = data.password;
updatedData.oldPassword = data.oldPassword;
} else if (data.password !== '' && data.password !== data.confirmPassword) {
NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR('New password not correctly confirmed'));
return
}
if (hasChanged || pwChanged) {
if (hasChanged){
this.setState({ currentUser: updatedUser })
}
AppDispatcher.dispatch({
type: 'users/start-edit',
data: updatedData,
token: this.state.token
});
} else {
NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_WARNING('No update requested, no input data'));
}
}
}
}
getHumanRoleName(role_key) {
const HUMAN_ROLE_NAMES = {Admin: 'Administrator', User: 'User', Guest: 'Guest'};
return HUMAN_ROLE_NAMES.hasOwnProperty(role_key)? HUMAN_ROLE_NAMES[role_key] : '';
}
render() {
let user = this.state.currentUser;
return (
<div>
<h1>Your User Account</h1>
<h1>Account</h1>
{this.state.currentUser !== undefined && this.state.currentUser !== null ?
{user ?
<>
<Form>
<Form.Group as={Row} controlId="username">
<Form.Label column sm={2}>Username</Form.Label>
<Col sm={10}>
<Form.Control plaintext readOnly defaultValue={user.username} />
</Col>
</Form.Group>
<Form.Group as={Row} controlId="mail">
<Form.Label column sm={2}>E-mail</Form.Label>
<Col sm={10}>
<Form.Control plaintext readOnly defaultValue={user.mail} type="email" />
</Col>
</Form.Group>
<Form.Group as={Row} controlId="role">
<Form.Label column sm={2}>Role</Form.Label>
<Col sm={10}>
<Form.Control plaintext readOnly defaultValue={user.role} />
</Col>
</Form.Group>
<Form.Group as={Row} controlId="formBasicEmail">
<Form.Label column sm={2}>Created at</Form.Label>
<Col sm={10}>
<Form.Control plaintext readOnly defaultValue={user.createdAt} />
</Col>
</Form.Group>
<Button variant="primary" onClick={() => this.setState({ editModal: true })}>
<Icon icon='edit' /> Edit
</Button>
</Form>
<form>
<Row>
<FormGroup as={Col} sm={2} controlId="details">
<div style={{ alignItems: 'right' }}>Username:</div>
<div style={{ alignItems: 'right' }}>E-mail:</div>
<div style={{ alignItems: 'right' }}>Role:</div>
</FormGroup>
<FormGroup as={Col} sm={3} controlId="information" >
<div> {this.state.currentUser.username}</div>
<div>{this.state.currentUser.mail}</div>
<div>{this.state.currentUser.role}</div>
<span className='icon-button'>
<Button variant='light' size='lg' variant='light' style={{margin: '10px'}} onClick={() => this.setState({ editModal: true })}><Icon size='lg' classname='icon-color' icon='edit' /> </Button>
</span>
</FormGroup>
</Row>
<EditOwnUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)}
user={this.state.currentUser} />
</form> : "Loading user data..."
<EditOwnUserDialog
show={this.state.editModal}
onClose={(data) => this.closeEditModal(data)}
user={user}
/>
</>
: <div/>
}
</div>
);

View file

@ -50,7 +50,6 @@ class UsersStore extends ArrayStore {
return super.reduce(state, action);
}
}
}
export default new UsersStore();

View file

@ -17,12 +17,12 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import {Button, OverlayTrigger, Tooltip} from 'react-bootstrap';
import AppDispatcher from '../common/app-dispatcher';
import UsersStore from './users-store';
import Icon from '../common/icon';
import IconButton from '../common/icon-button';
import Table from '../common/table';
import TableColumn from '../common/table-column';
import NewUserDialog from './new-user';
@ -38,7 +38,6 @@ class Users extends Component {
}
static calculateState(prevState, props) {
let token = localStorage.getItem("token");
// If there is a token available and this method was called as a result of loading users
@ -56,7 +55,8 @@ class Users extends Component {
newModal: false,
editModal: false,
deleteModal: false,
modalData: {}
modalData: {},
currentUser: JSON.parse(localStorage.getItem("currentUser"))
};
}
@ -90,26 +90,18 @@ class Users extends Component {
this.setState({ editModal: false });
if (data) {
if(data.password === data.confirmpassword) {
if (data.password === data.confirmPassword) {
AppDispatcher.dispatch({
type: 'users/start-edit',
data: data,
token: this.state.token
});
} else{
} else {
NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR("New password not correctly confirmed"))
}
}
}
getHumanRoleName(role_key) {
const HUMAN_ROLE_NAMES = {Admin: 'Administrator', User: 'User', Guest: 'Guest'};
return HUMAN_ROLE_NAMES.hasOwnProperty(role_key)? HUMAN_ROLE_NAMES[role_key] : '';
}
onModalKeyPress = (event) => {
if (event.key === 'Enter') {
event.preventDefault();
@ -118,55 +110,69 @@ class Users extends Component {
}
};
modifyActiveColumn(active){
if(active){
return <Icon icon='check' />
} else {
return <Icon icon='times' />
}
modifyActiveColumn(active) {
return <Icon icon={active ? 'check' : 'times'} />
}
render() {
const buttonStyle = {
marginLeft: '10px',
};
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div>
return <div>
<h1>Users
<span className='icon-button'>
<OverlayTrigger
key={1}
placement={'top'}
overlay={<Tooltip id={`tooltip-${"add"}`}> Add User </Tooltip>} >
<Button variant='light' style={buttonStyle} onClick={() => this.setState({ newModal: true })}><Icon icon='plus' classname='icon-color' style={iconStyle} /> </Button>
</OverlayTrigger>
</span>
<IconButton
key={0}
tooltip='Add User'
onClick={() => this.setState({ newModal: true })}
icon='plus'
/>
</span>
</h1>
<Table data={this.state.users}>
<TableColumn title='Username' width='150' dataKey='username' />
<TableColumn title='ID' width='150' dataKey='id' />
<TableColumn title='E-mail' dataKey='mail' />
<TableColumn title='Role' dataKey='role' modifier={(role) => this.getHumanRoleName(role)} />
<TableColumn title='Active' dataKey='active' modifier={(active) => this.modifyActiveColumn(active)} />
<TableColumn width='200' editButton deleteButton onEdit={index => this.setState({ editModal: true, modalData: this.state.users[index] })} onDelete={index => this.setState({ deleteModal: true, modalData: this.state.users[index] })} />
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Username'
width='150'
dataKey='username'
/>
<TableColumn
title='E-mail'
dataKey='mail'
/>
<TableColumn
title='Role'
dataKey='role'
/>
<TableColumn
title='Active'
dataKey='active'
modifier={(active) => this.modifyActiveColumn(active)}
/>
<TableColumn
width='200'
align='right'
editButton
deleteButton
onEdit={index => this.setState({
editModal: true,
modalData: this.state.users[index]
})}
onDelete={index => this.setState({
deleteModal: true,
modalData: this.state.users[index]
})}
/>
</Table>
<NewUserDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
<EditUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} user={this.state.modalData} />
<DeleteDialog title="user" name={this.state.modalData.username} show={this.state.deleteModal} onClose={(e) => this.closeDeleteModal(e)} />
</div>
);
</div>;
}
}

View file

@ -16,6 +16,7 @@
******************************************************************************/
import React from 'react';
import { Form } from 'react-bootstrap';
import { SketchPicker } from 'react-color';
import Dialog from '../../common/dialogs/dialog';
@ -31,8 +32,7 @@ class ColorPicker extends React.Component {
};
}
static getDerivedStateFromProps(props, state){
static getDerivedStateFromProps(props, state) {
return {
widget: props.widget
};
@ -49,25 +49,24 @@ class ColorPicker extends React.Component {
}
handleChangeComplete = (color) => {
let temp = this.state.widget;
if(this.props.controlId === 'strokeStyle'){
if (this.props.controlId === 'strokeStyle'){
temp.customProperties.zones[this.props.zoneIndex]['strokeStyle'] = color.hex;
}
else if(this.props.controlId === 'lineColor'){
else if (this.props.controlId === 'lineColor'){
temp.customProperties.lineColors[this.props.lineIndex] = color.hex;
}
else{
let parts = this.props.controlId.split('.');
let isCustomProperty = true;
else {
let parts = this.props.controlId.split('.');
let isCustomProperty = true;
if (parts.length === 1){
isCustomProperty = false;
}
if (parts.length === 1){
isCustomProperty = false;
}
isCustomProperty ? temp[parts[0]][parts[1]] = color.hex : temp[this.props.controlId] = color.hex;
isCustomProperty ? temp[parts[0]][parts[1] + "_opacity"] = color.rgb.a : temp[this.props.controlId +"_opacity"] = color.rgb.a;
isCustomProperty ? temp[parts[0]][parts[1]] = color.hex : temp[this.props.controlId] = color.hex;
isCustomProperty ? temp[parts[0]][parts[1] + "_opacity"] = color.rgb.a : temp[this.props.controlId +"_opacity"] = color.rgb.a;
}
this.setState({ widget: temp });
@ -88,46 +87,45 @@ class ColorPicker extends React.Component {
};
render() {
let hexColor;
let opacity = 1;
let parts = this.props.controlId.split('.');
let isCustomProperty = true;
if (parts.length === 1){
if (parts.length === 1) {
isCustomProperty = false;
}
if(this.props.controlId === 'strokeStyle'){
if(typeof this.state.widget.customProperties.zones[this.props.zoneIndex] !== 'undefined'){
if (this.props.controlId === 'strokeStyle') {
if (typeof this.state.widget.customProperties.zones[this.props.zoneIndex] !== 'undefined') {
hexColor = this.state.widget.customProperties.zones[this.props.zoneIndex]['strokeStyle'];
}
}
else if(this.props.controlId === 'lineColor'){
if(typeof this.state.widget.customProperties.lineColors[this.props.lineIndex] !== 'undefined'){
else if (this.props.controlId === 'lineColor') {
if (typeof this.state.widget.customProperties.lineColors[this.props.lineIndex] !== 'undefined') {
hexColor = this.state.widget.customProperties.lineColors[this.props.lineIndex];
}
}
else{
hexColor = isCustomProperty ? this.state.widget[parts[0]][parts[1]]: this.state.widget[this.props.controlId];
opacity = isCustomProperty ? this.state.widget[parts[0]][parts[1] + "_opacity"]: this.state.widget[this.props.controlId + "_opacity"];
hexColor = isCustomProperty ? this.state.widget[parts[0]][parts[1]]: this.state.widget[this.props.controlId];
opacity = isCustomProperty ? this.state.widget[parts[0]][parts[1] + "_opacity"]: this.state.widget[this.props.controlId + "_opacity"];
}
let rgbColor = this.hexToRgb(hexColor, opacity);
return <Dialog show={this.props.show} title='Color Picker' buttonTitle='Save' onClose={(c) => this.onClose(c)} valid={true}>
<form>
return <Dialog
show={this.props.show}
title='Color Picker'
buttonTitle='Save'
onClose={(c) => this.onClose(c)}
valid={true}
>
<Form>
<SketchPicker
color={rgbColor}
disableAlpha={this.props.disableOpacity}
disableAlpha={this.props.disableOpacity}
onChangeComplete={ this.handleChangeComplete }
width={"300"}
width={300}
/>
</form>
</Form>
</Dialog>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormCheck } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetAspectControl extends React.Component {
constructor(props) {
@ -41,15 +41,15 @@ class EditWidgetAspectControl extends React.Component {
}
return (
<FormGroup>
<FormCheck
<Form.Group>
<Form.Check
type='checkbox'
id={this.props.controlId}
checked={isCustomProperty ? this.state.widget[parts[0]][parts[1]] : this.state.widget[this.props.controlId]}
label={"Lock Aspect Ratio"}
onChange={e => this.props.handleChange(e)}>
</FormCheck>
</FormGroup>
onChange={e => this.props.handleChange(e)}
/>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormCheck } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetCheckboxControl extends React.Component {
constructor(props) {
@ -45,16 +45,15 @@ class EditWidgetCheckboxControl extends React.Component {
}
render() {
return <FormGroup>
<FormCheck
return <Form.Group>
<Form.Check
type={"checkbox"}
id={this.props.controlId}
label={this.props.text}
defaultChecked={this.state.isChecked}
onChange={e => this.handleCheckboxChange(e)}>
</FormCheck>
</FormGroup>;
onChange={e => this.handleCheckboxChange(e)}
/>
</Form.Group>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React, { Component } from 'react';
import { FormGroup, OverlayTrigger, Tooltip , FormLabel, Button } from 'react-bootstrap';
import { Form, OverlayTrigger, Tooltip, Button } from 'react-bootstrap';
import ColorPicker from './color-picker'
import Icon from "../../common/icon";
@ -77,7 +77,7 @@ class EditWidgetColorControl extends Component {
let style = {
backgroundColor: color,
opacity: opacity,
width: '260px',
width: '260px',
height: '40px'
}
@ -86,23 +86,20 @@ class EditWidgetColorControl extends Component {
tooltipText = "Change border color";
}
return (
<FormGroup>
<FormLabel>{this.props.label}</FormLabel>
<div className='section-buttons-group-right'>
return <Form.Group>
<Form.Label>{this.props.label}</Form.Label>
<div className='section-buttons-group-right'>
<OverlayTrigger key={0} placement={'right'} overlay={<Tooltip id={`tooltip-${"color"}`}> {tooltipText} </Tooltip>} >
<Button key={2} style={style} onClick={this.openColorPicker.bind(this)} >
<Icon icon="paint-brush"/>
</Button>
</OverlayTrigger>
</div>
</div>
<ColorPicker show={this.state.showColorPicker} onClose={(data) => this.closeEditModal(data)} widget={this.state.widget} controlId={this.props.controlId} disableOpacity={this.props.disableOpacity}/>
</FormGroup>
)
<ColorPicker show={this.state.showColorPicker} onClose={(data) => this.closeEditModal(data)} widget={this.state.widget} controlId={this.props.controlId} disableOpacity={this.props.disableOpacity}/>
</Form.Group>;
}
}

View file

@ -16,11 +16,11 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, Table, FormLabel, Button, Tooltip, OverlayTrigger } from 'react-bootstrap';
import { Form, Table, Button, Tooltip, OverlayTrigger } from 'react-bootstrap';
import ColorPicker from './color-picker'
import Icon from '../../common/icon';
import {Collapse} from 'react-collapse';
import { Collapse } from 'react-collapse';
class EditWidgetColorZonesControl extends React.Component {
constructor(props) {
@ -51,8 +51,8 @@ class EditWidgetColorZonesControl extends React.Component {
// add row
const widget = this.state.widget;
widget.customProperties.zones.push({ strokeStyle: '#d3cbcb', min: 0, max: 100 });
if(widget.customProperties.zones.length > 0){
if(widget.customProperties.zones.length > 0){
let length = widget.customProperties.zones.length
for(let i= 0 ; i < length; i++){
@ -67,14 +67,14 @@ class EditWidgetColorZonesControl extends React.Component {
}
removeZones = () => {
let temp = this.state.widget;
temp.customProperties.zones.splice(this.state.selectedIndex, 1);
if(temp.customProperties.zones.length > 0){
if(temp.customProperties.zones.length > 0){
let length = temp.customProperties.zones.length
for(let i= 0 ; i < length; i++){
temp.customProperties.zones[i].min = i* 100/length;
temp.customProperties.zones[i].max = (i+1)* 100/length;
@ -111,8 +111,8 @@ class EditWidgetColorZonesControl extends React.Component {
}
}
openColorPicker = () =>{
openColorPicker = () => {
let color = this.state.selectedZone.strokeStyle;
this.setState({showColorPicker: true, originalColor: color});
@ -124,7 +124,7 @@ class EditWidgetColorZonesControl extends React.Component {
let temp = this.state.selectedZone;
temp.strokeStyle = this.state.originalColor;
this.setState({ selectedZone : temp });
}
}
@ -155,7 +155,7 @@ class EditWidgetColorZonesControl extends React.Component {
if(this.state.selectedIndex !== this.state.widget.customProperties.zones.length -1){
temp.customProperties.zones[this.state.selectedIndex + 1]['min'] = e.target.value
}
this.setState({ widget: temp });
}
@ -171,17 +171,17 @@ class EditWidgetColorZonesControl extends React.Component {
this.props.handleChange(event);
}
render() {
const buttonStyle = {
marginBottom: '10px',
marginBottom: '10px',
marginLeft: '120px',
};
const iconStyle = {
height: '25px',
height: '25px',
width : '25px'
}
@ -194,72 +194,75 @@ class EditWidgetColorZonesControl extends React.Component {
let pickerStyle = {
backgroundColor: tempColor,
width: '260px',
width: '260px',
height: '40px',
marginTop: '20px'
}
return <FormGroup>
<FormLabel>Color zones</FormLabel>
return <Form.Group>
<Form.Label>Color Zones</Form.Label>
<span className='icon-button'>
<Button variant='light' onClick={this.addZone} style={buttonStyle} disabled={!this.props.widget.customProperties.colorZones}><Icon icon="plus" className='icon-color' style={iconStyle} /></Button>
<Button
variant='light'
onClick={this.addZone}
style={buttonStyle}
disabled={!this.props.widget.customProperties.colorZones}
>
<Icon icon="plus" className='icon-color' style={iconStyle} />
</Button>
</span>
<div>
{
this.state.widget.customProperties.zones.map((zone, idx) => {
let color = zone.strokeStyle;
let width = (zone.max - zone.min)*(260/100);
let style = {
backgroundColor: color,
width: width,
height: '40px'
}
return (<Button
style={style} key={idx} onClick={i => this.editColorZone(idx)} disabled={!this.props.widget.customProperties.colorZones}><Icon icon="pen" /></Button>
)
this.state.widget.customProperties.zones.map((zone, idx) => {
let color = zone.strokeStyle;
let width = (zone.max - zone.min)*(260/100);
let style = {
backgroundColor: color,
width: width,
height: '40px'
}
return (<Button
style={style} key={idx} onClick={i => this.editColorZone(idx)} disabled={!this.props.widget.customProperties.colorZones}><Icon icon="pen" /></Button>
)
})
}
</div>
<Collapse isOpened={collapse} >
<OverlayTrigger key={0} placement={'right'} overlay={<Tooltip id={`tooltip-${"color"}`}>Change color</Tooltip>} >
<Button key={0} style={pickerStyle} onClick={this.openColorPicker.bind(this)} >
<Icon icon="paint-brush"/>
</Button>
</OverlayTrigger>
<Table>
<tbody>
<tr>
<td>
Min:
<FormControl
type="number"
step="any"
placeholder="Min"
value={this.state.minValue}
onChange={e => this.handleMinChange(e)} />
</td>
<td>
Max:
<FormControl
type="number"
step="any"
placeholder="Max"
value={ this.state.maxValue}
onChange={e => this.handleMaxChange(e)} />
</td>
</tr>
</tbody>
</Table>
<span className='icon-button'>
<Button variant='light' onClick={this.removeZones} ><Icon style={iconStyle} classname='icon-color' icon="trash-alt" /></Button>
</span>
</div>
<Collapse isOpened={collapse}>
<OverlayTrigger key={0} placement={'right'} overlay={<Tooltip id={`tooltip-${"color"}`}>Change color</Tooltip>} >
<Button key={0} style={pickerStyle} onClick={this.openColorPicker.bind(this)} >
<Icon icon="paint-brush"/>
</Button>
</OverlayTrigger>
<Table>
<tbody>
<tr>
<td>
Min:
<Form.Control
type="number"
step="any"
placeholder="Min"
value={this.state.minValue}
onChange={e => this.handleMinChange(e)} />
</td>
<td>
Max:
<Form.Control
type="number"
step="any"
placeholder="Max"
value={ this.state.maxValue}
onChange={e => this.handleMaxChange(e)} />
</td>
</tr>
</tbody>
</Table>
<span className='icon-button'>
<Button variant='light' onClick={this.removeZones} ><Icon style={iconStyle} classname='icon-color' icon="trash-alt" /></Button>
</span>
</Collapse>
<ColorPicker show={this.state.showColorPicker} onClose={(data) => this.closeEditModal(data)} widget={this.state.widget} zoneIndex={this.state.selectedIndex} controlId={'strokeStyle'} />
</FormGroup>;
</Form.Group>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel} from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditFileWidgetControl extends React.Component {
@ -59,13 +59,13 @@ class EditFileWidgetControl extends React.Component {
}
return <div>
<FormGroup controlId="file">
<FormLabel>File</FormLabel>
<FormControl
<Form.Group controlId="file">
<Form.Label>File</Form.Label>
<Form.Control
as="select"
value={isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]}
onChange={(e) => this.handleFileChange(e)}>{fileOptions} </FormControl>
</FormGroup>
onChange={(e) => this.handleFileChange(e)}>{fileOptions} </Form.Control>
</Form.Group>
</div>;
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetHTMLContent extends React.Component {
constructor(props) {
@ -60,20 +60,25 @@ class EditWidgetHTMLContent extends React.Component {
handleKeyIgnore(event){
// This function prevents a keystroke from beeing handled by dialog.js
event.stopPropagation();
}
event.stopPropagation();
}
render() {
return (
<FormGroup controlId={this.props.controlId}>
<FormLabel>HTML Content</FormLabel>
<FormControl onKeyDown={this.handleKeyIgnore} componentclass="textarea" style={{ height: 200 }} placeholder={this.props.placeholder} value={this.state.value} onChange={e => this.props.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<Form.Group controlId={this.props.controlId}>
<Form.Label>HTML Content</Form.Label>
<Form.Control
onKeyDown={this.handleKeyIgnore}
componentclass="textarea"
style={{ height: 200 }}
placeholder={this.props.placeholder}
value={this.state.value}
onChange={e => this.props.handleChange(e)}
/>
<Form.Control.Feedback />
</Form.Group>
);
}
}
export default EditWidgetHTMLContent;

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {FormGroup, FormControl, FormLabel} from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetICControl extends React.Component {
@ -60,13 +60,13 @@ class EditWidgetICControl extends React.Component {
}
return <div>
<FormGroup controlId="ic">
<FormLabel>IC</FormLabel>
<FormControl
<Form.Group controlId="ic">
<Form.Label>IC</Form.Label>
<Form.Control
as="select"
value={isCustomProperty ? this.props.widget[parts[0]][parts[1]] : this.props.widget[this.props.controlId]}
onChange={(e) => this.handleICChange(e)}>{icOptions} </FormControl>
</FormGroup>
onChange={(e) => this.handleICChange(e)}>{icOptions} </Form.Control>
</Form.Group>
</div>;
}
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel, FormCheck, Table } from 'react-bootstrap';
import { Form, Table } from 'react-bootstrap';
class EditWidgetMinMaxControl extends React.Component {
constructor(props) {
@ -75,20 +75,20 @@ class EditWidgetMinMaxControl extends React.Component {
render() {
return <FormGroup>
<FormLabel>{this.props.label}</FormLabel>
<FormCheck
return <Form.Group>
<Form.Label>{this.props.label}</Form.Label>
<Form.Check
label= {"UseMinMax"}
defaultChecked={this.state.useMinMax}
onChange={e => this.handleCheckboxChange(e)}>
</FormCheck>
</Form.Check>
<Table>
<tbody>
<tr>
<td>
Min:
<FormControl
<Form.Control
type="number"
step="any"
disabled={!this.state.useMinMax}
@ -98,7 +98,7 @@ class EditWidgetMinMaxControl extends React.Component {
</td>
<td>
Max:
<FormControl
<Form.Control
type="number"
step="any"
disabled={!this.state.useMinMax}
@ -109,7 +109,7 @@ class EditWidgetMinMaxControl extends React.Component {
</tr>
</tbody>
</Table>
</FormGroup>;
</Form.Group>;
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetNumberControl extends Component {
constructor(props) {
@ -48,14 +48,14 @@ class EditWidgetNumberControl extends Component {
}
return (
<FormGroup controlId={this.props.controlId}>
<FormLabel>{this.props.label}</FormLabel>
<FormControl
<Form.Group controlId={this.props.controlId}>
<Form.Label>{this.props.label}</Form.Label>
<Form.Control
type="number"
step={step}
value={isCustomProperty ? this.state.widget[parts[0]][parts[1]] : this.state.widget[this.props.controlId]}
onChange={e => this.props.handleChange(e)} />
</FormGroup>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, Col, Row, FormCheck, FormLabel } from 'react-bootstrap';
import { Col, Row, Form } from 'react-bootstrap';
import WidgetSlider from '../widgets/slider';
@ -50,12 +50,11 @@ class EditWidgetOrientation extends Component {
}
render() {
// The <Row> tag shouldn't be necessary, but it gives height to the row while combining horizontal and vertical forms
return (
<FormGroup controlId="orientation">
<Form.Group controlId="orientation">
<Row>
<Col className={FormLabel} sm={3}>
<Col className={Form.Label} sm={3}>
Orientation
</Col>
<Col sm={10}>
@ -64,14 +63,20 @@ class EditWidgetOrientation extends Component {
let value = WidgetSlider.OrientationTypes[type].value;
let name = WidgetSlider.OrientationTypes[type].name;
return (
<FormCheck inline label={name} key={value} id={value} type='radio' title='orientation' checked={ value === this.state.widget.customProperties.orientation } onChange={(e) => this.handleOrientationChange(value)}>
</FormCheck>)
return <Form.Check
inline label={name}
key={value}
id={value}
type='radio'
title='orientation'
checked={ value === this.state.widget.customProperties.orientation }
onChange={(e) => this.handleOrientationChange(value)}
/>
})
}
</Col>
</Row>
</FormGroup>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
import ParametersEditor from '../../common/parameters-editor';
class EditWidgetParametersControl extends Component {
@ -45,10 +45,13 @@ class EditWidgetParametersControl extends Component {
render() {
return (
<FormGroup controlId={this.props.controlId}>
<FormLabel>{this.props.label}</FormLabel>
<ParametersEditor content={this.state.widget[this.props.controlId] || {}} onChange={(v)=> this.handleChange(v)} />
</FormGroup>
<Form.Group controlId={this.props.controlId}>
<Form.Label>{this.props.label}</Form.Label>
<ParametersEditor
content={this.state.widget[this.props.controlId] || {}}
onChange={(v)=> this.handleChange(v)}
/>
</Form.Group>
);
}
}

View file

@ -16,11 +16,11 @@
******************************************************************************/
import React, { Component } from 'react';
import { FormGroup, OverlayTrigger, Tooltip , FormLabel, Button } from 'react-bootstrap';
import { OverlayTrigger, Tooltip , Button } from 'react-bootstrap';
import ColorPicker from './color-picker'
import Icon from "../../common/icon";
import {scaleOrdinal} from "d3-scale";
import {schemeCategory10} from "d3-scale-chromatic";
import { scaleOrdinal } from "d3-scale";
import { schemeCategory10 } from "d3-scale-chromatic";
// schemeCategory20 no longer available in d3
@ -80,33 +80,40 @@ class EditWidgetPlotColorsControl extends Component {
render() {
return (
<FormGroup>
<FormLabel>Line Colors</FormLabel>
<Form.Group>
<Form.Label>Line Colors</Form.Label>
<div>
{
this.state.widget.signalIDs.map((signalID, idx) => {
let color = this.state.widget.customProperties.lineColors[signalID];
let width = 260 / this.state.widget.signalIDs.length;
let style = {
backgroundColor: color,
width: width,
height: '40px'
}
<div>
{
this.state.widget.signalIDs.map((signalID, idx) => {
let color = this.state.widget.customProperties.lineColors[signalID];
let width = 260 / this.state.widget.signalIDs.length;
let style = {
backgroundColor: color,
width: width,
height: '40px'
}
let signal = this.props.signals.find(signal => signal.id === signalID);
let signal = this.props.signals.find(signal => signal.id === signalID);
return (<OverlayTrigger key={idx} placement={'bottom'} overlay={<Tooltip id={'tooltip-${"signal name"}'}>{signal.name}</Tooltip>}>
<Button
style={style} key={idx} onClick={i => this.editLineColor(signalID)} ><Icon icon="pen" /></Button>
</OverlayTrigger>
)
})
}
return <OverlayTrigger
key={idx}
placement={'bottom'}
overlay={<Tooltip id={'tooltip-${"signal name"}'}>{signal.name}</Tooltip>}
>
<Button
style={style}
key={idx}
onClick={i => this.editLineColor(signalID)}
>
<Icon icon="pen" />
</Button>
</OverlayTrigger>;
})
}
</div>
<ColorPicker show={this.state.showColorPicker} onClose={(data) => this.closeEditModal(data)} widget={this.state.widget} lineIndex={this.state.selectedIndex} controlId={'lineColor'} disableOpacity={true}/>
</FormGroup>
</Form.Group>
)
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetSignalControl extends Component {
constructor(props) {
@ -48,9 +48,9 @@ class EditWidgetSignalControl extends Component {
render() {
return (
<FormGroup controlId="signal">
<FormLabel>Select signal</FormLabel>
<FormControl as="select" value={this.props.widget.signalIDs[0] || ""} onChange={(e) => this.handleSignalChange(e)}>
<Form.Group controlId="signal">
<Form.Label>Select signal</Form.Label>
<Form.Control as="select" value={this.props.widget.signalIDs[0] || ""} onChange={(e) => this.handleSignalChange(e)}>
<option default>Select signal</option>
{
this.state.signals.length === 0 ? (
@ -61,8 +61,8 @@ class EditWidgetSignalControl extends Component {
))
)
}
</FormControl>
</FormGroup>
</Form.Control>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormCheck, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetSignalsControl extends Component {
constructor(props) {
@ -55,25 +55,25 @@ class EditWidgetSignalsControl extends Component {
render() {
return (
<FormGroup>
<FormLabel>Signals</FormLabel>
<Form.Group>
<Form.Label>Signals</Form.Label>
{
this.state.signals === 0 || !this.state.widget.hasOwnProperty(this.props.controlId)? (
<FormLabel>No signals available</FormLabel>
<Form.Label>No signals available</Form.Label>
) : (
this.state.signals.map((signal, index) => (
<FormCheck
<Form.Check
type={'checkbox'}
label={signal.name}
id={signal.id}
key={signal.id}
checked={this.state.checkedSignals.find(id => id === signal.id) !== undefined}
onChange={(e) => this.handleSignalChange(e.target.checked, signal.id)}>
</FormCheck>
</Form.Check>
))
)
}
</FormGroup>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetTextControl extends Component {
constructor(props) {
@ -60,11 +60,11 @@ class EditWidgetTextControl extends Component {
render() {
return (
<FormGroup controlId={this.props.controlId}>
<FormLabel>{this.props.label}</FormLabel>
<FormControl type="text" placeholder={this.props.placeholder} value={this.state.value} onChange={e => this.props.handleChange(e)} />
<FormControl.Feedback />
</FormGroup>
<Form.Group controlId={this.props.controlId}>
<Form.Label>{this.props.label}</Form.Label>
<Form.Control type="text" placeholder={this.props.placeholder} value={this.state.value} onChange={e => this.props.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
);
}
}

View file

@ -16,21 +16,21 @@
**********************************************************************************/
import React from 'react';
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetTextSizeControl extends React.Component {
render() {
const sizes = [11, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 46, 52, 60, 72];
return (
<FormGroup controlId="customProperties.textSize">
<FormLabel>Text size</FormLabel>
<FormControl as="select" value={this.props.widget.customProperties.textSize} onChange={e => this.props.handleChange(e)}>
<Form.Group controlId="customProperties.textSize">
<Form.Label>Text size</Form.Label>
<Form.Control as="select" value={this.props.widget.customProperties.textSize} onChange={e => this.props.handleChange(e)}>
{sizes.map((size, index) => (
<option key={index} value={size}>{size}</option>
))}
</FormControl>
</FormGroup>
</Form.Control>
</Form.Group>
);
}
}

View file

@ -16,7 +16,7 @@
**********************************************************************************/
import React, { Component } from 'react';
import { FormGroup, FormControl, FormLabel, FormText } from 'react-bootstrap';
import { Form } from 'react-bootstrap';
class EditWidgetTimeControl extends Component {
constructor(props) {
@ -43,9 +43,9 @@ class EditWidgetTimeControl extends Component {
}
return (
<FormGroup controlId= {this.props.controlId}>
<FormLabel>Time</FormLabel>
<FormControl
<Form.Group controlId= {this.props.controlId}>
<Form.Label>Time</Form.Label>
<Form.Control
type="number"
min="1"
max="300"
@ -53,8 +53,8 @@ class EditWidgetTimeControl extends Component {
value={isCustomProperty ? this.state.widget[parts[0]][parts[1]] : this.state.widget[this.props.controlId]}
onChange={(e) => this.props.handleChange(e)}
/>
<FormText>Time in seconds</FormText>
</FormGroup>
<Form.Text>Time in seconds</Form.Text>
</Form.Group>
);
}
}

View file

@ -16,13 +16,13 @@
******************************************************************************/
import React from 'react';
import { Form } from 'react-bootstrap';
import Dialog from '../../common/dialogs/dialog';
import CreateControls from './edit-widget-control-creator';
class EditWidgetDialog extends React.Component {
valid = true;
constructor(props) {
super(props);
@ -37,7 +37,6 @@ class EditWidgetDialog extends React.Component {
};
}
onClose(canceled) {
if (canceled === false) {
this.props.onClose(this.state.temporal);
@ -52,9 +51,9 @@ class EditWidgetDialog extends React.Component {
const file = this.props.files.find(element => element.id === fileId);
// scale width to match aspect
if(file.imageWidth && file.imageHeight){
const aspectRatio = file.imageWidth / file.imageHeight;
changeObject.width = parseInt(this.state.temporal.height * aspectRatio,10);
if (file.imageWidth && file.imageHeight) {
const aspectRatio = file.imageWidth / file.imageHeight;
changeObject.width = parseInt(this.state.temporal.height * aspectRatio,10);
}
return changeObject;
@ -69,26 +68,21 @@ class EditWidgetDialog extends React.Component {
return metrics.width;
}
setMaxWidth(changeObject){
if(changeObject.type === 'Label'){
setMaxWidth(changeObject) {
if (changeObject.type === 'Label') {
changeObject.customProperties.maxWidth = Math.ceil(this.getTextWidth(changeObject.name, changeObject.customProperties.textSize));
changeObject.width = changeObject.customProperties.maxWidth;
}
/*else if (changeObject.type === 'Value'){
changeObject.customProperties.maxWidth = Math.ceil(this.getTextWidth(changeObject.name, changeObject.customProperties.textSize));
}
if(this.state.temporal.width > changeObject.customProperties.maxWidth){
changeObject.width = changeObject.customProperties.maxWidth;
}*/
return changeObject;
}
setNewLockRestrictions(changeObject){
if(changeObject.customProperties.orientation === 0){
setNewLockRestrictions(changeObject) {
if (changeObject.customProperties.orientation === 0) {
changeObject.customProperties.resizeTopBottomLock = true;
changeObject.customProperties.resizeRightLeftLock = false;
}
else if(changeObject.customProperties.orientation === 1){
else if (changeObject.customProperties.orientation === 1) {
changeObject.customProperties.resizeTopBottomLock = false;
changeObject.customProperties.resizeRightLeftLock = true;
}
@ -96,7 +90,6 @@ class EditWidgetDialog extends React.Component {
}
handleChange(e) {
// TODO: check what we really need in this function. Can we reduce its complexity?
let parts = e.target.id.split('.');
let changeObject = this.state.temporal;
@ -174,10 +167,17 @@ class EditWidgetDialog extends React.Component {
}
return (
<Dialog show={this.props.show} title="Edit Widget" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
<form encType='multipart/form-data'>
<Dialog
show={this.props.show}
title="Edit Widget"
buttonTitle="Save"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form encType='multipart/form-data'>
{ controls || '' }
</form>
</Form>
</Dialog>
);
}

View file

@ -17,7 +17,7 @@
import React from 'react';
import { scaleOrdinal} from 'd3-scale';
import {schemeCategory10} from 'd3-scale-chromatic'
import { schemeCategory10 } from 'd3-scale-chromatic'
function Legend(props){
@ -44,7 +44,6 @@ function Legend(props){
</li>
)
}
}
class PlotLegend extends React.Component {
@ -60,7 +59,6 @@ class PlotLegend extends React.Component {
<Legend key={signal.id} sig={signal} lineColor={"undefined"}/>
))
}
</ul>
</div>;
}

View file

@ -17,7 +17,7 @@
import React from 'react';
import { scaleLinear, scaleTime, scaleOrdinal} from 'd3-scale';
import {schemeCategory10} from 'd3-scale-chromatic'
import { schemeCategory10 } from 'd3-scale-chromatic'
import { extent } from 'd3-array';
import { line } from 'd3-shape';
import { axisBottom, axisLeft } from 'd3-axis';
@ -103,7 +103,6 @@ class Plot extends React.Component {
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
return{
stopTime: stopTime,
data: null,
@ -113,7 +112,6 @@ class Plot extends React.Component {
};
}
// only show data in requested time
let data = props.data;
let icDataset = data.find(function(element) {
@ -138,8 +136,6 @@ class Plot extends React.Component {
if (prevProps.time !== this.props.time) {
this.createInterval();
}
}
createInterval() {
@ -222,7 +218,6 @@ class Plot extends React.Component {
}
render() {
const yLabelPos = {
x: 12,
y: this.props.height / 2

View file

@ -16,12 +16,11 @@
******************************************************************************/
import React from 'react';
import { Collapse } from 'react-collapse';
import PropTypes from 'prop-types';
import Slider from 'rc-slider';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
import Icon from "../common/icon";
import {Collapse} from 'react-collapse';
import ToolboxItem from './toolbox-item';
class WidgetToolbox extends React.Component {
@ -101,8 +100,8 @@ class WidgetToolbox extends React.Component {
}
const buttonStyle = {
marginRight: '3px',
height: '40px',
marginRight: '3px',
height: '40px',
}
const thereIsTopologyWidget = this.props.widgets != null && Object.values(this.props.widgets).filter(w => w.type === 'Topology').length > 0;

View file

@ -17,7 +17,6 @@
import React, { Component } from 'react';
import { Gauge } from 'gaugeJS';
//import {update} from "immutable";
class WidgetGauge extends Component {
constructor(props) {
@ -70,7 +69,6 @@ class WidgetGauge extends Component {
this.gauge.set(this.state.value);
this.updateLabels(this.state.minValue, this.state.maxValue)
}
}
static getDerivedStateFromProps(props, state){
@ -79,12 +77,12 @@ class WidgetGauge extends Component {
return{ value: 0, minValue: 0, maxValue: 10};
}
// get the signal with the selected signal ID
// Get the signal with the selected signal ID
let signalID = props.widget.signalIDs[0];
let signal = props.signals.filter(s => s.id === signalID)
if(signal.length > 0) {
// determine ID of infrastructure component related to signal[0] (there is only one signal for a lamp widget)
// Determine ID of infrastructure component related to signal[0] (there is only one signal for a lamp widget)
let icID = props.icIDs[signal[0].id];
let returnState = {}
@ -100,9 +98,9 @@ class WidgetGauge extends Component {
returnState["scalingFactor"] = signal[0].scalingFactor;
}
// update value
// Update value
// check if data available
// Check if data available
if (props.data == null
|| props.data[icID] == null
|| props.data[icID].output == null
@ -114,12 +112,12 @@ class WidgetGauge extends Component {
return returnState;
}
// memorize if min or max value is updated
// Memorize if min or max value is updated
let updateValue = false;
let updateMinValue = false;
let updateMaxValue = false;
// check if value has changed
// Check if value has changed
const data = props.data[icID].output.values[signal[0].index - 1];
// Take just 3 decimal positions
// Note: Favor this method over Number.toFixed(n) in order to avoid a type conversion, since it returns a String
@ -129,10 +127,10 @@ class WidgetGauge extends Component {
let maxValue = null;
if ((state.value !== value && value != null) || props.widget.customProperties.valueUseMinMax || state.useMinMaxChange) {
//value has changed
// Value has changed
updateValue = true;
// update min-max if needed
// Update min-max if needed
let updateLabels = false;
minValue = state.minValue;
@ -157,7 +155,6 @@ class WidgetGauge extends Component {
maxValue = props.widget.customProperties.valueMax;
updateMaxValue = true;
updateLabels = true;
}
if (updateLabels === false && state.gauge) {
@ -178,7 +175,7 @@ class WidgetGauge extends Component {
returnState["useMinMax"] = props.widget.customProperties.valueUseMinMax;
}
// prepare returned state
// Prepare returned state
if (updateValue === true) {
returnState["value"] = value;
}
@ -190,7 +187,7 @@ class WidgetGauge extends Component {
}
} // if there is signal data
} // If there is signal data
if (JSON.stringify(returnState) !== JSON.stringify({})) {
return returnState;
@ -198,8 +195,8 @@ class WidgetGauge extends Component {
return null;
}
}
return null;
return null;
}
updateLabels(minValue, maxValue, force) {
@ -223,17 +220,17 @@ class WidgetGauge extends Component {
});
}
if(this.state.signalID !== ''){
this.gauge.setOptions({
staticLabels: {
font: '10px "Helvetica Neue"',
labels,
color: "#000000",
fractionDigits: 1
},
staticZones: zones
});
}
if (this.state.signalID !== '') {
this.gauge.setOptions({
staticLabels: {
font: '10px "Helvetica Neue"',
labels,
color: "#000000",
fractionDigits: 1
},
staticZones: zones
});
}
}
computeGaugeOptions(widget) {
@ -267,8 +264,6 @@ class WidgetGauge extends Component {
<canvas ref={node => this.gaugeCanvas = node} />
<div className="gauge-unit">{this.state.unit + scaleText}</div>
<div className="gauge-value">{this.state.value}</div>
</div>
);
}

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React, { Component } from 'react';
import { Form, FormGroup, Col, FormLabel, FormControl, InputGroup } from 'react-bootstrap';
import { Form, Col, InputGroup } from 'react-bootstrap';
class WidgetInput extends Component {
@ -87,13 +87,13 @@ class WidgetInput extends Component {
return (
<div className="number-input-widget full">
<Form componentclass="fieldset" horizontal="true">
<FormGroup>
<Col as={FormLabel}>
<Form.Group>
<Col as={Form.Label}>
{this.props.widget.name}
</Col>
<Col>
<InputGroup>
<FormControl
<Form.Control
type="number"
step="any"
disabled={ this.props.editing }
@ -112,7 +112,7 @@ class WidgetInput extends Component {
)}
</InputGroup>
</Col>
</FormGroup>
</Form.Group>
</Form>
</div>
);

View file

@ -17,8 +17,7 @@
import React, { Component } from 'react';
import TrafficLight from 'react-trafficlight';
import {OverlayTrigger, Tooltip } from 'react-bootstrap';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
class WidgetTimeOffset extends Component {
constructor(props) {
@ -51,7 +50,7 @@ class WidgetTimeOffset extends Component {
let serverTime = props.data[state.icID].output.timestamp;
let localTime = Date.now();
let absoluteOffset = Math.abs(serverTime - localTime);
if(typeof websocket === 'undefined'){
return {timeOffset: Number.parseFloat(absoluteOffset/1000).toPrecision(5)}
}
@ -68,25 +67,25 @@ class WidgetTimeOffset extends Component {
}
return (
<div className="time-offset">
{this.props.widget.customProperties.icID !== -1 ?
{this.props.widget.customProperties.icID !== -1 ?
(<span></span>) : (<span>no IC</span>)
}
{this.props.widget.customProperties.icID !== -1 && this.props.widget.customProperties.showName ?
{this.props.widget.customProperties.icID !== -1 && this.props.widget.customProperties.showName ?
(<span>{this.state.icName}</span>) : (<span></span>)
}
}
<OverlayTrigger key={0} placement={'left'} overlay={<Tooltip id={`tooltip-${"traffic-light"}`}>
{this.props.widget.customProperties.icID !== -1 ?
(<span>{this.state.icName}<br></br>Offset: {this.state.timeOffset + "s"}</span>)
:
{this.props.widget.customProperties.icID !== -1 ?
(<span>{this.state.icName}<br></br>Offset: {this.state.timeOffset + "s"}</span>)
:
(<span>Please select Infrastructure Component</span>)}
</Tooltip>}>
</Tooltip>}>
<TrafficLight Horizontal={this.props.widget.customProperties.horizontal} width={this.props.widget.width - 40} height={this.props.widget.height - 40}
RedOn={(this.props.widget.customProperties.threshold_red <= this.state.timeOffset) || !this.state.websocketOpen}
YellowOn={(this.props.widget.customProperties.threshold_yellow <= this.state.timeOffset) && (this.state.timeOffset < this.props.widget.customProperties.threshold_red) && this.state.websocketOpen}
GreenOn={(this.state.timeOffset < this.props.widget.customProperties.threshold_yellow) && this.state.websocketOpen}
/>
</OverlayTrigger>
{this.props.widget.customProperties.icID !== -1 ?
{this.props.widget.customProperties.icID !== -1 ?
(
<span>{icSelected}</span>)
:

View file

@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
import {UncontrolledReactSVGPanZoom} from 'react-svg-pan-zoom';
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
import '../../styles/simple-spinner.css';
import { cimsvg } from 'libcimsvg';
import AppDispatcher from "../../common/app-dispatcher";
@ -94,7 +94,7 @@ class WidgetTopology extends React.Component {
}
static getDerivedStateFromProps(props, state){
// find the selected file of the widget, is undefined if no file is selected
// Find the selected file of the widget, is undefined if no file is selected
let file = props.files.find(file => file.id === parseInt(props.widget.customProperties.file, 10));
let dashboardState = state.dashboardState;
@ -104,7 +104,7 @@ class WidgetTopology extends React.Component {
dashboardState = 'show_message';
message = 'Select a topology model.'
} else if (!file.hasOwnProperty('data') && dashboardState === 'show_message'){
// data of file is missing, start download
// Data of file is missing, start download
dashboardState = 'loading';
message = '';
AppDispatcher.dispatch({
@ -113,7 +113,7 @@ class WidgetTopology extends React.Component {
token: props.token
});
} else if (file.hasOwnProperty('data') && (dashboardState === 'loading'|| dashboardState === 'show_message')){
//file is available set state to ready
// File is available set state to ready
dashboardState = 'ready'
message = '';
}
@ -135,8 +135,6 @@ class WidgetTopology extends React.Component {
window.onMouseMove = function() {};
}
//this.Viewer.fitToViewer();
// Query the file referenced by the widget (if any)
if (this.state.file !== undefined) {
this.setState({dashboardState: 'loading'});
@ -155,7 +153,7 @@ class WidgetTopology extends React.Component {
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
if(this.state.dashboardState === 'ready'){
//Topology file incl data downloaded, init SVG (should happen only once!)
// Topology file incl data downloaded, init SVG (should happen only once!)
if (this.svgElem) {
let cimsvgInstance = new cimsvg(this.svgElem.current);
cimsvg.setCimsvg(cimsvgInstance);