diff --git a/src/app.js b/src/app.js index 5558bfa..07207a9 100644 --- a/src/app.js +++ b/src/app.js @@ -122,6 +122,7 @@ class App extends React.Component { + : '' } { currentUser.role === "Admin" || pages.infrastructure ? <> diff --git a/src/config-reader.js b/src/config-reader.js index 1893910..712680e 100644 --- a/src/config-reader.js +++ b/src/config-reader.js @@ -15,29 +15,30 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import RestDataManager from './common/data-managers/rest-data-manager'; -import RestAPI from './common/api/rest-api'; -import AppDispatcher from './common/app-dispatcher'; - +import RestDataManager from "./common/data-managers/rest-data-manager"; +import RestAPI from "./common/api/rest-api"; +import AppDispatcher from "./common/app-dispatcher"; class ConfigReader extends RestDataManager { constructor() { - super('config', '/config'); + super("config", "/config"); } loadConfig() { - RestAPI.get(this.makeURL('/config'), null).then(response => { - AppDispatcher.dispatch({ - type: 'config/loaded', - data: response, + RestAPI.get(this.makeURL("/config"), null) + .then((response) => { + AppDispatcher.dispatch({ + type: "config/loaded", + data: response, + }); + }) + .catch((error) => { + AppDispatcher.dispatch({ + type: "config/load-error", + error: error, + }); }); - }).catch(error => { - AppDispatcher.dispatch({ - type: 'config/load-error', - error: error, - }); - }); } -}; +} -export default new ConfigReader(); \ No newline at end of file +export default new ConfigReader(); diff --git a/src/dashboard/dashboards-data-manager.js b/src/dashboard/dashboards-data-manager.js index 451d3bf..da649e1 100644 --- a/src/dashboard/dashboards-data-manager.js +++ b/src/dashboard/dashboards-data-manager.js @@ -18,7 +18,7 @@ import RestDataManager from '../common/data-managers/rest-data-manager'; import AppDispatcher from "../common/app-dispatcher"; -class DashboardsDataManager extends RestDataManager{ +class DashboardsDataManager extends RestDataManager { constructor() { super('dashboard', '/dashboards'); this.onLoad = this.onDashboardsLoad diff --git a/src/index.js b/src/index.js index 4d73f3c..c7a7018 100644 --- a/src/index.js +++ b/src/index.js @@ -15,15 +15,21 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; -import ReactDOM from 'react-dom'; +import React from "react"; +import ReactDOM from "react-dom"; -import Router from './router'; +import Router from "./router"; -import 'bootstrap/dist/css/bootstrap.css'; -import './styles/index.css'; +// Redux +import { Provider } from "react-redux"; +import store from "./redux/store"; + +import "bootstrap/dist/css/bootstrap.css"; +import "./styles/index.css"; ReactDOM.render( - , - document.getElementById('root') + + + , + document.getElementById("root") ); diff --git a/src/new_redux/slices/arraySlice.js b/src/new_redux/slices/arraySlice.js new file mode 100644 index 0000000..73777e1 --- /dev/null +++ b/src/new_redux/slices/arraySlice.js @@ -0,0 +1,110 @@ +import { createSlice } from "@reduxjs/toolkit"; +import AppDispatcher from "./app-dispatcher"; +import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; +import NotificationsFactory from "./data-managers/notifications-factory"; + +const initialState = []; +const arraySlice = createSlice({ + name: "array", + initialState, + reducers: { + startLoad: (state, action) => { + if (Array.isArray(action.payload.data)) { + action.payload.data.forEach((id) => { + // Load logic here using dataManager + }); + } else { + // Load logic here using dataManager + } + }, + loaded: (state, action) => { + if (Array.isArray(action.payload.data)) { + // Handle loaded data using updateElements + } else { + // Handle loaded data using updateElements + } + }, + loadError: (state, action) => { + if ( + action.payload.error && + !action.payload.error.handled && + action.payload.error.response + ) { + // Handle load error and add notification + NotificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR( + action.payload.error.response.body.message + ) + ); + } + }, + startAdd: (state, action) => { + // Add logic here using dataManager + }, + added: (state, action) => { + if ( + typeof action.payload.data.managedexternally !== "undefined" && + action.payload.data.managedexternally === true + ) + return state; + // Handle added data using updateElements + }, + addError: (state, action) => { + // Handle add error + }, + startRemove: (state, action) => { + // Remove logic here using dataManager + }, + removed: (state, action) => { + if (action.payload.original) { + // Handle item removal using filter + } else { + // Handle item removal using filter + } + }, + removeError: (state, action) => { + if ( + action.payload.error && + !action.payload.error.handled && + action.payload.error.response + ) { + // Handle remove error and add notification + NotificationsDataManager.addNotification( + NotificationsFactory.DELETE_ERROR( + action.payload.error.response.body.message + ) + ); + } + }, + startEdit: (state, action) => { + if (action.payload.id) { + // Edit logic here using dataManager with an ID + } else { + // Edit logic here using dataManager without an ID + } + }, + edited: (state, action) => { + // Handle edited data using updateElements + }, + editError: (state, action) => { + // Handle edit error + }, + }, +}); + +export const { + startLoad, + loaded, + loadError, + startAdd, + added, + addError, + startRemove, + removed, + removeError, + startEdit, + edited, + editError, +} = arraySlice.actions; + +export default arraySlice.reducer; diff --git a/src/new_redux/slices/counterSlice.js b/src/new_redux/slices/counterSlice.js new file mode 100644 index 0000000..a1b9dcf --- /dev/null +++ b/src/new_redux/slices/counterSlice.js @@ -0,0 +1,28 @@ +import { createSlice } from '@reduxjs/toolkit' + +export const counterSlice = createSlice({ + name: 'counter', + initialState: { + value: 0 + }, + reducers: { + increment: state => { + // Redux Toolkit allows us to write "mutating" logic in reducers. It + // doesn't actually mutate the state because it uses the Immer library, + // which detects changes to a "draft state" and produces a brand new + // immutable state based off those changes + state.value += 1 + }, + decrement: state => { + state.value -= 1 + }, + incrementByAmount: (state, action) => { + state.value += action.payload + } + } +}) + +// Action creators are generated for each case reducer function +export const { increment, decrement, incrementByAmount } = counterSlice.actions + +export default counterSlice.reducer \ No newline at end of file diff --git a/src/new_redux/slices/icdataSlice.js b/src/new_redux/slices/icdataSlice.js new file mode 100644 index 0000000..c2ff980 --- /dev/null +++ b/src/new_redux/slices/icdataSlice.js @@ -0,0 +1,131 @@ +import { createSlice } from "@reduxjs/toolkit"; +import ICDataDataManager from "./ic-data-data-manager"; + +const MAX_VALUES = 10000; + +const initialState = {}; + +const icDataSlice = createSlice({ + name: "icData", + initialState, + reducers: { + opened: (state, action) => { + // Create an entry for the infrastructure component + if (state[action.payload.id] === undefined) { + state[action.payload.id] = {}; + } + }, + prepareSignalsIn: (state, action) => { + if (state[action.payload.id] === undefined) { + state[action.payload.id] = {}; + } + state[action.payload.id].input = { + sequence: -1, + length: action.payload.length, + version: 2, + type: 0, + timestamp: Date.now(), + values: new Array(action.payload.length).fill(0), + }; + }, + prepareSignalsOut: (state, action) => { + if (state[action.payload.id] === undefined) { + state[action.payload.id] = {}; + } + state[action.payload.id].output = { + sequence: -1, + length: action.payload.length, + values: [], + }; + }, + dataChanged: (state, action) => { + if (state[action.payload.id] == null) { + return; + } + + if (state[action.payload.id].output == null) { + state[action.payload.id].output = { + values: [], + }; + } + + for (let j = 0; j < action.payload.data.length; j++) { + let smp = action.payload.data[j]; + + if (smp.source_index !== 0) { + for (let i = 0; i < smp.length; i++) { + while (state[action.payload.id].input.values.length < i + 1) { + state[action.payload.id].input.values.push([]); + } + + state[action.payload.id].input.values[i] = smp.values[i]; + + if (state[action.payload.id].input.values[i].length > MAX_VALUES) { + const pos = + state[action.payload.id].input.values[i].length - MAX_VALUES; + state[action.payload.id].input.values[i].splice(0, pos); + } + } + + state[action.payload.id].input.timestamp = smp.timestamp; + state[action.payload.id].input.sequence = smp.sequence; + + // Trigger updates of widgets that use this signal + ICDataDataManager.updateSignalValueInWidgets( + smp.source_index, + smp.values + ); + } else { + for (let i = 0; i < smp.length; i++) { + while (state[action.payload.id].output.values.length < i + 1) { + state[action.payload.id].output.values.push([]); + } + + state[action.payload.id].output.values[i].push({ + x: smp.timestamp, + y: smp.values[i], + }); + + if (state[action.payload.id].output.values[i].length > MAX_VALUES) { + const pos = + state[action.payload.id].output.values[i].length - MAX_VALUES; + state[action.payload.id].output.values[i].splice(0, pos); + } + + state[action.payload.id].output.timestamp = smp.timestamp; + state[action.payload.id].output.sequence = smp.sequence; + } + } + } + }, + inputChanged: (state, action) => { + if ( + state[action.payload.ic] == null || + state[action.payload.ic].input == null + ) { + return; + } + + state[action.payload.ic].input.timestamp = Date.now(); + state[action.payload.ic].input.sequence++; + state[action.payload.ic].input.values[action.payload.signalIndex] = + action.payload.data; + state[action.payload.ic].input.length = + state[action.payload.ic].input.values.length; + state[action.payload.ic].input.source_index = action.payload.signalID; + + let input = JSON.parse(JSON.stringify(state[action.payload.ic].input)); + ICDataDataManager.send(input, action.payload.ic); + }, + }, +}); + +export const { + opened, + prepareSignalsIn, + prepareSignalsOut, + dataChanged, + inputChanged, +} = icDataSlice.actions; + +export default icDataSlice.reducer; diff --git a/src/new_redux/slices/loginSlice.js b/src/new_redux/slices/loginSlice.js new file mode 100644 index 0000000..417fefb --- /dev/null +++ b/src/new_redux/slices/loginSlice.js @@ -0,0 +1,59 @@ +import { createSlice } from "@reduxjs/toolkit"; +import ICDataDataManager from "../../user/users-data-manager"; + +const initialState = { + currentUser: null, + token: null, + loginMessage: null, + config: null, +}; + +const loginSlice = createSlice({ + name: "login", + initialState, + reducers: { + config_loaded: (state, action) => { + state.config = action.payload; + }, + config_load_error: (state, action) => { + state.config = null; + }, + users_login: (state, action) => { + UsersDataManager.login(action.payload.username, action.payload.password); + state.loginMessage = null; + }, + users_extlogin: (state, action) => { + UsersDataManager.login(); + state.loginMessage = null; + }, + users_logout: (state, action) => { + ICDataDataManager.closeAll(); + localStorage.clear(); + state.token = null; + state.currentUser = null; + state.loginMessage = null; + }, + users_logged_in: (state, action) => { + state.token = action.payload.token; + state.currentUser = action.payload.currentUser; + }, + users_login_error: (state, action) => { + if (action.payload.error && !action.payload.error.handled) { + state.loginMessage = "Wrong credentials! Please try again."; + } + }, + }, +}); + +// Action creators are generated for each case reducer function +export const { + config_loaded, + config_load_error, + users_logged_in, + users_login_error, + users_login, + users_extlogin, + users_logout, +} = loginSlice.actions; + +export default loginSlice.reducer; diff --git a/src/new_redux/slices/resultSlice.js b/src/new_redux/slices/resultSlice.js new file mode 100644 index 0000000..e6a992d --- /dev/null +++ b/src/new_redux/slices/resultSlice.js @@ -0,0 +1,33 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { arraySlice } from './arrayReducer'; +// Import any other necessary utilities + +export const resultSlice = createSlice({ + name: 'results', + initialState: arraySlice.getInitialState(), + reducers: { + loaded: (state, action) => { + // Use your simplifyTimestamps function here before deferring to the arraySlice reducer + arraySlice.caseReducers.loaded(state, action); + }, + added: (state, action) => { + // Similar changes go here + arraySlice.caseReducers.added(state, action); + }, + edited: (state, action) => { + // Repeat the process for the 'edited' logic + arraySlice.caseReducers.edited(state, action); + }, + removed: (state, action) => { + // Implement additional logic if needed and then do the same + arraySlice.caseReducers.removed(state, action); + }, + // Add additional case reducers here + }, +}); + +// Export actions +export const { loaded, added, edited, removed } = resultSlice.actions; + +// Export the reducer +export default resultSlice.reducer; \ No newline at end of file diff --git a/src/new_redux/slices/urlSlice.js b/src/new_redux/slices/urlSlice.js new file mode 100644 index 0000000..f46164d --- /dev/null +++ b/src/new_redux/slices/urlSlice.js @@ -0,0 +1,28 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const urlParamsString = localStorage.getItem("urlParams"); +const urlParams = + urlParamsString !== null ? JSON.parse(urlParamsString) : []; + +const setUrlFunction = (urlParams) => { + localStorage.setItem("urlParams", JSON.stringify(urlParams)); +}; + +const initialState = { + urlParams: urlParams, +}; + +export const urlSlice = createSlice({ + name: "urlslice", + initialState, + reducers: { + setUrlParams: (state, action) => { + state.urlParams = action.payload.urlParams; + setUrlFunction(action.payload.urlParams); + }, + }, +}); + +export const { setUrlParams } = urlSlice.actions; + +export default urlSlice.reducer; diff --git a/src/new_redux/store.js b/src/new_redux/store.js new file mode 100644 index 0000000..5ac0a52 --- /dev/null +++ b/src/new_redux/store.js @@ -0,0 +1,10 @@ +import { configureStore } from "@reduxjs/toolkit"; +import loginReducer from "./slices/loginSlice"; +import counterReducer from "./slices/counterSlice"; + +export default configureStore({ + reducer: { + login: loginReducer, + counter: counterReducer, + }, +}); diff --git a/src/redux/actionTypes.js b/src/redux/actionTypes.js new file mode 100644 index 0000000..7aabf93 --- /dev/null +++ b/src/redux/actionTypes.js @@ -0,0 +1,3 @@ +export const ADD_TODO = "ADD_TODO"; +export const TOGGLE_TODO = "TOGGLE_TODO"; +export const SET_FILTER = "SET_FILTER"; diff --git a/src/redux/actions.js b/src/redux/actions.js new file mode 100644 index 0000000..d005235 --- /dev/null +++ b/src/redux/actions.js @@ -0,0 +1,18 @@ +import { ADD_TODO, TOGGLE_TODO, SET_FILTER } from "./actionTypes"; + +let nextTodoId = 0; + +export const addTodo = content => ({ + type: ADD_TODO, + payload: { + id: ++nextTodoId, + content + } +}); + +export const toggleTodo = id => ({ + type: TOGGLE_TODO, + payload: { id } +}); + +export const setFilter = filter => ({ type: SET_FILTER, payload: { filter } }); diff --git a/src/redux/actions/index.js b/src/redux/actions/index.js new file mode 100644 index 0000000..7866814 --- /dev/null +++ b/src/redux/actions/index.js @@ -0,0 +1,5 @@ +const loAction = () => ({ + type: "login", +}); + +export { loAction }; diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js new file mode 100644 index 0000000..b78a0e8 --- /dev/null +++ b/src/redux/reducers/index.js @@ -0,0 +1,5 @@ +import { combineReducers } from "redux"; + +import todos from "./todos"; + +export default combineReducers({ todos }); diff --git a/src/redux/reducers/login.js b/src/redux/reducers/login.js new file mode 100644 index 0000000..fc9e459 --- /dev/null +++ b/src/redux/reducers/login.js @@ -0,0 +1,33 @@ +const initialState = { + currentUser: null, + token: null, + loginMessage: null, + config: null, +}; + +const loginReducer = (state = initialState, action) => { + switch (action.type) { + case "config_loaded": + return { ...state, config: action.payload }; + case "config_load_error": + return { ...state, config: null }; + case "users_logged_in": + return { + ...state, + token: action.payload.token, + currentUser: action.payload.currentUser, + }; + case "users_login_error": + if (action.payload.error && !action.payload.error.handled) { + return { + ...state, + loginMessage: "Wrong credentials! Please try again.", + }; + } + return state; + default: + return state; + } +}; + +export default loginReducer; diff --git a/src/redux/reducers/todos.js b/src/redux/reducers/todos.js new file mode 100644 index 0000000..5da1d10 --- /dev/null +++ b/src/redux/reducers/todos.js @@ -0,0 +1,40 @@ +import { ADD_TODO, TOGGLE_TODO } from "../actionTypes"; + +const initialState = { + allIds: [], + byIds: {}, +}; + +export default function (state = initialState, action) { + switch (action.type) { + case ADD_TODO: { + const { id, content } = action.payload; + return { + ...state, + allIds: [...state.allIds, id], + byIds: { + ...state.byIds, + [id]: { + content, + completed: false, + }, + }, + }; + } + case TOGGLE_TODO: { + const { id } = action.payload; + return { + ...state, + byIds: { + ...state.byIds, + [id]: { + ...state.byIds[id], + completed: !state.byIds[id].completed, + }, + }, + }; + } + default: + return state; + } +} diff --git a/src/redux/selectors.js b/src/redux/selectors.js new file mode 100644 index 0000000..b50de2c --- /dev/null +++ b/src/redux/selectors.js @@ -0,0 +1,14 @@ +export const getTodosState = (store) => store.todos; + +export const getTodoList = (store) => + getTodosState(store) ? getTodosState(store).allIds : []; + +export const getTodoById = (store, id) => + getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {}; + +/** + * example of a slightly more complex selector + * select from store combining information from multiple reducers + */ +export const getTodos = (store) => + getTodoList(store).map((id) => getTodoById(store, id)); diff --git a/src/redux/store.js b/src/redux/store.js new file mode 100644 index 0000000..f02d47a --- /dev/null +++ b/src/redux/store.js @@ -0,0 +1,4 @@ +import { createStore } from "redux"; +import rootReducer from "./reducers"; + +export default createStore(rootReducer); diff --git a/src/router.js b/src/router.js index f28fe25..a303e20 100644 --- a/src/router.js +++ b/src/router.js @@ -15,24 +15,26 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; -import { BrowserRouter, Route, Switch } from 'react-router-dom'; -import App from './app'; -import Login from './user/login'; -import Logout from './user/logout'; -import LoginComplete from './user/login-complete' +import React from "react"; +import { BrowserRouter, Route, Switch } from "react-router-dom"; +import App from "./app"; +import Login from "./user/login"; +import Logout from "./user/logout"; +import LoginComplete from "./user/login-complete"; class Root extends React.Component { render() { return ( - - - - - - - - + + + + + + + + + + ); } } diff --git a/src/scenario/scenario.js b/src/scenario/scenario.js index f06d80b..d4f15a1 100644 --- a/src/scenario/scenario.js +++ b/src/scenario/scenario.js @@ -41,6 +41,9 @@ import EditFilesDialog from '../file/edit-files' import ScenarioUsersTable from "./scenario-users-table"; +import { useSelector, useDispatch } from "react-redux"; +import { decrement, increment } from "../new_redux/slices/counterSlice"; + class Scenario extends React.Component { constructor(props) { @@ -49,6 +52,8 @@ class Scenario extends React.Component { this.state = { filesEditModal: false, filesEditSaveState: [], + // count: useSelector((state) => state.counter.value), + // dispatch: useDispatch(), } } diff --git a/src/scenario/scenarios.js b/src/scenario/scenarios.js index 4acd322..86d82fd 100644 --- a/src/scenario/scenarios.js +++ b/src/scenario/scenarios.js @@ -15,33 +15,61 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React, { Component } from 'react'; -import { Container } from 'flux/utils'; -import FileSaver from 'file-saver'; -import AppDispatcher from '../common/app-dispatcher'; -import ScenarioStore from './scenario-store'; -import DashboardStore from '../dashboard/dashboard-store'; +import React, { Component } from "react"; +import { Container } from "flux/utils"; +import FileSaver from "file-saver"; +import AppDispatcher from "../common/app-dispatcher"; +import ScenarioStore from "./scenario-store"; +import DashboardStore from "../dashboard/dashboard-store"; import WidgetStore from "../widget/widget-store"; -import ComponentConfigStore from '../componentconfig/config-store'; -import SignalStore from '../signal/signal-store' -import ResultStore from '../result/result-store' -import FileStore from '../file/file-store' -import LoginStore from '../user/login-store' -import UsersStore from '../user/users-store' -import ICStore from '../ic/ic-store' -import ICDataStore from '../ic/ic-data-store' -import { Table, ButtonColumn, DataColumn, LinkColumn } from '../common/table'; -import NewScenarioDialog from './new-scenario'; -import EditScenarioDialog from './edit-scenario'; -import ImportScenarioDialog from './import-scenario'; -import DeleteDialog from '../common/dialogs/delete-dialog'; -import IconButton from '../common/buttons/icon-button'; +import ComponentConfigStore from "../componentconfig/config-store"; +import SignalStore from "../signal/signal-store"; +import ResultStore from "../result/result-store"; +import FileStore from "../file/file-store"; +import LoginStore from "../user/login-store"; +import UsersStore from "../user/users-store"; +import ICStore from "../ic/ic-store"; +import ICDataStore from "../ic/ic-data-store"; +import { Table, ButtonColumn, DataColumn, LinkColumn } from "../common/table"; +import NewScenarioDialog from "./new-scenario"; +import EditScenarioDialog from "./edit-scenario"; +import ImportScenarioDialog from "./import-scenario"; +import DeleteDialog from "../common/dialogs/delete-dialog"; +import IconButton from "../common/buttons/icon-button"; +import { connect } from "react-redux"; +import { addTodo } from "../redux/actions"; +import { getTodos } from "../redux/selectors"; class Scenarios extends Component { + constructor(props) { + super(props); + this.state = { input: "" }; + } + + updateInput = (input) => { + this.setState({ input }); + }; + + handleAddTodo = () => { + this.props.addTodo(this.state.input); + this.setState({ input: "" }); + }; static getStores() { - return [ScenarioStore, DashboardStore, WidgetStore, ComponentConfigStore, SignalStore, ResultStore, FileStore,LoginStore, UsersStore, ICStore, ICDataStore]; + return [ + ScenarioStore, + DashboardStore, + WidgetStore, + ComponentConfigStore, + SignalStore, + ResultStore, + FileStore, + LoginStore, + UsersStore, + ICStore, + ICDataStore, + ]; } static calculateState(prevState, props) { @@ -62,21 +90,21 @@ class Scenarios extends Component { importModal: false, modalScenario: {}, selectedScenarios: prevState.selectedScenarios || [], - currentUser: JSON.parse(localStorage.getItem("currentUser")) + currentUser: JSON.parse(localStorage.getItem("currentUser")), }; } componentDidMount() { AppDispatcher.dispatch({ - type: 'scenarios/start-load', - token: this.state.sessionToken + type: "scenarios/start-load", + token: this.state.sessionToken, }); } closeNewModal(data) { if (data) { AppDispatcher.dispatch({ - type: 'scenarios/start-add', + type: "scenarios/start-add", data: data, token: this.state.sessionToken, }); @@ -107,28 +135,28 @@ class Scenarios extends Component { this.state.dashboards.forEach((dashboard) => { if (dashboard.id === this.state.modalScenario.id) { AppDispatcher.dispatch({ - type: 'dashboards/start-remove', + type: "dashboards/start-remove", data: dashboard, - token: this.state.sessionToken - }) + token: this.state.sessionToken, + }); } }); AppDispatcher.dispatch({ - type: 'scenarios/start-remove', + type: "scenarios/start-remove", data: this.state.modalScenario, - token: this.state.sessionToken + token: this.state.sessionToken, }); - }; + } closeEditModal(data) { this.setState({ editModal: false }); if (data != null) { AppDispatcher.dispatch({ - type: 'scenarios/start-edit', + type: "scenarios/start-edit", data, - token: this.state.sessionToken + token: this.state.sessionToken, }); } } @@ -138,7 +166,7 @@ class Scenarios extends Component { if (data) { AppDispatcher.dispatch({ - type: 'scenarios/start-add', + type: "scenarios/start-add", data: data, token: this.state.sessionToken, }); @@ -146,7 +174,7 @@ class Scenarios extends Component { } onModalKeyPress = (event) => { - if (event.key === 'Enter') { + if (event.key === "Enter") { event.preventDefault(); this.confirmDeleteModal(); @@ -154,17 +182,27 @@ class Scenarios extends Component { }; getConfigs(scenarioID) { - let configs = JSON.parse(JSON.stringify(this.state.configs.filter(config => config.scenarioID === scenarioID))); + let configs = JSON.parse( + JSON.stringify( + this.state.configs.filter((config) => config.scenarioID === scenarioID) + ) + ); configs.forEach((config) => { - let signals = JSON.parse(JSON.stringify(SignalStore.getState().filter(s => s.configID === parseInt(config.id, 10)))); + let signals = JSON.parse( + JSON.stringify( + SignalStore.getState().filter( + (s) => s.configID === parseInt(config.id, 10) + ) + ) + ); signals.forEach((signal) => { delete signal.configID; delete signal.id; - }) + }); // two separate lists for inputMapping and outputMapping - let inputSignals = signals.filter(s => s.direction === 'in'); - let outputSignals = signals.filter(s => s.direction === 'out'); + let inputSignals = signals.filter((s) => s.direction === "in"); + let outputSignals = signals.filter((s) => s.direction === "out"); // add signal mappings to config config["inputMapping"] = inputSignals; @@ -172,20 +210,30 @@ class Scenarios extends Component { delete config.id; delete config.scenarioID; - }) + }); return configs; } getDashboards(scenarioID) { - let dashboards = JSON.parse(JSON.stringify(this.state.dashboards.filter(dashb => dashb.scenarioID === scenarioID))); + let dashboards = JSON.parse( + JSON.stringify( + this.state.dashboards.filter((dashb) => dashb.scenarioID === scenarioID) + ) + ); // add Dashboards and Widgets to JSON object dashboards.forEach((dboard) => { - let widgets = JSON.parse(JSON.stringify(WidgetStore.getState().filter(w => w.dashboardID === parseInt(dboard.id, 10)))); + let widgets = JSON.parse( + JSON.stringify( + WidgetStore.getState().filter( + (w) => w.dashboardID === parseInt(dboard.id, 10) + ) + ) + ); widgets.forEach((widget) => { delete widget.dashboardID; delete widget.id; - }) + }); dboard["widgets"] = widgets; delete dboard.scenarioID; delete dboard.id; @@ -205,13 +253,15 @@ class Scenarios extends Component { jsonObj["dashboards"] = this.getDashboards(scenarioID); // create JSON string and show save dialog - const blob = new Blob([JSON.stringify(jsonObj, null, 2)], { type: 'application/json' }); - FileSaver.saveAs(blob, 'scenario - ' + scenario.name + '.json'); + const blob = new Blob([JSON.stringify(jsonObj, null, 2)], { + type: "application/json", + }); + FileSaver.saveAs(blob, "scenario - " + scenario.name + ".json"); } duplicateScenario(index) { let scenario = JSON.parse(JSON.stringify(this.state.scenarios[index])); - scenario.name = scenario.name + '_copy'; + scenario.name = scenario.name + "_copy"; let jsonObj = scenario; jsonObj["configs"] = this.getConfigs(scenario.id); @@ -219,7 +269,7 @@ class Scenarios extends Component { if (jsonObj) { AppDispatcher.dispatch({ - type: 'scenarios/start-add', + type: "scenarios/start-add", data: jsonObj, token: this.state.sessionToken, }); @@ -236,92 +286,152 @@ class Scenarios extends Component { data.isLocked = !this.state.scenarios[index].isLocked; AppDispatcher.dispatch({ - type: 'scenarios/start-edit', + type: "scenarios/start-edit", data, - token: this.state.sessionToken + token: this.state.sessionToken, }); } render() { const buttonStyle = { - marginLeft: '10px', - } + marginLeft: "10px", + }; const iconStyle = { - height: '30px', - width: '30px' - } + height: "30px", + width: "30px", + }; + + const { todos } = this.props; return ( -
-

Scenarios - - this.setState({ newModal: true })} - icon='plus' - buttonStyle={buttonStyle} - iconStyle={iconStyle} +
+
+ this.updateInput(e.target.value)} + value={this.state.input} /> - this.setState({ importModal: true })} - icon='upload' - buttonStyle={buttonStyle} - iconStyle={iconStyle} - /> - -

+ +
+
+
    + {todos.map((todo, index) => ( +
  • {todo.content}
  • + ))} +
+
- - {this.state.currentUser.role === "Admin" ? - + Scenarios + + this.setState({ newModal: true })} + icon="plus" + buttonStyle={buttonStyle} + iconStyle={iconStyle} + /> + this.setState({ importModal: true })} + icon="upload" + buttonStyle={buttonStyle} + iconStyle={iconStyle} + /> + + + +
+ {this.state.currentUser.role === "Admin" ? ( + + ) : ( + <> + )} + - : <> - } - - {this.state.currentUser.role === "Admin" ? + {this.state.currentUser.role === "Admin" ? ( + this.onLock(index)} + isLocked={(index) => this.isLocked(index)} + /> + ) : ( + <> + )} + this.onLock(index)} - isLocked={index => this.isLocked(index)} + width="200" + align="right" + editButton + deleteButton + exportButton + duplicateButton + onEdit={(index) => + this.setState({ + editModal: true, + modalScenario: this.state.scenarios[index], + }) + } + onDelete={(index) => + this.setState({ + deleteModal: true, + modalScenario: this.state.scenarios[index], + }) + } + onExport={(index) => this.exportScenario(index)} + onDuplicate={(index) => this.duplicateScenario(index)} + isLocked={(index) => this.isLocked(index)} /> - : <> - } - this.setState({ editModal: true, modalScenario: this.state.scenarios[index] })} - onDelete={index => this.setState({ deleteModal: true, modalScenario: this.state.scenarios[index] })} - onExport={index => this.exportScenario(index)} - onDuplicate={index => this.duplicateScenario(index)} - isLocked={index => this.isLocked(index)} +
+ + this.closeNewModal(data)} + /> + this.closeEditModal(data)} + scenario={this.state.modalScenario} + /> + this.closeImportModal(data)} + nodes={this.state.nodes} /> - - this.closeNewModal(data)} /> - this.closeEditModal(data)} scenario={this.state.modalScenario} /> - this.closeImportModal(data)} nodes={this.state.nodes} /> - - this.closeDeleteModal(e)} /> - - ); + this.closeDeleteModal(e)} + /> + + ); } } -let fluxContainerConverter = require('../common/FluxContainerConverter'); -export default Container.create(fluxContainerConverter.convert(Scenarios)); +let fluxContainerConverter = require("../common/FluxContainerConverter"); +// export default Container.create(fluxContainerConverter.convert(Scenarios)); + +const mapStateToProps = (state) => ({ + todos: getTodos(state), // Assuming you have a selector called getTodos +}); + +// Map the actions to the component's props +const mapDispatchToProps = { + addTodo, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Container.create(fluxContainerConverter.convert(Scenarios))); diff --git a/src/signal/signal-store.js b/src/signal/signal-store.js index 40eae35..af50541 100644 --- a/src/signal/signal-store.js +++ b/src/signal/signal-store.js @@ -20,7 +20,7 @@ import SignalsDataManager from './signals-data-manager' import NotificationsDataManager from "../common/data-managers/notifications-data-manager"; import NotificationsFactory from "../common/data-managers/notifications-factory"; -class SignalStore extends ArrayStore{ +class SignalStore extends ArrayStore{ constructor() { super('signals', SignalsDataManager); }