diff --git a/src/scenario/scenario-store.js b/src/scenario/scenario-store.js index 15a14f3..8164229 100644 --- a/src/scenario/scenario-store.js +++ b/src/scenario/scenario-store.js @@ -18,12 +18,28 @@ import ScenariosDataManager from './scenarios-data-manager'; import ArrayStore from '../common/array-store'; +import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; + class ScenarioStore extends ArrayStore{ constructor() { super('scenarios', ScenariosDataManager); } + // store function, called when calls to backend have returned + // save users after they are loaded ('getUsers' call) + storeUsers(state, action) { + let scenarioID = action.scenarioID; + state.forEach((element, index, array) => { + if (element.id === scenarioID) { + array[index]["users"] = action.users; + } + }) + this.__emitChange(); + return state; + + } + reduce(state, action) { switch (action.type) { @@ -57,6 +73,34 @@ class ScenarioStore extends ArrayStore{ return super.reduce(state, action); } + case 'scenarios/start-load-users': + this.dataManager.getUsers(action.token, action.data); + return super.reduce(state, action); + + case 'scenarios/users-loaded': + return this.storeUsers(state, action); + + case 'scenarios/add-user': + this.dataManager.addUser(action.token, action.data, action.username); + return super.reduce(state, action); + + case 'scenarios/remove-user': + this.dataManager.deleteUser(action.token, action.data, action.username) + return super.reduce(state, action); + + case 'scenarios/users-error': + if (action.error && !action.error.handled && action.error.response) { + + const SCENARIO_USERS_ERROR_NOTIFICATION = { + title: 'Failed to modify scenario users ', + message: action.error.response.body.message, + level: 'error' + }; + NotificationsDataManager.addNotification(SCENARIO_USERS_ERROR_NOTIFICATION); + + } + return super.reduce(state, action); + default: return super.reduce(state, action); } diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index 3e2d7cd..8d3495e 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -17,7 +17,8 @@ import React from 'react'; import { Container } from 'flux/utils'; -import { Button } from 'react-bootstrap'; +import { Button, InputGroup, FormControl } from 'react-bootstrap'; + import FileSaver from 'file-saver'; import ScenarioStore from './scenario-store'; @@ -77,7 +78,6 @@ class Scenario extends React.Component { let signals = SignalStore.getState(); - return { scenario, sessionToken, @@ -101,10 +101,14 @@ class Scenario extends React.Component { deleteDashboardModal: false, importDashboardModal: false, modalDashboardData: {}, + + deleteUserName: '', + deleteUserModal: false, } } componentDidMount() { + //load selected scenario AppDispatcher.dispatch({ type: 'scenarios/start-load', @@ -112,6 +116,13 @@ class Scenario extends React.Component { token: this.state.sessionToken }); + + AppDispatcher.dispatch({ + type: 'scenarios/start-load-users', + data: this.state.scenario.id, + token: this.state.sessionToken + }); + // load ICs to enable that component configs and dashboards work with them AppDispatcher.dispatch({ type: 'ics/start-load', @@ -119,12 +130,35 @@ class Scenario extends React.Component { }); } + /* ############################################## + * User modification methods + ############################################## */ + + addUser() { + AppDispatcher.dispatch({ + type: 'scenarios/add-user', + data: this.state.scenario.id, + username: this.userToAdd, + token: this.state.sessionToken + }); + } + + closeDeleteUserModal() { + this.setState({ deleteUserModal: false }); + + AppDispatcher.dispatch({ + type: 'scenarios/remove-user', + data: this.state.scenario.id, + username: this.state.deleteUserName, + token: this.state.sessionToken + }); + } /* ############################################## * Component Configuration modification methods ############################################## */ - addConfig(){ + addConfig() { const config = { scenarioID: this.state.scenario.id, name: 'New Component Configuration', @@ -140,8 +174,8 @@ class Scenario extends React.Component { } - closeEditConfigModal(data){ - this.setState({ editConfigModal : false }); + closeEditConfigModal(data) { + this.setState({ editConfigModal: false }); if (data) { AppDispatcher.dispatch({ @@ -166,7 +200,7 @@ class Scenario extends React.Component { }); } - importConfig(data){ + importConfig(data) { this.setState({ importConfigModal: false }); if (data == null) { @@ -274,7 +308,7 @@ class Scenario extends React.Component { getICName(icID) { for (let ic of this.state.ics) { if (ic.id === icID) { - return ic.name || ic.uuid; + return ic.name || ic.uuid; } } } @@ -284,7 +318,7 @@ class Scenario extends React.Component { ############################################## */ closeNewDashboardModal(data) { - this.setState({ newDashboardModal : false }); + this.setState({ newDashboardModal: false }); if (data) { let newDashboard = data; // add default grid value and scenarioID @@ -299,7 +333,7 @@ class Scenario extends React.Component { } } - closeDeleteDashboardModal(confirmDelete){ + closeDeleteDashboardModal(confirmDelete) { this.setState({ deleteDashboardModal: false }); if (confirmDelete === false) { @@ -362,7 +396,7 @@ class Scenario extends React.Component { * File modification methods ############################################## */ - getFileName(id){ + getFileName(id) { for (let file of this.state.files) { if (file.id === id) { return file.name; @@ -387,12 +421,14 @@ class Scenario extends React.Component { return

{this.state.scenario.name}

+ + {/*Component Configurations table*/}

Component Configurations

this.onConfigChecked(index, event)} width='30' /> - this.getFileName(selectedFileID)}/> + this.getFileName(selectedFileID)} /> + ]} />
@@ -489,10 +525,42 @@ class Scenario extends React.Component {
- this.closeNewDashboardModal(data)}/> - this.closeImportDashboardModal(data)} /> + this.closeNewDashboardModal(data)} /> + this.closeImportDashboardModal(data)} /> - this.closeDeleteDashboardModal(e)}/> + this.closeDeleteDashboardModal(e)} /> + + + {/*Scenario Users table*/} +

