diff --git a/.gitignore b/.gitignore
index 801e1dd..699692c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ yarn-debug.log*
yarn-error.log*
.vscode/
*.code-workspace
+package-lock.json
diff --git a/src/result/edit-result.js b/src/result/edit-result.js
index 4833251..e1992c1 100644
--- a/src/result/edit-result.js
+++ b/src/result/edit-result.js
@@ -16,7 +16,7 @@
******************************************************************************/
import React from 'react';
-import {FormGroup, FormControl, FormLabel, Col, Button, ProgressBar} from 'react-bootstrap';
+import { FormGroup, FormControl, FormLabel, Col, Row, Button, ProgressBar } from 'react-bootstrap';
import AppDispatcher from "../common/app-dispatcher";
import FileStore from "../file/file-store"
@@ -38,8 +38,6 @@ class EditResultDialog extends React.Component {
uploadFile: null,
uploadProgress: 0,
files: null,
- result: null,
- resultExists: false,
};
}
@@ -53,26 +51,38 @@ class EditResultDialog extends React.Component {
this.setState({ [event.target.id]: event.target.value });
};
+ isEmpty(val) {
+ return (val === undefined || val == null || val.length <= 0);
+ };
+
componentDidUpdate(prevProps, prevState) {
- if (this.state.resultExists && this.props.files != prevProps.files) {
- this.setState({files: FileStore.getState().filter(file => this.state.result.resultFileIDs.includes(file.id))});
- }
- if (this.props.result != prevProps.result && Object.keys(this.props.result).length != 0) {
- this.setState({
- id: this.props.result.id,
- description: this.props.result.description,
- result: this.props.result,
- resultExists: true,
- files: FileStore.getState().filter(file => this.props.result.resultFileIDs.includes(file.id)),
- })
- }
- }
+ if (this.props.resultId != prevProps.resultId || this.props.results != prevProps.results) {
+ let result = this.props.results[this.props.resultId];
+
+ if (result && Object.keys(result).length != 0) {
+ let hasFiles = !this.isEmpty(result.resultFileIDs);
+ if (hasFiles) {
+ this.setState({
+ id: result.id,
+ description: result.description,
+ files: FileStore.getState().filter(file => result.resultFileIDs.includes(file.id)),
+ })
+ } else {
+ this.setState({
+ id: result.id,
+ description: result.description,
+ files: null,
+ })
+ }
+ }
+ }
+ };
selectUploadFile(event) {
this.setState({ uploadFile: event.target.files[0] });
};
- startFileUpload(){
+ startFileUpload() {
const formData = new FormData();
formData.append("file", this.state.uploadFile);
@@ -97,68 +107,98 @@ class EditResultDialog extends React.Component {
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
};
- deleteFile(index){
+ deleteFile(index) {
let file = this.state.files[index];
AppDispatcher.dispatch({
- type: 'files/start-remove',
- data: file,
+ type: 'resultfiles/start-remove',
+ resultID: this.state.id,
+ fileID: file.id,
token: this.props.sessionToken
});
+
+ }
+
+ submitDescription() {
+ let result = this.props.results[this.props.resultId];
+ if (!this.isEmpty(result)) {
+ result.description = this.state.description;
+ AppDispatcher.dispatch({
+ type: 'results/start-edit',
+ data: result,
+ token: this.props.sessionToken
+ });
+ }
}
render() {
return ;
}
diff --git a/src/result/result-store.js b/src/result/result-store.js
index d824b1e..519c94a 100644
--- a/src/result/result-store.js
+++ b/src/result/result-store.js
@@ -15,40 +15,14 @@
* along with VILLASweb. If not, see .
******************************************************************************/
-
import ArrayStore from '../common/array-store';
import ResultsDataManager from './results-data-manager';
-import FilesDataManager from '../file/files-data-manager'
class ResultStore extends ArrayStore {
constructor() {
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];
@@ -67,17 +41,29 @@ class ResultStore extends ArrayStore {
reduce(state, action) {
switch (action.type) {
case 'results/loaded':
- this.simplifyTimestamps(action.data);
+ if (Array.isArray(action.data)) {
+ this.simplifyTimestamps(action.data);
+ } else {
+ this.simplifyTimestamps([action.data]);
+ }
return super.reduce(state, action);
case 'results/added':
this.simplifyTimestamps([action.data]);
return super.reduce(state, action);
+ case 'results/edited':
+ this.simplifyTimestamps([action.data]);
+ return super.reduce(state, action);
+
case 'resultfiles/start-upload':
ResultsDataManager.uploadFile(action.data, action.resultID, action.token, action.progressCallback, action.finishedCallback, action.scenarioID);
return state;
+ case 'resultfiles/start-remove':
+ ResultsDataManager.removeFile(action.resultID, action.fileID, action.token);
+ 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 5c7e2e0..3584c67 100644
--- a/src/result/results-data-manager.js
+++ b/src/result/results-data-manager.js
@@ -19,24 +19,24 @@ 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{
+class ResultsDataManager extends RestDataManager {
constructor() {
super('result', '/results');
}
uploadFile(file, resultID, token = null, progressCallback = null, finishedCallback = null, scenarioID) {
- RestAPI.upload(this.makeURL(this.url + '/' + resultID + '/file') , file, token, progressCallback, scenarioID).then(response => {
+ RestAPI.upload(this.makeURL(this.url + '/' + resultID + '/file'), file, token, progressCallback, scenarioID).then(response => {
AppDispatcher.dispatch({
type: 'files/uploaded',
});
- // Trigger a results reload
+ // Trigger a result reload
AppDispatcher.dispatch({
type: 'results/start-load',
- param: '?scenarioID=' + scenarioID,
- token: token
+ data: resultID,
+ token: token,
});
// Trigger a files reload
@@ -57,6 +57,23 @@ class ResultsDataManager extends RestDataManager{
});
}
+ removeFile(resultID, fileID, token) {
+ RestAPI.delete(this.makeURL(this.url + '/' + resultID + '/file/' + fileID), token).then(response => {
+ // reload result
+ AppDispatcher.dispatch({
+ type: 'results/start-load',
+ data: resultID,
+ token: token,
+ });
+
+ // update files
+ AppDispatcher.dispatch({
+ type: 'files/removed',
+ data: fileID,
+ token: token,
+ });
+ });
+ }
}
export default new ResultsDataManager();
\ No newline at end of file
diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js
index 01045fe..5397bb5 100644
--- a/src/scenario/scenario.js
+++ b/src/scenario/scenario.js
@@ -114,9 +114,11 @@ class Scenario extends React.Component {
editResultsModal: prevState.editResultsModal || false,
modalResultsData: {},
- modalResultsIndex: 0,
+ modalResultsIndex: prevState.modalResultsIndex,
newResultModal: false,
- filesToDownload: [],
+ filesToDownload: prevState.filesToDownload,
+ zipfiles: prevState.zipfiles || false,
+ resultNodl: prevState.resultNodl,
editOutputSignalsModal: prevState.editOutputSignalsModal || false,
editInputSignalsModal: prevState.editInputSignalsModal || false,
@@ -159,25 +161,28 @@ class Scenario extends React.Component {
componentDidUpdate(prevProps, prevState) {
// check whether file data has been loaded
- if (this.state.filesToDownload.length > 0 ) {
- if (this.state.filesToDownload.length === 1) {
- 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 several files
- 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);
- });
- zip.generateAsync({type: "blob"}).then(function(content) {
- saveAs(content, "results.zip");
- });
- this.setState({ filesToDownload: [] });
+ 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 + "_" + Date.now();
+ zip.generateAsync({type: "blob"}).then(function(content) {
+ saveAs(content, zipname);
+ });
+ this.setState({ filesToDownload: [] });
+ }
}
}
}
@@ -637,21 +642,19 @@ class Scenario extends React.Component {
closeEditResultsModal() {
this.setState({ editResultsModal: false });
-
- AppDispatcher.dispatch({
- type: 'results/start-load',
- token: this.state.sessionToken,
- param: '?scenarioID=' + this.state.scenario.id
- })
}
downloadResultData(param) {
let toDownload = [];
+ let zip = false;
- if (typeof(param) === 'object') {
+ if (typeof(param) === 'object') { // download all files
toDownload = param.resultFileIDs;
- } else {
+ 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 => {
@@ -661,8 +664,6 @@ class Scenario extends React.Component {
token: this.state.sessionToken
});
});
-
- this.setState({ filesToDownload: toDownload });
}
closeDeleteResultsModal(confirmDelete) {
@@ -760,7 +761,7 @@ class Scenario extends React.Component {
editButton
downloadAllButton
deleteButton
- onEdit={index => this.setState({ editResultsModal: true, modalResultsData: this.state.results[index], modalResultsIndex: index })}
+ 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 })}
/>
@@ -770,7 +771,8 @@ class Scenario extends React.Component {
sessionToken={this.state.sessionToken}
show={this.state.editResultsModal}
files={this.state.files}
- result={this.state.modalResultsData}
+ results={this.state.results}
+ resultId={this.state.modalResultsIndex}
scenarioID={this.state.scenario.id}
onClose={this.closeEditResultsModal.bind(this)} />
this.closeDeleteResultsModal(e)} />