Commit 8066cbb1 authored by Sonja Happ's avatar Sonja Happ

merge example-dashboard

parents cfb6cae6 95c7a865
This diff is collapsed.
......@@ -3,51 +3,54 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.19",
"@fortawesome/free-solid-svg-icons": "^5.9.0",
"@fortawesome/react-fontawesome": "^0.1.4",
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8",
"babel-runtime": "^6.26.0",
"bootstrap": "^4.3.1",
"bootstrap": "^4.4.1",
"classnames": "^2.2.6",
"d3-array": "^2.2.0",
"d3-array": "^2.4.0",
"d3-axis": "^1.0.12",
"d3-scale": "^3.0.0",
"d3-scale-chromatic": "^1.3.3",
"d3-selection": "^1.4.0",
"d3-shape": "^1.3.5",
"d3-time-format": "^2.1.3",
"d3-scale": "^3.2.1",
"d3-scale-chromatic": "^1.5.0",
"d3-selection": "^1.4.1",
"d3-shape": "^1.3.7",
"d3-time-format": "^2.2.3",
"es6-promise": "^4.2.8",
"fibers": "^4.0.2",
"file-saver": "^2.0.2",
"flux": "^3.1.3",
"frontend-collective-react-dnd-scrollzone": "^1.0.2",
"gaugeJS": "^1.3.7",
"handlebars": "^4.5.1",
"handlebars": "^4.7.1",
"immutable": "^4.0.0-rc.12",
"jquery": "^3.4.1",
"jszip": "^3.2.2",
"libcimsvg": "git+https://git.rwth-aachen.de/acs/public/cim/pintura-npm-package.git",
"lodash": "^4.17.15",
"node-sass": "^4.13.0",
"prop-types": "^15.7.2",
"rc-slider": "^8.6.13",
"react": "^16.8.6",
"react-bootstrap": "^1.0.0-beta.9",
"rc-slider": "^8.7.1",
"react": "^16.12.0",
"react-bootstrap": "^1.0.0-beta.16",
"react-contexify": "^4.1.1",
"react-d3": "^0.4.0",
"react-dnd": "^9.3.2",
"react-dnd-html5-backend": "^9.3.2",
"react-dom": "^16.8.6",
"react-dnd": "^9.5.1",
"react-dnd-html5-backend": "^9.5.1",
"react-dom": "^16.12.0",
"react-fullscreenable": "^2.5.1-0",
"react-grid-system": "^4.4.10",
"react-grid-system": "^4.4.11",
"react-json-view": "^1.19.1",
"react-notification-system": "^0.2.17",
"react-rnd": "^10.0.0",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-scripts": "^3.0.1",
"react-sortable-tree": "^2.6.2",
"react-svg-pan-zoom": "^3.1.0",
"superagent": "^5.1.0",
"typescript": "^3.5.3",
"react-rnd": "^10.1.4",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.3.0",
"react-sortable-tree": "^2.7.1",
"react-svg-pan-zoom": "^3.8.0",
"sass": "^1.24.4",
"superagent": "^5.2.1",
"typescript": "^3.7.4",
"validator": "^11.1.0"
},
"devDependencies": {
......
......@@ -31,7 +31,7 @@ import { Hidden } from 'react-grid-system'
import AppDispatcher from './common/app-dispatcher';
import ScenarioStore from './scenario/scenario-store';
import SimulatorStore from './simulator/simulator-store';
import UserStore from './user/user-store';
import LoginStore from './user/login-store';
import NotificationsDataManager from './common/data-managers/notifications-data-manager';
import Home from './common/home';
......@@ -40,73 +40,60 @@ import Footer from './common/footer';
import SidebarMenu from './common/menu-sidebar';
import HeaderMenu from './common/header-menu';
//import Projects from './project/projects';
//import Project from './project/project';
import Simulators from './simulator/simulators';
import Dashboard from './dashboard/dashboard';
//import Simulations from './simulation/simulations';
//import Simulation from './simulation/simulation';
import Scenarios from './scenario/scenarios';
import Scenario from './scenario/scenario';
import SimulationModel from './simulationmodel/simulation-model';
import Users from './user/users';
import User from './user/user';
import FluxContainerConverter from "./common/FluxContainerConverter";
import './styles/app.css';
class App extends React.Component {
static getStores() {
return [ SimulatorStore, UserStore, ScenarioStore];
return [ SimulatorStore, LoginStore, ScenarioStore];
}
static calculateState(prevState) {
let currentUser = UserStore.getState().currentUser;
return {
simulators: SimulatorStore.getState(),
scenarios: ScenarioStore.getState(),
currentRole: currentUser ? currentUser.role : '',
currentUsername: currentUser ? currentUser.username: '',
currentUserID: UserStore.getState().userid,
token: UserStore.getState().token,
currentUser: LoginStore.getState().currentUser,
token: LoginStore.getState().token,
showSidebarMenu: false,
};
}
componentWillMount() {
// if token stored locally, request user
const token = localStorage.getItem('token');
const userid = localStorage.getItem('userid');
componentDidMount() {
NotificationsDataManager.setSystem(this.refs.notificationSystem);
// if token stored locally, request user
let token = localStorage.getItem("token");
let currentUser = JSON.parse(localStorage.getItem("currentUser"));
if (token != null && token !== '') {
// save token so we dont logout
this.setState({ token });
AppDispatcher.dispatch({
type: 'users/logged-in',
token: token,
userid: userid
currentUser: currentUser
});
}
}
componentDidMount() {
// load all simulators and scenarios to fetch data
AppDispatcher.dispatch({
type: 'simulators/start-load',
token: this.state.token
});
// AppDispatcher.dispatch({
// type: 'simulators/start-load',
// token: this.state.token
// });
//
// AppDispatcher.dispatch({
// type: 'scenarios/start-load',
// token: this.state.token
// });
AppDispatcher.dispatch({
type: 'scenarios/start-load',
token: this.state.token
});
NotificationsDataManager.setSystem(this.refs.notificationSystem);
}
showSidebarMenu = () => {
......@@ -130,7 +117,7 @@ class App extends React.Component {
*/}
<Hidden sm md lg xl>
<Col style={{ width: this.state.showSidebarMenu ? '280px' : '0px' }} className="sidenav">
<HeaderMenu onClose={this.hideSidebarMenu} currentRole={this.state.currentRole} />
<HeaderMenu onClose={this.hideSidebarMenu} currentRole={this.state.currentUser.role} />
</Col>
</Hidden>
......@@ -141,7 +128,7 @@ class App extends React.Component {
<div className={`app-body app-body-spacing`} >
<Col xs={false}>
<SidebarMenu currentRole={this.state.currentRole} />
<SidebarMenu currentRole={this.state.currentUser.role} />
</Col>
<div className={`app-content app-content-margin-left`}>
......@@ -171,5 +158,6 @@ class App extends React.Component {
//<Route exact path="/simulations" component={Simulations} />
//<Route path="/simulations/:simulation" component={Simulation} />
export default Container.create(FluxContainerConverter.convert(App));
let fluxContainerConverter = require('./common/FluxContainerConverter');
export default Container.create(fluxContainerConverter.convert(App));
//DragDropContext(HTML5Backend)(Container.create(App));
/// FluxContainerConverter.js
/// This is an ugly workaround found here https://github.com/facebook/flux/issues/351 to make Flux Containers work with ES6
export default {
module.exports = {
convert: function(containerClass) {
const tmp = containerClass;
containerClass = function(...args) {
......
......@@ -37,7 +37,7 @@ const REQUEST_TIMEOUT_NOTIFICATION = {
level: 'error'
};
// Check if the error was due to network failure, timeouts, etc.
// Check if the error was due to network failure, timeouts, etc.
// Can be used for the rest of requests
function isNetworkError(err) {
let result = false;
......@@ -45,7 +45,7 @@ function isNetworkError(err) {
// If not status nor response fields, it is a network error. TODO: Handle timeouts
if (err.status == null || err.response == null) {
result = true;
let notification = err.timeout? REQUEST_TIMEOUT_NOTIFICATION : SERVER_NOT_REACHABLE_NOTIFICATION;
NotificationsDataManager.addNotification(notification);
}
......@@ -79,7 +79,7 @@ class RestAPI {
if (token != null) {
req.set('Authorization', "Bearer " + token);
}
req.end(function (error, res) {
if (res == null || res.status !== 200) {
......
......@@ -68,7 +68,6 @@ class ArrayStore extends ReduceStore {
reduce(state, action) {
switch (action.type) {
case this.type + '/start-load':
if (Array.isArray(action.data)) {
action.data.forEach((id) => {
this.dataManager.load(id, action.token,action.param);
......@@ -87,17 +86,17 @@ class ArrayStore extends ReduceStore {
case this.type + '/load-error':
if (action.error && !action.error.handled && action.error.response) {
const USER_LOAD_ERROR_NOTIFICATION = {
title: 'Failed to load',
message: action.error.response.body.message,
level: 'error'
};
NotificationsDataManager.addNotification(USER_LOAD_ERROR_NOTIFICATION);
}
return super.reduce(state, action);
case this.type + '/start-add':
this.dataManager.add(action.data, action.token,action.param);
return state;
......@@ -106,7 +105,7 @@ class ArrayStore extends ReduceStore {
return this.updateElements(state, [action.data]);
case this.type + '/add-error':
return state;
......@@ -128,30 +127,17 @@ class ArrayStore extends ReduceStore {
level: 'error'
};
NotificationsDataManager.addNotification(USER_REMOVE_ERROR_NOTIFICATION);
}
return super.reduce(state, action);
case this.type + '/start-edit':
this.dataManager.update(action.data, action.token,action.param);
return state;
case this.type + '/start-own-edit':
case this.type + '/start-edit':
this.dataManager.update(action.data, action.token,action.param);
return state;
case this.type + '/edited':
return this.updateElements(state, [action.data]);
case this.type + '/confirm-pw-doesnt-match':
const USER_PW_ERROR_NOTIFICATION = {
title: 'The new password does not match',
message: 'Try again',
level: 'error'
};
NotificationsDataManager.addNotification(USER_PW_ERROR_NOTIFICATION);
return state;
case this.type + '/edit-error':
return state;
......
......@@ -65,10 +65,10 @@ class RestDataManager {
}
else{
if(id != null){
return this.makeURL(this.url + '/' + id + '?' + param);
return this.makeURL(this.url + '/' + id + param);
}
else {
return this.makeURL(this.url + '?' + param);
return this.makeURL(this.url + param)
}
}
case 'remove/update':
......@@ -76,7 +76,7 @@ class RestDataManager {
return this.makeURL(this.url + '/' + object.id);
}
else{
return this.makeURL(this.url + '/' + object.id + '?' + param);
return this.makeURL(this.url + '/' + object.id + param);
}
default:
console.log("something went wrong");
......
......@@ -36,8 +36,10 @@ class EditableHeader extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
this.setState({ title: nextProps.title });
static getDerivedStateFromProps(props, state){
return {
title: props.title
};
}
edit = () => {
......
......@@ -26,18 +26,15 @@ import React from 'react';
//import RestAPI from '../api/rest-api';
import config from '../config';
import UserStore from "../user/user-store";
import LoginStore from "../user/login-store";
class Home extends React.Component {
constructor(props) {
super(props);
let currentUser = UserStore.getState().currentUser;
this.state = {
currentRole: currentUser ? currentUser.role : '',
currentUsername: currentUser ? currentUser.username: '',
currentUserID: currentUser ? currentUser.id: 0,
token: UserStore.getState().token
currentUser: LoginStore.getState().currentUser,
token: LoginStore.getState().token
};
}
......@@ -48,12 +45,6 @@ class Home extends React.Component {
return '?';
}
componentDidMount() {
//RestAPI.get('/api/v1/counts').then(response => {
// this.setState({ counts: response });
//});
}
render() {
return (
<div className="home-container">
......@@ -64,7 +55,7 @@ class Home extends React.Component {
VILLASweb is a frontend for distributed real-time simulation hosted by <a href={"mailto:" + config.admin.mail}>{config.admin.name}</a>.
</p>
<p>
You are logged in as user <b>{this.state.currentUsername}</b> with <b>ID {this.state.currentUserID}</b> and role <b>{this.state.currentRole}</b>.
You are logged in as user <b>{this.state.currentUser.username}</b> with <b>ID {this.state.currentUser.id}</b> and role <b>{this.state.currentUser.role}</b>.
</p>
{/*
<p>
......
......@@ -32,7 +32,7 @@ class CustomTable extends Component {
this.activeInput = null;
this.state = {
rows: this.getRows(props),
rows: CustomTable.getRows(props),
editCell: [ -1, -1 ]
};
}
......@@ -45,7 +45,7 @@ class CustomTable extends Component {
this.setState({ editCell: [ column, row ]}); // x, y
}
addCell(data, index, child) {
static addCell(data, index, child) {
// add data to cell
let content = null;
......@@ -112,7 +112,7 @@ class CustomTable extends Component {
}
if (child.props.checkbox) {
const checkboxKey = this.props.checkboxKey;
const checkboxKey = child.props.checkboxKey;
cell.push(<FormCheck className="table-control-checkbox" inline checked={checkboxKey ? data[checkboxKey] : null} onChange={e => child.props.onChecked(index, e)} />);
}
......@@ -122,12 +122,12 @@ class CustomTable extends Component {
}
return cell;
}
} // addCell
componentWillReceiveProps(nextProps) {
const rows = this.getRows(nextProps);
static getDerivedStateFromProps(props, state){
const rows = CustomTable.getRows(props);
this.setState({ rows });
return { rows };
}
componentDidUpdate() {
......@@ -147,7 +147,7 @@ class CustomTable extends Component {
this.setState({ editCell: [ -1, -1 ] });
}
getRows(props) {
static getRows(props) {
if (props.data == null) {
return [];
}
......@@ -156,13 +156,13 @@ class CustomTable extends Component {
// check if multiple columns
if (Array.isArray(props.children) === false) {
// table only has a single column
return [ this.addCell(data, index, props.children) ];
return [ CustomTable.addCell(data, index, props.children) ];
}
const row = [];
for (let child of props.children) {
row.push(this.addCell(data, index, child));
row.push(CustomTable.addCell(data, index, child));
}
return row;
......
......@@ -22,6 +22,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';
import Icon from "../common/icon";
class DashboardButtonGroup extends React.Component {
render() {
......@@ -38,39 +39,39 @@ class DashboardButtonGroup extends React.Component {
if (this.props.editing) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onSave} style={buttonStyle}>
<span class="glyphicon glyphicon-floppy-disk"></span> Save
<Button key={key++} onClick={this.props.onSave} style={buttonStyle}>
<Icon icon="save" /> Save
</Button>,
<Button key={key++} bsStyle="info" onClick={this.props.onCancel} style={buttonStyle}>
<span class="glyphicon glyphicon-remove" ></span> Cancel
<Button key={key++} onClick={this.props.onCancel} style={buttonStyle}>
<Icon icon="times" /> Cancel
</Button>
);
} else {
if (this.props.fullscreen !== true) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onFullscreen} style={buttonStyle}>
<span className="glyphicon glyphicon-resize-full"></span> Fullscreen
<Button key={key++} onClick={this.props.onFullscreen} style={buttonStyle}>
<Icon icon="expand" /> Fullscreen
</Button>
);
}
if (this.props.paused) {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onUnpause} style={buttonStyle}>
<span className="glyphicon glyphicon-play"></span> Live
<Button key={key++} onClick={this.props.onUnpause} style={buttonStyle}>
<Icon icon="play" /> Live
</Button>
);
} else {
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onPause} style={buttonStyle}>
<span className="glyphicon glyphicon-pause"></span> Pause
<Button key={key++} onClick={this.props.onPause} style={buttonStyle}>
<Icon icon="pause" /> Pause
</Button>
);
}
buttons.push(
<Button key={key++} bsStyle="info" onClick={this.props.onEdit} style={buttonStyle}>
<span className="glyphicon glyphicon-pencil"></span> Pause
<Button key={key++} onClick={this.props.onEdit} style={buttonStyle}>
<Icon icon="pen" /> Edit
</Button>
);
}
......
This diff is collapsed.
......@@ -52,6 +52,7 @@ const dropzoneTarget = {
};
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
......@@ -61,6 +62,7 @@ function collect(connect, monitor) {
class Dropzone extends React.Component {
render() {
var toolboxClass = classNames({
'box-content': true,
'toolbox-dropzone': true,
......
......@@ -24,7 +24,7 @@ import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
import Dialog from '../common/dialogs/dialog';
class NewVisualzationDialog extends React.Component {
class NewDashboardDialog extends React.Component {
valid: false;
constructor(props) {
......@@ -84,4 +84,4 @@ class NewVisualzationDialog extends React.Component {
}
}
export default NewVisualzationDialog;
export default NewDashboardDialog;
......@@ -48,7 +48,7 @@ class WidgetArea extends React.Component {
}
render() {
const maxHeight = Object.values(this.props.widgets).reduce((currentHeight, widget) => {
const maxHeight = Object.values(this.props.widgets).reduce((currentHeight, widget) => {
const absolutHeight = widget.y + widget.height;
return absolutHeight > currentHeight ? absolutHeight : currentHeight;
......@@ -67,7 +67,7 @@ WidgetArea.propTypes = {
editing: PropTypes.bool,
grid: PropTypes.number,
defaultSimulationModel: PropTypes.string,
widgets: PropTypes.object,
//widgets: PropTypes.array,
onWidgetAdded: PropTypes.func
};
......
......@@ -21,7 +21,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { contextMenu, Item, Separator } from 'react-contexify';
import { Menu, Item, Separator, MenuProvider } from 'react-contexify';
import Widget from '../widget/widget';
class WidgetContextMenu extends React.Component {
editWidget = event => {
......@@ -92,8 +93,8 @@ class WidgetContextMenu extends React.Component {
render() {
const isLocked = this.props.widget.locked;
return <contextMenu id={'widgetMenu'+ this.props.index}>
const ContextMenu = () => (
<Menu id={'widgetMenu'+ this.props.index}>
<Item disabled={isLocked} onClick={this.editWidget}>Edit</Item>
<Item disabled={isLocked} onClick={this.deleteWidget}>Delete</Item>
......@@ -108,14 +109,31 @@ class WidgetContextMenu extends React.Component {
<Item disabled={isLocked} onClick={this.lockWidget}>Lock</Item>
<Item disabled={isLocked === false} onClick={this.unlockWidget}>Unlock</Item>
</contextMenu>;
</Menu>
);
return <div>
<MenuProvider id={'widgetMenu'+ this.props.index} style={{ border: '1px solid purple', display: 'inline-block' }}>
<Widget
data={this.props.widget}
simulation={this.props.simulation}
onWidgetChange={this.props.onWidgetChange}
onWidgetStatusChange={this.props.onWidgetStatusChange}
editing={this.props.editing}
index={this.props.index}
grid={this.props.grid}
paused={this.props.paused}
/>
</MenuProvider>
<ContextMenu />
</div>
}
}
WidgetContextMenu.propTypes = {
index: PropTypes.number.isRequired,
widget: PropTypes.object.isRequired,
onEdit: PropTypes.func,
onEdit: PropTypes.func.isRequired,
onDelete: PropTypes.func,
onChange: PropTypes.func
};
......
......@@ -24,130 +24,130 @@ import { Container } from 'flux/utils';
import { FormGroup, FormControl, FormLabel, Button, ProgressBar, Col } from 'react-bootstrap';
import FileStore from './file-store';