Users sharing this scenario

+
+
+ + + this.setState({ deleteUserModal: true, deleteUserName: this.state.scenario.users[index].username, modalUserIndex: index })} + /> +
+ + + this.userToAdd = e.target.value} + /> + + + +

+
+ + this.closeDeleteUserModal(c)} /> ; diff --git a/src/scenario/scenarios-data-manager.js b/src/scenario/scenarios-data-manager.js index 3f06f05..bd89f31 100644 --- a/src/scenario/scenarios-data-manager.js +++ b/src/scenario/scenarios-data-manager.js @@ -17,21 +17,72 @@ import RestDataManager from '../common/data-managers/rest-data-manager'; import AppDispatcher from "../common/app-dispatcher"; +import RestAPI from '../common/api/rest-api'; + class ScenariosDataManager extends RestDataManager { constructor() { super('scenario', '/scenarios'); - this.onLoad = this.onScenariosLoad + this.onLoad = this.onScenariosLoad } - onScenariosLoad(data, token){ + getUsers(token, id) { + RestAPI.get(this.makeURL('/scenarios/' + id + '/users'), token).then(response => { + AppDispatcher.dispatch({ + type: 'scenarios/users-loaded', + users: response.users, + scenarioID: id + }); + }).catch(error => { + AppDispatcher.dispatch({ + type: 'scenarios/users-error', + error: error + }) + }) + } + + addUser(token, id, username) { + let path = id + '/user'; + RestAPI.put(this.requestURL('load/add', path, '?username=' + username), null, token).then(response => { + AppDispatcher.dispatch({ + type: 'scenarios/start-load-users', + data: id, + token: token + }); + + }).catch(error => { + AppDispatcher.dispatch({ + type: 'scenarios/users-error', + error: error + }) + }) + } + + deleteUser(token, id, username) { + let path = id + '/user'; + RestAPI.delete(this.makeURL(this.url + '/' + path + '?username=' + username), token).then(response => { + AppDispatcher.dispatch({ + type: 'scenarios/start-load-users', + data: id, + token: token + }); + + }).catch(error => { + AppDispatcher.dispatch({ + type: 'scenarios/users-error', + error: error + }) + }) + } + + onScenariosLoad(data, token) { if (!Array.isArray(data)) { data = [data]; } - for (let scenario of data){ + for (let scenario of data) { AppDispatcher.dispatch({ type: 'configs/start-load', token: token, @@ -47,7 +98,7 @@ class ScenariosDataManager extends RestDataManager { AppDispatcher.dispatch({ type: 'files/start-load', token: token, - param: '?scenarioID='+scenario.id, + param: '?scenarioID=' + scenario.id, }); } } diff --git a/src/user/login-store.js b/src/user/login-store.js index 0b461e2..47e603d 100644 --- a/src/user/login-store.js +++ b/src/user/login-store.js @@ -30,7 +30,8 @@ class LoginStore extends ReduceStore { return { currentUser: null, token: null, - loginMessage: null + loginMessage: null, + scenarioUsers: null }; } @@ -76,7 +77,6 @@ class LoginStore extends ReduceStore { // If it was an error and hasn't been handled, the credentials must have been wrong. state = Object.assign({}, state, { loginMessage: 'Wrong credentials! Please try again.' }); } - return state; default: