diff --git a/src/__tests__/widget/edit-widget-control-creator.js b/src/__tests__/widget/edit-widget-control-creator.js index b338f7c..73efcd9 100644 --- a/src/__tests__/widget/edit-widget-control-creator.js +++ b/src/__tests__/widget/edit-widget-control-creator.js @@ -5,7 +5,7 @@ import createControls from '../../widget/edit-widget/edit-widget-control-creator import EditWidgetTextControl from '../../widget/edit-widget/edit-widget-text-control'; import EditWidgetColorControl from '../../widget/edit-widget/edit-widget-color-control'; import EditWidgetTimeControl from '../../widget/edit-widget/edit-widget-time-control'; -import EditImageWidgetControl from '../../widget/edit-widget/edit-widget-image-control'; +import EditFileWidgetControl from '../../widget/edit-widget/edit-widget-file-control'; import EditWidgetSignalControl from '../../widget/edit-widget/edit-widget-signal-control'; import EditWidgetSignalsControl from '../../widget/edit-widget/edit-widget-signals-control'; import EditWidgetOrientation from '../../widget/edit-widget/edit-widget-orientation'; @@ -28,7 +28,7 @@ describe('edit widget control creator', () => { { args: { widgetType: 'Value' }, result: { controlNumber: 5, controlTypes: [EditWidgetTextControl, EditWidgetSignalControl, EditWidgetTextSizeControl, EditWidgetCheckboxControl] } }, { args: { widgetType: 'Plot' }, result: { controlNumber: 5, controlTypes: [EditWidgetTimeControl, EditWidgetSignalsControl, EditWidgetTextControl, EditWidgetMinMaxControl] } }, { args: { widgetType: 'Table' }, result: { controlNumber: 2, controlTypes: [EditWidgetCheckboxControl] } }, - { args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditImageWidgetControl, EditWidgetAspectControl] } }, + { args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditFileWidgetControl, EditWidgetAspectControl] } }, { args: { widgetType: 'Gauge' }, result: { controlNumber: 6, controlTypes: [EditWidgetTextControl, EditWidgetSignalControl, EditWidgetCheckboxControl, EditWidgetColorZonesControl, EditWidgetMinMaxControl] } }, { args: { widgetType: 'PlotTable' }, result: { controlNumber: 5, controlTypes: [EditWidgetSignalsControl, EditWidgetTextControl, EditWidgetTimeControl, EditWidgetMinMaxControl] } }, { args: { widgetType: 'Slider' }, result: { controlNumber: 9, controlTypes: [EditWidgetTextControl, EditWidgetOrientation, EditWidgetSignalControl, EditWidgetCheckboxControl, EditWidgetCheckboxControl, EditWidgetMinMaxControl, EditWidgetNumberControl, EditWidgetNumberControl] } }, diff --git a/src/common/api/rest-api.js b/src/common/api/rest-api.js index 53a6a16..3bb18d1 100644 --- a/src/common/api/rest-api.js +++ b/src/common/api/rest-api.js @@ -123,9 +123,9 @@ class RestAPI { }); } - upload(url, data, token, progressCallback, objectType, objectID) { + upload(url, data, token, progressCallback, scenarioID) { return new Promise(function (resolve, reject) { - const req = request.post(url + "?objectType=" + objectType + "&objectID=" + objectID).send(data); //.on('progress', progressCallback); + const req = request.post(url + "?scenarioID=" + scenarioID).send(data).on('progress', progressCallback); if (token != null) { req.set('Authorization', "Bearer " + token); diff --git a/src/componentconfig/configs-data-manager.js b/src/componentconfig/configs-data-manager.js index 64245a8..426c4a6 100644 --- a/src/componentconfig/configs-data-manager.js +++ b/src/componentconfig/configs-data-manager.js @@ -60,7 +60,7 @@ class ConfigDataManager extends RestDataManager { loadFiles(token, configs){ for (let config of configs) { // request files of config - RestAPI.get(this.makeURL('/files?objectType=config&objectID=' + config.id), token).then(response => { + RestAPI.get(this.makeURL('/files?scenarioID' + config.scenarioID), token).then(response => { AppDispatcher.dispatch({ type: 'files/loaded', data: response.files diff --git a/src/componentconfig/edit-config.js b/src/componentconfig/edit-config.js index e629347..a69159d 100644 --- a/src/componentconfig/edit-config.js +++ b/src/componentconfig/edit-config.js @@ -32,8 +32,8 @@ class EditConfigDialog extends React.Component { name: '', icID: '', configuration: null, - startParameters: {}, - selectedFileID:0 + startParameters: this.props.config.startParameters, + selectedFileID: this.props.config.selectedFileID }; } @@ -49,7 +49,7 @@ class EditConfigDialog extends React.Component { if (this.state.icID !== '' && this.props.config.icID !== parseInt(this.state.icID)) { data.icID = parseInt(this.state.icID, 10); } - if(this.state.startParameters !== {} && this.props.config.startParameters !== this.state.startParameters){ + if(this.state.startParameters !== {} && JSON.stringify(this.props.config.startParameters) !== JSON.stringify(this.state.startParameters)){ data.startParameters = this.state.startParameters; } if (parseInt(this.state.selectedFileID, 10) !== 0 && @@ -79,9 +79,9 @@ class EditConfigDialog extends React.Component { this.valid = this.isValid() } - handleSelectedFileChange(newFileID){ - console.log("Config file change to: ", newFileID); - this.setState({selectedFileID: newFileID}) + handleSelectedFileChange(event){ + //console.log("Config file change to: ", event.target.value); + this.setState({selectedFileID: event.target.value}) this.valid = this.isValid() } @@ -121,11 +121,19 @@ class EditConfigDialog extends React.Component { - this.handleSelectedFileChange(e)} value={this.state.selectedFileID} objectID={this.props.config.id}/> + this.handleSelectedFileChange(e)} + files={this.props.files} + value={this.state.selectedFileID} + scenarioID={this.props.config.scenarioID} + sessionToken={this.props.sessionToken} + /> Start Parameters - this.handleParameterChange(data)} /> + this.handleParameterChange(data)} /> diff --git a/src/dashboard/dashboard.js b/src/dashboard/dashboard.js index 3f38e30..fb00237 100644 --- a/src/dashboard/dashboard.js +++ b/src/dashboard/dashboard.js @@ -75,8 +75,10 @@ class Dashboard extends Component { // filter component configurations to the ones that belong to this scenario let configs = [] + let files = [] if (dashboard !== null) { configs = ConfigStore.getState().filter(config => config.scenarioID === dashboard.scenarioID); + files = FileStore.getState().filter(file => file.scenarioID === dashboard.scenarioID); } // filter signals to the ones belonging to the scenario at hand @@ -91,18 +93,6 @@ class Dashboard extends Component { } } - // filter files to the ones associated with a widget of this dashboard - let allFiles = FileStore.getState(); - let files = []; - let file, widget; - for (file of allFiles){ - for (widget of widgets){ - if (file.widgetID === widget.id){ - files.push(file); - } - } - } - // filter ICs to the ones used by this scenario let ics = [] if (configs.length > 0){ @@ -277,8 +267,7 @@ class Dashboard extends Component { type: 'files/start-upload', data: data, token: this.state.sessionToken, - objectType: "widget", - objectID: widget.id, + scenarioID: this.state.dashboard.scenarioID, }); } diff --git a/src/file/file-store.js b/src/file/file-store.js index c5c87b9..ae6779d 100644 --- a/src/file/file-store.js +++ b/src/file/file-store.js @@ -54,7 +54,7 @@ class FileStore extends ArrayStore { return state case 'files/start-upload': - FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.objectType, action.objectID); + FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.scenarioID); return state; case 'files/uploaded': diff --git a/src/file/files-data-manager.js b/src/file/files-data-manager.js index 1a61033..cc5cf4b 100644 --- a/src/file/files-data-manager.js +++ b/src/file/files-data-manager.js @@ -24,8 +24,8 @@ class FilesDataManager extends RestDataManager { super('file', '/files'); } - upload(file, token = null, progressCallback = null, finishedCallback = null, objectType, objectID) { - RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, objectType, objectID).then(response => { + upload(file, token = null, progressCallback = null, finishedCallback = null, scenarioID) { + RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, scenarioID).then(response => { AppDispatcher.dispatch({ type: 'files/uploaded', @@ -34,13 +34,13 @@ class FilesDataManager extends RestDataManager { // Trigger a files reload AppDispatcher.dispatch({ type: 'files/start-load', - param: '?objectType=' + objectType + '&objectID=' + objectID, + param: '?scenarioID=' + scenarioID, token: token }); - /*if (finishedCallback) { - finishedCallback(); - }*/ + if (finishedCallback) { + finishedCallback(response.file.id); + } }).catch(error => { AppDispatcher.dispatch({ type: 'files/upload-error', diff --git a/src/file/select-file.js b/src/file/select-file.js index b626ee3..132caf9 100644 --- a/src/file/select-file.js +++ b/src/file/select-file.js @@ -16,58 +16,20 @@ ******************************************************************************/ import React from 'react'; -import { Container } from 'flux/utils'; -import { FormGroup, FormControl, FormLabel, Button, Col } from 'react-bootstrap'; - -import FileStore from './file-store'; -import LoginStore from '../user/login-store'; - +import { FormGroup, FormControl, FormLabel, Button, Col, ProgressBar } from 'react-bootstrap'; import AppDispatcher from '../common/app-dispatcher'; -import Icon from "../common/icon"; class SelectFile extends React.Component { - static getStores() { - return [ FileStore, LoginStore ]; + + constructor() { + super(); + + this.state = { + uploadFile: null, + uploadProgress: 0 + } } - - static calculateState(prevState, props) { - - let files = FileStore.getState().filter((file) => { - return (file.configID === props.objectID) - }); - - console.log("props.objectID=", props.objectID) - - return { - files: files, - sessionToken: LoginStore.getState().token, - selectedFile: '', - uploadFile: null, - uploadProgress: 0 - }; - } - - /*componentDidMount() { - AppDispatcher.dispatch({ - type: 'files/start-load', - token: this.state.sessionToken - }); - }*/ - - static getDerivedStateFromProps(props, state){ - - - } - - handleChange(event) { - - // send file ID to callback - if (this.props.onChange != null) { - this.props.onChange(event.target.value); - } - }; - selectUploadFile(event) { this.setState({ uploadFile: event.target.files[0] }); }; @@ -80,40 +42,46 @@ class SelectFile extends React.Component { AppDispatcher.dispatch({ type: 'files/start-upload', data: formData, - token: this.state.sessionToken, - //progressCallback: this.updateUploadProgress, - //finishedCallback: this.clearProgress, - objectType: this.props.type, - objectID: this.props.objectID, + token: this.props.sessionToken, + progressCallback: this.updateUploadProgress, + finishedCallback: this.clearProgress, + scenarioID: this.props.scenarioID, }); + + // TODO make sure that edit config dialog remains open after clicking "Upload" button }; - updateUploadProgress = (event) => {// TODO: this callback does not work properly (access to setState) + updateUploadProgress = (event) => { this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) }); }; - 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); + clearProgress = (newFileID) => { + /*if (this.props.onChange != null) { + let event = {} + event["target"] = {} + event.target["value"] = newFileID + this.props.onChange(event); } this.setState({ uploadProgress: 0 }); + */ + }; render() { let fileOptions; - if (this.state.files.length > 0){ - fileOptions = this.state.files.map(f => + if (this.props.files.length > 0){ + fileOptions = this.props.files.map(f => ); } else { - fileOptions = + fileOptions = } - /*const progressBarStyle = { + const progressBarStyle = { marginLeft: '100px', - marginTop: '-25px' - };*/ + marginTop: '-40px' + }; return
@@ -122,29 +90,44 @@ class SelectFile extends React.Component { - this.handleChange(event)}> + this.props.onChange(event)}> {fileOptions} - - this.selectUploadFile(event)} /> + + this.selectUploadFile(event)} /> - - - {/* - + + + - */} +
; } } -let fluxContainerConverter = require('../common/FluxContainerConverter'); -export default Container.create(fluxContainerConverter.convert(SelectFile), { withProps: true }); +export default SelectFile; diff --git a/src/img/datamodel.png b/src/img/datamodel.png index e59cbc2..f98745f 100644 Binary files a/src/img/datamodel.png and b/src/img/datamodel.png differ diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index 2f838ab..0e4b9ed 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -67,8 +67,11 @@ class Scenario extends React.Component { // obtain all component configurations of a scenario let configs = ConfigStore.getState().filter(config => config.scenarioID === parseInt(props.match.params.scenario, 10)); + // obtain all files of a scenario + let files = FileStore.getState().filter(file => file.scenarioID === parseInt(props.match.params.scenario, 10)); + let signals = SignalStore.getState(); - let files = FileStore.getState(); + return { @@ -131,13 +134,6 @@ class Scenario extends React.Component { token: this.state.sessionToken }); - this.setState({ scenario: {} }, () => { - AppDispatcher.dispatch({ - type: 'scenarios/start-load', - data: this.props.match.params.scenario, - token: this.state.sessionToken - }); - }); } closeEditConfigModal(data){ @@ -467,7 +463,15 @@ class Scenario extends React.Component {
- this.closeEditConfigModal(data)} config={this.state.modalConfigData} ics={this.state.ics} /> + this.closeEditConfigModal(data)} + config={this.state.modalConfigData} + ics={this.state.ics} + files={this.state.files} + sessionToken={this.state.sessionToken} + /> + this.importConfig(data)} ics={this.state.ics} /> this.closeDeleteConfigModal(c)} /> diff --git a/src/scenario/scenarios-data-manager.js b/src/scenario/scenarios-data-manager.js index a59d9a4..3f06f05 100644 --- a/src/scenario/scenarios-data-manager.js +++ b/src/scenario/scenarios-data-manager.js @@ -44,7 +44,11 @@ class ScenariosDataManager extends RestDataManager { param: '?scenarioID=' + scenario.id }); - // TODO add dispatch for files + AppDispatcher.dispatch({ + type: 'files/start-load', + token: token, + param: '?scenarioID='+scenario.id, + }); } } } diff --git a/src/widget/edit-widget/edit-widget-control-creator.js b/src/widget/edit-widget/edit-widget-control-creator.js index 3133d29..78bff0c 100644 --- a/src/widget/edit-widget/edit-widget-control-creator.js +++ b/src/widget/edit-widget/edit-widget-control-creator.js @@ -21,7 +21,7 @@ import EditWidgetTextControl from './edit-widget-text-control'; import EditWidgetNumberControl from './edit-widget-number-control'; import EditWidgetColorControl from './edit-widget-color-control'; import EditWidgetTimeControl from './edit-widget-time-control'; -import EditImageWidgetControl from './edit-widget-image-control'; +import EditFileWidgetControl from './edit-widget-file-control'; import EditWidgetSignalControl from './edit-widget-signal-control'; import EditWidgetSignalsControl from './edit-widget-signals-control'; import EditWidgetOrientation from './edit-widget-orientation'; @@ -84,7 +84,7 @@ export default function CreateControls(widgetType = null, widget = null, session // Restrict to only image file types (MIME) //let imageControlFiles = files == null? [] : files.filter(file => file.type.includes('image')); DialogControls.push( - handleChange(e)} onUpload={(f,i) => onUpload(f,i)} />, + handleChange(e)} onUpload={(f,i) => onUpload(f,i)} />, handleChange(e)} /> ); break; @@ -149,7 +149,7 @@ export default function CreateControls(widgetType = null, widget = null, session // Restrict to only xml files (MIME) //let topologyControlFiles = files == null? [] : files.filter( file => file.type.includes('xml')); DialogControls.push( - handleChange(e)} /> + handleChange(e) } onUpload={(f,i) => onUpload(f,i)} /> ); break; diff --git a/src/widget/edit-widget/edit-widget-image-control.js b/src/widget/edit-widget/edit-widget-file-control.js similarity index 83% rename from src/widget/edit-widget/edit-widget-image-control.js rename to src/widget/edit-widget/edit-widget-file-control.js index de98757..298ba01 100644 --- a/src/widget/edit-widget/edit-widget-image-control.js +++ b/src/widget/edit-widget/edit-widget-file-control.js @@ -18,15 +18,14 @@ import React from 'react'; import {FormGroup, FormControl, FormLabel, Button, ProgressBar} from 'react-bootstrap'; -//import AppDispatcher from '../../common/app-dispatcher'; - -class EditImageWidgetControl extends React.Component { +class EditFileWidgetControl extends React.Component { constructor(props) { super(props); this.state = { widget: { }, + files: [], fileList: null, progress: 0 }; @@ -34,7 +33,8 @@ class EditImageWidgetControl extends React.Component { static getDerivedStateFromProps(props, state){ return { - widget: props.widget + widget: props.widget, + files: props.files.filter(file => file.type.includes(props.type)) }; } @@ -47,19 +47,8 @@ class EditImageWidgetControl extends React.Component { formData.append("file", this.state.fileList[key]); } } - - this.props.onUpload(formData,this.props.widget); - // upload files - /* AppDispatcher.dispatch({ - type: 'files/start-upload', - data: formData, - token: this.props.sessionToken, - progressCallback: this.uploadProgress, - finishedCallback: this.clearProgress, - objectType: "widget", - objectID: this.props.widget.id, - });*/ + this.props.onUpload(formData,this.props.widget); } uploadProgress = (e) => { @@ -83,11 +72,11 @@ class EditImageWidgetControl extends React.Component { } let fileOptions = []; - if (this.props.files.length > 0){ + if (this.state.files.length > 0){ fileOptions.push( ) - fileOptions.push(this.props.files.map((file, index) => ( + fileOptions.push(this.state.files.map((file, index) => ( ))) } else { @@ -114,4 +103,4 @@ class EditImageWidgetControl extends React.Component { } } -export default EditImageWidgetControl; +export default EditFileWidgetControl; diff --git a/src/widget/edit-widget/edit-widget.js b/src/widget/edit-widget/edit-widget.js index 30b9fd0..594acf0 100644 --- a/src/widget/edit-widget/edit-widget.js +++ b/src/widget/edit-widget/edit-widget.js @@ -16,10 +16,7 @@ ******************************************************************************/ import React from 'react'; -//import { FormGroup, FormControl, FormLabel } from 'react-bootstrap'; - import Dialog from '../../common/dialogs/dialog'; - import CreateControls from './edit-widget-control-creator'; class EditWidgetDialog extends React.Component { @@ -110,11 +107,11 @@ class EditWidgetDialog extends React.Component { // not a customProperty customProperty = false; } - + if (parts[1] === 'lockAspect') { //not a customProperty customProperty ? changeObject[parts[0]][parts[1]] = e.target.checked : changeObject[e.target.id] = e.target.checked; - + // correct image aspect if turned on if (e.target.checked && (this.state.temporal.customProperties.file !== -1)) { changeObject = this.assignAspectRatio(changeObject, this.state.temporal.customProperties.file); @@ -123,7 +120,7 @@ class EditWidgetDialog extends React.Component { customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value; - // get file and update size (if it's an image) + // get file and update size (if it's an image) if ((changeObject.customProperties.file !== -1)&&('lockAspect' in this.state.temporal && this.state.temporal.lockAspect)) { // TODO this if condition requires changes to work!!! changeObject = this.assignAspectRatio(changeObject, e.target.value); @@ -135,7 +132,7 @@ class EditWidgetDialog extends React.Component { }else if(parts[1] === 'orientation'){ customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value ; changeObject = this.setNewLockRestrictions(changeObject); - } + } else if (e.target.type === 'number') { customProperty ? changeObject[parts[0]][parts[1]] = Number(e.target.value) : changeObject[e.target.id] = Number(e.target.value); } else if(e.target.id === 'name'){ diff --git a/src/widget/widget-store.js b/src/widget/widget-store.js index f49ca27..ff1ad64 100644 --- a/src/widget/widget-store.js +++ b/src/widget/widget-store.js @@ -29,7 +29,8 @@ class WidgetStore extends ArrayStore { case 'widgets/loaded': - WidgetsDataManager.loadFiles(action.token, action.data); + //WidgetsDataManager.loadFiles(action.token, action.data); + // TODO make sure files of scenario are loaded return super.reduce(state, action); default: diff --git a/src/widget/widget.js b/src/widget/widget.js index 1327414..a2eb294 100644 --- a/src/widget/widget.js +++ b/src/widget/widget.js @@ -88,27 +88,6 @@ class Widget extends React.Component { }; } - componentDidMount() { - if (this.state.sessionToken == null) { - return; - } - - /*AppDispatcher.dispatch({ - type: 'files/start-load', - token: this.state.sessionToken, - param: '?objectID=1&objectType=widget' - });*/ - - // TODO no not load component congfigs here, since they are loaded via the scenario, pass them as props - /* - AppDispatcher.dispatch({ - type: 'configs/start-load', - token: this.state.sessionToken, - param: '?scenarioID=1' // TODO do not hardcode scenarioID! - }); - */ - } - inputDataChanged(widget, data) { // The following assumes that a widget modifies/ uses exactly one signal AppDispatcher.dispatch({ diff --git a/src/widget/widgets-data-manager.js b/src/widget/widgets-data-manager.js index c32417d..61d1cf0 100644 --- a/src/widget/widgets-data-manager.js +++ b/src/widget/widgets-data-manager.js @@ -17,8 +17,6 @@ import RestDataManager from '../common/data-managers/rest-data-manager'; -import RestAPI from "../common/api/rest-api"; -import AppDispatcher from "../common/app-dispatcher"; class WidgetsDataManager extends RestDataManager{ @@ -26,18 +24,6 @@ class WidgetsDataManager extends RestDataManager{ super('widget', '/widgets'); } - loadFiles(token, widgets){ - for (let widget of widgets) { - // request files of widget - RestAPI.get(this.makeURL('/files?objectType=widget&objectID=' + widget.id), token).then(response => { - AppDispatcher.dispatch({ - type: 'files/loaded', - data: response.files - }); - }); - } - } - } export default new WidgetsDataManager()