diff --git a/src/componentconfig/config-table.js b/src/componentconfig/config-table.js
new file mode 100644
index 0000000..1452949
--- /dev/null
+++ b/src/componentconfig/config-table.js
@@ -0,0 +1,434 @@
+/**
+ * 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 .
+ ******************************************************************************/
+
+import React, {Component} from "react";
+import FileSaver from 'file-saver';
+import IconButton from "../common/icon-button";
+import Table from "../common/table";
+import TableColumn from "../common/table-column";
+import DeleteDialog from "../common/dialogs/delete-dialog";
+import AppDispatcher from "../common/app-dispatcher";
+import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
+import ICAction from "../ic/ic-action";
+import EditConfigDialog from "./edit-config";
+import ImportConfigDialog from "./import-config";
+import EditSignalMappingDialog from "../signal/edit-signal-mapping";
+
+class ConfigTable extends Component {
+
+ constructor() {
+ super();
+
+ this.state = {
+ editConfigModal: false,
+ modalConfigData: {},
+ modalConfigIndex: 0,
+ deleteConfigModal: false,
+ importConfigModal: false,
+ newConfig: false,
+ selectedConfigs: [],
+ ExternalICInUse: false,
+ editOutputSignalsModal: false,
+ editInputSignalsModal: false,
+ }
+ }
+
+ addConfig() {
+ const config = {
+ scenarioID: this.props.scenario.id,
+ name: 'New Component Configuration',
+ icID: this.props.ics.length > 0 ? this.props.ics[0].id : null,
+ startParameters: {},
+ };
+
+ AppDispatcher.dispatch({
+ type: 'configs/start-add',
+ data: config,
+ token: this.props.sessionToken
+ });
+
+ this.setState({ newConfig: true });
+
+ }
+
+ closeEditConfigModal(data) {
+ this.setState({ editConfigModal: false, newConfig: false });
+
+ if (data) {
+ AppDispatcher.dispatch({
+ type: 'configs/start-edit',
+ data: data,
+ token: this.props.sessionToken,
+ });
+ }
+ }
+
+ closeDeleteConfigModal(confirmDelete) {
+ this.setState({ deleteConfigModal: false });
+
+ if (confirmDelete === false) {
+ return;
+ }
+
+ AppDispatcher.dispatch({
+ type: 'configs/start-remove',
+ data: this.state.modalConfigData,
+ token: this.props.sessionToken
+ });
+ }
+
+ importConfig(data) {
+ this.setState({ importConfigModal: false });
+
+ if (data == null) {
+ return;
+ }
+
+ let newConfig = JSON.parse(JSON.stringify(data.config))
+
+ newConfig["scenarioID"] = this.props.scenario.id;
+ newConfig.name = data.name;
+
+ AppDispatcher.dispatch({
+ type: 'configs/start-add',
+ data: newConfig,
+ token: this.props.sessionToken
+ });
+ }
+
+ copyConfig(index) {
+ let config = JSON.parse(JSON.stringify(this.props.configs[index]));
+
+ let signals = JSON.parse(JSON.stringify(this.props.signals.filter(s => s.configID === parseInt(config.id, 10))));
+ signals.forEach((signal) => {
+ delete signal.configID;
+ delete signal.id;
+ })
+
+ // two separate lists for inputMapping and outputMapping
+ let inputSignals = signals.filter(s => s.direction === 'in');
+ let outputSignals = signals.filter(s => s.direction === 'out');
+
+ // add signal mappings to config
+ config["inputMapping"] = inputSignals;
+ config["outputMapping"] = outputSignals;
+
+ delete config.id;
+ delete config.scenarioID;
+ delete config.inputLength;
+ delete config.outputLength;
+
+ return config;
+ }
+
+ exportConfig(index) {
+ let config = this.copyConfig(index);
+
+ // show save dialog
+ const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
+ FileSaver.saveAs(blob, 'config-' + config.name + '.json');
+ }
+
+ duplicateConfig(index) {
+ let newConfig = this.copyConfig(index);
+ newConfig["scenarioID"] = this.props.scenario.id;
+ newConfig.name = newConfig.name + '_copy';
+
+ AppDispatcher.dispatch({
+ type: 'configs/start-add',
+ data: newConfig,
+ token: this.props.sessionToken
+ });
+ }
+
+ onConfigChecked(index, event) {
+ const selectedConfigs = Object.assign([], this.state.selectedConfigs);
+ for (let key in selectedConfigs) {
+ if (selectedConfigs[key] === index) {
+ // update existing entry
+ if (event.target.checked) {
+ return;
+ }
+
+ selectedConfigs.splice(key, 1);
+
+ this.setState({ selectedConfigs: selectedConfigs });
+ return;
+ }
+ }
+
+ // add new entry
+ if (event.target.checked === false) {
+ return;
+ }
+
+ selectedConfigs.push(index);
+ this.setState({ selectedConfigs: selectedConfigs });
+ }
+
+ usesExternalIC(index) {
+ let icID = this.props.configs[index].icID;
+
+ let ic = null;
+ for (let component of this.props.ics) {
+ if (component.id === icID) {
+ ic = component;
+ }
+ }
+
+ if (ic == null) {
+ return false;
+ }
+
+ if (ic.managedexternally === true) {
+ this.setState({ ExternalICInUse: true })
+ return true
+ }
+
+ return false
+ }
+
+ getICName(icID) {
+ for (let ic of this.props.ics) {
+ if (ic.id === icID) {
+ return ic.name || ic.uuid;
+ }
+ }
+ }
+
+ /* ##############################################
+ * Signal modification methods
+ ############################################## */
+
+ closeEditSignalsModal(direction) {
+
+ // reload the config
+ AppDispatcher.dispatch({
+ type: 'configs/start-load',
+ data: this.state.modalConfigData.id,
+ token: this.props.sessionToken
+ });
+
+ if (direction === "in") {
+ this.setState({ editInputSignalsModal: false });
+ } else if (direction === "out") {
+ this.setState({ editOutputSignalsModal: false });
+ }
+ }
+
+ signalsAutoConf(index) {
+ let componentConfig = this.props.configs[index];
+ // determine apiurl of infrastructure component
+ let ic = this.props.ics.find(ic => ic.id === componentConfig.icID)
+ if (!ic.type.includes("villas-node")) {
+ let message = "Cannot autoconfigure signals for IC type " + ic.type + " of category " + ic.category + ". This is only possible for gateway ICs of type 'VILLASnode'."
+ console.warn(message);
+
+ const SIGNAL_AUTOCONF_WARN_NOTIFICATION = {
+ title: 'Failed to load signal config for IC ' + ic.name,
+ message: message,
+ level: 'warning'
+ };
+ NotificationsDataManager.addNotification(SIGNAL_AUTOCONF_WARN_NOTIFICATION);
+ return;
+ }
+
+ let splitWebsocketURL = ic.websocketurl.split("/")
+
+ AppDispatcher.dispatch({
+ type: 'signals/start-autoconfig',
+ url: ic.apiurl + "/nodes",
+ socketname: splitWebsocketURL[splitWebsocketURL.length - 1],
+ token: this.props.sessionToken,
+ configID: componentConfig.id
+ });
+
+ }
+
+ startPintura(configIndex) {
+ let config = this.props.configs[configIndex];
+
+ // get xml / CIM file
+ let files = []
+ for (let id of config.fileIDs) {
+ for (let file of this.props.files) {
+ if (file.id === id && ['xml'].some(e => file.type.includes(e))) {
+ files.push(file);
+ }
+ }
+ }
+
+ if (files.length > 1) {
+ // more than one CIM file...
+ console.warn("There is more than one CIM file selected in this component configuration. I will open them all in a separate tab.")
+ }
+
+ let baseURL = 'aaa.bbb.ccc.ddd/api/v2/files/'
+ for (let file of files) {
+ // endpoint param serves for download and upload of CIM file, token is required for authentication
+ let params = {
+ token: this.props.sessionToken,
+ endpoint: baseURL + String(file.id),
+ }
+
+ // TODO start Pintura for editing CIM/ XML file from here
+ console.warn("Starting Pintura... and nothing happens so far :-) ", params)
+ }
+ }
+
+ render() {
+ return (
+
+ {/*Component Configurations table*/}
+
Component Configurations
+
+ this.addConfig()}
+ icon='plus'
+ />
+ this.setState({ importConfigModal: true })}
+ icon='upload'
+ />
+
+
+
+ !this.usesExternalIC(index)}
+ onChecked={(index, event) => this.onConfigChecked(index, event)}
+ width={20}
+ />
+ {this.props.currentUser.role === "Admin" ?
+
+ : <>>
+ }
+
+ this.setState({ editOutputSignalsModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
+ width={150}
+ />
+ this.setState({ editInputSignalsModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
+ width={150}
+ />
+ this.signalsAutoConf(index)}
+ width={150}
+ />
+ this.getICName(icID)}
+ width={200}
+ />
+ this.setState({ editConfigModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
+ onDelete={(index) => this.setState({ deleteConfigModal: true, modalConfigData: this.props.configs[index], modalConfigIndex: index })}
+ onExport={index => this.exportConfig(index)}
+ onDuplicate={index => this.duplicateConfig(index)}
+ />
+
+
+ {this.state.ExternalICInUse ?
+
+ this.copyConfig(index)}
+ token = {this.props.sessionToken}
+ actions={[
+ { 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' } }
+ ]} />
+
+ :
+ }
+
+
+
+
this.closeEditConfigModal(data)}
+ config={this.state.modalConfigData}
+ ics={this.props.ics}
+ files={this.props.files}
+ sessionToken={this.props.sessionToken}
+ />
+ this.importConfig(data)}
+ ics={this.props.ics}
+ />
+ this.closeDeleteConfigModal(c)}
+ />
+ this.closeEditSignalsModal(direction)}
+ direction="Output"
+ signals={this.props.signals}
+ configID={this.state.modalConfigData.id}
+ sessionToken={this.props.sessionToken}
+ />
+ this.closeEditSignalsModal(direction)}
+ direction="Input"
+ signals={this.props.signals}
+ configID={this.state.modalConfigData.id}
+ sessionToken={this.props.sessionToken}
+ />
+
+ )
+ }
+}
+
+export default ConfigTable;
diff --git a/src/dashboard/dashboard-table.js b/src/dashboard/dashboard-table.js
new file mode 100644
index 0000000..89f5bf0
--- /dev/null
+++ b/src/dashboard/dashboard-table.js
@@ -0,0 +1,229 @@
+/**
+ * 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 .
+ ******************************************************************************/
+
+import React, {Component} from "react";
+import FileSaver from 'file-saver';
+import IconButton from "../common/icon-button";
+import Table from "../common/table";
+import TableColumn from "../common/table-column";
+import NewDashboardDialog from "./new-dashboard";
+import EditDashboardDialog from "./edit-dashboard";
+import ImportDashboardDialog from "./import-dashboard";
+import DeleteDialog from "../common/dialogs/delete-dialog";
+import AppDispatcher from "../common/app-dispatcher";
+
+class DashboardTable extends Component {
+
+ constructor() {
+ super();
+
+ this.state = {
+ newDashboardModal: false,
+ deleteDashboardModal: false,
+ importDashboardModal: false,
+ modalDashboardData: {},
+ dashboardEditModal: false,
+ }
+ }
+
+ closeNewDashboardModal(data) {
+ this.setState({ newDashboardModal: false });
+ if (data) {
+ // TODO: 'newDashboard' not used, check this
+ let newDashboard = data;
+ // add default grid value and scenarioID
+ newDashboard["grid"] = 15;
+ newDashboard["scenarioID"] = this.props.scenario.id;
+
+ AppDispatcher.dispatch({
+ type: 'dashboards/start-add',
+ data,
+ token: this.props.sessionToken,
+ });
+ }
+ }
+
+ closeEditDashboardModal(data) {
+ this.setState({ dashboardEditModal: false });
+
+ let editDashboard = this.state.modalDashboardData;
+
+ if (data != null) {
+ editDashboard.name = data.name;
+ AppDispatcher.dispatch({
+ type: 'dashboards/start-edit',
+ data: editDashboard,
+ token: this.props.sessionToken
+ });
+ }
+ }
+
+ closeDeleteDashboardModal(confirmDelete) {
+ this.setState({ deleteDashboardModal: false });
+
+ if (confirmDelete === false) {
+ return;
+ }
+
+ AppDispatcher.dispatch({
+ type: 'dashboards/start-remove',
+ data: this.state.modalDashboardData,
+ token: this.props.sessionToken,
+ });
+ }
+
+ closeImportDashboardModal(data) {
+ this.setState({ importDashboardModal: false });
+
+ if (data) {
+ let newDashboard = JSON.parse(JSON.stringify(data));
+ newDashboard["scenarioID"] = this.props.scenario.id;
+
+ AppDispatcher.dispatch({
+ type: 'dashboards/start-add',
+ data: newDashboard,
+ token: this.props.sessionToken,
+ });
+ }
+ }
+
+ copyDashboard(index) {
+ let dashboard = JSON.parse(JSON.stringify(this.props.dashboards[index]));
+
+ let widgets = JSON.parse(JSON.stringify(this.props.widgets.filter(w => w.dashboardID === parseInt(dashboard.id, 10))));
+ widgets.forEach((widget) => {
+ delete widget.dashboardID;
+ delete widget.id;
+ })
+ dashboard["widgets"] = widgets;
+ delete dashboard.scenarioID;
+ delete dashboard.id;
+
+ return dashboard;
+ }
+
+ exportDashboard(index) {
+ let dashboard = this.copyDashboard(index);
+
+ // show save dialog
+ const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
+ FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
+ }
+
+ duplicateDashboard(index) {
+ let newDashboard = this.copyDashboard(index);
+ newDashboard.scenarioID = this.props.scenario.id;
+ newDashboard.name = newDashboard.name + '_copy';
+
+ AppDispatcher.dispatch({
+ type: 'dashboards/start-add',
+ data: newDashboard,
+ token: this.props.sessionToken,
+ });
+ }
+
+ render() {
+
+ return (
+
+ {/*Dashboard table*/}
+
Dashboards
+
+ this.setState({newDashboardModal: true})}
+ icon='plus'
+ />
+ this.setState({importDashboardModal: true})}
+ icon='upload'
+ />
+
+
+
+ {this.props.currentUser.role === "Admin" ?
+
+ : <>>
+ }
+
+
+
+ this.setState({
+ dashboardEditModal: true,
+ modalDashboardData: this.props.dashboards[index]
+ })}
+ onDelete={(index) => this.setState({
+ deleteDashboardModal: true,
+ modalDashboardData: this.props.dashboards[index],
+ modalDashboardIndex: index
+ })}
+ onExport={index => this.exportDashboard(index)}
+ onDuplicate={index => this.duplicateDashboard(index)}
+ />
+
+
+
this.closeNewDashboardModal(data)}
+ />
+ this.closeEditDashboardModal(data)}
+ />
+ this.closeImportDashboardModal(data)}
+ />
+ this.closeDeleteDashboardModal(e)}
+ />
+
+
+ )
+ }
+}
+
+export default DashboardTable;
diff --git a/src/result/result-table.js b/src/result/result-table.js
new file mode 100644
index 0000000..cac8ab5
--- /dev/null
+++ b/src/result/result-table.js
@@ -0,0 +1,244 @@
+/**
+ * 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 .
+ ******************************************************************************/
+
+import React, {Component} from "react";
+import JSZip from 'jszip';
+import {Button} from "react-bootstrap";
+import FileSaver from 'file-saver';
+import AppDispatcher from "../common/app-dispatcher";
+import IconButton from "../common/icon-button";
+import Table from "../common/table";
+import TableColumn from "../common/table-column";
+import DeleteDialog from "../common/dialogs/delete-dialog";
+import EditResultDialog from "./edit-result";
+import ResultConfigDialog from "./result-configs-dialog";
+import NewResultDialog from "./new-result";
+
+class ResultTable extends Component {
+
+ constructor() {
+ super();
+
+ this.state = {
+ editResultsModal: false,
+ modalResultsData: {},
+ modalResultsIndex: 0,
+ newResultModal: false,
+ filesToDownload: [],
+ zipfiles: false,
+ resultNodl: 0,
+ resultConfigsModal: false,
+ modalResultConfigs: {},
+ modalResultConfigsIndex: 0,
+ }
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ // check whether file data has been loaded
+ if (this.state.filesToDownload && this.state.filesToDownload.length > 0) {
+ if (this.props.files !== prevProps.files) {
+ if (!this.state.zipfiles) {
+ let fileToDownload = this.props.files.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 });
+ FileSaver.saveAs(blob, fileToDownload[0].name);
+ this.setState({ filesToDownload: [] });
+ }
+ } else { // zip and save one or more files (download all button)
+ let filesToDownload = this.props.files.filter(file => this.state.filesToDownload.includes(file.id) && file.data);
+ if (filesToDownload.length === this.state.filesToDownload.length) { // all requested files have been loaded
+ var zip = new JSZip();
+ filesToDownload.forEach(file => {
+ zip.file(file.name, file.data);
+ });
+ let zipname = "result_" + this.state.resultNodl + "_" + (new Date()).toISOString();
+ zip.generateAsync({ type: "blob" }).then(function (content) {
+ saveAs(content, zipname);
+ });
+ this.setState({ filesToDownload: [] });
+ }
+ }
+ }
+ }
+ }
+
+ closeNewResultModal(data) {
+ this.setState({ newResultModal: false });
+ if (data) {
+ data["scenarioID"] = this.props.scenario.id;
+ AppDispatcher.dispatch({
+ type: 'results/start-add',
+ data,
+ token: this.props.sessionToken,
+ });
+ }
+ }
+
+ closeEditResultsModal() {
+ this.setState({ editResultsModal: false });
+ }
+
+ downloadResultData(param) {
+ let toDownload = [];
+ let zip = false;
+
+ 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 });
+ }
+
+ toDownload.forEach(fileid => {
+ AppDispatcher.dispatch({
+ type: 'files/start-download',
+ data: fileid,
+ token: this.props.sessionToken
+ });
+ });
+ }
+
+ closeDeleteResultsModal(confirmDelete) {
+ this.setState({ deleteResultsModal: false });
+
+ if (confirmDelete === false) {
+ return;
+ }
+
+ AppDispatcher.dispatch({
+ type: 'results/start-remove',
+ data: this.state.modalResultsData,
+ token: this.props.sessionToken,
+ });
+ }
+
+
+ openResultConfigSnapshots(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 this.openResultConfigSnapshots(result)}>{id}
+ }
+
+ render() {
+
+ return (
+
+ {/*Result table*/}
+
Results
+
+ this.setState({ newResultModal: true })}
+ icon='plus'
+ />
+
+
+
+
+ this.modifyResultNoColumn(id, result)}
+ width={70}
+ />
+
+
+
+ this.downloadResultData(index)}
+ width={300}
+ />
+ 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 })}
+ />
+
+
+
+
this.closeDeleteResultsModal(e)}
+ />
+
+ this.closeNewResultModal(data)}
+ />
+
+ )
+ }
+}
+
+export default ResultTable;
diff --git a/src/scenario/scenario-users-table.js b/src/scenario/scenario-users-table.js
new file mode 100644
index 0000000..c93b356
--- /dev/null
+++ b/src/scenario/scenario-users-table.js
@@ -0,0 +1,169 @@
+/**
+ * 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 .
+ ******************************************************************************/
+
+import React, {Component} from "react";
+import {Button, 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 DeleteDialog from "../common/dialogs/delete-dialog";
+import AppDispatcher from "../common/app-dispatcher";
+
+class ScenarioUsersTable extends Component {
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ userToAdd: '',
+ deleteUserName: '',
+ deleteUserModal: false,
+ goToScenarios: false
+ }
+ }
+
+ onUserInputChange(e) {
+ this.setState({ userToAdd: e.target.value });
+ }
+
+ addUser() {
+ AppDispatcher.dispatch({
+ type: 'scenarios/add-user',
+ data: this.props.scenario.id,
+ username: this.state.userToAdd,
+ token: this.props.sessionToken
+ });
+
+ this.setState({ userToAdd: '' });
+ }
+
+ closeDeleteUserModal() {
+ let scenarioID = this.props.scenario.id;
+ if (this.state.deleteUserName === this.props.currentUser.username) {
+ AppDispatcher.dispatch({
+ type: 'scenarios/remove-user',
+ data: scenarioID,
+ username: this.state.deleteUserName,
+ token: this.props.sessionToken,
+ ownuser: true
+ });
+ this.setState({ goToScenarios: true });
+ } else {
+ AppDispatcher.dispatch({
+ type: 'scenarios/remove-user',
+ data: scenarioID,
+ username: this.state.deleteUserName,
+ token: this.props.sessionToken,
+ ownuser: false
+ });
+ }
+ this.setState({ deleteUserModal: false });
+ }
+
+
+ render() {
+ if (this.state.goToScenarios) {
+ console.log("redirect to scenario overview")
+ return ( );
+ }
+
+ const altButtonStyle = {
+ marginLeft: '10px',
+ }
+
+ const iconStyle = {
+ height: '30px',
+ width: '30px'
+ }
+
+ return (
+
+ {/*Scenario Users table*/}
+
Users sharing this scenario
+
+ {this.props.currentUser.role === "Admin" ?
+
+ : <>>
+ }
+
+
+ this.setState({
+ deleteUserModal: true,
+ deleteUserName: this.props.scenario.users[index].username,
+ modalUserIndex: index
+ })}
+ />
+
+
+
+ this.onUserInputChange(e)}
+ value={this.state.userToAdd}
+ type="text"
+ />
+
+
+ this.addUser()}>
+
+
+
+
+
+
+
+
+
this.closeDeleteUserModal(c)}
+ />
+
+ )
+ }
+}
+
+export default ScenarioUsersTable;
+
diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js
index 80f2e60..01dfd60 100644
--- a/src/scenario/scenario.js
+++ b/src/scenario/scenario.js
@@ -17,514 +17,86 @@
import React from 'react';
import { Container } from 'flux/utils';
-import { Button, InputGroup, Form } from 'react-bootstrap';
-import FileSaver from 'file-saver';
+import AppDispatcher from '../common/app-dispatcher';
+import IconButton from '../common/icon-button';
import ScenarioStore from './scenario-store';
import ICStore from '../ic/ic-store';
import DashboardStore from '../dashboard/dashboard-store';
import ConfigStore from '../componentconfig/config-store';
import SignalStore from '../signal/signal-store'
-import AppDispatcher from '../common/app-dispatcher';
-
-import Icon from '../common/icon';
-import Table from '../common/table';
-import TableColumn from '../common/table-column';
-import IconButton from '../common/icon-button';
-import ImportConfigDialog from '../componentconfig/import-config';
-import ImportDashboardDialog from "../dashboard/import-dashboard";
-import NewDashboardDialog from "../dashboard/new-dashboard";
-import EditDashboardDialog from '../dashboard/edit-dashboard';
-import EditFilesDialog from '../file/edit-files'
-import NewResultDialog from '../result/new-result';
-import EditResultDialog from '../result/edit-result';
-import ResultConfigDialog from '../result/result-configs-dialog';
-
-
-import ICAction from '../ic/ic-action';
-import DeleteDialog from '../common/dialogs/delete-dialog';
-import EditConfigDialog from "../componentconfig/edit-config";
-import EditSignalMappingDialog from "../signal/edit-signal-mapping";
import FileStore from "../file/file-store"
import WidgetStore from "../widget/widget-store";
import ResultStore from "../result/result-store"
-import { Redirect } from 'react-router-dom';
-import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
-var JSZip = require("jszip");
+import DashboardTable from '../dashboard/dashboard-table'
+import ResultTable from "../result/result-table";
+import ConfigTable from "../componentconfig/config-table";
+import EditFilesDialog from '../file/edit-files'
+import ScenarioUsersTable from "./scenario-users-table";
+
class Scenario extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ filesEditModal: false,
+ filesEditSaveState: [],
+ }
+ }
+
+ static calculateState(prevState, props){
+ let scenarioID = parseInt(props.match.params.scenario, 10)
+
+ return{
+ scenario: ScenarioStore.getState().find(s => s.id === scenarioID),
+ results: ResultStore.getState().filter(result => result.scenarioID === scenarioID),
+ sessionToken: localStorage.getItem("token"),
+ configs: ConfigStore.getState().filter(config => config.scenarioID === scenarioID),
+ widgets: WidgetStore.getState(),
+ dashboards: DashboardStore.getState().filter(dashb => dashb.scenarioID === scenarioID),
+ signals: SignalStore.getState(),
+ currentUser: JSON.parse(localStorage.getItem("currentUser")),
+ files: FileStore.getState().filter(file => file.scenarioID === scenarioID),
+ ics: ICStore.getState(),
+ }
+ }
+
static getStores() {
return [ScenarioStore, ConfigStore, DashboardStore, ICStore, SignalStore, FileStore, WidgetStore, ResultStore];
}
- static calculateState(prevState, props) {
- if (prevState == null) {
- prevState = {};
- }
-
- // get selected scenario
- const sessionToken = localStorage.getItem("token");
-
- const scenario = ScenarioStore.getState().find(s => s.id === parseInt(props.match.params.scenario, 10));
- if (scenario === undefined) {
- AppDispatcher.dispatch({
- type: 'scenarios/start-load',
- data: props.match.params.scenario,
- token: sessionToken
- });
- }
-
- // obtain all component configurations of a scenario
- let configs = ConfigStore.getState().filter(config => config.scenarioID === parseInt(props.match.params.scenario, 10));
- let editConfigModal = prevState.editConfigModal || false;
- let modalConfigData = (prevState.modalConfigData !== {} && prevState.modalConfigData !== undefined) ? prevState.modalConfigData : {};
- let modalConfigIndex = 0;
-
- if ((typeof prevState.configs !== "undefined") && (prevState.newConfig === true) && (configs.length !== prevState.configs.length)) {
- let index = configs.length - 1;
- editConfigModal = true;
- modalConfigData = configs[index];
- modalConfigIndex = index;
- }
-
- let results = ResultStore.getState().filter(result => result.scenarioID === parseInt(props.match.params.scenario, 10));
-
- return {
- scenario,
- results,
- sessionToken,
- configs,
- editConfigModal,
- modalConfigData,
- modalConfigIndex,
- dashboards: DashboardStore.getState().filter(dashb => dashb.scenarioID === parseInt(props.match.params.scenario, 10)),
- signals: SignalStore.getState(),
- currentUser: JSON.parse(localStorage.getItem("currentUser")),
- files: FileStore.getState().filter(file => file.scenarioID === parseInt(props.match.params.scenario, 10)),
-
- ics: ICStore.getState(),
- ExternalICInUse: false,
-
- deleteConfigModal: false,
- importConfigModal: false,
- newConfig: prevState.newConfig || false,
- selectedConfigs: prevState.selectedConfigs || [],
- filesEditModal: prevState.filesEditModal || false,
- filesEditSaveState: prevState.filesEditSaveState || [],
-
- editResultsModal: prevState.editResultsModal || false,
- modalResultsData: {},
- modalResultsIndex: prevState.modalResultsIndex,
- newResultModal: false,
- filesToDownload: prevState.filesToDownload,
- zipfiles: prevState.zipfiles || false,
- resultNodl: prevState.resultNodl,
- resultConfigsModal: false,
- modalResultConfigs: {},
- modalResultConfigsIndex: 0,
-
- editOutputSignalsModal: prevState.editOutputSignalsModal || false,
- editInputSignalsModal: prevState.editInputSignalsModal || false,
-
- newDashboardModal: false,
- dashboardEditModal: prevState.dashboardEditModal || false,
- deleteDashboardModal: false,
- importDashboardModal: false,
- modalDashboardData: {},
-
- userToAdd: '',
- deleteUserName: '',
- deleteUserModal: false,
- goToScenarios: false
- }
- }
-
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: parseInt(this.props.match.params.scenario, 10),
- token: this.state.sessionToken
+ data: scenarioID,
+ token: token
});
-
AppDispatcher.dispatch({
type: 'scenarios/start-load-users',
- data: parseInt(this.props.match.params.scenario, 10),
- token: this.state.sessionToken
+ data: scenarioID,
+ token: token
});
// load ICs to enable that component configs and dashboards work with them
AppDispatcher.dispatch({
type: 'ics/start-load',
- token: this.state.sessionToken
- });
- }
-
- componentDidUpdate(prevProps, prevState) {
- // check whether file data has been loaded
- 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 });
- FileSaver.saveAs(blob, fileToDownload[0].name);
- this.setState({ filesToDownload: [] });
- }
- } else { // zip and save one or more files (download all button)
- let filesToDownload = FileStore.getState().filter(file => this.state.filesToDownload.includes(file.id) && file.data);
- if (filesToDownload.length === this.state.filesToDownload.length) { // all requested files have been loaded
- var zip = new JSZip();
- filesToDownload.forEach(file => {
- zip.file(file.name, file.data);
- });
- let zipname = "result_" + this.state.resultNodl + "_" + (new Date()).toISOString();
- zip.generateAsync({ type: "blob" }).then(function (content) {
- saveAs(content, zipname);
- });
- this.setState({ filesToDownload: [] });
- }
- }
- }
- }
- }
-
- /* ##############################################
- * User modification methods
- ############################################## */
-
- onUserInputChange(e) {
- this.setState({ userToAdd: e.target.value });
- }
-
- addUser() {
- AppDispatcher.dispatch({
- type: 'scenarios/add-user',
- data: this.state.scenario.id,
- username: this.state.userToAdd,
- token: this.state.sessionToken
- });
-
- this.setState({ userToAdd: '' });
- }
-
- closeDeleteUserModal() {
- let scenarioID = this.state.scenario.id;
- if (this.state.deleteUserName === this.state.currentUser.username) {
- AppDispatcher.dispatch({
- type: 'scenarios/remove-user',
- data: scenarioID,
- username: this.state.deleteUserName,
- token: this.state.sessionToken,
- ownuser: true
- });
- this.setState({ goToScenarios: true });
- } else {
- AppDispatcher.dispatch({
- type: 'scenarios/remove-user',
- data: scenarioID,
- username: this.state.deleteUserName,
- token: this.state.sessionToken,
- ownuser: false
- });
- }
- this.setState({ deleteUserModal: false });
- }
-
- /* ##############################################
- * Component Configuration modification methods
- ############################################## */
-
- addConfig() {
- const config = {
- scenarioID: this.state.scenario.id,
- name: 'New Component Configuration',
- icID: this.state.ics.length > 0 ? this.state.ics[0].id : null,
- startParameters: {},
- };
-
- AppDispatcher.dispatch({
- type: 'configs/start-add',
- data: config,
- token: this.state.sessionToken
- });
-
- this.setState({ newConfig: true });
-
- }
-
- closeEditConfigModal(data) {
- this.setState({ editConfigModal: false, newConfig: false });
-
- if (data) {
- AppDispatcher.dispatch({
- type: 'configs/start-edit',
- data: data,
- token: this.state.sessionToken,
- });
- }
- }
-
- closeDeleteConfigModal(confirmDelete) {
- this.setState({ deleteConfigModal: false });
-
- if (confirmDelete === false) {
- return;
- }
-
- AppDispatcher.dispatch({
- type: 'configs/start-remove',
- data: this.state.modalConfigData,
- token: this.state.sessionToken
- });
- }
-
- importConfig(data) {
- this.setState({ importConfigModal: false });
-
- if (data == null) {
- return;
- }
-
- let newConfig = JSON.parse(JSON.stringify(data.config))
-
- newConfig["scenarioID"] = this.state.scenario.id;
- newConfig.name = data.name;
-
- AppDispatcher.dispatch({
- type: 'configs/start-add',
- data: newConfig,
- token: this.state.sessionToken
- });
- }
-
- copyConfig(index) {
- let config = JSON.parse(JSON.stringify(this.state.configs[index]));
-
- let signals = JSON.parse(JSON.stringify(SignalStore.getState().filter(s => s.configID === parseInt(config.id, 10))));
- signals.forEach((signal) => {
- delete signal.configID;
- delete signal.id;
- })
-
- // two separate lists for inputMapping and outputMapping
- let inputSignals = signals.filter(s => s.direction === 'in');
- let outputSignals = signals.filter(s => s.direction === 'out');
-
- // add signal mappings to config
- config["inputMapping"] = inputSignals;
- config["outputMapping"] = outputSignals;
-
- delete config.id;
- delete config.scenarioID;
- delete config.inputLength;
- delete config.outputLength;
-
- return config;
- }
-
- exportConfig(index) {
- let config = this.copyConfig(index);
-
- // show save dialog
- const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
- FileSaver.saveAs(blob, 'config-' + config.name + '.json');
- }
-
- duplicateConfig(index) {
- let newConfig = this.copyConfig(index);
- newConfig["scenarioID"] = this.state.scenario.id;
- newConfig.name = newConfig.name + '_copy';
-
- AppDispatcher.dispatch({
- type: 'configs/start-add',
- data: newConfig,
- token: this.state.sessionToken
- });
- }
-
- onConfigChecked(index, event) {
- const selectedConfigs = Object.assign([], this.state.selectedConfigs);
- for (let key in selectedConfigs) {
- if (selectedConfigs[key] === index) {
- // update existing entry
- if (event.target.checked) {
- return;
- }
-
- selectedConfigs.splice(key, 1);
-
- this.setState({ selectedConfigs: selectedConfigs });
- return;
- }
- }
-
- // add new entry
- if (event.target.checked === false) {
- return;
- }
-
- selectedConfigs.push(index);
- this.setState({ selectedConfigs: selectedConfigs });
- }
-
- usesExternalIC(index) {
- let icID = this.state.configs[index].icID;
-
- let ic = null;
- for (let component of this.state.ics) {
- if (component.id === icID) {
- ic = component;
- }
- }
-
- if (ic == null) {
- return false;
- }
-
- if (ic.managedexternally === true) {
- this.setState({ ExternalICInUse: true })
- return true
- }
-
- return false
- }
-
- getICName(icID) {
- for (let ic of this.state.ics) {
- if (ic.id === icID) {
- return ic.name || ic.uuid;
- }
- }
- }
-
- /* ##############################################
- * Dashboard modification methods
- ############################################## */
-
- closeNewDashboardModal(data) {
- this.setState({ newDashboardModal: false });
- if (data) {
- // TODO: 'newDashboard' not used, check this
- let newDashboard = data;
- // add default grid value and scenarioID
- newDashboard["grid"] = 15;
- newDashboard["scenarioID"] = this.state.scenario.id;
-
- AppDispatcher.dispatch({
- type: 'dashboards/start-add',
- data,
- token: this.state.sessionToken,
- });
- }
- }
-
- closeEditDashboardModal(data) {
- this.setState({ dashboardEditModal: false });
-
- let editDashboard = this.state.modalDashboardData;
-
- if (data != null) {
- editDashboard.name = data.name;
- AppDispatcher.dispatch({
- type: 'dashboards/start-edit',
- data: editDashboard,
- token: this.state.sessionToken
- });
- }
- }
-
- closeDeleteDashboardModal(confirmDelete) {
- this.setState({ deleteDashboardModal: false });
-
- if (confirmDelete === false) {
- return;
- }
-
- AppDispatcher.dispatch({
- type: 'dashboards/start-remove',
- data: this.state.modalDashboardData,
- token: this.state.sessionToken,
- });
- }
-
- closeImportDashboardModal(data) {
- this.setState({ importDashboardModal: false });
-
- if (data) {
- let newDashboard = JSON.parse(JSON.stringify(data));
- newDashboard["scenarioID"] = this.state.scenario.id;
-
- AppDispatcher.dispatch({
- type: 'dashboards/start-add',
- data: newDashboard,
- token: this.state.sessionToken,
- });
- }
- }
-
- copyDashboard(index) {
- let dashboard = JSON.parse(JSON.stringify(this.state.dashboards[index]));
-
- let widgets = JSON.parse(JSON.stringify(WidgetStore.getState().filter(w => w.dashboardID === parseInt(dashboard.id, 10))));
- widgets.forEach((widget) => {
- delete widget.dashboardID;
- delete widget.id;
- })
- dashboard["widgets"] = widgets;
- delete dashboard.scenarioID;
- delete dashboard.id;
-
- return dashboard;
- }
-
- exportDashboard(index) {
- let dashboard = this.copyDashboard(index);
-
- // show save dialog
- const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
- FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
- }
-
- duplicateDashboard(index) {
- let newDashboard = this.copyDashboard(index);
- newDashboard.scenarioID = this.state.scenario.id;
- newDashboard.name = newDashboard.name + '_copy';
-
- AppDispatcher.dispatch({
- type: 'dashboards/start-add',
- data: newDashboard,
- token: this.state.sessionToken,
+ token: token
});
}
/* ##############################################
- * Signal modification methods
+ * File modification methods
############################################## */
- 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() {
let tempFiles = [];
this.state.files.forEach(file => {
@@ -538,199 +110,18 @@ class Scenario extends React.Component {
closeEditFiles() {
this.setState({ filesEditModal: false });
- // TODO do we need this if the dispatches happen in the dialog?
}
- signalsAutoConf(index) {
- let componentConfig = this.state.configs[index];
- // determine apiurl of infrastructure component
- let ic = this.state.ics.find(ic => ic.id === componentConfig.icID)
- if (!ic.type.includes("villas-node")) {
- let message = "Cannot autoconfigure signals for IC type " + ic.type + " of category " + ic.category + ". This is only possible for gateway ICs of type 'VILLASnode'."
- console.warn(message);
-
- const SIGNAL_AUTOCONF_WARN_NOTIFICATION = {
- title: 'Failed to load signal config for IC ' + ic.name,
- message: message,
- level: 'warning'
- };
- NotificationsDataManager.addNotification(SIGNAL_AUTOCONF_WARN_NOTIFICATION);
- return;
- }
-
- let splitWebsocketURL = ic.websocketurl.split("/")
-
- AppDispatcher.dispatch({
- type: 'signals/start-autoconfig',
- url: ic.apiurl + "/nodes",
- socketname: splitWebsocketURL[splitWebsocketURL.length - 1],
- token: this.state.sessionToken,
- configID: componentConfig.id
- });
-
- }
-
- uuidv4() {
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
- // eslint-disable-next-line
- var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
- return v.toString(16);
- });
- }
-
- /* ##############################################
- * File modification methods
- ############################################## */
-
- getListOfFiles(files, fileIDs) {
- let fileList = [];
-
- for (let id of fileIDs) {
- for (let file of files) {
- if (file.id === id) {
- fileList.push(file.name)
- }
- }
- }
-
- return fileList.join(';');
- }
-
- /* ##############################################
- * Result modification methods
- ############################################## */
-
- closeNewResultModal(data) {
- this.setState({ newResultModal: false });
- if (data) {
- data["scenarioID"] = this.state.scenario.id;
- AppDispatcher.dispatch({
- type: 'results/start-add',
- data,
- token: this.state.sessionToken,
- });
- }
- }
-
- closeEditResultsModal() {
- this.setState({ editResultsModal: false });
- }
-
- downloadResultData(param) {
- let toDownload = [];
- let zip = false;
-
- 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 });
- }
-
- toDownload.forEach(fileid => {
- AppDispatcher.dispatch({
- type: 'files/start-download',
- data: fileid,
- token: this.state.sessionToken
- });
- });
- }
-
- closeDeleteResultsModal(confirmDelete) {
- this.setState({ deleteResultsModal: false });
-
- if (confirmDelete === false) {
- return;
- }
-
- AppDispatcher.dispatch({
- type: 'results/start-remove',
- data: this.state.modalResultsData,
- token: this.state.sessionToken,
- });
- }
-
- 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 this.openResultConfigSnaphots(result)}>{id}
- }
-
- startPintura(configIndex) {
- let config = this.state.configs[configIndex];
-
- // get xml / CIM file
- let files = []
- for (let id of config.fileIDs) {
- for (let file of this.state.files) {
- if (file.id === id && ['xml'].some(e => file.type.includes(e))) {
- files.push(file);
- }
- }
- }
-
- if (files.length > 1) {
- // more than one CIM file...
- console.warn("There is more than one CIM file selected in this component configuration. I will open them all in a separate tab.")
- }
-
- let baseURL = 'aaa.bbb.ccc.ddd/api/v2/files/'
- for (let file of files) {
- // endpoint param serves for download and upload of CIM file, token is required for authentication
- let params = {
- token: this.state.sessionToken,
- endpoint: baseURL + String(file.id),
- }
-
- // TODO start Pintura for editing CIM/ XML file from here
- console.warn("Starting Pintura... and nothing happens so far :-) ", params)
- }
- }
-
-
/* ##############################################
* Render method
############################################## */
render() {
- if (this.state.goToScenarios) {
- console.log("redirect to scenario overview")
- return ( );
- }
-
- const altButtonStyle = {
- marginLeft: '10px',
- }
const tableHeadingStyle = {
paddingTop: '30px'
}
- const iconStyle = {
- height: '30px',
- width: '30px'
- }
-
if (this.state.scenario === undefined) {
return Loading Scenario... ;
}
@@ -755,362 +146,42 @@ class Scenario extends React.Component {
scenarioID={this.state.scenario.id}
/>
- {/*Component Configurations table*/}
- Component Configurations
-
- this.addConfig()}
- icon='plus'
- />
- this.setState({ importConfigModal: true })}
- icon='upload'
- />
-
-
-
- !this.usesExternalIC(index)}
- onChecked={(index, event) => this.onConfigChecked(index, event)}
- width={20}
- />
- {this.state.currentUser.role === "Admin" ?
-
- : <>>
- }
-
- this.setState({ editOutputSignalsModal: true, modalConfigData: this.state.configs[index], modalConfigIndex: index })}
- width={150}
- />
- this.setState({ editInputSignalsModal: true, modalConfigData: this.state.configs[index], modalConfigIndex: index })}
- width={150}
- />
- this.signalsAutoConf(index)}
- width={150}
- />
- this.getICName(icID)}
- width={300}
- />
- this.setState({ editConfigModal: true, modalConfigData: this.state.configs[index], modalConfigIndex: index })}
- onDelete={(index) => this.setState({ deleteConfigModal: true, modalConfigData: this.state.configs[index], modalConfigIndex: index })}
- onExport={index => this.exportConfig(index)}
- onDuplicate={index => this.duplicateConfig(index)}
- />
-
-
- {this.state.ExternalICInUse ?
-
- this.copyConfig(index)}
- token = {this.state.sessionToken}
- actions={[
- { 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' } }
- ]} />
-
- :
- }
-
-
-
- this.closeEditConfigModal(data)}
- config={this.state.modalConfigData}
- ics={this.state.ics}
+
- this.importConfig(data)}
ics={this.state.ics}
- />
- this.closeDeleteConfigModal(c)}
- />
- this.closeEditSignalsModal(direction)}
- direction="Output"
signals={this.state.signals}
- configID={this.state.modalConfigData.id}
+ scenario={this.state.scenario}
sessionToken={this.state.sessionToken}
+ currentUser={this.state.currentUser}
+ tableHeadingStyle={tableHeadingStyle}
/>
- this.closeEditSignalsModal(direction)}
- direction="Input"
- signals={this.state.signals}
- configID={this.state.modalConfigData.id}
+
+
- {/*Dashboard table*/}
- Dashboards
-
- this.setState({ newDashboardModal: true })}
- icon='plus'
- />
- this.setState({ importDashboardModal: true })}
- icon='upload'
- />
-
-
-
- {this.state.currentUser.role === "Admin" ?
-
- : <>>
- }
-
-
-
- this.setState({ dashboardEditModal: true, modalDashboardData: this.state.dashboards[index] })}
- onDelete={(index) => this.setState({ deleteDashboardModal: true, modalDashboardData: this.state.dashboards[index], modalDashboardIndex: index })}
- onExport={index => this.exportDashboard(index)}
- onDuplicate={index => this.duplicateDashboard(index)}
- />
-
-
- this.closeNewDashboardModal(data)}
- />
- this.closeEditDashboardModal(data)}
- />
- this.closeImportDashboardModal(data)}
- />
- this.closeDeleteDashboardModal(e)}
- />
-
- {/*Result table*/}
- Results
-
- this.setState({ newResultModal: true })}
- icon='plus'
- />
-
-
-
-
- this.modifyResultNoColumn(id, result)}
- width={70}
- />
-
-
-
- this.downloadResultData(index)}
- width={300}
- />
- 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 })}
- />
-
-
-
- this.closeDeleteResultsModal(e)}
- />
-
- this.closeNewResultModal(data)}
+ files={this.state.files}
+ scenario={this.state.scenario}
+ sessionToken={this.state.sessionToken}
+ tableHeadingStyle={tableHeadingStyle}
/>
- {/*Scenario Users table*/}
- Users sharing this scenario
-
- {this.state.currentUser.role === "Admin" ?
-
- : <>>
- }
-
-
- this.setState({
- deleteUserModal: true,
- deleteUserName: this.state.scenario.users[index].username,
- modalUserIndex: index
- })}
- />
-
-
-
- this.onUserInputChange(e)}
- value={this.state.userToAdd}
- type="text"
- />
-
-
- this.addUser()}>
-
-
-
-
-
-
-
-
- this.closeDeleteUserModal(c)}
+
- ;
+
+
}
}