mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
78dd5f571c
5 changed files with 166 additions and 120 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,3 +17,4 @@ yarn-debug.log*
|
|||
yarn-error.log*
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
package-lock.json
|
||||
|
|
|
@ -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 <Dialog show={this.props.show}
|
||||
title={'Edit Result No. '+this.state.id}
|
||||
buttonTitle='Close'
|
||||
onClose={() => this.onClose()}
|
||||
blendOutCancel = {true}
|
||||
valid={true}
|
||||
size = 'lg'>
|
||||
title={'Edit Result No. ' + this.state.id}
|
||||
buttonTitle='Close'
|
||||
onClose={() => this.onClose()}
|
||||
blendOutCancel={true}
|
||||
valid={true}
|
||||
size='lg'>
|
||||
|
||||
|
||||
<div>
|
||||
<FormGroup as={Col} controlId='description'>
|
||||
<FormLabel column={false}>Description</FormLabel>
|
||||
<FormControl type='text' placeholder='Enter description' value={this.state.description} onChange={this.handleChange} />
|
||||
<FormControl.Feedback />
|
||||
|
||||
<Row style={{ float: 'center' }} >
|
||||
<Col xs="auto">
|
||||
<FormLabel>Description</FormLabel>
|
||||
</Col>
|
||||
<Col xs="auto">
|
||||
<FormControl type='text' placeholder={this.state.description} value={this.state.description} onChange={this.handleChange} />
|
||||
<FormControl.Feedback />
|
||||
</Col>
|
||||
<Col xs="auto">
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={() => this.submitDescription()}>
|
||||
Save
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
</FormGroup>
|
||||
|
||||
<Table data={this.state.files}>
|
||||
<TableColumn title='ID' dataKey='id'/>
|
||||
<TableColumn title='Name' dataKey='name'/>
|
||||
<TableColumn title='Size (bytes)' dataKey='size'/>
|
||||
<TableColumn title='Type' dataKey='type'/>
|
||||
<TableColumn
|
||||
title=''
|
||||
deleteButton
|
||||
onDelete={(index) => this.deleteFile(index)}
|
||||
/>
|
||||
<TableColumn title='ID' dataKey='id' />
|
||||
<TableColumn title='Name' dataKey='name' />
|
||||
<TableColumn title='Size (bytes)' dataKey='size' />
|
||||
<TableColumn title='Type' dataKey='type' />
|
||||
<TableColumn
|
||||
title=''
|
||||
deleteButton
|
||||
onDelete={(index) => this.deleteFile(index)}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
|
||||
<FormGroup controlId='resultfile'>
|
||||
<FormLabel>Add Result File</FormLabel>
|
||||
<FormControl type='file' onChange={(event) => this.selectUploadFile(event)} />
|
||||
<FormLabel>Add Result File</FormLabel>
|
||||
<FormControl type='file' onChange={(event) => this.selectUploadFile(event)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
<Button
|
||||
disabled={this.state.uploadFile === null}
|
||||
onClick={() => this.startFileUpload()}>
|
||||
Upload
|
||||
<FormGroup as={Col} >
|
||||
<Button
|
||||
disabled={this.state.uploadFile === null}
|
||||
onClick={() => this.startFileUpload()}>
|
||||
Upload
|
||||
</Button>
|
||||
</FormGroup>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
<ProgressBar
|
||||
striped={true}
|
||||
animated={true}
|
||||
now={this.state.uploadProgress}
|
||||
label={this.state.uploadProgress + '%'}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
<ProgressBar
|
||||
striped={true}
|
||||
animated={true}
|
||||
now={this.state.uploadProgress}
|
||||
label={this.state.uploadProgress + '%'}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
</div>
|
||||
</Dialog>;
|
||||
}
|
||||
|
|
|
@ -15,40 +15,14 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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();
|
|
@ -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)} />
|
||||
<DeleteDialog title="result" name={this.state.modalResultsData.id} show={this.state.deleteResultsModal} onClose={(e) => this.closeDeleteResultsModal(e)} />
|
||||
|
|
Loading…
Add table
Reference in a new issue