mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
merge with master
This commit is contained in:
commit
a62a7e8c04
35 changed files with 968 additions and 295 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -1179,6 +1179,14 @@
|
|||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@createnl/grouped-checkboxes": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@createnl/grouped-checkboxes/-/grouped-checkboxes-1.1.2.tgz",
|
||||
"integrity": "sha512-F4SoFF7UwktrpVeJPs/cQFP5Nn+1wpUm5v1/vzUcgseDGxvjXjLrs+F99ivH/TA3VDJbeFdBPi7gymmw2cessQ==",
|
||||
"requires": {
|
||||
"lodash.debounce": "^4.0.8"
|
||||
}
|
||||
},
|
||||
"@csstools/convert-colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.1",
|
||||
"@createnl/grouped-checkboxes": "^1.1.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.13",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bootstrap": "^4.5.3",
|
||||
|
|
|
@ -138,6 +138,14 @@ class NotificationsFactory {
|
|||
};
|
||||
}
|
||||
|
||||
static ACTION_INFO() {
|
||||
return {
|
||||
title: 'Action successfully requested',
|
||||
level: 'info'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default NotificationsFactory;
|
||||
|
|
|
@ -42,7 +42,9 @@ class DeleteDialog extends React.Component {
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => this.props.onClose(false)}>Cancel</Button>
|
||||
<span className='solid-button'>
|
||||
<Button variant='secondary' onClick={() => this.props.onClose(false)}>Cancel</Button>
|
||||
</span>
|
||||
<Button variant="danger" onClick={() => this.props.onClose(true)}>Delete</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>;
|
||||
|
|
|
@ -45,6 +45,11 @@ class Dialog extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
|
||||
const buttonStyle = {
|
||||
marginLeft: '10px'
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal size={this.props.size || 'sm'} keyboard show={this.props.show} onEnter={this.props.onReset} onHide={this.cancelModal} onKeyPress={this.onKeyPress}>
|
||||
<Modal.Header>
|
||||
|
@ -56,8 +61,10 @@ class Dialog extends React.Component {
|
|||
</Modal.Body>
|
||||
|
||||
<Modal.Footer>
|
||||
{this.props.blendOutCancel? <div></div>: <Button onClick={this.cancelModal}>Cancel</Button>}
|
||||
<Button onClick={this.closeModal} disabled={!this.props.valid}>{this.props.buttonTitle}</Button>
|
||||
<span className='solid-button'>
|
||||
{this.props.blendOutCancel? <div/>: <Button variant='secondary' onClick={this.cancelModal} style={buttonStyle}>Cancel</Button>}
|
||||
<Button variant='secondary' onClick={this.closeModal} disabled={!this.props.valid} style={buttonStyle}>{this.props.buttonTitle}</Button>
|
||||
</span>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -43,17 +43,15 @@ class Home extends React.Component {
|
|||
<img style={{height: 120, float: 'right'}} src={require('../img/villas_web.svg').default} alt="Logo VILLASweb" />
|
||||
<h1>Home</h1>
|
||||
<p>
|
||||
Welcome to <b>{config.instance}</b> hosted by <a href={"mailto:" + config.admin.mail}>{config.admin.name}</a>!<br />
|
||||
{/*Welcome to <b>{config.instance}</b> hosted by <a href={"mailto:" + config.admin.mail}>{config.admin.name}</a>!<br />*/}
|
||||
Welcome to <b>{config.instance}</b>!
|
||||
</p>
|
||||
<p>
|
||||
You are logged in as user <b>{currentUser.username}</b> with <b>ID {currentUser.id}</b> and role <b>{currentUser.role}</b>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An interactive documentation of the VILLASweb API is available <NavLink to="/api">here</NavLink>.
|
||||
</p>
|
||||
|
||||
<h3>Data Model</h3>
|
||||
{/*<h3>Data Model</h3>
|
||||
<img height={400} src={require('../img/datamodel.png').default} alt="Datamodel VILLASweb" />
|
||||
|
||||
<h3>Terminology </h3>
|
||||
|
@ -82,32 +80,47 @@ class Home extends React.Component {
|
|||
<ul>
|
||||
<li>A collection of component configurations and dashboards for a specific experiment</li>
|
||||
<li>Users can have access to multiple scenarios</li>
|
||||
</ul>
|
||||
</ul>*/}
|
||||
|
||||
<h3>Credits</h3>
|
||||
<p>VILLASweb is developed by the <a href="http://acs.eonerc.rwth-aachen.de">Institute for Automation of Complex Power Systems</a> at the <a href="https;//www.rwth-aachen.de">RWTH Aachen University</a>.</p>
|
||||
<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><a href="http://fein-aachen.org/projects/villas-framework/">Project Page</a></li>
|
||||
<li><a href="https://villas.fein-aachen.org/doc/web.html">Documentation</a></li>
|
||||
<li><a href="https://git.rwth-aachen.de/acs/public/villas/web">Source Code</a></li>
|
||||
<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 have received funding from</p>
|
||||
<p>The development of <a href="http://fein-aachen.org/projects/villas-framework/">VILLASframework</a> projects has received funding from</p>
|
||||
<ul>
|
||||
<li><a href="http://www.acs.eonerc.rwth-aachen.de/cms/E-ON-ERC-ACS/Forschung/Forschungsprojekte/Gruppe-Real-Time-Simulation-and-Hardware/~qxvw/Urban-Energy-Lab-4/">Urban Energy Lab 4.0</a> a project funded by OP EFRE NRW (European Regional Development Fund) for the setup of a novel energy research infrastructure.</li>
|
||||
<li><a href="http://www.re-serve.eu">RESERVE</a> a European Union’s Horizon 2020 research and innovation programme under grant agreement No 727481</li>
|
||||
<li><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.</li>
|
||||
<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>
|
||||
<img height={100} src={require('../img/european_commission.svg').default} alt="Logo EU" />
|
||||
<img height={70} src={require('../img/reserve.svg').default} alt="Logo EU" />
|
||||
<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" />
|
||||
<img height={60} src={require('../img/eonerc_rwth.svg').default} alt="Logo ACS" />
|
||||
|
||||
|
||||
|
||||
{
|
||||
//<img height={70} src={require('../img/jara.svg').default} alt="Logo JARA" />
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ library.add(fas);
|
|||
class Icon extends React.Component {
|
||||
|
||||
render() {
|
||||
return <FontAwesomeIcon size={this.props.size} style={this.props.style} icon={this.props.icon} />
|
||||
return <FontAwesomeIcon className={this.props.classname} size={this.props.size} style={this.props.style} icon={this.props.icon} />
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,9 @@ class TableColumn extends Component {
|
|||
modifier: null,
|
||||
width: null,
|
||||
editButton: false,
|
||||
showEditButton: null,
|
||||
deleteButton: false,
|
||||
showDeleteButton: null,
|
||||
exportButton: false,
|
||||
duplicateButton: false,
|
||||
link: '/',
|
||||
|
|
|
@ -34,7 +34,8 @@ class CustomTable extends Component {
|
|||
}
|
||||
|
||||
static defaultProps = {
|
||||
width: null
|
||||
width: null,
|
||||
checked: true
|
||||
};
|
||||
|
||||
onClick(event, row, column) {
|
||||
|
@ -78,9 +79,19 @@ class CustomTable extends Component {
|
|||
cell.push(<Button variant="link" onClick={() => child.props.onClick(index)}>{content}</Button>);
|
||||
} else if (linkKey === 'filebuttons') {
|
||||
content.forEach((contentvalue, contentkey) => {
|
||||
cell.push(<OverlayTrigger key={contentkey} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"export"}`}>Download {contentvalue}</Tooltip>} >
|
||||
<Button variant='table-control-button' onClick={() => child.props.onDownload(contentkey)} disabled={child.props.onDownload == null}>{contentkey + ' ' }
|
||||
<Icon icon='file-download' /></Button></OverlayTrigger>);
|
||||
cell.push(
|
||||
<OverlayTrigger
|
||||
key={contentkey}
|
||||
placement={'bottom'}
|
||||
overlay={<Tooltip id={`tooltip-${"export"}`}>Download {contentvalue}</Tooltip>} >
|
||||
<Button
|
||||
variant='table-control-button'
|
||||
onClick={() => child.props.onDownload(contentkey)}
|
||||
disabled={child.props.onDownload == null}>
|
||||
{contentkey + ' ' }
|
||||
<Icon icon='file-download' />
|
||||
</Button>
|
||||
</OverlayTrigger>);
|
||||
});
|
||||
} else {
|
||||
cell.push(content);
|
||||
|
@ -113,17 +124,34 @@ class CustomTable extends Component {
|
|||
}
|
||||
|
||||
// add buttons
|
||||
if (child.props.editButton) {
|
||||
let disable = (typeof data.managedexternally !== "undefined" && data.managedexternally);
|
||||
cell.push(<OverlayTrigger key={0} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"edit"}`}>{disable ? "Externally managed ICs cannot be edited" : "edit"} </Tooltip>} >
|
||||
<Button variant='table-control-button' onClick={() => child.props.onEdit(index)} disabled={disable || child.props.onEdit == null}><Icon icon='edit' /></Button></OverlayTrigger>);
|
||||
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>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (child.props.checkbox) {
|
||||
const checkboxKey = child.props.checkboxKey;
|
||||
let isDisabled = false;
|
||||
if (child.props.checkboxDisabled != null){
|
||||
isDisabled = !child.props.checkboxDisabled(index)
|
||||
isDisabled = child.props.checkboxDisabled(index)
|
||||
}
|
||||
cell.push(
|
||||
<FormCheck
|
||||
|
@ -136,30 +164,89 @@ class CustomTable extends Component {
|
|||
}
|
||||
|
||||
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>);
|
||||
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>);
|
||||
}
|
||||
|
||||
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>);
|
||||
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>);
|
||||
}
|
||||
|
||||
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>);
|
||||
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>);
|
||||
}
|
||||
|
||||
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>);
|
||||
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>);
|
||||
}
|
||||
|
||||
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>);
|
||||
let showDeleteButton = true;
|
||||
if (child.props.showDeleteButton !== null){
|
||||
showDeleteButton = child.props.showDeleteButton(index)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
@ -243,9 +330,19 @@ class CustomTable extends Component {
|
|||
onCellBlur: () => { }
|
||||
};
|
||||
|
||||
return (<td key={cellIndex} tabIndex={tabIndex} onClick={evtHdls.onCellClick} onFocus={evtHdls.onCellFocus} onBlur={evtHdls.onCellBlur}>
|
||||
return (<td
|
||||
key={cellIndex}
|
||||
tabIndex={tabIndex}
|
||||
onClick={evtHdls.onCellClick}
|
||||
onFocus={evtHdls.onCellFocus}
|
||||
onBlur={evtHdls.onCellBlur}>
|
||||
{(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex) ? (
|
||||
<FormControl as='input' type={children[cellIndex].props.inputType} value={cell} onChange={(event) => children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} ref={ref => { this.activeInput = ref; }} />
|
||||
<FormControl
|
||||
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) => (
|
||||
|
|
|
@ -24,13 +24,12 @@ class DashboardButtonGroup extends React.Component {
|
|||
render() {
|
||||
const buttonStyle = {
|
||||
marginLeft: '12px',
|
||||
height: '44px',
|
||||
width : '35px'
|
||||
height: '44px',
|
||||
width : '35px',
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
color: '#007bff',
|
||||
height: '25px',
|
||||
height: '25px',
|
||||
width : '25px'
|
||||
}
|
||||
|
||||
|
@ -45,21 +44,22 @@ class DashboardButtonGroup extends React.Component {
|
|||
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" style={iconStyle} />
|
||||
<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" style={iconStyle}/>
|
||||
<Icon icon="times" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
} 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" style={iconStyle}/>
|
||||
<Icon icon="expand" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -67,7 +67,7 @@ class DashboardButtonGroup extends React.Component {
|
|||
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" style={iconStyle}/>
|
||||
<Icon icon="compress" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -77,7 +77,7 @@ class DashboardButtonGroup extends React.Component {
|
|||
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" style={iconStyle}/>
|
||||
<Icon icon="play" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -85,7 +85,7 @@ class DashboardButtonGroup extends React.Component {
|
|||
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" style={iconStyle}/>
|
||||
<Icon icon="pause" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -96,16 +96,15 @@ class DashboardButtonGroup extends React.Component {
|
|||
<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" style={iconStyle}/>
|
||||
<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>}>
|
||||
<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" style={iconStyle}/>
|
||||
<Icon icon="sign-in-alt" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -114,7 +113,7 @@ class DashboardButtonGroup extends React.Component {
|
|||
<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" style={iconStyle}/>
|
||||
<Icon icon="sign-out-alt" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
@ -123,7 +122,7 @@ class DashboardButtonGroup extends React.Component {
|
|||
<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" style={iconStyle}/>
|
||||
<Icon icon="pen" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
|
|
@ -478,7 +478,7 @@ class Dashboard extends Component {
|
|||
return <div className={boxClasses} >
|
||||
<div className='section-header box-header'>
|
||||
<div className="section-title">
|
||||
<span>{this.state.dashboard.name}</span>
|
||||
<h2>{this.state.dashboard.name}</h2>
|
||||
</div>
|
||||
|
||||
<DashboardButtonGroup
|
||||
|
|
|
@ -73,7 +73,7 @@ class EditDashboardDialog extends React.Component {
|
|||
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" validationstate={this.validateForm('name')}>
|
||||
<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 />
|
||||
|
|
|
@ -69,7 +69,7 @@ class NewDashboardDialog extends React.Component {
|
|||
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" validationstate={this.validateForm('name')}>
|
||||
<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 />
|
||||
|
|
|
@ -149,11 +149,14 @@ class EditFilesDialog extends React.Component {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
<span className='solid-button'>
|
||||
<Button
|
||||
variant='secondary'
|
||||
disabled={this.state.uploadFile === null}
|
||||
onClick={() => this.startFileUpload()}>
|
||||
Upload
|
||||
</Button>
|
||||
</span>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { Button, ButtonToolbar, DropdownButton, Dropdown, InputGroup, FormControl } from 'react-bootstrap';
|
||||
import { Button, DropdownButton, Dropdown, InputGroup, FormControl } 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";
|
||||
|
||||
class ICAction extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -47,9 +50,187 @@ class ICAction extends React.Component {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
runAction(action, when) {
|
||||
|
||||
if (action.data.action === 'none') {
|
||||
console.warn("No command selected. Nothing was sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.props.hasConfigs){
|
||||
let newAction = {};
|
||||
newAction["action"] = action.data.action
|
||||
newAction["when"] = when
|
||||
|
||||
for (let index of this.props.selectedICs) {
|
||||
let ic = this.props.ics[index];
|
||||
let icID = ic.id;
|
||||
|
||||
/* VILLAScontroller protocol
|
||||
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"){
|
||||
// prepare parameters for delete incl. correct IC id
|
||||
newAction["parameters"] = {};
|
||||
newAction.parameters["uuid"] = ic.uuid;
|
||||
// get the ID of the manager IC
|
||||
let managerIC = null;
|
||||
for (let i of this.props.ics){
|
||||
if (i.uuid === ic.manager){
|
||||
managerIC = i;
|
||||
}
|
||||
}
|
||||
if (managerIC == null){
|
||||
NotificationsDataManager.addNotification(NotificationsFactory.DELETE_ERROR("Could not find manager IC with UUID " + ic.manager));
|
||||
continue;
|
||||
}
|
||||
|
||||
icID = managerIC.id; // send delete action to manager of IC
|
||||
}
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
icid: icID,
|
||||
action: newAction,
|
||||
result: null,
|
||||
token: this.props.token
|
||||
});
|
||||
|
||||
} // 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"
|
||||
}
|
||||
}
|
||||
*
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
let newActions = [];
|
||||
for (let config of this.props.selectedConfigs) {
|
||||
let newAction = {}
|
||||
newAction["action"] = action.data.action
|
||||
newAction["when"] = when
|
||||
|
||||
// get IC for component config
|
||||
let ic = null;
|
||||
for (let component of this.props.ics) {
|
||||
if (component.id === config.icID) {
|
||||
ic = component;
|
||||
}
|
||||
}
|
||||
|
||||
if (ic == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// the following is not required by the protocol; it is an internal help
|
||||
newAction["icid"] = ic.id
|
||||
|
||||
if (newAction.action === 'start') {
|
||||
newAction["parameters"] = config.startParameters;
|
||||
|
||||
|
||||
if (config.fileIDs.length > 0){
|
||||
newAction["model"] = {}
|
||||
newAction.model["type"] = "url"
|
||||
newAction.model["token"] = this.props.token
|
||||
|
||||
let fileURLs = []
|
||||
for (let fileID of config.fileIDs){
|
||||
fileURLs.push("/files/" + fileID.toString())
|
||||
}
|
||||
newAction.model["url"] = fileURLs
|
||||
}
|
||||
|
||||
newAction["results"] = {}
|
||||
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
|
||||
newActions.push(newAction);
|
||||
|
||||
} // end for loop over selected configs
|
||||
|
||||
|
||||
let newResult = {}
|
||||
newResult["result"] = {}
|
||||
if (action.data.action === 'start') {
|
||||
|
||||
let configSnapshots = [];
|
||||
// create config snapshots in case action is start
|
||||
for (let config of this.props.selectedConfigs) {
|
||||
let index = this.props.configs.indexOf(config)
|
||||
configSnapshots.push(this.props.snapshotConfig(index));
|
||||
}
|
||||
|
||||
// create new result for new run
|
||||
newResult.result["description"] = "Start at " + when;
|
||||
newResult.result["scenarioID"] = this.props.selectedConfigs[0].scenarioID
|
||||
newResult.result["configSnapshots"] = configSnapshots
|
||||
}
|
||||
|
||||
|
||||
console.log("Dispatching actions for configs", newActions, newResult)
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
action: newActions,
|
||||
result: newResult,
|
||||
token: this.props.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setAction = id => {
|
||||
// search action
|
||||
for (let action of this.props.actions) {
|
||||
|
@ -65,7 +246,13 @@ class ICAction extends React.Component {
|
|||
|
||||
render() {
|
||||
|
||||
let sendCommandDisabled = this.props.runDisabled || this.state.selectedAction == null || this.state.selectedAction.id === "-1"
|
||||
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 time = this.state.time.getFullYear().pad(4) + '-' +
|
||||
this.state.time.getMonth().pad(2) + '-' +
|
||||
|
@ -79,11 +266,11 @@ class ICAction extends React.Component {
|
|||
</Dropdown.Item>
|
||||
));
|
||||
|
||||
return <div>
|
||||
return <div className='solid-button'>
|
||||
<InputGroup>
|
||||
<InputGroup.Prepend>
|
||||
<DropdownButton
|
||||
variant="outline-secondary"
|
||||
variant="secondary"
|
||||
title={this.state.selectedAction != null ? this.state.selectedAction.title : ''}
|
||||
id="action-dropdown"
|
||||
onSelect={this.setAction}>
|
||||
|
@ -96,9 +283,9 @@ class ICAction extends React.Component {
|
|||
onChange={this.setTimeForAction} />
|
||||
</InputGroup.Prepend>
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
variant="secondary"
|
||||
disabled={sendCommandDisabled}
|
||||
onClick={() => this.props.runAction(this.state.selectedAction, this.state.time)}>Run</Button>
|
||||
onClick={() => this.runAction(this.state.selectedAction, this.state.time)}>Run</Button>
|
||||
</InputGroup>
|
||||
<small className="text-muted">Select time for synced command execution</small>
|
||||
</div>;
|
||||
|
|
|
@ -113,10 +113,10 @@ class ICDialog extends React.Component {
|
|||
{this.props.userRole === "Admin" ? (
|
||||
<div>
|
||||
<h5>Controls:</h5>
|
||||
<div>
|
||||
<Button style={{margin: '5px'}} size='lg'
|
||||
<div className='solid-button'>
|
||||
<Button variant='secondary' style={{margin: '5px'}} size='lg'
|
||||
onClick={() => this.setState({confirmCommand: true, command: 'restart'})}>Restart</Button>
|
||||
<Button style={{margin: '5px'}} size='lg' onClick={() => this.setState({
|
||||
<Button variant='secondary' style={{margin: '5px'}} size='lg' onClick={() => this.setState({
|
||||
confirmCommand: true,
|
||||
command: 'shutdown'
|
||||
})}>Shutdown</Button>
|
||||
|
|
|
@ -66,16 +66,48 @@ class InfrastructureComponentStore extends ArrayStore {
|
|||
return state;
|
||||
|
||||
case 'ics/start-action':
|
||||
if (!Array.isArray(action.data))
|
||||
action.data = [ action.data ]
|
||||
if (!Array.isArray(action.action))
|
||||
action.action = [ action.action ]
|
||||
|
||||
ICsDataManager.doActions(action.ic, action.data, action.token);
|
||||
ICsDataManager.doActions(action.icid, action.action, action.token, action.result);
|
||||
return state;
|
||||
|
||||
case 'ics/action-started':
|
||||
NotificationsDataManager.addNotification(NotificationsFactory.ACTION_INFO());
|
||||
return state;
|
||||
|
||||
case 'ics/action-error':
|
||||
console.log(action.error);
|
||||
return state;
|
||||
|
||||
case 'ics/action-result-added':
|
||||
|
||||
for (let a of action.actions){
|
||||
|
||||
if (a.results !== undefined && a.results != null){
|
||||
// adapt URL for newly created result ID
|
||||
a.results.url = a.results.url.replace("RESULTID", action.data.result.id);
|
||||
a.results.url = ICsDataManager.makeURL(a.results.url);
|
||||
a.results.url = window.location.host + a.results.url;
|
||||
}
|
||||
if (a.model !== undefined && a.model != null && JSON.stringify(a.model) !== JSON.stringify({})) {
|
||||
// adapt URL(s) for model file
|
||||
let modelURLs = []
|
||||
for (let url of a.model.url){
|
||||
let modifiedURL = ICsDataManager.makeURL(url);
|
||||
modifiedURL = window.location.host + modifiedURL;
|
||||
modelURLs.push(modifiedURL)
|
||||
}
|
||||
a.model.url = modelURLs
|
||||
}
|
||||
ICsDataManager.doActions(a.icid, [a], action.token)
|
||||
}
|
||||
return state;
|
||||
|
||||
case 'ics/action-result-add-error':
|
||||
console.log(action.error);
|
||||
return state
|
||||
|
||||
case 'ics/get-status':
|
||||
ICsDataManager.getStatus(action.url, action.token, action.ic);
|
||||
return super.reduce(state, action);
|
||||
|
|
|
@ -24,24 +24,86 @@ class IcsDataManager extends RestDataManager {
|
|||
super('ic', '/ic');
|
||||
}
|
||||
|
||||
doActions(ic, actions, token = null) {
|
||||
for (let action of actions) {
|
||||
if (action.when)
|
||||
// Send timestamp as Unix Timestamp
|
||||
action.when = Math.round(action.when.getTime() / 1000);
|
||||
}
|
||||
doActions(icid, actions, token = null, result=null) {
|
||||
|
||||
RestAPI.post(this.makeURL(this.url + '/' + ic.id + '/action'), actions, token).then(response => {
|
||||
|
||||
if (icid !== undefined && icid != null && JSON.stringify(icid) !== JSON.stringify({})) {
|
||||
|
||||
for (let action of actions) {
|
||||
if (action.when) {
|
||||
// Send timestamp as Unix Timestamp
|
||||
action.when = Math.round(action.when.getTime() / 1000);
|
||||
}
|
||||
}
|
||||
// sending action to a specific IC via IC list
|
||||
|
||||
RestAPI.post(this.makeURL(this.url + '/' + icid + '/action'), actions, token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-started',
|
||||
data: response
|
||||
type: 'ics/action-started',
|
||||
data: response
|
||||
});
|
||||
}).catch(error => {
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-error',
|
||||
type: 'ics/action-error',
|
||||
error
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// sending the same action to multiple ICs via scenario controls
|
||||
|
||||
// distinguish between "start" action and any other
|
||||
|
||||
if (actions[0].action !== "start"){
|
||||
for (let a of actions){
|
||||
|
||||
// sending action to a specific IC via IC list
|
||||
if (a.when) {
|
||||
// Send timestamp as Unix Timestamp
|
||||
a.when = Math.round(a.when.getTime() / 1000);
|
||||
}
|
||||
|
||||
RestAPI.post(this.makeURL(this.url + '/' + a.icid + '/action'), [a], token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-started',
|
||||
data: response
|
||||
});
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-error',
|
||||
error
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
} else{
|
||||
// for start actions procedure is different
|
||||
// first a result needs to be created, then the start actions can be sent
|
||||
|
||||
RestAPI.post(this.makeURL( '/results'), result, token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-result-added',
|
||||
data: response,
|
||||
actions: actions,
|
||||
token: token,
|
||||
});
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: "results/added",
|
||||
data: response.result,
|
||||
});
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/action-result-add-error',
|
||||
error
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
getStatus(url,token,ic){
|
||||
|
|
|
@ -36,6 +36,8 @@ import ICDialog from './ic-dialog';
|
|||
|
||||
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";
|
||||
|
||||
class InfrastructureComponents extends Component {
|
||||
static getStores() {
|
||||
|
@ -149,11 +151,35 @@ class InfrastructureComponents extends Component {
|
|||
this.setState({ newModal : false });
|
||||
|
||||
if (data) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-add',
|
||||
data,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
if (!data.managedexternally) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-add',
|
||||
data,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
} else {
|
||||
// externally managed IC: dispatch create action to selected manager
|
||||
let newAction = {};
|
||||
newAction["action"] = "create";
|
||||
newAction["parameters"] = data;
|
||||
newAction["when"] = new Date()
|
||||
|
||||
// find the manager IC
|
||||
let managerIC = this.state.ics.find(ic => ic.uuid === data.manager)
|
||||
if (managerIC === null || managerIC === undefined){
|
||||
NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Could not find manager IC with UUID " + data.manager));
|
||||
return;
|
||||
}
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
icid: managerIC.id,
|
||||
action: newAction,
|
||||
result: null,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,18 +266,7 @@ class InfrastructureComponents extends Component {
|
|||
this.setState({ selectedICs: selectedICs });
|
||||
}
|
||||
|
||||
runAction(action, when) {
|
||||
for (let index of this.state.selectedICs) {
|
||||
action.when = when;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
ic: this.state.ics[index],
|
||||
data: action.data,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static isICOutdated(component) {
|
||||
if (!component.stateUpdateAt)
|
||||
|
@ -354,7 +369,7 @@ class InfrastructureComponents extends Component {
|
|||
|
||||
modifyNameColumn(name, component){
|
||||
let index = this.state.ics.indexOf(component);
|
||||
return <Button variant="link" style={{color: '#047cab'}} onClick={() => this.openICStatus(component)}>{name}</Button>
|
||||
return <Button variant="link" onClick={() => this.openICStatus(component)}>{name}</Button>
|
||||
}
|
||||
|
||||
openICStatus(ic){
|
||||
|
@ -378,9 +393,9 @@ class InfrastructureComponents extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
isExternalIC(index){
|
||||
let ic = this.state.ics[index]
|
||||
return ic.managedexternally
|
||||
isLocalIC(index, ics){
|
||||
let ic = ics[index]
|
||||
return !ic.managedexternally
|
||||
}
|
||||
|
||||
getICCategoryTable(ics, editable, title){
|
||||
|
@ -390,7 +405,7 @@ class InfrastructureComponents extends Component {
|
|||
<Table data={ics}>
|
||||
<TableColumn
|
||||
checkbox
|
||||
checkboxDisabled={(index) => this.isExternalIC(index)}
|
||||
checkboxDisabled={(index) => this.isLocalIC(index, ics) === true}
|
||||
onChecked={(ic, event) => this.onICChecked(ic, event)}
|
||||
width='30'
|
||||
/>
|
||||
|
@ -420,19 +435,21 @@ class InfrastructureComponents extends Component {
|
|||
modifier={(stateUpdateAt, component) => this.stateUpdateModifier(stateUpdateAt, component)}
|
||||
/>
|
||||
|
||||
{this.state.currentUser.role === "Admin" && editable ?
|
||||
{this.state.currentUser.role === "Admin" ?
|
||||
<TableColumn
|
||||
width='200'
|
||||
width='150'
|
||||
editButton
|
||||
showEditButton ={(index) => this.isLocalIC(index, ics)}
|
||||
exportButton
|
||||
deleteButton
|
||||
showDeleteButton = {(index) => this.isLocalIC(index, ics)}
|
||||
onEdit={index => this.setState({editModal: true, modalIC: ics[index], modalIndex: index})}
|
||||
onExport={index => this.exportIC(index)}
|
||||
onDelete={index => this.setState({deleteModal: true, modalIC: ics[index], modalIndex: index})}
|
||||
/>
|
||||
:
|
||||
<TableColumn
|
||||
width='100'
|
||||
width='50'
|
||||
exportButton
|
||||
onExport={index => this.exportIC(index)}
|
||||
/>
|
||||
|
@ -451,6 +468,11 @@ class InfrastructureComponents extends Component {
|
|||
marginLeft: '10px'
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
height: '30px',
|
||||
width: '30px'
|
||||
}
|
||||
|
||||
let managerTable = this.getICCategoryTable(this.state.managers, false, "IC Managers")
|
||||
let simulatorTable = this.getICCategoryTable(this.state.simulators, true, "Simulators")
|
||||
let gatewayTable = this.getICCategoryTable(this.state.gateways, true, "Gateways")
|
||||
|
@ -461,19 +483,19 @@ class InfrastructureComponents extends Component {
|
|||
<div className='section'>
|
||||
<h1>Infrastructure Components
|
||||
{this.state.currentUser.role === "Admin" ?
|
||||
(<span>
|
||||
(<span className='icon-button'>
|
||||
<OverlayTrigger
|
||||
key={1}
|
||||
placement={'top'}
|
||||
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Infrastructure Component </Tooltip>} >
|
||||
<Button onClick={() => this.setState({newModal: true})} style={buttonStyle}><Icon icon="plus"
|
||||
<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 onClick={() => this.setState({importModal: true})} style={buttonStyle}><Icon icon="upload"
|
||||
<Button variant='light' onClick={() => this.setState({importModal: true})} style={buttonStyle}><Icon icon="upload" classname='icon-color' style={iconStyle}
|
||||
/></Button>
|
||||
</OverlayTrigger>
|
||||
</span>)
|
||||
|
@ -491,12 +513,15 @@ class InfrastructureComponents extends Component {
|
|||
{this.state.currentUser.role === "Admin" && this.state.numberOfExternalICs > 0 ?
|
||||
<div style={{float: 'left'}}>
|
||||
<ICAction
|
||||
runDisabled={this.state.selectedICs.length === 0}
|
||||
runAction={(action, when) => this.runAction(action, when)}
|
||||
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'}}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -118,7 +118,7 @@ class ImportICDialog extends React.Component {
|
|||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<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 />
|
||||
|
@ -128,7 +128,7 @@ class ImportICDialog extends React.Component {
|
|||
<FormControl type="text" placeholder="Enter websocketurl" value={this.state.websocketurl} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="uuid" validationState={this.validateForm('uuid')}>
|
||||
<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 />
|
||||
|
|
|
@ -170,7 +170,7 @@ class NewICDialog extends React.Component {
|
|||
{this.props.managers.length > 0 ?
|
||||
<>
|
||||
<FormGroup controlId="managedexternally">
|
||||
<OverlayTrigger key="3" placement={'left'} overlay={<Tooltip id={`tooltip-${"me"}`}>An externally managed component is created and managed by an IC manager via AMQP</Tooltip>} >
|
||||
<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>
|
||||
</OverlayTrigger>
|
||||
|
|
BIN
src/img/erigrid2.png
Normal file
BIN
src/img/erigrid2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
64
src/result/result-configs-dialog.js
Normal file
64
src/result/result-configs-dialog.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* 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 Dialog from '../common/dialogs/dialog';
|
||||
import ReactJson from 'react-json-view';
|
||||
|
||||
|
||||
class ResultConfigDialog extends React.Component {
|
||||
valid = true;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
confirmCommand: false,
|
||||
command: '',
|
||||
};
|
||||
}
|
||||
|
||||
onClose(canceled) {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog
|
||||
show={this.props.show}
|
||||
title={"Component Configurations for Result No. " + this.props.resultNo}
|
||||
buttonTitle="Close"
|
||||
onClose={(c) => this.onClose(c)}
|
||||
valid={true}
|
||||
size="lg"
|
||||
blendOutCancel={true}
|
||||
>
|
||||
<form>
|
||||
<ReactJson
|
||||
src={this.props.configs}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
displayObjectSize={false}
|
||||
enableClipboard={false}
|
||||
collapsed={false}
|
||||
/>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ResultConfigDialog;
|
|
@ -37,7 +37,8 @@ import NewDashboardDialog from "../dashboard/new-dashboard";
|
|||
import EditDashboardDialog from '../dashboard/edit-dashboard';
|
||||
import EditFiles from '../file/edit-files'
|
||||
import NewResultDialog from '../result/new-result';
|
||||
import EditResultDialog from '../result/edit-result'
|
||||
import EditResultDialog from '../result/edit-result';
|
||||
import ResultConfigDialog from '../result/result-configs-dialog';
|
||||
|
||||
|
||||
import ICAction from '../ic/ic-action';
|
||||
|
@ -119,6 +120,9 @@ class Scenario extends React.Component {
|
|||
filesToDownload: prevState.filesToDownload,
|
||||
zipfiles: prevState.zipfiles || false,
|
||||
resultNodl: prevState.resultNodl,
|
||||
resultConfigsModal: false,
|
||||
modalResultConfigs: {},
|
||||
modalResultConfigsIndex: 0,
|
||||
|
||||
editOutputSignalsModal: prevState.editOutputSignalsModal || false,
|
||||
editInputSignalsModal: prevState.editInputSignalsModal || false,
|
||||
|
@ -161,12 +165,12 @@ class Scenario extends React.Component {
|
|||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// check whether file data has been loaded
|
||||
if (this.state.filesToDownload && this.state.filesToDownload.length > 0 ) {
|
||||
if (this.state.filesToDownload && this.state.filesToDownload.length > 0) {
|
||||
if (this.state.files != prevState.files) {
|
||||
if (!this.state.zipfiles) {
|
||||
let fileToDownload = FileStore.getState().filter(file => file.id === this.state.filesToDownload[0])
|
||||
if (fileToDownload.length === 1 && fileToDownload[0].data) {
|
||||
const blob = new Blob([fileToDownload[0].data], {type: fileToDownload[0].type});
|
||||
const blob = new Blob([fileToDownload[0].data], { type: fileToDownload[0].type });
|
||||
FileSaver.saveAs(blob, fileToDownload[0].name);
|
||||
this.setState({ filesToDownload: [] });
|
||||
}
|
||||
|
@ -178,7 +182,7 @@ class Scenario extends React.Component {
|
|||
zip.file(file.name, file.data);
|
||||
});
|
||||
let zipname = "result_" + this.state.resultNodl + "_" + (new Date()).toISOString();
|
||||
zip.generateAsync({type: "blob"}).then(function(content) {
|
||||
zip.generateAsync({ type: "blob" }).then(function (content) {
|
||||
saveAs(content, zipname);
|
||||
});
|
||||
this.setState({ filesToDownload: [] });
|
||||
|
@ -367,7 +371,7 @@ class Scenario extends React.Component {
|
|||
this.setState({ selectedConfigs: selectedConfigs });
|
||||
}
|
||||
|
||||
usesExternalIC(index){
|
||||
usesExternalIC(index) {
|
||||
let icID = this.state.configs[index].icID;
|
||||
|
||||
let ic = null;
|
||||
|
@ -381,8 +385,8 @@ class Scenario extends React.Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (ic.managedexternally === true){
|
||||
this.setState({ExternalICInUse: true})
|
||||
if (ic.managedexternally === true) {
|
||||
this.setState({ ExternalICInUse: true })
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -390,42 +394,6 @@ class Scenario extends React.Component {
|
|||
|
||||
}
|
||||
|
||||
runAction(action, when) {
|
||||
if (action.data.action === 'none') {
|
||||
console.warn("No command selected. Nothing was sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (let index of this.state.selectedConfigs) {
|
||||
// get IC for component config
|
||||
let ic = null;
|
||||
for (let component of this.state.ics) {
|
||||
if (component.id === this.state.configs[index].icID) {
|
||||
ic = component;
|
||||
}
|
||||
}
|
||||
|
||||
if (ic == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (action.data.action === 'start') {
|
||||
action.data.parameters = this.state.configs[index].startParameters;
|
||||
}
|
||||
|
||||
action.data.when = when;
|
||||
|
||||
console.log("Sending action: ", action.data)
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
ic: ic,
|
||||
data: action.data,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getICName(icID) {
|
||||
for (let ic of this.state.ics) {
|
||||
if (ic.id === icID) {
|
||||
|
@ -539,11 +507,21 @@ class Scenario extends React.Component {
|
|||
############################################## */
|
||||
|
||||
closeEditSignalsModal(direction) {
|
||||
|
||||
// reload the config
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-load',
|
||||
data: this.state.modalConfigData.id,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
if (direction === "in") {
|
||||
this.setState({ editInputSignalsModal: false });
|
||||
} else if (direction === "out") {
|
||||
this.setState({ editOutputSignalsModal: false });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
onEditFiles() {
|
||||
|
@ -645,13 +623,13 @@ class Scenario extends React.Component {
|
|||
let toDownload = [];
|
||||
let zip = false;
|
||||
|
||||
if (typeof(param) === 'object') { // download all files
|
||||
if (typeof (param) === 'object') { // download all files
|
||||
toDownload = param.resultFileIDs;
|
||||
zip = true;
|
||||
this.setState({ filesToDownload: toDownload, zipfiles: zip, resultNodl: param.id });
|
||||
} else { // download one file
|
||||
toDownload.push(param);
|
||||
this.setState({ filesToDownload: toDownload, zipfiles: zip});
|
||||
this.setState({ filesToDownload: toDownload, zipfiles: zip });
|
||||
}
|
||||
|
||||
toDownload.forEach(fileid => {
|
||||
|
@ -677,6 +655,30 @@ class Scenario extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
openResultConfigSnaphots(result) {
|
||||
if (result.configSnapshots === null || result.configSnapshots === undefined) {
|
||||
this.setState({
|
||||
modalResultConfigs: {"configs": []},
|
||||
modalResultConfigsIndex: result.id,
|
||||
resultConfigsModal: true
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
modalResultConfigs: result.configSnapshots,
|
||||
modalResultConfigsIndex: result.id,
|
||||
resultConfigsModal: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
closeResultConfigSnapshots() {
|
||||
this.setState({ resultConfigsModal: false });
|
||||
}
|
||||
|
||||
modifyResultNoColumn(id, result) {
|
||||
return <Button variant="link" style={{ color: '#047cab' }} onClick={() => this.openResultConfigSnaphots(result)}>{id}</Button>
|
||||
}
|
||||
|
||||
startPintura(configIndex) {
|
||||
let config = this.state.configs[configIndex];
|
||||
|
||||
|
@ -719,18 +721,17 @@ class Scenario extends React.Component {
|
|||
return (<Redirect to="/scenarios" />);
|
||||
}
|
||||
|
||||
const buttonStyle = {
|
||||
marginLeft: '10px'
|
||||
};
|
||||
const altButtonStyle = {
|
||||
marginLeft: '10px',
|
||||
}
|
||||
|
||||
const tableHeadingStyle = {
|
||||
paddingTop: '30px'
|
||||
}
|
||||
|
||||
const iconStyle = {
|
||||
color: '#007bff',
|
||||
height: '25px',
|
||||
width: '25px'
|
||||
height: '30px',
|
||||
width: '30px'
|
||||
}
|
||||
|
||||
if (this.state.scenario === undefined) {
|
||||
|
@ -740,48 +741,58 @@ class Scenario extends React.Component {
|
|||
let resulttable;
|
||||
if (this.state.results && this.state.results.length > 0) {
|
||||
resulttable = <div>
|
||||
<Table data={this.state.results}>
|
||||
<TableColumn title='Result No.' dataKey='id' />
|
||||
<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>
|
||||
<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)} />
|
||||
</div>
|
||||
<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 key={0} variant='light' size="lg" onClick={this.onEditFiles.bind(this)} style={buttonStyle}>
|
||||
<Icon icon="file" style={iconStyle} />
|
||||
<Button variant='light' key={0} size="lg" onClick={this.onEditFiles.bind(this)}>
|
||||
<Icon icon="file" classname={'icon-color'} style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
|
@ -798,18 +809,20 @@ 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 onClick={() => this.addConfig()} style={buttonStyle}><Icon icon="plus" /></Button>
|
||||
<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 onClick={() => this.setState({ importConfigModal: true })} style={buttonStyle}><Icon icon="upload" /></Button>
|
||||
<Button variant='light' onClick={() => this.setState({ importConfigModal: true })} style={altButtonStyle}><Icon icon="upload" classname={'icon-color'} style={iconStyle}/></Button>
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
</h2>
|
||||
<Table data={this.state.configs}>
|
||||
<TableColumn
|
||||
|
@ -858,22 +871,27 @@ class Scenario extends React.Component {
|
|||
/>
|
||||
</Table>
|
||||
|
||||
{ this.state.ExternalICInUse ? (
|
||||
<div style={{float: 'left'}}>
|
||||
{this.state.ExternalICInUse ? (
|
||||
<div style={{ float: 'left' }}>
|
||||
<ICAction
|
||||
runDisabled={this.state.selectedConfigs.length === 0}
|
||||
runAction={(action, when) => this.runAction(action, when)}
|
||||
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'}}
|
||||
]}/>
|
||||
{ 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}
|
||||
|
@ -906,18 +924,20 @@ 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 onClick={() => this.setState({ newDashboardModal: true })} style={buttonStyle}><Icon icon="plus" /></Button>
|
||||
<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 onClick={() => this.setState({ importDashboardModal: true })} style={buttonStyle}><Icon icon="upload" /></Button>
|
||||
<Button variant='light' onClick={() => this.setState({ importDashboardModal: true })} style={altButtonStyle}><Icon icon="upload" classname={'icon-color'} style={iconStyle} /></Button>
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
</h2>
|
||||
<Table data={this.state.dashboards}>
|
||||
<TableColumn title='Name' dataKey='name' link='/dashboards/' linkKey='id' />
|
||||
|
@ -944,12 +964,14 @@ class Scenario extends React.Component {
|
|||
|
||||
{/*Result table*/}
|
||||
<h2 style={tableHeadingStyle}>Results
|
||||
<span className='icon-button'>
|
||||
<OverlayTrigger
|
||||
key={1}
|
||||
placement={'top'}
|
||||
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Result </Tooltip>} >
|
||||
<Button onClick={() => this.setState({ newResultModal: true })} style={buttonStyle}><Icon icon="plus" /></Button>
|
||||
<Button variant='light' onClick={() => this.setState({ newResultModal: true })} style={altButtonStyle}><Icon icon="plus" classname={'icon-color'} style={iconStyle} /></Button>
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
</h2>
|
||||
{resulttable}
|
||||
<NewResultDialog show={this.state.newResultModal} onClose={data => this.closeNewResultModal(data)} />
|
||||
|
@ -976,11 +998,15 @@ class Scenario extends React.Component {
|
|||
type="text"
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
<span className='icon-button'>
|
||||
<Button
|
||||
variant='light'
|
||||
type="submit"
|
||||
style={altButtonStyle}
|
||||
onClick={() => this.addUser()}>
|
||||
Add User
|
||||
<Icon icon="plus" classname={'icon-color'} style={iconStyle} />
|
||||
</Button>
|
||||
</span>
|
||||
</InputGroup.Append>
|
||||
</InputGroup><br /><br />
|
||||
</div>
|
||||
|
|
|
@ -236,24 +236,31 @@ class Scenarios extends Component {
|
|||
|
||||
render() {
|
||||
const buttonStyle = {
|
||||
marginLeft: '10px'
|
||||
marginLeft: '10px',
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
height: '30px',
|
||||
width: '30px'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='section'>
|
||||
<h1>Scenarios
|
||||
<span className='icon-button'>
|
||||
<OverlayTrigger
|
||||
key={1}
|
||||
placement={'top'}
|
||||
overlay={<Tooltip id={`tooltip-${"add"}`}> Add Scenario </Tooltip>} >
|
||||
<Button onClick={() => this.setState({ newModal: true })} style={buttonStyle}><Icon icon="plus" /></Button>
|
||||
<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 onClick={() => this.setState({ importModal: true })} style={buttonStyle}><Icon icon="upload" /></Button>
|
||||
<Button variant='light' onClick={() => this.setState({ importModal: true })} style={buttonStyle}><Icon icon="upload" classname='icon-color' style={iconStyle} /></Button>
|
||||
</OverlayTrigger>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<Table data={this.state.scenarios}>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Button, FormGroup, FormLabel, FormText} from 'react-bootstrap';
|
||||
import {Button, FormGroup, FormLabel, FormText, OverlayTrigger, Tooltip} from 'react-bootstrap';
|
||||
import {Collapse} from 'react-collapse';
|
||||
import Table from '../common/table';
|
||||
import TableColumn from '../common/table-column';
|
||||
|
@ -64,6 +64,11 @@ class EditSignalMapping extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
signals.forEach(signal => {
|
||||
if(signal.checked === undefined) signal.checked = false
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
signals: signals,
|
||||
};
|
||||
|
@ -92,23 +97,24 @@ class EditSignalMapping extends React.Component {
|
|||
let signals = this.state.signals;
|
||||
let modifiedSignals = this.state.modifiedSignalIDs;
|
||||
|
||||
|
||||
if (column === 1) { // Name change
|
||||
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){
|
||||
modifiedSignals.push(signals[row].id);
|
||||
}
|
||||
} else if (column === 2) { // unit change
|
||||
} else if (column === 3) { // unit change
|
||||
signals[row].unit = event.target.value;
|
||||
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
|
||||
modifiedSignals.push(signals[row].id);
|
||||
}
|
||||
} else if (column === 3) { // scaling factor change
|
||||
} else if (column === 4) { // scaling factor change
|
||||
signals[row].scalingFactor = parseFloat(event.target.value);
|
||||
if (modifiedSignals.find(id => id === signals[row].id) === undefined){
|
||||
modifiedSignals.push(signals[row].id);
|
||||
}
|
||||
} else if (column === 0) { //index change
|
||||
} 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){
|
||||
modifiedSignals.push(signals[row].id);
|
||||
|
@ -134,6 +140,21 @@ class EditSignalMapping extends React.Component {
|
|||
|
||||
};
|
||||
|
||||
handleRemove = () => {
|
||||
|
||||
let checkedSignals = this.state.signals.filter(signal => signal.checked === true);
|
||||
|
||||
checkedSignals.forEach(signal => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'signals/start-remove',
|
||||
data: signal,
|
||||
token: this.props.sessionToken
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
handleAdd = (configID = null) => {
|
||||
|
||||
if(typeof this.props.configs !== "undefined"){
|
||||
|
@ -173,10 +194,38 @@ class EditSignalMapping extends React.Component {
|
|||
this.setState({signals: signals})
|
||||
}
|
||||
|
||||
onSignalChecked(signal) {
|
||||
let tempSignals = this.state.signals;
|
||||
const index = tempSignals.indexOf(signal);
|
||||
|
||||
tempSignals[index].checked = !tempSignals[index].checked;
|
||||
|
||||
this.setState({signals: tempSignals});
|
||||
|
||||
}
|
||||
|
||||
checkAll(){
|
||||
let tempSignals = this.state.signals;
|
||||
let allChecked = true;
|
||||
|
||||
tempSignals.forEach(signal =>
|
||||
{
|
||||
if(signal.checked === false){
|
||||
signal.checked = true;
|
||||
allChecked = false;
|
||||
}
|
||||
});
|
||||
|
||||
if(allChecked){
|
||||
tempSignals.forEach(signal => signal.checked = false);
|
||||
}
|
||||
this.setState({signals: tempSignals});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const buttonStyle = {
|
||||
marginLeft: '10px'
|
||||
marginLeft: '10px',
|
||||
};
|
||||
|
||||
return(
|
||||
|
@ -185,7 +234,7 @@ class EditSignalMapping extends React.Component {
|
|||
show={this.props.show}
|
||||
title="Edit Signal Mapping"
|
||||
buttonTitle="Save"
|
||||
blendOutCancel = {true}
|
||||
blendOutCancel = {false}
|
||||
onClose={(c) => this.onClose(c)}
|
||||
onReset={() => this.resetState()}
|
||||
valid={true}
|
||||
|
@ -194,7 +243,8 @@ class EditSignalMapping extends React.Component {
|
|||
<FormGroup>
|
||||
<FormLabel>{this.props.direction} Mapping</FormLabel>
|
||||
<FormText>Click <i>Index</i>, <i>Name</i> or <i>Unit</i> cell to edit</FormText>
|
||||
<Table data={this.state.signals}>
|
||||
<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)} />
|
||||
|
@ -202,20 +252,24 @@ class EditSignalMapping extends React.Component {
|
|||
<TableColumn title='Remove' deleteButton onDelete={(index) => this.handleDelete(index)} />
|
||||
</Table>
|
||||
|
||||
<div style={{ float: 'right' }}>
|
||||
<Button key={50} 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}> 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>
|
||||
<div className='solid-button'>
|
||||
{typeof this.props.configs !== "undefined" && this.props.configs.map(config => (
|
||||
|
||||
<Button key ={config.id} onClick={() => this.handleAdd(config.id)} style={buttonStyle}>{config.name}</Button>
|
||||
<Button variant='secondary' key ={config.id} onClick={() => this.handleAdd(config.id)} style={buttonStyle}>{config.name}</Button>
|
||||
|
||||
))}
|
||||
</div>
|
||||
</Collapse>
|
||||
</Collapse>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Dialog>
|
||||
|
|
|
@ -27,11 +27,9 @@ class SignalStore extends ArrayStore{
|
|||
|
||||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
|
||||
case 'signals/added':
|
||||
this.dataManager.reloadConfig(action.token, action.data);
|
||||
return super.reduce(state, action);
|
||||
case 'signals/removed':
|
||||
this.dataManager.reloadConfig(action.token, action.data);
|
||||
this.dataManager.reloadConfig(action.token, action.data.configID);
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'signals/start-autoconfig':
|
||||
|
@ -41,7 +39,6 @@ class SignalStore extends ArrayStore{
|
|||
case 'signals/autoconfig-loaded':
|
||||
console.log("AutoConfig Loaded: ", action.data)
|
||||
this.dataManager.saveSignals(action.data, action.token, action.configID, action.socketname);
|
||||
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'signals/autoconfig-error':
|
||||
|
|
|
@ -27,9 +27,9 @@ class SignalsDataManager extends RestDataManager{
|
|||
super('signal', '/signals');
|
||||
}
|
||||
|
||||
reloadConfig(token, data){
|
||||
reloadConfig(token, id){
|
||||
// request in signals
|
||||
RestAPI.get(this.makeURL('/configs/' + data.configID), token).then(response => {
|
||||
RestAPI.get(this.makeURL('/configs/' + id), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/edited',
|
||||
data: response.config
|
||||
|
|
|
@ -403,6 +403,18 @@ hr {
|
|||
height: auto !important;
|
||||
padding: 5px;
|
||||
float: right;
|
||||
border-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.section-buttons-group-right .btn{
|
||||
border-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.section-buttons-group-right .btn:hover{
|
||||
border-color: #e3e3e3;
|
||||
background-color: #e3e3e3;
|
||||
}
|
||||
|
||||
.section-buttons-group-left {
|
||||
|
@ -411,10 +423,60 @@ hr {
|
|||
float: left;
|
||||
}
|
||||
|
||||
.section-buttons-group-left .btn{
|
||||
background-color: #527984;
|
||||
border-color: #527984;
|
||||
}
|
||||
|
||||
.section-buttons-group-left .btn:hover{
|
||||
background-color: #31484f;
|
||||
border-color: #31484f;
|
||||
}
|
||||
|
||||
.drag-and-drop .btn{
|
||||
color: #527984;
|
||||
border-color: #527984;
|
||||
}
|
||||
|
||||
.drag-and-drop .btn:hover{
|
||||
color: #527984;
|
||||
border-color: #527984;
|
||||
}
|
||||
|
||||
|
||||
.section-buttons-group-right .rc-slider {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.solid-button .btn{
|
||||
background-color: #527984;
|
||||
border-color: #527984;
|
||||
}
|
||||
|
||||
.solid-button .btn:hover{
|
||||
background-color: #31484f;
|
||||
border-color: #31484f;
|
||||
}
|
||||
|
||||
.solid-button .btn:disabled{
|
||||
background-color: #527984;
|
||||
border-color: #527984;
|
||||
}
|
||||
|
||||
.icon-button .btn{
|
||||
border-color: #ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.icon-button .btn:hover{
|
||||
border-color: #e3e3e3;
|
||||
background-color: #e3e3e3;
|
||||
}
|
||||
|
||||
.icon-color {
|
||||
color: #527984;
|
||||
}
|
||||
|
||||
.form-horizontal .form-group {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
|
|
|
@ -93,8 +93,10 @@ class LoginForm extends Component {
|
|||
|
||||
<FormGroup style={{ paddingTop: 15, paddingBottom: 5 }}>
|
||||
<Col>
|
||||
<Button style={{ width: 90 }} type="submit" disabled={this.state.disableLogin} onClick={e => this.login(e)}>Login</Button>
|
||||
<Button variant="link" size="sm" style={{ marginLeft: 85 }} onClick={() => this.openRecoverPassword()}>Forgot your password?</Button>
|
||||
<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>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
import React, { Component } from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import {Button, Col, Row} from 'react-bootstrap';
|
||||
import {Button, Col, Row, FormGroup} from 'react-bootstrap';
|
||||
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
import UsersStore from './users-store';
|
||||
|
@ -112,6 +112,7 @@ class User extends Component {
|
|||
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Your User Account</h1>
|
||||
|
@ -119,27 +120,25 @@ class User extends Component {
|
|||
{this.state.currentUser !== undefined && this.state.currentUser !== null ?
|
||||
|
||||
<form>
|
||||
|
||||
<Row>
|
||||
<Col xs={3}>Username: </Col>
|
||||
<Col xs={3}> {this.state.currentUser.username} </Col>
|
||||
<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>
|
||||
|
||||
|
||||
<Row as={Col}>
|
||||
<Col xs={3}>E-mail: </Col>
|
||||
<Col xs={3}> {this.state.currentUser.mail} </Col>
|
||||
</Row>
|
||||
|
||||
<Row as={Col}>
|
||||
<Col xs={3}>Role: </Col>
|
||||
<Col xs={3}> {this.state.currentUser.role} </Col>
|
||||
</Row>
|
||||
|
||||
|
||||
<Button onClick={() => this.setState({editModal: true})}><Icon icon='edit'/> Edit</Button>
|
||||
|
||||
<EditOwnUserDialog show={this.state.editModal} onClose={(data) => this.closeEditModal(data)}
|
||||
user={this.state.currentUser}/>
|
||||
user={this.state.currentUser} />
|
||||
|
||||
</form> : "Loading user data..."
|
||||
}
|
||||
|
@ -148,8 +147,5 @@ class User extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let fluxContainerConverter = require('../common/FluxContainerConverter');
|
||||
export default Container.create(fluxContainerConverter.convert(User));
|
||||
|
|
|
@ -131,20 +131,25 @@ class Users extends Component {
|
|||
render() {
|
||||
|
||||
const buttonStyle = {
|
||||
marginLeft: '10px'
|
||||
marginLeft: '10px',
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
height: '30px',
|
||||
width: '30px'
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Users
|
||||
|
||||
<span className='icon-button'>
|
||||
<OverlayTrigger
|
||||
key={1}
|
||||
placement={'top'}
|
||||
overlay={<Tooltip id={`tooltip-${"add"}`}> Add User </Tooltip>} >
|
||||
<Button style={buttonStyle} onClick={() => this.setState({ newModal: true })}><Icon icon='plus' /> </Button>
|
||||
<Button variant='light' style={buttonStyle} onClick={() => this.setState({ newModal: true })}><Icon icon='plus' classname='icon-color' style={iconStyle} /> </Button>
|
||||
</OverlayTrigger>
|
||||
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<Table data={this.state.users}>
|
||||
|
@ -156,8 +161,6 @@ class Users extends Component {
|
|||
<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] })} />
|
||||
</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} />
|
||||
|
||||
|
|
|
@ -175,6 +175,15 @@ class EditWidgetColorZonesControl extends React.Component {
|
|||
|
||||
render() {
|
||||
|
||||
const buttonStyle = {
|
||||
marginBottom: '10px',
|
||||
marginLeft: '120px',
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
height: '25px',
|
||||
width : '25px'
|
||||
}
|
||||
|
||||
let tempColor = 'FFFFFF';
|
||||
let collapse = false;
|
||||
|
@ -191,9 +200,10 @@ class EditWidgetColorZonesControl extends React.Component {
|
|||
}
|
||||
|
||||
return <FormGroup>
|
||||
<FormLabel>Color Zones</FormLabel>
|
||||
<Button onClick={this.addZone} style={{marginBottom: '10px', marginLeft: '120px'}} disabled={!this.props.widget.customProperties.colorZones}><Icon size='xs' icon="plus" /></Button>
|
||||
|
||||
<FormLabel>Color zones</FormLabel>
|
||||
<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>
|
||||
</span>
|
||||
<div>
|
||||
{
|
||||
this.state.widget.customProperties.zones.map((zone, idx) => {
|
||||
|
@ -243,7 +253,9 @@ class EditWidgetColorZonesControl extends React.Component {
|
|||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
<Button onClick={this.removeZones}><Icon size='xs' icon="trash-alt" /></Button>
|
||||
<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'} />
|
||||
|
|
|
@ -51,7 +51,7 @@ class ToolboxItem extends React.Component {
|
|||
if (this.props.disabled === false) {
|
||||
return this.props.connectDragSource(
|
||||
<div className={itemClass}>
|
||||
<span className="btn " style={{marginTop: '5px', color: '#6ea2b0', borderColor: '#6ea2b0'}}>
|
||||
<span className="btn " style={{marginTop: '5px'}}>
|
||||
{this.props.icon && <Icon style={{marginRight: '5px'}} icon={this.props.icon} /> }
|
||||
{this.props.name}
|
||||
</span>
|
||||
|
|
|
@ -96,11 +96,15 @@ class WidgetToolbox extends React.Component {
|
|||
const disableDecrease = this.disableDecrease();
|
||||
// Only one topology widget at the time is supported
|
||||
const iconStyle = {
|
||||
color: '#007bff',
|
||||
height: '25px',
|
||||
width : '25px'
|
||||
}
|
||||
|
||||
const buttonStyle = {
|
||||
marginRight: '3px',
|
||||
height: '40px',
|
||||
}
|
||||
|
||||
const thereIsTopologyWidget = this.props.widgets != null && Object.values(this.props.widgets).filter(w => w.type === 'Topology').length > 0;
|
||||
const topologyItemMsg = thereIsTopologyWidget? 'Currently only one is supported' : '';
|
||||
|
||||
|
@ -110,16 +114,16 @@ class WidgetToolbox extends React.Component {
|
|||
<div className='section-buttons-group-left'>
|
||||
<div>
|
||||
<OverlayTrigger key={2} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"cosmetic"}`}> Show/ hide available Cosmetic Widgets </Tooltip>} >
|
||||
<Button variant="light" key={2} style={{ marginRight: '3px', height: '40px' }} onClick={() => this.showWidgets('cosmetic')} >
|
||||
<Icon icon={cosmeticIcon} style={{color: '#007bff'}}/> Cosmetic Widgets</Button>
|
||||
<Button key={2} variant="secondary" style={buttonStyle} onClick={() => this.showWidgets('cosmetic')} >
|
||||
<Icon icon={cosmeticIcon}/> Cosmetic Widgets</Button>
|
||||
</OverlayTrigger>
|
||||
<OverlayTrigger key={3} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"displaying"}`}> Show/ hide available Displaying Widgets </Tooltip>} >
|
||||
<Button variant="light" key={3} style={{ marginRight: '3px', height: '40px' }} onClick={() => this.showWidgets('displaying')} >
|
||||
<Icon icon={displayingIcon} style={{color: '#007bff'}} /> Displaying Widgets</Button>
|
||||
<Button key={3} variant="secondary" style={buttonStyle} onClick={() => this.showWidgets('displaying')} >
|
||||
<Icon icon={displayingIcon}/> Displaying Widgets</Button>
|
||||
</OverlayTrigger>
|
||||
<OverlayTrigger key={4} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"manipulation"}`}> Show/ hide available Manipulation Widgets </Tooltip>} >
|
||||
<Button variant="light" key={2} style={{ marginRight: '3px', height: '40px' }} onClick={() => this.showWidgets('manipulation')} >
|
||||
<Icon icon={manipulationIcon} style={{color: '#007bff'}} /> Manipulation Widgets</Button>
|
||||
<Button key={2} variant="secondary" style={buttonStyle} onClick={() => this.showWidgets('manipulation')} >
|
||||
<Icon icon={manipulationIcon}/> Manipulation Widgets</Button>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -128,13 +132,13 @@ class WidgetToolbox extends React.Component {
|
|||
<span>Grid: { this.props.grid > 1 ? this.props.grid : 'Disabled' }</span>
|
||||
<Slider value={this.props.grid} style={{ width: '80px' }} step={5} onChange={this.onGridChange} />
|
||||
<OverlayTrigger key={0} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"increase"}`}> Increase dashboard height </Tooltip>} >
|
||||
<Button variant="light" key={0} style={{marginRight: '3px', height: '40px'}} onClick={() => this.props.onDashboardSizeChange(1)} >
|
||||
<Icon icon="plus" style={iconStyle}/>
|
||||
<Button variant="light" key={0} style={buttonStyle} onClick={() => this.props.onDashboardSizeChange(1)} >
|
||||
<Icon icon="plus" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
<OverlayTrigger key={1} placement={'bottom'} overlay={<Tooltip id={`tooltip-${"decrease"}`}> Decrease dashboard height </Tooltip>} >
|
||||
<Button variant="light" key={1} disabled={disableDecrease} style={{marginRight: '3px', height: '40px'}} onClick={() => this.props.onDashboardSizeChange(-1)} >
|
||||
<Icon icon="minus" style={iconStyle}/>
|
||||
<Button variant="light" key={1} disabled={disableDecrease} style={buttonStyle} onClick={() => this.props.onDashboardSizeChange(-1)} >
|
||||
<Icon icon="minus" classname='icon-color' style={iconStyle}/>
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
|
||||
|
@ -143,7 +147,7 @@ class WidgetToolbox extends React.Component {
|
|||
<br></br>
|
||||
<br></br>
|
||||
<br></br>
|
||||
<div className= 'section-buttons-group-left'>
|
||||
<div className= 'drag-and-drop'>
|
||||
<span>
|
||||
<Collapse isOpened={this.state.showCosmeticWidgets} >
|
||||
<ToolboxItem name='Line' type='widget' icon='plus'/>
|
||||
|
|
Loading…
Add table
Reference in a new issue