diff --git a/src/result/edit-result.js b/src/result/edit-result.js index 2b2fac6..c956c06 100644 --- a/src/result/edit-result.js +++ b/src/result/edit-result.js @@ -16,7 +16,9 @@ ******************************************************************************/ import React from 'react'; -import {FormGroup, FormControl, FormLabel, Col} from 'react-bootstrap'; +import {FormGroup, FormControl, FormLabel, Col, Button, ProgressBar} from 'react-bootstrap'; +import AppDispatcher from "../common/app-dispatcher"; + import Table from "../common/table"; import Dialog from '../common/dialogs/dialog'; @@ -29,10 +31,10 @@ class EditResultDialog extends React.Component { super(props); this.state = { - configSnapshots: '', + id: 0, description: '', - resultFileIDs: [], - id:0, + uploadFile: null, + uploadProgress: 0, }; } @@ -55,10 +57,9 @@ class EditResultDialog extends React.Component { resetState = () => { this.setState({ - configSnapshots: this.props.configSnapshots, - description: this.props.description, - resultFileIDs: this.props.resultFileIDs, - id: this.props.id, + id: this.props.result.id, + description: this.props.result.description, + result: this.props.result, }); }; @@ -66,28 +67,46 @@ class EditResultDialog extends React.Component { this.setState({ startParameters }); }; - // TODO: file reading necessary? or can it just be saved? - loadFile = event => { - // get file - const file = event.target.files[0]; - - // create file reader - let reader = new FileReader(); - let self = this; + selectUploadFile(event) { + this.setState({ uploadFile: event.target.files[0] }); + }; - reader.onload = event => { - const config = JSON.parse(event.target.result); + startFileUpload(){ + // upload file + const formData = new FormData(); + formData.append("file", this.state.uploadFile); + //console.log("formData: "); + //console.log(formData); - self.imported = true; - self.valid = true; - this.setState({filename: config.name, file: config }); - }; + AppDispatcher.dispatch({ + type: 'resultfiles/start-upload', + data: formData, + resultID: this.state.id, + token: this.props.sessionToken, + progressCallback: this.updateUploadProgress, + finishedCallback: this.clearProgress, + scenarioID: this.props.scenarioID, + }); - reader.readAsBinaryString(file); - } + this.setState({ uploadFile: null }); + }; + + clearProgress = (newFileID) => { + this.setState({ uploadProgress: 0 }); + }; + + updateUploadProgress = (event) => { + this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); + }; render() { - return + return +
Description @@ -95,12 +114,27 @@ class EditResultDialog extends React.Component { - - Add Result File - + this.selectUploadFile(event)} /> + + + + + + + +
; diff --git a/src/result/result-store.js b/src/result/result-store.js index fb6ab36..15958cf 100644 --- a/src/result/result-store.js +++ b/src/result/result-store.js @@ -24,8 +24,83 @@ class ResultStore extends ArrayStore { super('results', ResultsDataManager); } + saveFile(state, action){ + + let fileID = parseInt(action.id) + state.forEach((element, index, array) => { + if (element.id === fileID) { + // save blob object + array[index]["data"] = new Blob([action.data.data], {type: action.data.type}); + // update file type + array[index]["type"] = action.data.type; + + if (array[index]["objectURL"] !== ''){ + // free memory of previously generated object URL + URL.revokeObjectURL(array[index]["objectURL"]); + } + // create an object URL for the file + array[index]["objectURL"] = URL.createObjectURL(array[index]["data"]) + } + }); + + // announce change to listeners + this.__emitChange(); + return state + } + + simplify(timestamp) { + let parts = timestamp.split("T"); + let datestr = parts[0]; + let time = parts[1].split("."); + + return datestr + ', ' + time[0];; + } + + simplifyTimestamps(data) { + data.forEach((result) => { + result.createdAt = this.simplify(result.createdAt); + result.updatedAt = this.simplify(result.updatedAt); + }); + } + reduce(state, action) { - return super.reduce(state, action); + switch (action.type) { + case 'results/loaded': + this.simplifyTimestamps(action.data); + return super.reduce(state, action); + + case 'resultfiles/start-download': + ResultsDataManager.download(action) + return state + + case 'resultfiles/start-upload': + ResultsDataManager.upload(action.data, action.resultID, action.token, action.progressCallback, action.finishedCallback, action.scenarioID); + return state; + + case 'resultfiles/uploaded': + return state; + + case 'resultfiles/upload-error': + console.log(action.error); + return state; + + case 'resultfiles/downloaded': + // in this case a file is contained in the response (no JSON) + return this.saveFile(state, action); + + case 'resultfiles/start-edit': + ResultsDataManager.update(action.data, action.token, action.id); + return state; + + case 'resultfiles/edited': + return this.updateElements(state, [action.data]); + + case 'resultfiles/edit-error': + return state; + + default: + return super.reduce(state, action); + } } } diff --git a/src/result/results-data-manager.js b/src/result/results-data-manager.js index b30c9c1..6720b3d 100644 --- a/src/result/results-data-manager.js +++ b/src/result/results-data-manager.js @@ -15,8 +15,9 @@ * along with VILLASweb. If not, see . ******************************************************************************/ - import RestDataManager from '../common/data-managers/rest-data-manager'; +import RestAPI from '../common/api/rest-api'; +import AppDispatcher from '../common/app-dispatcher'; class ResultsDataManager extends RestDataManager{ @@ -24,6 +25,65 @@ class ResultsDataManager extends RestDataManager{ super('result', '/results'); } + upload(file, resultID, token = null, progressCallback = null, finishedCallback = null, scenarioID) { + RestAPI.upload(this.makeURL(this.url + '/' + resultID + '/file') , file, token, progressCallback, scenarioID).then(response => { + + AppDispatcher.dispatch({ + type: 'resultfiles/uploaded', + }); + + // Trigger a files reload + AppDispatcher.dispatch({ + type: 'results/start-load', + param: '?scenarioID=' + scenarioID, + token: token + }); + + if (finishedCallback) { + finishedCallback(response.result.id); + } + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/upload-error', + error + }); + }); + } + + download(action){ + RestAPI.download(this.makeURL(this.url), action.token, action.data).then(response => { + AppDispatcher.dispatch({ + type: 'resultfiles/downloaded', + data: response, + id: action.data, + token: action.token + }); + + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/load-error', + error: error + }); + }); + } + + /* only update file ids + update(file, token, id) { + + RestAPI.put(this.makeURL(this.url + '/' + id), file, token).then(response => { + AppDispatcher.dispatch({ + type: 'resultfiles/edited', + data: response[this.type] + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'resultfiles/edit-error', + error: error + }); + }); + } + }*/ + } -export default new ResultsDataManager() \ No newline at end of file +export default new ResultsDataManager(); \ No newline at end of file diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index b89f5ef..806e4ee 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -572,8 +572,6 @@ class Scenario extends React.Component { ############################################## */ closeNewResultModal(data) { - console.log(this.state.results); - console.log(data); this.setState({ newResultModal: false }); if (data) { data["scenarioID"] = this.state.scenario.id; @@ -691,17 +689,20 @@ class Scenario extends React.Component { title='Options' width='300' editButton - addRemoveFilesButton downloadAllButton deleteButton onEdit={index => this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} - onAddRemove={(index) => this.setState({ editFilesModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index})} onDownloadAll={(index) => this.downloadData(this.state.results[index])} onDelete={(index) => this.setState({ deleteResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })} /> - this.closeEditResultsModal(data)} /> + this.closeEditResultsModal(data)} /> this.closeDeleteResultsModal(e)} /> }