/** * This file is part of VILLASweb. * * VILLASweb is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VILLASweb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VILLASweb. If not, see . ******************************************************************************/ import React, {Component} from 'react'; import { Container } from 'flux/utils'; import Fullscreenable from 'react-fullscreenable'; import classNames from 'classnames'; import Widget from '../widget/widget'; import EditWidget from '../widget/edit-widget/edit-widget'; import WidgetContextMenu from '../widget/widget-context-menu'; import WidgetToolbox from '../widget/widget-toolbox'; import WidgetArea from '../widget/widget-area'; import DashboardButtonGroup from './dashboard-button-group'; import LoginStore from '../user/login-store'; import DashboardStore from './dashboard-store'; import SignalStore from '../signal/signal-store' import FileStore from '../file/file-store'; import WidgetStore from '../widget/widget-store'; import AppDispatcher from '../common/app-dispatcher'; import 'react-contexify/dist/ReactContexify.min.css'; class Dashboard extends Component { static lastWidgetKey = 0; static getStores() { return [ DashboardStore, FileStore, LoginStore, WidgetStore, SignalStore ]; } static calculateState(prevState, props) { if (prevState == null) { prevState = {}; } const sessionToken = LoginStore.getState().token; let dashboard = DashboardStore.getState().find(d => d.id === parseInt(props.match.params.dashboard, 10)); if (dashboard == null){ AppDispatcher.dispatch({ type: 'dashboards/start-load', data: props.match.params.dashboard, token: sessionToken }); } // obtain all widgets of a dashboard let widgets = WidgetStore.getState().filter(w => w.dashboardID === parseInt(props.match.params.dashboard, 10)); // compute max y coordinate let maxHeight = null; maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => { let thisWidget = widgets[widgetKey]; let thisWidgetHeight = thisWidget.y + thisWidget.height; return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar; }, 0); // TODO filter signals to the ones belonging to the scenario at hand! let signals = SignalStore.getState(); // get files of all widgets 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); } } } // TODO create list of infrastructure components in use return { dashboard, widgets, signals, sessionToken: sessionToken, files: files, editing: prevState.editing || false, paused: prevState.paused || false, editModal: false, modalData: null, modalIndex: null, widgetChangeData: [], widgetAddData:prevState.widgetAddData || [], maxWidgetHeight: maxHeight || null, dropZoneHeight: maxHeight +80 || null, }; } static getNewWidgetKey() { const widgetKey = this.lastWidgetKey; this.lastWidgetKey++; return widgetKey; } //!!!won't work anymore componentDidMount() { // load widgets of dashboard AppDispatcher.dispatch({ type: 'widgets/start-load', token: this.state.sessionToken, param: '?dashboardID=' + this.state.dashboard.id }); // TODO open websockets in componentDidMount // TODO close websockets in componentWillUnmount } handleKeydown(e) { switch (e.key) { case ' ': case 'p': this.setState({ paused: !this.state.paused }); break; case 'e': this.setState({ editing: !this.state.editing }); break; case 'f': this.props.toggleFullscreen(); break; default: } } /* * Adapt the area's height with the position of the new widget. * Return true if the height increased, otherwise false. */ increaseHeightWithWidget(widget) { let increased = false; let thisWidgetHeight = widget.y + widget.height; if (thisWidgetHeight > this.state.maxWidgetHeight) { increased = true; this.setState({ maxWidgetHeight: thisWidgetHeight, dropZoneHeight: thisWidgetHeight + 40 }); } return increased; } transformToWidgetsList(widgets) { return Object.keys(widgets).map( (key) => widgets[key]); } handleDrop(widget) { widget.dashboardID = this.state.dashboard.id; let tempChanges = this.state.widgetAddData; tempChanges.push(widget); this.setState({ widgetAddData: tempChanges}) AppDispatcher.dispatch({ type: 'widgets/start-add', token: this.state.sessionToken, data: widget }); }; widgetStatusChange(updated_widget, key) { // Widget changed internally, make changes effective then save them this.widgetChange(updated_widget, key, this.saveChanges); } widgetChange(widget, index, callback = null){ let temp = this.state.widgetChangeData; temp.push(widget); this.setState({widgetChangeData: temp}); } /* * Set the initial height state based on the existing widgets */ computeHeightWithWidgets(widgets) { // Compute max height from widgets let maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => { let thisWidget = widgets[widgetKey]; let thisWidgetHeight = thisWidget.y + thisWidget.height; return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar; }, 0); this.setState({ maxWidgetHeight: maxHeight, dropZoneHeight: maxHeight + 80 }); } editWidget(widget, index){ this.setState({ editModal: true, modalData: widget, modalIndex: index }); }; closeEdit(data){ if (data == null) { AppDispatcher.dispatch({ type: 'widgets/start-load', token: this.state.sessionToken, param: '?dashboardID=1' }); this.setState({ editModal: false }); return; } AppDispatcher.dispatch({ type: 'widgets/start-edit', token: this.state.sessionToken, data: data }); this.setState({ editModal: false }); }; deleteWidget(widget, index) { AppDispatcher.dispatch({ type: 'widgets/start-remove', data: widget, token: this.state.sessionToken }); }; startEditing(){ this.state.widgets.forEach( widget => { if(widget.type === 'Slider' || widget.type === 'NumberInput' || widget.type === 'Button'){ console.log("we should move in here"); AppDispatcher.dispatch({ type: 'widgets/start-edit', token: this.state.sessionToken, data: widget }); } }); this.setState({ editing: true }); }; saveEditing() { // Provide the callback so it can be called when state change is applied // TODO: Check if callback is needed AppDispatcher.dispatch({ type: 'dashboards/start-edit', data: this.state.dashboard, token: this.state.sessionToken }); this.state.widgetChangeData.forEach( widget => { AppDispatcher.dispatch({ type: 'widgets/start-edit', token: this.state.sessionToken, data: widget }); }); this.setState({ editing: false, widgetChangeData: [], widgetAddData: [] }); }; saveChanges() { // Transform to a list const dashboard = Object.assign({}, this.state.dashboard.toJS(), { widgets: this.transformToWidgetsList(this.state.widgets) }); AppDispatcher.dispatch({ type: 'dashboards/start-edit', data: dashboard, token: this.state.sessionToken }); } cancelEditing() { //raw widget has no id -> cannot be deleted in its original form let temp = []; this.state.widgetAddData.forEach(rawWidget => { this.state.widgets.forEach(widget => { if(widget.y === rawWidget.y && widget.x === rawWidget.x && widget.type === rawWidget.type){ temp.push(widget); } }) }) temp.forEach( widget => { AppDispatcher.dispatch({ type: 'widgets/start-remove', data: widget, token: this.state.sessionToken }); }); AppDispatcher.dispatch({ type: 'widgets/start-load', token: this.state.sessionToken, param: '?dashboardID=1' }); this.setState({ editing: false, widgetChangeData: [], widgetAddData: []}); }; setGrid(value) { let dashboard = this.state.dashboard; dashboard.grid = value; this.setState({ dashboard }); this.forceUpdate(); }; pauseData(){ this.setState({ paused: true }); }; unpauseData() { this.setState({ paused: false }); }; render() { const grid = this.state.dashboard.grid; const boxClasses = classNames('section', 'box', { 'fullscreen-padding': this.props.isFullscreen }); let draggable = this.state.editing; return
{this.state.dashboard.name}
e.preventDefault() }> {this.state.editing && } {!draggable?( {this.state.widgets != null && Object.keys(this.state.widgets).map(widgetKey => ( ))} ) : ( {this.state.widgets != null && Object.keys(this.state.widgets).map(widgetKey => ( ))} )}
; } } let fluxContainerConverter = require('../common/FluxContainerConverter'); export default Fullscreenable()(Container.create(fluxContainerConverter.convert(Dashboard), { withProps: true }));