From 5409e485375123052f1bde66c883e9f73e7cfdcf Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 18 Feb 2020 16:29:11 +0100 Subject: [PATCH] file upload works for simulation model edit dialog (for now without progress bar) --- src/common/api/rest-api.js | 4 +- src/file/file-store.js | 2 +- src/file/files-data-manager.js | 11 +- src/file/select-file.js | 75 +++-- src/scenario/scenario.js | 256 ++++++++++-------- src/simulationmodel/edit-simulation-model.js | 47 +++- src/simulationmodel/simulation-model-store.js | 1 + .../simulation-models-data-manager.js | 12 + 8 files changed, 229 insertions(+), 179 deletions(-) diff --git a/src/common/api/rest-api.js b/src/common/api/rest-api.js index bb92e10..33c30ac 100644 --- a/src/common/api/rest-api.js +++ b/src/common/api/rest-api.js @@ -129,9 +129,9 @@ class RestAPI { }); } - upload(url, data, token, progressCallback) { + upload(url, data, token, progressCallback, objectType, objectID) { return new Promise(function (resolve, reject) { - const req = request.post(url).send(data).on('progress', progressCallback); + const req = request.post(url + "?objectType=" + objectType + "&objectID=" + objectID).send(data); //.on('progress', progressCallback); if (token != null) { req.set('Authorization', "Bearer " + token); diff --git a/src/file/file-store.js b/src/file/file-store.js index f2d3a07..4336ebc 100644 --- a/src/file/file-store.js +++ b/src/file/file-store.js @@ -30,7 +30,7 @@ class FileStore extends ArrayStore { reduce(state, action) { switch (action.type) { case 'files/start-upload': - FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback); + FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.objectType, action.objectID); return state; case 'files/uploaded': diff --git a/src/file/files-data-manager.js b/src/file/files-data-manager.js index e92aeff..91fb4a6 100644 --- a/src/file/files-data-manager.js +++ b/src/file/files-data-manager.js @@ -28,8 +28,8 @@ class FilesDataManager extends RestDataManager { super('file', '/files'); } - upload(file, token = null, progressCallback = null, finishedCallback = null) { - RestAPI.upload(this.makeURL('/upload'), file, token, progressCallback).then(response => { + upload(file, token = null, progressCallback = null, finishedCallback = null, objectType, objectID) { + RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, objectType, objectID).then(response => { AppDispatcher.dispatch({ type: 'files/uploaded', @@ -38,12 +38,13 @@ class FilesDataManager extends RestDataManager { // Trigger a files reload AppDispatcher.dispatch({ type: 'files/start-load', - token + param: '?objectType=' + objectType + '&objectID=' + objectID, + token: token }); - if (finishedCallback) { + /*if (finishedCallback) { finishedCallback(); - } + }*/ }).catch(error => { AppDispatcher.dispatch({ type: 'files/upload-error', diff --git a/src/file/select-file.js b/src/file/select-file.js index 5e273dc..9602f80 100644 --- a/src/file/select-file.js +++ b/src/file/select-file.js @@ -27,15 +27,24 @@ import FileStore from './file-store'; import LoginStore from '../user/login-store'; import AppDispatcher from '../common/app-dispatcher'; +import Icon from "../common/icon"; class SelectFile extends React.Component { static getStores() { return [ FileStore, LoginStore ]; } - static calculateState() { + + static calculateState(prevState, props) { + + let files = FileStore.getState().filter((file) => { + return (file.simulationModelID === props.objectID) + }); + + console.log("props.objectID=", props.objectID) + return { - files: FileStore.getState(), + files: files, sessionToken: LoginStore.getState().token, selectedFile: '', uploadFile: null, @@ -51,59 +60,47 @@ class SelectFile extends React.Component { }*/ static getDerivedStateFromProps(props, state){ - if (props.value === state.selectedSimulator) { - return null; - } - let selectedSimulator = props.value; - if (selectedSimulator == null) { - if (state.simulators.length > 0) { - selectedSimulator = state.simulators[0]._id; - } else { - selectedSimulator = ''; - } - } - return {selectedSimulator}; } - handleChange = event => { - this.setState({ selectedFile: event.target.value }); + handleChange(event) { - // send file to callback + // send file ID to callback if (this.props.onChange != null) { - const file = this.state.files.find(f => f.id === event.target.value); - - this.props.onChange(file); + this.props.onChange(event.target.value); } }; - selectUploadFile = event => { + selectUploadFile(event) { this.setState({ uploadFile: event.target.files[0] }); }; - startFileUpload = () => { + startFileUpload(){ // upload file const formData = new FormData(); - formData.append(0, this.state.uploadFile); + formData.append("file", this.state.uploadFile); AppDispatcher.dispatch({ type: 'files/start-upload', data: formData, token: this.state.sessionToken, - progressCallback: this.updateUploadProgress, - finishedCallback: this.clearProgress + //progressCallback: this.updateUploadProgress, + //finishedCallback: this.clearProgress, + objectType: this.props.type, + objectID: this.props.objectID, }); }; - updateUploadProgress = event => { + updateUploadProgress = (event) => {// TODO: this callback does not work properly (access to setState) this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); }; - clearProgress = () => { - // select uploaded file - const selectedFile = this.state.files[this.state.files.length - 1]._id; - this.setState({ selectedFile, uploadProgress: 0 }); + clearProgress = () => { // TODO this callback does not work properly (access to setState) + if (this.props.onChange != null) { + this.props.onChange(this.state.files[this.state.files.length - 1].id); + } + this.setState({ uploadProgress: 0 }); }; render() { @@ -129,27 +126,29 @@ class SelectFile extends React.Component { - this.handleChange(e)}> + this.handleChange(event)}> {fileOptions} - + this.selectUploadFile(event)} /> - - - - + {/* + + + */} ; } } let fluxContainerConverter = require('../common/FluxContainerConverter'); -export default Container.create(fluxContainerConverter.convert(SelectFile)); +export default Container.create(fluxContainerConverter.convert(SelectFile), { withProps: true }); diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index 048643a..fd6171f 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -44,10 +44,11 @@ import SimulatorAction from '../simulator/simulator-action'; import DeleteDialog from '../common/dialogs/delete-dialog'; import EditSimulationModelDialog from "../simulationmodel/edit-simulation-model"; import EditSignalMapping from "../signal/edit-signal-mapping"; +import FileStore from "../file/file-store" class Scenario extends React.Component { static getStores() { - return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore]; + return [ ScenarioStore, SimulationModelStore, DashboardStore, SimulatorStore, LoginStore, SignalStore, FileStore]; } static calculateState(prevState, props) { @@ -70,6 +71,7 @@ class Scenario extends React.Component { let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10)); let signals = SignalStore.getState(); + let files = FileStore.getState(); return { @@ -78,6 +80,7 @@ class Scenario extends React.Component { simulationModels, dashboards, signals, + files, simulators: SimulatorStore.getState(), deleteSimulationModelModal: false, @@ -129,14 +132,9 @@ class Scenario extends React.Component { } - static getDerivedStateFromProps(props, state){ - - let simulationModels = SimulationModelStore.getState().filter(simmodel => simmodel.scenarioID === parseInt(props.match.params.scenario, 10)); - - return { - simulationModels: simulationModels - }; - } + /* ############################################## + * Simulation Model modification methods + ############################################## */ addSimulationModel(){ const simulationModel = { @@ -173,54 +171,6 @@ class Scenario extends React.Component { } } - closeDeleteSignalModal(data){ - // data contains the signal to be deleted - if (data){ - - AppDispatcher.dispatch({ - type: 'signals/start-remove', - data: data, - token: this.state.sessionToken - }); - - } - } - - closeNewSignalModal(data){ - //data contains the new signal incl. simulationModelID and direction - if (data) { - AppDispatcher.dispatch({ - type: 'signals/start-add', - data: data, - token: this.state.sessionToken - }); - } - } - - closeEditSignalsModal(data, direction){ - - if( direction === "in") { - this.setState({editInputSignalsModal: false}); - } else if( direction === "out"){ - this.setState({editOutputSignalsModal: false}); - } else { - return; // no valid direction - } - - if (data){ - //data is an array of signals - for (let sig of data) { - //dispatch changes to signals - AppDispatcher.dispatch({ - type: 'signals/start-edit', - data: sig, - token: this.state.sessionToken, - }); - } - } - - } - closeDeleteSimulationModelModal(confirmDelete) { this.setState({ deleteSimulationModelModal: false }); @@ -259,76 +209,15 @@ class Scenario extends React.Component { }); } - closeNewDashboardModal(data) { - this.setState({ newDashboardModal : false }); - - if (data) { - AppDispatcher.dispatch({ - type: 'dashboards/start-add', - data, - 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) { - AppDispatcher.dispatch({ - type: 'dashboards/start-add', - data, - token: this.state.sessionToken, - }); - } - } - - getSimulatorName(simulatorId) { - for (let simulator of this.state.simulators) { - if (simulator.id === simulatorId) { - return _.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name') || simulator.uuid; - } - } - } - exportModel(index) { // filter properties const model = Object.assign({}, this.state.simulationModels[index]); - //delete model.simulator; - //delete model.scenario; - // TODO get elements recursively - // show save dialog const blob = new Blob([JSON.stringify(model, null, 2)], { type: 'application/json' }); FileSaver.saveAs(blob, 'simulation model - ' + model.name + '.json'); } - exportDashboard(index) { - // filter properties - const dashboard = Object.assign({}, this.state.dashboards[index]); - - // TODO get elements recursively - - // show save dialog - const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' }); - FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json'); - } - onSimulationModelChecked(index, event) { const selectedSimulationModels = Object.assign([], this.state.selectedSimulationModels); for (let key in selectedSimulationModels) { @@ -379,9 +268,139 @@ class Scenario extends React.Component { token: this.state.sessionToken }); } + }; + + getSimulatorName(simulatorId) { + for (let simulator of this.state.simulators) { + if (simulator.id === simulatorId) { + return _.get(simulator, 'properties.name') || _.get(simulator, 'rawProperties.name') || simulator.uuid; + } + } } + /* ############################################## + * Dashboard modification methods + ############################################## */ + + closeNewDashboardModal(data) { + this.setState({ newDashboardModal : false }); + + if (data) { + AppDispatcher.dispatch({ + type: 'dashboards/start-add', + data, + 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) { + AppDispatcher.dispatch({ + type: 'dashboards/start-add', + data, + token: this.state.sessionToken, + }); + } + } + + exportDashboard(index) { + // filter properties + const dashboard = Object.assign({}, this.state.dashboards[index]); + + // TODO get elements recursively + + // show save dialog + const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' }); + FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json'); + } + + /* ############################################## + * Signal modification methods + ############################################## */ + + closeDeleteSignalModal(data){ + // data contains the signal to be deleted + if (data){ + + AppDispatcher.dispatch({ + type: 'signals/start-remove', + data: data, + token: this.state.sessionToken + }); + + } + } + + closeNewSignalModal(data){ + //data contains the new signal incl. simulationModelID and direction + if (data) { + AppDispatcher.dispatch({ + type: 'signals/start-add', + data: data, + token: this.state.sessionToken + }); + } + } + + closeEditSignalsModal(data, direction){ + + if( direction === "in") { + this.setState({editInputSignalsModal: false}); + } else if( direction === "out"){ + this.setState({editOutputSignalsModal: false}); + } else { + return; // no valid direction + } + + if (data){ + //data is an array of signals + for (let sig of data) { + //dispatch changes to signals + AppDispatcher.dispatch({ + type: 'signals/start-edit', + data: sig, + token: this.state.sessionToken, + }); + } + } + + } + + /* ############################################## + * File modification methods + ############################################## */ + + getFileName(id){ + for (let file of this.state.files) { + if (file.id === id) { + return file.name; + } + } + } + + /* ############################################## + * Render method + ############################################## */ + render() { + const buttonStyle = { marginLeft: '10px' }; @@ -394,6 +413,7 @@ class Scenario extends React.Component { this.onSimulationModelChecked(index, event)} width='30' /> + this.getFileName(selectedModelFileID)}/> - {/* - this.handleChange(e)} - value={this.state.selectedFile}/> - < SelectFile type='configuration' name='Configuration' onChange={(e) => this.handleChange(e)} value={this.state.configuration} /> - */} + + this.handleSelectedModelFileChange(e)} value={this.state.selectedModelFileID} objectID={this.props.simulationModel.id}/> + + + {/* this.handleChange(e)} value={this.state.configuration} />*/} + + diff --git a/src/simulationmodel/simulation-model-store.js b/src/simulationmodel/simulation-model-store.js index 5ef17a3..fe90662 100644 --- a/src/simulationmodel/simulation-model-store.js +++ b/src/simulationmodel/simulation-model-store.js @@ -34,6 +34,7 @@ class SimulationModelStore extends ArrayStore { case 'simulationModels/loaded': SimulationModelsDataManager.loadSignals(action.token, action.data); + SimulationModelsDataManager.loadFiles(action.token, action.data); return super.reduce(state, action); default: diff --git a/src/simulationmodel/simulation-models-data-manager.js b/src/simulationmodel/simulation-models-data-manager.js index b53c0d9..c9554a8 100644 --- a/src/simulationmodel/simulation-models-data-manager.js +++ b/src/simulationmodel/simulation-models-data-manager.js @@ -68,6 +68,18 @@ class SimulationModelDataManager extends RestDataManager { } } + + loadFiles(token, models){ + for (let model of models) { + // request files of simulation model + RestAPI.get(this.makeURL('/files?objectType=model&objectID=' + model.id), token).then(response => { + AppDispatcher.dispatch({ + type: 'files/loaded', + data: response.files + }); + }); + } + } } export default new SimulationModelDataManager();