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

Merge branch 'master' of git.rwth-aachen.de:acs/public/villas/web

This commit is contained in:
Sonja Happ 2021-04-21 11:25:14 +02:00
commit 7b44a8f0d0
14 changed files with 511 additions and 282 deletions

View file

@ -6,6 +6,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@rjsf/core": "^2.5.1",
"babel-runtime": "^6.26.0",
"bootstrap": "^4.6.0",
"classnames": "^2.3.1",
@ -61,7 +62,7 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "https://villas.k8s.eonerc.rwth-aachen.de",
"proxy": "http://localhost:4000",
"browserslist": {
"production": [
">0.2%",

View file

@ -25,31 +25,32 @@ import Icon from '../common/icon';
class IconButton extends React.Component {
render() {
const altButtonStyle = {
marginLeft: '10px',
let btn = <Button
variant={this.props.variant ? this.props.variant : 'light'}
disabled={this.props.disabled}
onClick={this.props.onClick}
style={this.props.buttonStyle}
>
<Icon
icon={this.props.icon}
classname={'icon-color'}
style={this.props.iconStyle}
/>
</Button>
let button;
if (!this.props.tooltip || this.props.hidetooltip) {
button = btn;
} else {
button = <OverlayTrigger
key={this.props.ikey}
placement={this.props.tipPlacement ? this.props.tipPlacement : 'top'}
overlay={<Tooltip id={`tooltip-${this.props.ikey}`}>{this.props.tooltip}</Tooltip>} >
{btn}
</OverlayTrigger>
}
const iconStyle = {
height: '30px',
width: '30px'
}
return <OverlayTrigger
key={this.props.ikey}
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>
return button;
}
}

View file

@ -0,0 +1,62 @@
/**
* 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 { ToggleButton, ButtonGroup, Tooltip, OverlayTrigger } from 'react-bootstrap';
import Icon from './icon';
class IconToggleButton extends React.Component {
render() {
let tooltip = this.props.checked ? this.props.tooltipChecked : this.props.tooltipUnchecked;
return <OverlayTrigger
key={this.props.ikey}
placement={'top'}
overlay={<Tooltip id={`tooltip-${this.props.ikey}`}>{tooltip}</Tooltip>} >
<ButtonGroup toggle>
<ToggleButton
variant={this.props.variant ? this.props.variant : 'light'}
type='checkbox'
onChange={this.props.onChange}
style={this.props.buttonStyle}
disabled={this.props.disabled}
checked={this.props.checked}
>
{this.props.checked ?
<Icon
icon={this.props.checkedIcon}
classname={'icon-color'}
style={this.props.iconStyle}
/>
:
<Icon
icon={this.props.uncheckedIcon}
classname={'icon-color'}
style={this.props.iconStyle}
/>
}
</ToggleButton>
</ButtonGroup>
</OverlayTrigger>
}
}
export default IconToggleButton;

View file

@ -27,7 +27,10 @@ class TableColumn extends Component {
deleteButton: false,
showDeleteButton: null,
exportButton: false,
signalButton: false,
duplicateButton: false,
isLocked: null,
locked: false,
link: '/',
linkKey: '',
dataIndex: false,

View file

@ -20,6 +20,9 @@ import _ from 'lodash';
import { Table, Button, Form, Tooltip, OverlayTrigger } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import Icon from './icon';
import IconToggleButton from './icon-toggle-button';
import IconButton from '../common/icon-button';
class CustomTable extends Component {
constructor(props) {
@ -45,6 +48,7 @@ class CustomTable extends Component {
static addCell(data, index, child) {
// add data to cell
let content = null;
let childkey = 0;
if ('dataKeys' in child.props) {
for (let key of child.props.dataKeys) {
@ -90,7 +94,7 @@ class CustomTable extends Component {
onClick={() => child.props.onDownload(contentkey)}
disabled={child.props.onDownload == null}>
{contentkey + ' '}
<Icon icon='file-download' />
<Icon icon='file-download' classname={'icon-color'}/>
</Button>
</OverlayTrigger>);
});
@ -125,26 +129,26 @@ class CustomTable extends Component {
cell.push(index);
}
let isLocked = child.props.locked || (child.props.isLocked != null && child.props.isLocked(index));
// add buttons
let showEditButton = child.props.showEditButton !== null && child.props.showEditButton !== undefined
let showEditButton = child.props.showEditButton !== null && child.props.showEditButton !== undefined
? 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>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'edit'}
disabled={child.props.onEdit == null || isLocked}
hidetooltip={isLocked}
tooltip={"Edit"}
tipPlacement={'bottom'}
onClick={() => child.props.onEdit(index)}
variant={'table-control-button'}
/>)
}
if (child.props.checkbox) {
@ -164,89 +168,112 @@ class CustomTable extends Component {
);
}
if (child.props.lockButton) {
cell.push(
<IconToggleButton
ikey={childkey++}
onChange={() => child.props.onChangeLock(index)}
checked={isLocked}
checkedIcon='lock'
uncheckedIcon='lock-open'
tooltipChecked='Scenario is locked, cannot be edited'
tooltipUnchecked='Scenario is unlocked, can be edited'
disabled={false}
variant={'table-control-button'}
/>
);
}
if (child.props.exportButton) {
cell.push(
<OverlayTrigger
key={1}
placement={'bottom'}
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>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'download'}
disabled={child.props.onExport == null}
tooltip={"Export"}
tipPlacement={'bottom'}
onClick={() => child.props.onExport(index)}
variant={'table-control-button'}
/>);
}
if (child.props.signalButton) {
cell.push(
<IconButton
key={childkey++}
ikey={childkey++}
icon={'wave-square'}
disabled={child.props.onAutoConf == null || child.props.locked }
hidetooltip={isLocked}
tooltip={"Autoconfigure Signals"}
tipPlacement={'bottom'}
onClick={() => child.props.onAutoConf(index)}
variant={'table-control-button'}
/>);
}
if (child.props.duplicateButton) {
cell.push(
<OverlayTrigger
key={2}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"duplicate"}`}> Duplicate </Tooltip>} >
<Button
variant='table-control-button'
onClick={() => child.props.onDuplicate(index)}
disabled={child.props.onDuplicate == null}>
<Icon icon='copy' />
</Button>
</OverlayTrigger>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'copy'}
disabled={child.props.onDuplicate == null || child.props.locked}
hidetooltip={isLocked}
tooltip={"Duplicate"}
tipPlacement={'bottom'}
onClick={() => child.props.onDuplicate(index)}
variant={'table-control-button'}
/>);
}
if (child.props.addRemoveFilesButton) {
cell.push(
<OverlayTrigger
key={3}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"export"}`}>Add/remove File(s)</Tooltip>} >
<Button
variant='table-control-button'
onClick={() => child.props.onAddRemove(index)}
disabled={child.props.onAddRemove == null}>
<Icon icon='file' />
</Button>
</OverlayTrigger>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'file'}
disabled={child.props.onAddRemove == null || isLocked}
hidetooltip={isLocked}
tooltip={"Add/remove File(s)"}
tipPlacement={'bottom'}
onClick={() => child.props.onAddRemove(index)}
variant={'table-control-button'}
/>);
}
if (child.props.downloadAllButton) {
cell.push(
<OverlayTrigger
key={4}
placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"export"}`}>Download All Files</Tooltip>} >
<Button
variant='table-control-button'
onClick={() => child.props.onDownloadAll(index)}
disabled={child.props.onDownloadAll == null}>
<Icon icon='file-download' />
</Button>
</OverlayTrigger>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'file-download'}
disabled={child.props.onDownloadAll == null}
tooltip={"Download All Files"}
tipPlacement={'bottom'}
onClick={() => child.props.onDownloadAll(index)}
variant={'table-control-button'}
/>);
}
let showDeleteButton = child.props.showDeleteButton !== null && child.props.showDeleteButton !== undefined
let showDeleteButton = child.props.showDeleteButton !== null && child.props.showDeleteButton !== undefined
? 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>
);
<IconButton
key={childkey++}
ikey={childkey++}
icon={'trash'}
disabled={child.props.onDelete == null || isLocked}
hidetooltip={isLocked}
tooltip={"Delete"}
tipPlacement={'bottom'}
onClick={() => child.props.onDelete(index)}
variant={'table-control-button'}
/>);
}
return cell;
@ -351,14 +378,14 @@ class CustomTable extends Component {
value={cell}
onChange={(event) => children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)}
ref={ref => { this.activeInput = ref; }} />
: <span>
: <span>
{
cell.map((element, elementIndex) =>
<span key={elementIndex}>{element}</span>
)
}
</span>
}
}
</td>
})
}

View file

@ -298,6 +298,15 @@ class ConfigTable extends Component {
}
render() {
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div>
{/*Component Configurations table*/}
@ -308,12 +317,20 @@ class ConfigTable extends Component {
tooltip='Add Component Configuration'
onClick={() => this.addConfig()}
icon='plus'
disabled={this.props.locked}
hidetooltip={this.props.locked}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
<IconButton
ikey={1}
tooltip='Import Component Configuration'
onClick={() => this.setState({ importConfigModal: true })}
icon='upload'
disabled={this.props.locked}
hidetooltip={this.props.locked}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h2>
@ -343,6 +360,7 @@ class ConfigTable extends Component {
editButton
onEdit={index => this.setState({ editOutputSignalsModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
width={150}
locked={this.props.locked}
/>
<TableColumn
title='# Input Signals'
@ -350,12 +368,14 @@ class ConfigTable extends Component {
editButton
onEdit={index => this.setState({ editInputSignalsModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
width={150}
locked={this.props.locked}
/>
<TableColumn
title='Import Signals'
exportButton
onExport={(index) => this.signalsAutoConf(index)}
width={150}
title='Autoconfigure Signals'
signalButton
onAutoConf={(index) => this.signalsAutoConf(index)}
width={170}
locked={this.props.locked}
/>
<TableColumn
title='Infrastructure Component'
@ -375,6 +395,7 @@ class ConfigTable extends Component {
onDelete={(index) => this.setState({ deleteConfigModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
onExport={index => this.exportConfig(index)}
onDuplicate={index => this.duplicateConfig(index)}
locked={this.props.locked}
/>
</Table>

View file

@ -17,114 +17,77 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button,OverlayTrigger, Tooltip } from 'react-bootstrap';
import Icon from "../common/icon";
import IconButton from '../common/icon-button';
const buttonStyle = {
marginLeft: '12px',
height: '44px',
width: '35px',
};
const iconStyle = {
height: '25px',
width: '25px'
}
let buttonkey = 0;
class DashboardButtonGroup extends React.Component {
render() {
const buttonStyle = {
marginLeft: '12px',
height: '44px',
width : '35px',
};
const iconStyle = {
height: '25px',
width : '25px'
getBtn(icon, tooltip, clickFn, locked = false) {
if (locked) {
return <IconButton
key={buttonkey++}
ikey={buttonkey}
icon={icon}
disabled={true}
hidetooltip={true}
tooltip={tooltip}
tipPlacement={'bottom'}
onClick={clickFn}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
} else {
return <IconButton
key={buttonkey++}
ikey={buttonkey}
icon={icon}
tooltip={tooltip}
tipPlacement={'bottom'}
onClick={clickFn}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
}
}
render() {
const buttons = [];
let key = 0;
/*if (this.props.fullscreen) {
return null;
}*/
buttonkey = 0;
if (this.props.editing) {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"save"}`}> Save changes </Tooltip>} >
<Button variant= 'light' size="lg" key={key} onClick={this.props.onSave} style={buttonStyle}>
<Icon icon="save" classname='icon-color' style={iconStyle} />
</Button>
</OverlayTrigger>,
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"cancel"}`}> Discard changes </Tooltip>} >
<Button key={key} variant= 'light' size="lg" onClick={this.props.onCancel} style={buttonStyle}>
<Icon icon="times" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(this.getBtn("save", "Save changes", this.props.onSave));
buttons.push(this.getBtn("times", "Discard changes", this.props.onCancel));
} else {
if (this.props.fullscreen !== true) {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"expand"}`}> Change to fullscreen view </Tooltip>} >
<Button key={key} variant= 'light' size="lg" onClick={this.props.onFullscreen} style={buttonStyle}>
<Icon icon="expand" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(this.getBtn("expand", "Change to fullscreen view", this.props.onFullscreen));
} else {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"compress"}`}> Back to normal view </Tooltip>} >
<Button key={key} variant= 'light' size="lg" onClick={this.props.onFullscreen} style={buttonStyle}>
<Icon icon="compress" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(this.getBtn("compress", "Back to normal view", this.props.onFullscreen));
}
if (this.props.paused) {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"play"}`}> Continue simulation </Tooltip>} >
<Button key={key} variant= 'light' size="lg" onClick={this.props.onUnpause} style={buttonStyle}>
<Icon icon="play" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(this.getBtn("play", "Continue simulation", this.props.onUnpause));
} else {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"pause"}`}> Pause simulation </Tooltip>} >
<Button key={key} variant= 'light' size="lg" onClick={this.props.onPause} style={buttonStyle}>
<Icon icon="pause" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(this.getBtn("pause", "Pause simulation", this.props.onPause));
}
if (this.props.fullscreen !== true) {
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"file"}`}> Add, edit or delete files of scenario </Tooltip>}>
<Button key={key} variant='light' size="lg" onClick={this.props.onEditFiles} style={buttonStyle}>
<Icon icon="file" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"file"}`}> Add, edit or delete input signals </Tooltip>}>
<Button key={key} variant='light' size="lg" onClick={this.props.onEditInputSignals} style={buttonStyle}>
<Icon icon="sign-in-alt" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"file"}`}> Add, edit or delete output signals </Tooltip>}>
<Button key={key} variant='light' size="lg" onClick={this.props.onEditOutputSignals} style={buttonStyle}>
<Icon icon="sign-out-alt" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
buttons.push(
<OverlayTrigger key={key++} placement={'bottom'}
overlay={<Tooltip id={`tooltip-${"layout"}`}> Add widgets and edit layout </Tooltip>}>
<Button key={key} variant='light' size="lg" onClick={this.props.onEdit} style={buttonStyle}>
<Icon icon="pen" classname='icon-color' style={iconStyle}/>
</Button>
</OverlayTrigger>
);
let tooltip = this.props.locked ? "View files of scenario" : "Add, edit or delete files of scenario";
buttons.push(this.getBtn("file", tooltip, this.props.onEditFiles));
buttons.push(this.getBtn("sign-in-alt", "Add, edit or delete input signals", this.props.onEditInputSignals, this.props.locked));
buttons.push(this.getBtn("sign-out-alt", "Add, edit or delete output signals", this.props.onEditOutputSignals, this.props.locked));
buttons.push(this.getBtn("pen", "Add widgets and edit layout", this.props.onEdit, this.props.locked));
}
}

View file

@ -137,6 +137,14 @@ class DashboardTable extends Component {
}
render() {
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div>
@ -148,12 +156,20 @@ class DashboardTable extends Component {
tooltip='Add Dashboard'
onClick={() => this.setState({newDashboardModal: true})}
icon='plus'
disabled={this.props.locked}
hidetooltip={this.props.locked}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
<IconButton
ikey={1}
tooltip='Import Dashboard'
onClick={() => this.setState({importDashboardModal: true})}
icon='upload'
disabled={this.props.locked}
hidetooltip={this.props.locked}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h2>
@ -198,6 +214,7 @@ class DashboardTable extends Component {
})}
onExport={index => this.exportDashboard(index)}
onDuplicate={index => this.duplicateDashboard(index)}
locked={this.props.locked}
/>
</Table>

View file

@ -27,6 +27,7 @@ import WidgetContextMenu from '../widget/widget-context-menu';
import WidgetToolbox from '../widget/widget-toolbox';
import WidgetArea from '../widget/widget-area';
import DashboardButtonGroup from './dashboard-button-group';
import IconToggleButton from '../common/icon-toggle-button';
import DashboardStore from './dashboard-store';
import SignalStore from '../signal/signal-store'
@ -35,6 +36,8 @@ import WidgetStore from '../widget/widget-store';
import ICStore from '../ic/ic-store'
import ConfigStore from '../componentconfig/config-store'
import AppDispatcher from '../common/app-dispatcher';
import ScenarioStore from '../scenario/scenario-store';
import 'react-contexify/dist/ReactContexify.min.css';
import WidgetContainer from '../widget/widget-container';
@ -45,7 +48,7 @@ class Dashboard extends Component {
static lastWidgetKey = 0;
static webSocketsOpened = false;
static getStores() {
return [DashboardStore, FileStore, WidgetStore, SignalStore, ConfigStore, ICStore];
return [DashboardStore, FileStore, WidgetStore, SignalStore, ConfigStore, ICStore, ScenarioStore];
}
static calculateState(prevState, props) {
@ -80,9 +83,14 @@ class Dashboard extends Component {
// filter component configurations to the ones that belong to this scenario
let configs = [];
let files = [];
let locked = false;
if (dashboard !== undefined) {
configs = ConfigStore.getState().filter(config => config.scenarioID === dashboard.scenarioID);
files = FileStore.getState().filter(file => file.scenarioID === dashboard.scenarioID);
let scenario = ScenarioStore.getState().find(s => s.id === dashboard.scenarioID);
if (scenario) {
locked = scenario.isLocked;
}
if (dashboard.height === 0) {
dashboard.height = 400;
}
@ -144,6 +152,7 @@ class Dashboard extends Component {
widgetOrigIDs: prevState.widgetOrigIDs || [],
maxWidgetHeight: maxHeight || null,
locked,
};
}
@ -216,6 +225,13 @@ class Dashboard extends Component {
param: '?scenarioID=' + this.state.dashboard.scenarioID,
token: this.state.sessionToken
});
// load scenario for 'isLocked' value
AppDispatcher.dispatch({
type: 'scenarios/start-load',
data: this.state.dashboard.scenarioID,
token: this.state.sessionToken
});
}
}
@ -482,6 +498,15 @@ class Dashboard extends Component {
return <div className="section-title"> <span>{"Loading Dashboard..."}</span> </div>
}
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '25px',
width: '25px'
}
const grid = this.state.dashboard.grid;
const boxClasses = classNames('section', 'box', { 'fullscreen-padding': this.props.isFullscreen });
let draggable = this.state.editing;
@ -489,10 +514,26 @@ class Dashboard extends Component {
return (<div className={boxClasses} >
<div className='section-header box-header'>
<div className="section-title">
<h2>{this.state.dashboard.name}</h2>
<h2>
{this.state.dashboard.name}
<span className='icon-button'>
<IconToggleButton
ikey={0}
checked={this.state.locked}
checkedIcon='lock'
uncheckedIcon='lock-open'
tooltipChecked='Dashboard is locked, cannot be edited'
tooltipUnchecked='Dashboard is unlocked, can be edited'
disabled={true}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h2>
</div>
<DashboardButtonGroup
locked={this.state.locked}
editing={this.state.editing}
onEdit={this.startEditing.bind(this)}
fullscreen={this.props.isFullscreen}
@ -578,6 +619,7 @@ class Dashboard extends Component {
signals={this.state.signals}
files={this.state.files}
scenarioID={this.state.dashboard.scenarioID}
locked={this.state.locked}
/>
<EditSignalMappingDialog

View file

@ -106,10 +106,12 @@ class EditFilesDialog extends React.Component {
marginTop: '-40px'
};
let title = this.props.locked ? "View files of scenario" : "Edit Files of Scenario";
return (
<Dialog
show={this.props.show}
title="Edit Files of Scenario"
title={title}
buttonTitle="Close"
onClose={() => this.onClose()}
blendOutCancel = {true}
@ -139,6 +141,7 @@ class EditFilesDialog extends React.Component {
onDelete={(index) => this.deleteFile(index)}
editButton
onEdit={index => this.setState({ editModal: true, modalFile: this.props.files[index] })}
locked={this.props.locked}
/>
</Table>
@ -146,13 +149,17 @@ class EditFilesDialog extends React.Component {
<h5>Add file</h5>
<Row>
<Col xs lg="4">
<Form.Control type='file' onChange={(event) => this.selectUploadFile(event)} />
<Form.Control
type='file'
onChange={(event) => this.selectUploadFile(event)}
disabled={this.props.locked}
/>
</Col>
<Col xs lg="2">
<span className='solid-button'>
<Button
variant='secondary'
disabled={this.state.uploadFile === null}
disabled={this.state.uploadFile === null || this.props.locked}
onClick={() => this.startFileUpload()}>
Upload
</Button>

View file

@ -154,19 +154,31 @@ class ResultTable extends Component {
}
render() {
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div>
{/*Result table*/}
<h2 style={this.props.tableHeadingStyle}>Results
<span className='icon-button'>
<span className='icon-button'>
<IconButton
ikey={1}
tooltip='Add Result'
onClick={() => this.setState({ newResultModal: true })}
icon='plus'
disabled={this.props.locked}
hidetooltip={this.props.locked}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</span>
</h2>
<Table data={this.props.results}>
@ -208,6 +220,7 @@ class ResultTable extends Component {
onEdit={index => this.setState({ editResultsModal: true, modalResultsIndex: index })}
onDownloadAll={(index) => this.downloadResultData(this.props.results[index])}
onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.props.results[index], modalResultsIndex: index })}
locked={this.props.locked}
/>
</Table>

View file

@ -16,11 +16,11 @@
******************************************************************************/
import React, {Component} from "react";
import {Button, Form, InputGroup} from "react-bootstrap";
import { Form, InputGroup} from "react-bootstrap";
import {Redirect} from "react-router-dom";
import Table from "../common/table";
import TableColumn from "../common/table-column";
import Icon from "../common/icon";
import IconButton from "../common/icon-button";
import DeleteDialog from "../common/dialogs/delete-dialog";
import AppDispatcher from "../common/app-dispatcher";
@ -82,15 +82,6 @@ class ScenarioUsersTable extends Component {
return (<Redirect to="/scenarios" />);
}
const altButtonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return (
<div>
{/*Scenario Users table*/}
@ -124,6 +115,7 @@ class ScenarioUsersTable extends Component {
deleteUserName: this.props.scenario.users[index].username,
modalUserIndex: index
})}
locked={this.props.locked}
/>
</Table>
@ -141,13 +133,14 @@ class ScenarioUsersTable extends Component {
/>
<InputGroup.Append>
<span className='icon-button'>
<Button
variant='light'
type="submit"
style={altButtonStyle}
onClick={() => this.addUser()}>
<Icon icon="plus" classname={'icon-color'} style={iconStyle} />
</Button>
<IconButton
ikey={1}
tooltip='Add User to Scenario'
onClick={() => this.addUser()}
icon='plus'
disabled={this.props.locked}
hidetooltip={this.props.locked}
/>
</span>
</InputGroup.Append>
</InputGroup>

View file

@ -20,6 +20,7 @@ import { Container } from 'flux/utils';
import AppDispatcher from '../common/app-dispatcher';
import IconButton from '../common/icon-button';
import IconToggleButton from '../common/icon-toggle-button';
import ScenarioStore from './scenario-store';
import ICStore from '../ic/ic-store';
@ -70,26 +71,24 @@ class Scenario extends React.Component {
}
componentDidMount() {
let token = localStorage.getItem("token")
let scenarioID = parseInt(this.props.match.params.scenario, 10)
//load selected scenario
AppDispatcher.dispatch({
type: 'scenarios/start-load',
data: scenarioID,
token: token
token: this.state.sessionToken
});
AppDispatcher.dispatch({
type: 'scenarios/start-load-users',
data: scenarioID,
token: token
token: this.state.sessionToken
});
// load ICs to enable that component configs and dashboards work with them
AppDispatcher.dispatch({
type: 'ics/start-load',
token: token
token: this.state.sessionToken
});
}
@ -112,11 +111,35 @@ class Scenario extends React.Component {
this.setState({ filesEditModal: false });
}
/* ##############################################
* Change locked state of scenario
############################################## */
onChangeLock() {
let data = {};
data.id = this.state.scenario.id;
data.isLocked = !this.state.scenario.isLocked;
AppDispatcher.dispatch({
type: 'scenarios/start-edit',
data,
token: this.state.sessionToken
});
}
/* ##############################################
* Render method
############################################## */
render() {
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
const tableHeadingStyle = {
paddingTop: '30px'
@ -126,16 +149,36 @@ class Scenario extends React.Component {
return <h1>Loading Scenario...</h1>;
}
let tooltip = this.state.scenario.isLocked ? "View files of scenario" : "Add, edit or delete files of scenario";
return <div className='section'>
<div className='section-buttons-group-right'>
<IconButton
ikey="0"
tooltip="Add, edit or delete files of scenario"
tooltip={tooltip}
onClick={this.onEditFiles.bind(this)}
icon="file"
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</div>
<h1>{this.state.scenario.name}</h1>
<h1>
{this.state.scenario.name}
<span className='icon-button'>
<IconToggleButton
ikey={0}
onChange={() => this.onChangeLock()}
checked={this.state.scenario.isLocked}
checkedIcon='lock'
uncheckedIcon='lock-open'
tooltipChecked='Scenario is locked, cannot be edited'
tooltipUnchecked='Scenario is unlocked, can be edited'
disabled={this.state.currentUser.role !== "Admin"}
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h1>
<EditFilesDialog
sessionToken={this.state.sessionToken}
@ -144,6 +187,7 @@ class Scenario extends React.Component {
signals={this.state.signals}
files={this.state.files}
scenarioID={this.state.scenario.id}
locked={this.state.scenario.isLocked}
/>
<ConfigTable
@ -155,6 +199,7 @@ class Scenario extends React.Component {
sessionToken={this.state.sessionToken}
currentUser={this.state.currentUser}
tableHeadingStyle={tableHeadingStyle}
locked={this.state.scenario.isLocked}
/>
<DashboardTable
@ -164,6 +209,7 @@ class Scenario extends React.Component {
sessionToken={this.state.sessionToken}
currentUser={this.state.currentUser}
tableHeadingStyle={tableHeadingStyle}
locked={this.state.scenario.isLocked}
/>
<ResultTable
@ -172,6 +218,7 @@ class Scenario extends React.Component {
scenario={this.state.scenario}
sessionToken={this.state.sessionToken}
tableHeadingStyle={tableHeadingStyle}
locked={this.state.scenario.isLocked}
/>
<ScenarioUsersTable
@ -179,6 +226,7 @@ class Scenario extends React.Component {
currentUser={this.state.currentUser}
sessionToken={this.state.sessionToken}
tableHeadingStyle={tableHeadingStyle}
locked={this.state.scenario.isLocked}
/>
</div>

View file

@ -73,7 +73,7 @@ class Scenarios extends Component {
}
closeNewModal(data) {
if(data) {
if (data) {
AppDispatcher.dispatch({
type: 'scenarios/start-add',
data: data,
@ -218,7 +218,7 @@ class Scenarios extends Component {
jsonObj["configs"] = this.getConfigs(scenario.id);
jsonObj["dashboards"] = this.getDashboards(scenario.id);
if(jsonObj) {
if (jsonObj) {
AppDispatcher.dispatch({
type: 'scenarios/start-add',
data: jsonObj,
@ -227,68 +227,99 @@ class Scenarios extends Component {
}
}
modifyRunningColumn(running){
return <Icon icon={ running ? 'check' : 'times' } />
isLocked(index) {
return this.state.scenarios[index].isLocked;
}
onLock(index) {
let data = {};
data.id = this.state.scenarios[index].id;
data.isLocked = !this.state.scenarios[index].isLocked;
AppDispatcher.dispatch({
type: 'scenarios/start-edit',
data,
token: this.state.sessionToken
});
}
render() {
const buttonStyle = {
marginLeft: '10px',
}
const iconStyle = {
height: '30px',
width: '30px'
}
return <div className='section'>
<h1>Scenarios
<h1>Scenarios
<span className='icon-button'>
<IconButton
ikey={0}
tooltip='Add Scenario'
onClick={() => this.setState({ newModal: true })}
icon='plus'
/>
<IconButton
ikey={1}
tooltip='Import Scenario'
onClick={() => this.setState({ importModal: true })}
icon='upload'
/>
</span>
</h1>
<Table data={this.state.scenarios}>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='ID'
dataKey='id'
/>
: <></>
}
<TableColumn
title='Name'
dataKey='name'
link='/scenarios/'
linkKey='id'
<IconButton
ikey={0}
tooltip='Add Scenario'
onClick={() => this.setState({ newModal: true })}
icon='plus'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
<TableColumn
title='Running'
dataKey='running'
modifier={(running) => this.modifyRunningColumn(running)}
<IconButton
ikey={1}
tooltip='Import Scenario'
onClick={() => this.setState({ importModal: true })}
icon='upload'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h1>
<Table data={this.state.scenarios}>
{this.state.currentUser.role === "Admin" ?
<TableColumn
width='200'
align='right'
editButton
deleteButton
exportButton
duplicateButton
onEdit={index => this.setState({ editModal: true, modalScenario: this.state.scenarios[index] })}
onDelete={index => this.setState({ deleteModal: true, modalScenario: this.state.scenarios[index] })}
onExport={index => this.exportScenario(index)}
onDuplicate={index => this.duplicateScenario(index)}
title='ID'
dataKey='id'
/>
</Table>
: <></>
}
<TableColumn
title='Name'
dataKey='name'
link='/scenarios/'
linkKey='id'
/>
{this.state.currentUser.role === "Admin" ?
<TableColumn
title='Locked'
lockButton
checkboxKey='isLocked'
onChangeLock={(index, event) => this.onLock(index)}
isLocked={index => this.isLocked(index)}
/>
: <></>
}
<TableColumn
width='200'
align='right'
editButton
deleteButton
exportButton
duplicateButton
onEdit={index => this.setState({ editModal: true, modalScenario: this.state.scenarios[index] })}
onDelete={index => this.setState({ deleteModal: true, modalScenario: this.state.scenarios[index] })}
onExport={index => this.exportScenario(index)}
onDuplicate={index => this.duplicateScenario(index)}
isLocked={index => this.isLocked(index)}
/>
</Table>
<NewScenarioDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
<EditScenarioDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} scenario={this.state.modalScenario} />
<ImportScenarioDialog show={this.state.importModal} onClose={data => this.closeImportModal(data)} nodes={this.state.nodes} />
<NewScenarioDialog show={this.state.newModal} onClose={(data) => this.closeNewModal(data)} />
<EditScenarioDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)} scenario={this.state.modalScenario} />
<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>;
<DeleteDialog title="scenario" name={this.state.modalScenario.name} show={this.state.deleteModal} onClose={(e) => this.closeDeleteModal(e)} />
</div>;
}
}