From 4284fc1876b3b55c715405080b58dd567ded8fa0 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Thu, 25 Jul 2024 13:53:26 +0200 Subject: [PATCH] updated infrastructure page structure Signed-off-by: Andrii Podriez --- .../infrastructure/dialogs/edit-ic-dialog.js | 248 ++++++++++++ .../dialogs/import-ic-dialog.js | 148 ++++++++ .../infrastructure/dialogs/new-ic-dialog.js | 354 ++++++++++++++++++ src/pages/infrastructure/ic-action-board.js | 114 ++++++ src/pages/infrastructure/ic-category-table.js | 116 +++--- src/pages/infrastructure/infrastructure.js | 121 ++++-- 6 files changed, 1018 insertions(+), 83 deletions(-) create mode 100644 src/pages/infrastructure/dialogs/edit-ic-dialog.js create mode 100644 src/pages/infrastructure/dialogs/import-ic-dialog.js create mode 100644 src/pages/infrastructure/dialogs/new-ic-dialog.js create mode 100644 src/pages/infrastructure/ic-action-board.js diff --git a/src/pages/infrastructure/dialogs/edit-ic-dialog.js b/src/pages/infrastructure/dialogs/edit-ic-dialog.js new file mode 100644 index 0000000..9a87e7d --- /dev/null +++ b/src/pages/infrastructure/dialogs/edit-ic-dialog.js @@ -0,0 +1,248 @@ +/** + * 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 from 'react'; +import { Form, Col, Row } from 'react-bootstrap'; +import Dialog from '../../../common/dialogs/dialog'; +import ParametersEditor from '../../../common/parameters-editor'; +import NotificationsDataManager from "../../../common/data-managers/notifications-data-manager"; +import NotificationsFactory from "../../../common/data-managers/notifications-factory"; + +class EditICDialog extends React.Component { + valid = true; + + constructor(props) { + super(props); + + this.state = { + name: '', + websocketurl: '', + apiurl: '', + location: '', + description: '', + type: '', + category: '', + icstate: '', + managedexternally: false, + startparameterschema: {} + }; + } + + + onClose(canceled) { + if (canceled === false) { + if (this.valid) { + let data = JSON.parse(JSON.stringify(this.props.ic)); + + if (this.state.name != null && this.state.name !== "" && this.state.name !== this.props.ic.name) { + data.name = this.state.name; + } + + data.websocketurl = this.state.websocketurl; + data.apiurl = this.state.apiurl; + + if (this.state.location != null && this.state.location !== this.props.ic.location) { + data.location = this.state.location; + } + + if (this.state.description != null && this.state.description !== this.props.ic.description) { + data.description = this.state.description; + } + + if (this.state.type != null && this.state.type !== "" && this.state.type !== this.props.ic.type) { + data.type = this.state.type; + } + + if (this.state.category != null && this.state.category !== "" && this.state.category !== this.props.ic.category) { + data.category = this.state.category; + } + + if (this.state.icstate != null && this.state.icstate !== "" && this.state.icstate !== this.props.ic.state) { + data.state = this.state.icstate; + } + + if (Object.keys(this.state.startparameterschema).length === 0 && this.state.startparameterschema.constructor === Object) { + data.startparameterschema = this.state.startparameterschema; + } + + data.managedexternally = this.state.managedexternally; + + this.props.onClose(data); + this.setState({managedexternally: false}); + } + } else { + this.props.onClose(); + this.setState({managedexternally: false}); + } + } + + handleChange(e) { + if(e.target.id === "managedexternally"){ + this.setState({ managedexternally : !this.state.managedexternally}); + } + else{ + this.setState({ [e.target.id]: e.target.value }); + } + } + + handleStartParameterSchemaChange(data) { + this.setState({ startparameterschema: data }); + } + + resetState() { + this.setState({ + name: this.props.ic.name, + websocketurl: this.props.ic.websocketurl, + apiurl: this.props.ic.apiurl, + type: this.props.ic.type, + location: this.props.ic.location, + description: this.props.ic.description, + category: this.props.ic.category, + icstate: this.props.ic.state, + managedexternally: false, + startparameterschema: this.props.ic.startparameterschema || {}, + }); + } + + selectStartParamsFile(event) { + const file = event.target.files[0]; + + if (!file.type.match('application/json')) { + console.error("Not a json file. Will not process file '" + file.name + "'.") + NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR("Not a json file. Will not process file \'" + file.name + "\'.")); + return; + } + + let reader = new FileReader(); + reader.readAsText(file); + + reader.onload = event => { + const params = JSON.parse(reader.result); + this.setState({ startparameterschema: params}) + } + }; + + render() { + let typeOptions = []; + switch(this.state.category){ + case "simulator": + typeOptions = ["dummy","generic","dpsim","rtlab","rscad","rtlab","kubernetes"]; + break; + case "manager": + typeOptions = ["villas-node","villas-relay","generic"]; + break; + case "gateway": + typeOptions = ["villas-node","villas-relay"]; + break; + case "service": + typeOptions = ["ems","custom"]; + break; + case "equipment": + typeOptions = ["chroma-emulator","chroma-loads","sma-sunnyboy","fleps","sonnenbatterie"]; + break; + default: + typeOptions =[]; + } + + let stateOptions = ["idle", "starting", "running", "pausing", "paused", "resuming", "stopping", "resetting", "error", "gone", "shuttingdown", "shutdown"]; + + return ( + this.onClose(c)} + onReset={() => this.resetState()} + valid={this.valid} + > +
+ UUID: {this.props.ic.uuid} + + Name + this.handleChange(e)} /> + + + + Category + this.handleChange(e)}> + + + + + + + + + Type + this.handleChange(e)}> + + {typeOptions.map((name,index) => ( + + ))} + + + { + this.state.type === "dummy" ? + + State + this.handleChange(e)}> + + {stateOptions.map((name,index) => ( + + ))} + + + : <> + } + + Websocket URL + this.handleChange(e)} /> + + + + API URL + this.handleChange(e)} /> + + + + Location + this.handleChange(e)} /> + + + + Description + this.handleChange(e)} /> + + +
+ + Start parameter schema of IC + this.handleStartParameterSchemaChange(data)} + disabled={true} + /> + Select JSON file to update start parameter schema: + this.selectStartParamsFile(event)} /> + +
+
+ ); + } +} + +export default EditICDialog; diff --git a/src/pages/infrastructure/dialogs/import-ic-dialog.js b/src/pages/infrastructure/dialogs/import-ic-dialog.js new file mode 100644 index 0000000..e8db469 --- /dev/null +++ b/src/pages/infrastructure/dialogs/import-ic-dialog.js @@ -0,0 +1,148 @@ +/** + * 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 from 'react'; +import { Form } from 'react-bootstrap'; +import _ from 'lodash'; +import Dialog from '../../../common/dialogs/dialog'; + +class ImportICDialog extends React.Component { + valid = false; + imported = false; + + constructor(props) { + super(props); + + this.state = { + name: '', + websocketurl: '', + uuid: '' + }; + } + + onClose(canceled) { + if (canceled === false) { + if (this.valid) { + const data = { + properties: { + name: this.state.name + }, + uuid: this.state.uuid + }; + + if (this.state.websocketurl != null && this.state.websocketurl !== "" && this.state.websocketurl !== 'http://') { + data.websocketurl = this.state.websocketurl; + } + + this.props.onClose(data); + } + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ name: '', websocketurl: 'http://', uuid: '' }); + } + + loadFile(fileList) { + // get file + const file = fileList[0]; + if (!file.type.match('application/json')) { + return; + } + + // create file reader + const reader = new FileReader(); + const self = this; + + reader.onload = function(event) { + // read component + const ic = JSON.parse(event.target.result); + self.imported = true; + self.setState({ + name: _.get(ic, 'properties.name') || _.get(ic, 'rawProperties.name'), + websocketurl: _.get(ic, 'websocketurl'), + uuid: ic.uuid + }); + }; + + reader.readAsText(file); + } + + validateForm(target) { + // check all controls + let name = true; + let uuid = true; + + if (this.state.name === '') { + name = false; + } + + if (this.state.uuid === '') { + uuid = false; + } + + this.valid = name || uuid; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + if (target === 'uuid') return uuid ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} + onReset={() => this.resetState()} + valid={this.valid} + > +
+ + Infrastructure Component File + this.loadFile(e.target.files)} /> + + + + Name + this.handleChange(e)} /> + + + + Websocket URL + this.handleChange(e)} /> + + + + UUID + this.handleChange(e)} /> + + +
+
+ ); + } +} + +export default ImportICDialog; diff --git a/src/pages/infrastructure/dialogs/new-ic-dialog.js b/src/pages/infrastructure/dialogs/new-ic-dialog.js new file mode 100644 index 0000000..a4380e7 --- /dev/null +++ b/src/pages/infrastructure/dialogs/new-ic-dialog.js @@ -0,0 +1,354 @@ +/** + * 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 from 'react'; +import { Form as BForm, OverlayTrigger, Tooltip} from 'react-bootstrap'; +import Dialog from '../../../common/dialogs/dialog'; +import ParametersEditor from '../../../common/parameters-editor'; +import Form from "@rjsf/core"; +import $RefParser from '@apidevtools/json-schema-ref-parser'; + +class NewICDialog extends React.Component { + valid = false; + + constructor(props) { + super(props); + + this.state = { + name: '', + websocketurl: '', + apiurl: '', + uuid: '', + type: '', + category: '', + managedexternally: false, + description: '', + location: '', + manager: '', + properties: {}, + schema: {}, + formData: {}, + }; + } + + onClose(canceled) { + if (canceled === false) { + if (this.valid) { + const parameters = { + name: this.state.name, + type: this.state.type, + category: this.state.category, + uuid: this.state.uuid, + location: this.state.location, + description: this.state.description, + } + + const data = { + managedexternally: this.state.managedexternally, + manager: this.state.manager, + name: this.state.name, + type: this.state.type, + category: this.state.category, + uuid: this.state.uuid, + description: this.state.description, + location: this.state.location, + parameters: parameters + }; + + // Add custom properties + if (this.state.managedexternally) + Object.assign(parameters, this.state.properties); + + if (this.state.websocketurl != null && this.state.websocketurl !== "") { + parameters.websocketurl = this.state.websocketurl; + data.websocketurl = this.state.websocketurl; + } + + if (this.state.apiurl != null && this.state.apiurl !== "") { + parameters.apiurl = this.state.apiurl; + data.apiurl = this.state.apiurl; + } + + this.props.onClose(data); + this.setState({managedexternally: false}); + } + } else { + this.props.onClose(); + this.setState({managedexternally: false}); + } + } + + handleChange(e) { + if(e.target.id === "managedexternally"){ + this.setState({ managedexternally : !this.state.managedexternally}); + } + else{ + this.setState({ [e.target.id]: e.target.value }); + } + } + + setManager(e) { + this.setState({ [e.target.id]: e.target.value }); + + if (this.props.managers) { + let schema = this.props.managers.find(m => m.uuid === e.target.value).createparameterschema + if (schema) { + $RefParser.dereference(schema, (err, deref) => { + if (err) { + console.error(err) + } + else { + this.setState({schema: schema}) + } + }) + } + } + } + + handlePropertiesChange = properties => { + this.setState({ + properties: properties + }); + }; + + handleFormChange({formData}) { + this.setState({properties: formData, formData: formData}) + } + + resetState() { + this.setState({ + name: '', + websocketurl: '', + apiurl: '', + uuid: this.uuidv4(), + type: '', + category: '', + managedexternally: false, + description: '', + location: '', + properties: {}, + }); + } + + validateForm(target) { + + if (this.state.managedexternally) { + this.valid = this.state.manager !== ''; + return this.state.manager !== '' ? "success" : "error"; + } + + // check all controls + let name = true; + let uuid = true; + let type = true; + let category = true; + + if (this.state.name === '') { + name = false; + } + + if (this.state.uuid === '') { + uuid = false; + } + + if (this.state.type === '') { + type = false; + } + + if (this.state.category === '') { + category = false; + } + + this.valid = name && uuid && type && category; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + if (target === 'uuid') return uuid ? "success" : "error"; + if (target === 'type') return type ? "success" : "error"; + if (target === 'category') return category ? "success" : "error"; + + return this.valid; + } + + uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + // eslint-disable-next-line + var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + render() { + let typeOptions = []; + switch(this.state.category){ + case "simulator": + typeOptions = ["dummy","generic","dpsim","rtlab","rscad","rtlab","kubernetes"]; + break; + case "manager": + typeOptions = ["villas-node","villas-relay","generic","kubernetes"]; + break; + case "gateway": + typeOptions = ["villas-node","villas-relay"]; + break; + case "service": + typeOptions = ["ems","custom"]; + break; + case "equipment": + typeOptions = ["chroma-emulator","chroma-loads","sma-sunnyboy","fleps","sonnenbatterie"]; + break; + default: + typeOptions =[]; + } + + let managerOptions = []; + managerOptions.push(); + for (let m of this.props.managers) { + managerOptions.push ( + + ); + } + + return ( + this.onClose(c)} + onReset={() => this.resetState()} + valid={this.validateForm()} + > + {this.props.managers.length > 0 ? + + + An externally managed component is created and + managed by an IC manager via AMQP}> + this.handleChange(e)}> + + + + : <> + } + {this.state.managedexternally === true ? + <> + + + + Required field } > + Manager to create new IC * + + this.setManager(e)}> + {managerOptions} + + + + + {this.state.schema ? +
this.handleFormChange({formData})} + children={true} // hides submit button + /> + : + + + Create Properties + this.handlePropertiesChange(data)} + /> + + + } + + : + + + Required field } > + Name * + + this.handleChange(e)} /> + + + + + UUID + this.handleChange(e)}/> + + + + + Location + this.handleChange(e)} /> + + + + + Description + this.handleChange(e)} /> + + + + + Required field } > + Category of component * + + this.handleChange(e)}> + + + + + + + + + + + Required field } > + Type of component * + + this.handleChange(e)}> + + {typeOptions.map((name,index) => ( + + ))} + + + + + Websocket URL + this.handleChange(e)} /> + + + + + API URL + this.handleChange(e)} /> + + + + } +
+ ); + } +} + +export default NewICDialog; diff --git a/src/pages/infrastructure/ic-action-board.js b/src/pages/infrastructure/ic-action-board.js new file mode 100644 index 0000000..f330777 --- /dev/null +++ b/src/pages/infrastructure/ic-action-board.js @@ -0,0 +1,114 @@ +/** + * 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 { Form, Row, Col } from 'react-bootstrap'; +import DateTimePicker from 'react-datetime-picker'; +import ActionBoardButtonGroup from '../../common/buttons/action-board-button-group'; +import classNames from 'classnames'; +import { useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { sessionToken } from '../../localStorage'; +import { clearCheckedICs, deleteIC, loadAllICs, sendActionToIC } from '../../store/icSlice'; + +const ICActionBoard = (props) => { + const dispatch = useDispatch(); + const checkedICsIds = useSelector(state => state.infrastructure.checkedICsIds); + + let pickedTime = new Date(); + pickedTime.setMinutes(5 * Math.round(pickedTime.getMinutes() / 5 + 1)); + + const [time, setTime] = useState(pickedTime); + + const onReset = () => { + let newAction = {}; + newAction['action'] = 'reset'; + newAction['when'] = time; + + checkedICsIds.forEach((id) => { + dispatch(sendActionToIC({token: sessionToken, id: id, actions: newAction})); + }); + } + + const onRecreate = () => { + let newAction = {}; + newAction['action'] = 'create'; + newAction['when'] = time; + + checkedICsIds.forEach((id) => { + dispatch(sendActionToIC({token: sessionToken, id: id, actions: newAction})); + }); + } + + const onDelete = () => { + checkedICsIds.forEach((id) => { + console.log(id) + dispatch(deleteIC({token: sessionToken, id: id})); + }); + + dispatch(clearCheckedICs()); + dispatch(loadAllICs({token: sessionToken})); + } + + const onShutdown = () => { + let newAction = {}; + newAction['action'] = 'shutdown'; + newAction['when'] = time; + + checkedICsIds.forEach((id) => { + dispatch(sendActionToIC({token: sessionToken, id: id, actions: newAction})); + }); + } + + return (
+ + + + setTime(newTime)} + value={time} + disableClock={true} + /> + + + + onReset()} + onShutdown={() => onShutdown()} + onDelete={() => onDelete()} + onRecreate={() => onRecreate()} + paused={false} + /> + + {false ? + + + + + : <> + } + + Select time for synced command execution +
); +} + +export default ICActionBoard; diff --git a/src/pages/infrastructure/ic-category-table.js b/src/pages/infrastructure/ic-category-table.js index dce214a..3e3a38a 100644 --- a/src/pages/infrastructure/ic-category-table.js +++ b/src/pages/infrastructure/ic-category-table.js @@ -15,41 +15,25 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import { useSelector, useDispatch } from "react-redux"; +import { useState, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; import {Table, ButtonColumn, CheckboxColumn, DataColumn, LabelColumn, LinkColumn } from '../../common/table'; import { Badge } from 'react-bootstrap'; import FileSaver from 'file-saver'; import _ from 'lodash'; import moment from 'moment' import IconToggleButton from "../../common/buttons/icon-toggle-button"; - -import { checkICsByCategory } from "../../store/icSlice"; -import { useState } from "react"; - +import { updateCheckedICs, openDeleteModal, openEditModal } from "../../store/icSlice"; import { stateLabelStyle } from "./styles"; +import { currentUser } from "../../localStorage"; //a Table of IC components of specific category from props.category //titled with props.title const ICCategoryTable = (props) => { - + const dispatch = useDispatch(); const ics = useSelector(state => state.infrastructure.ICsArray); - - let currentUser = JSON.parse(localStorage.getItem("currentUser")); - - const checkedICs = useSelector(state => state.infrastructure.checkedICsArray); - - const [isGenericDisplayed, setIsGenericDisplayed] = useState(false) - - const exportIC = (index) => { - // filter properties - let ic = Object.assign({}, ics[index]); - delete ic.id; - - // show save dialog - const blob = new Blob([JSON.stringify(ic, null, 2)], { type: 'application/json' }); - FileSaver.saveAs(blob, 'ic-' + (_.get(ic, 'name') || 'undefined') + '.json'); - } + const [isGenericDisplayed, setIsGenericDisplayed] = useState(false); const modifyUptimeColumn = (uptime, component) => { if (uptime >= 0) { @@ -89,8 +73,6 @@ const ICCategoryTable = (props) => { return 99; } } - - //if category of the table is manager we need to filter the generic-typed ics //according to the state of IconToggleButton let tableData = []; @@ -113,29 +95,63 @@ const ICCategoryTable = (props) => { } }) - const isLocalIC = (index, ics) => { - let ic = ics[index] + const [checkedICs, setCheckedICs] = useState({}); + const [areAllICsChecked, setAreAllICsChecked] = useState(false); + + useEffect(() => { + if(tableData.length > 0){ + for(let ic in tableData){ + if(!checkedICs.hasOwnProperty(tableData[ic].id) && !isLocalIC(tableData[ic])){ + setCheckedICs(prevState => ({...prevState, [tableData[ic].id]: false})); + } + } + } + }, [tableData]) + + useEffect(() => { + dispatch(updateCheckedICs(checkedICs)); + + //every time some ic is checked we need to check wether or not all ics are checked afterwards + if(Object.keys(checkedICs).length > 0){ + setAreAllICsChecked(Object.values(checkedICs).every((value)=> value)) + } + }, [checkedICs]) + + const exportIC = (index) => { + // filter properties + let toExport = {...tableData[index]}; + delete toExport.id; + + const fileName = toExport.name.replace(/\s+/g, '-').toLowerCase(); + + // show save dialog + const blob = new Blob([JSON.stringify(toExport, null, 2)], { type: 'application/json' }); + FileSaver.saveAs(blob, 'ic-' + fileName + '.json'); + } + + const isLocalIC = (ic) => { return !ic.managedexternally } - const checkAllICs = (ics, title) => { - //TODO - } - - const isICchecked = (ic) => { - //TODO - return false - } - - const areAllChecked = (title) => { - //TODO - return false - } - - const onICChecked = (ic, event) => { - //TODO + const checkAllICs = () => { + if(areAllICsChecked){ + for(const id in checkedICs){ + setCheckedICs(prevState => ({...prevState, [id]: false})); + } + } else { + for(const id in checkedICs){ + setCheckedICs(prevState => ({...prevState, [id]: true})); + } + } } + const toggleCheck = (id) => { + setCheckedICs(prevState => { + return { + ...prevState, [id]: !prevState[id] + } + }) + } return (

{props.title} @@ -157,11 +173,11 @@ const ICCategoryTable = (props) => { checkAllICs(ics, props.title)} - allChecked={areAllChecked(props.title)} - checkboxDisabled={(index) => isLocalIC(index, ics) === true} - checked={(ic) => isICchecked(ic)} - onChecked={(ic, event) => onICChecked(ic, event)} + onCheckAll={() => checkAllICs()} + allChecked={areAllICsChecked} + checkboxDisabled={(index) => isLocalIC(tableData[index])} + checked={(ic) => checkedICs[ic.id]} + onChecked={(ic, event) => toggleCheck(ic.id)} width='30' /> {currentUser.role === "Admin" ? @@ -202,13 +218,13 @@ const ICCategoryTable = (props) => { width='150' align='right' editButton - showEditButton ={(index) => isLocalIC(index, ics)} + showEditButton ={(index) => isLocalIC(tableData[index])} exportButton deleteButton showDeleteButton = {null} - onEdit={index => {}} + onEdit={index => {dispatch(openEditModal(tableData[index]))}} onExport={index => exportIC(index)} - onDelete={index => {}} + onDelete={index => {dispatch(openDeleteModal(tableData[index]))}} /> : { const dispatch = useDispatch(); - const ICsArray = useSelector(state => state.infrastructure.ICsArray); + const ics = useSelector(state => state.infrastructure.ICsArray); + const externalICs = ics.filter(ic => ic.managedexternally === true); //track status of the modals - const [isEditModalOpened, setIsEditModalOpened] = useState(false); const [isNewModalOpened, setIsNewModalOpened] = useState(false); const [isImportModalOpened, setIsImportModalOpened] = useState(false); - const [isDeleteModalOpened, setIsDeleteModalOpened] = useState(false); const [isICModalOpened, setIsICModalOpened] = useState(false); - const [checkedICs, setCheckedICs] = useState([]); - - const currentUser = JSON.parse(localStorage.getItem("currentUser")); - + useEffect(() => { //load array of ics and start a timer for periodic refresh dispatch(loadAllICs({token: sessionToken})); @@ -62,21 +61,69 @@ const Infrastructure = (props) => { } } - const buttonStyle = { - marginLeft: '10px', - } - - const iconStyle = { - height: '30px', - width: '30px' + //modal actions and selectors + + const isEditModalOpened = useSelector(state => state.infrastructure.isEditModalOpened); + const isDeleteModalOpened = useSelector(state => state.infrastructure.isDeleteModalOpened); + const editModalIC = useSelector(state => state.infrastructure.editModalIC); + const deleteModalIC = useSelector(state => state.infrastructure.deleteModalIC); + + const onNewModalClose = (data) => { + setIsNewModalOpened(false); + + console.log("Adding ic. External: ", !data.managedexternally) + + if(data){ + if(!data.managedexternally){ + dispatch(addIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken}))); + }else { + // externally managed IC: dispatch create action to selected manager + let newAction = {}; + + newAction["action"] = "create"; + newAction["parameters"] = data.parameters; + newAction["when"] = new Date(); + + // find the manager IC + const managerIC = ics.find(ic => ic.uuid === data.manager) + if (managerIC === null || managerIC === undefined) { + NotificationsDataManager.addNotification(NotificationsFactory.ADD_ERROR("Could not find manager IC with UUID " + data.manager)); + return; + } + + dispatch(sendActionToIC({token: sessionToken, id: managerIC.id, actions: newAction})).then(res => dispatch(loadAllICs({token: sessionToken}))); + } + } } + const onImportModalClose = (data) => { + setIsImportModalOpened(false); + + dispatch(addIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken}))); + } + + const onEditModalClose = (data) => { + if(data){ + //some changes where done + dispatch(editIC({token: sessionToken, ic: data})).then(res => dispatch(loadAllICs({token: sessionToken}))); + } + dispatch(closeEditModal(data)); + } + + const onCloseDeleteModal = (isDeleteConfirmed) => { + if(isDeleteConfirmed){ + dispatch(deleteIC({token: sessionToken, id:deleteModalIC.id})).then(res => dispatch(loadAllICs({token: sessionToken}))); + } + dispatch(closeDeleteModal()); + } + + //getting list of managers for the new IC modal + const managers = ics.filter(ic => ic.category === "manager"); return (

Infrastructure - {//TODO - /* {currentUser.role === "Admin" ? + {currentUser.role === "Admin" ? { /> : - - } */} + }

{ category={"equipment"} /> + {currentUser.role === "Admin" ? : null} +
- {//TODO - /* this.closeNewModal(data)} managers={this.state.managers} /> - this.closeEditModal(data)} ic={this.state.modalIC} /> - this.closeImportModal(data)} /> - this.closeDeleteModal(e)} /> */} - + onNewModalClose(data)} managers={managers} /> + onImportModalClose(data)} /> + onEditModalClose(data)} + ic={editModalIC ? editModalIC : {}} + /> + onCloseDeleteModal(e)} + />
); } -export default Infrastructure; \ No newline at end of file +export default Infrastructure;