From d52c2de29ee884bdb632e85aada917a9d9830825 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Fri, 17 Oct 2025 12:29:57 +0200 Subject: [PATCH 1/4] Replaced multi-select-rect-dropdown with a custom component Custom component with same functionalities was made using bootstrap components Package was deleted from the project Replaced import of multiselect in edit-config.js Signed-off-by: Andrii Podriez --- package.json | 1 - src/common/multiselect.jsx | 79 +++++++++ src/pages/scenarios/dialogs/edit-config.js | 185 ++++++++++++--------- 3 files changed, 186 insertions(+), 79 deletions(-) create mode 100644 src/common/multiselect.jsx diff --git a/package.json b/package.json index b666cf9b..2380a807 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "lodash": "^4.17.21", "moment": "^2.29.3", "moment-duration-format": "^2.3.2", - "multiselect-react-dropdown": "^2.0.21", "prop-types": "^15.8.1", "rc-slider": "^10.0.0", "react": "^17.0.2", diff --git a/src/common/multiselect.jsx b/src/common/multiselect.jsx new file mode 100644 index 00000000..b3127f28 --- /dev/null +++ b/src/common/multiselect.jsx @@ -0,0 +1,79 @@ +/** + * 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 { useEffect, useState } from "react"; +import { Dropdown, Form } from "react-bootstrap"; + +//a dropdown with a checklist +//each item has to be an object and have parameters id and name +const MultiselectDropdown = ({ items, checkedInitialyIDs, onUpdate }) => { + const [selectedItems, setSelectedItems] = useState([]); + + //push items that are to be checked upon component mount + useEffect(() => { + if (checkedInitialyIDs.length > 0) { + const initialItems = items.filter((item) => + checkedInitialyIDs.includes(item.id) + ); + setSelectedItems(initialItems); + } + }, [checkedInitialyIDs]); + + const handleItemCheck = (item) => { + let updatedList; + + if (selectedItems.some((i) => i.id === item.id)) { + updatedList = selectedItems.filter((i) => i.id !== item.id); + } else { + updatedList = [...selectedItems, item]; + } + + setSelectedItems(updatedList); + onUpdate(updatedList, item); + }; + + return ( + + + {selectedItems.length > 0 + ? selectedItems.map((i) => ( +
{i.name}
+ )) + : "Select file(s)..."} +
+ + + {items.map((item) => ( + i.id == item.id)} + onChange={() => handleItemCheck(item)} + style={{ + marginLeft: "0.5em", + marginRight: "0.5em", + paddingTop: "1em", + }} + /> + ))} + +
+ ); +}; + +export default MultiselectDropdown; diff --git a/src/pages/scenarios/dialogs/edit-config.js b/src/pages/scenarios/dialogs/edit-config.js index e2d73a6b..6801857a 100644 --- a/src/pages/scenarios/dialogs/edit-config.js +++ b/src/pages/scenarios/dialogs/edit-config.js @@ -15,14 +15,13 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; +import React from "react"; import Form from "@rjsf/core"; -import { Form as BForm } from 'react-bootstrap'; -import { Multiselect } from 'multiselect-react-dropdown' -import Dialog from '../../../common/dialogs/dialog'; -import ParametersEditor from '../../../common/parameters-editor'; - +import { Form as BForm } from "react-bootstrap"; +import MultiselectDropdown from "../../../common/multiselect"; +import Dialog from "../../../common/dialogs/dialog"; +import ParametersEditor from "../../../common/parameters-editor"; class EditConfigDialog extends React.Component { valid = false; @@ -30,12 +29,12 @@ class EditConfigDialog extends React.Component { constructor(props) { super(props); this.state = { - name: '', - icID: '', + name: "", + icID: "", startParameters: {}, formData: {}, startparamTemplate: null, - selectedFiles: [] // list of selected files {name, id}, this is not the fileIDs list of the config! + selectedFiles: [], // list of selected files {name, id}, this is not the fileIDs list of the config! }; } @@ -43,52 +42,69 @@ class EditConfigDialog extends React.Component { if (canceled === false) { if (this.valid) { let data = JSON.parse(JSON.stringify(this.props.config)); - if (this.state.name !== '' && this.props.config.name !== this.state.name) { + if ( + this.state.name !== "" && + this.props.config.name !== this.state.name + ) { data.name = this.state.name; } - if (this.state.icID !== '' && this.props.config.icID !== parseInt(this.state.icID)) { + if ( + this.state.icID !== "" && + this.props.config.icID !== parseInt(this.state.icID) + ) { data.icID = parseInt(this.state.icID, 10); } - if (Object.keys(this.state.startParameters).length === 0 && this.state.startParameters.constructor === Object && - JSON.stringify(this.props.config.startParameters) !== JSON.stringify(this.state.startParameters)) { + if ( + Object.keys(this.state.startParameters).length === 0 && + this.state.startParameters.constructor === Object && + JSON.stringify(this.props.config.startParameters) !== + JSON.stringify(this.state.startParameters) + ) { data.startParameters = this.state.startParameters; } - let IDs = [] + let IDs = []; for (let e of this.state.selectedFiles) { - IDs.push(e.id) + IDs.push(e.id); } - if (this.props.config.fileIDs !== null && this.props.config.fileIDs !== undefined) { - if (JSON.stringify(IDs) !== JSON.stringify(this.props.config.fileIDs)) { + if ( + this.props.config.fileIDs !== null && + this.props.config.fileIDs !== undefined + ) { + if ( + JSON.stringify(IDs) !== JSON.stringify(this.props.config.fileIDs) + ) { data.fileIDs = IDs; } - } - else { - data.fileIDs = IDs + } else { + data.fileIDs = IDs; } //forward modified config to callback function - this.props.onClose(data) + this.props.onClose(data); } } else { this.props.onClose(); } - this.setState({ startparamTemplate: null }) - this.valid = false + this.setState({ startparamTemplate: null }); + this.valid = false; } handleChange(e) { this.setState({ [e.target.id]: e.target.value }); - this.valid = this.isValid() + this.valid = this.isValid(); } changeIC(id) { let schema = null; if (this.props.ics) { - let currentIC = this.props.ics.find(ic => ic.id === parseInt(id, 10)); + let currentIC = this.props.ics.find((ic) => ic.id === parseInt(id, 10)); if (currentIC) { - if (currentIC.startparameterschema !== null && currentIC.startparameterschema.hasOwnProperty('type')) { + if ( + currentIC.startparameterschema !== null && + currentIC.startparameterschema.hasOwnProperty("type") + ) { schema = currentIC.startparameterschema; } } @@ -99,40 +115,44 @@ class EditConfigDialog extends React.Component { startparamTemplate: schema, }); - this.valid = this.isValid() + this.valid = this.isValid(); } handleParameterChange(data) { if (data) { this.setState({ startParameters: data }); } - this.valid = this.isValid() + this.valid = this.isValid(); } onFileChange(selectedList, changedItem) { this.setState({ - selectedFiles: selectedList - }) - this.valid = this.isValid() + selectedFiles: selectedList, + }); + this.valid = this.isValid(); } - isValid() { // input is valid if at least one element has changed from its initial value - return this.state.name !== '' - || this.state.icID !== '' - || Object.keys(this.state.startParameters).length === 0 && this.state.startParameters.constructor === Object + return ( + this.state.name !== "" || + this.state.icID !== "" || + (Object.keys(this.state.startParameters).length === 0 && + this.state.startParameters.constructor === Object) + ); } resetState() { - // determine list of selected files incl id and filename - let selectedFiles = [] - if (this.props.config.fileIDs !== null && this.props.config.fileIDs !== undefined) { + let selectedFiles = []; + if ( + this.props.config.fileIDs !== null && + this.props.config.fileIDs !== undefined + ) { for (let selectedFileID of this.props.config.fileIDs) { for (let file of this.props.files) { if (file.id === selectedFileID) { - selectedFiles.push({ name: file.name, id: file.id }) + selectedFiles.push({ name: file.name, id: file.id }); } } } @@ -140,9 +160,14 @@ class EditConfigDialog extends React.Component { let schema = null; if (this.props.ics && this.props.config.icID) { - let currentIC = this.props.ics.find(ic => ic.id === parseInt(this.props.config.icID, 10)); + let currentIC = this.props.ics.find( + (ic) => ic.id === parseInt(this.props.config.icID, 10) + ); if (currentIC) { - if (currentIC.startparameterschema !== null && currentIC.startparameterschema.hasOwnProperty('type')) { + if ( + currentIC.startparameterschema !== null && + currentIC.startparameterschema.hasOwnProperty("type") + ) { schema = currentIC.startparameterschema; } } @@ -157,21 +182,21 @@ class EditConfigDialog extends React.Component { }); } - handleFormChange({formData}) { - this.setState({formData: formData, startParameters: formData}) - this.valid = this.isValid() + handleFormChange({ formData }) { + this.setState({ formData: formData, startParameters: formData }); + this.valid = this.isValid(); } render() { - const ICOptions = this.props.ics.map(s => - - ); + const ICOptions = this.props.ics.map((s) => ( + + )); let configFileOptions = []; for (let file of this.props.files) { - configFileOptions.push( - { name: file.name, id: file.id } - ); + configFileOptions.push({ name: file.name, id: file.id }); } return ( @@ -184,7 +209,7 @@ class EditConfigDialog extends React.Component { valid={this.valid} > - + Name - - Infrastructure Component + + Infrastructure Component this.changeIC(e.target.value)} > @@ -207,35 +232,39 @@ class EditConfigDialog extends React.Component { - this.onFileChange(selectedList, selectedItem)} - onRemove={(selectedList, removedItem) => this.onFileChange(selectedList, removedItem)} - displayValue={'name'} - placeholder={'Select file(s)...'} + + this.onFileChange(selectedItems, item) + } /> -
- Start Parameters +
+ + Start Parameters + - {!this.state.startparamTemplate ? + {!this.state.startparamTemplate ? ( this.handleParameterChange(data)} - /> - : <>} + content={this.state.startParameters} + onChange={(data) => this.handleParameterChange(data)} + /> + ) : ( + <> + )}
- {this.state.startparamTemplate ? -
this.handleFormChange({formData})} - children={true} // hides submit button - /> - : <> } + {this.state.startparamTemplate ? ( + this.handleFormChange({ formData })} + children={true} // hides submit button + /> + ) : ( + <> + )} ); } From 1d33b865b10f2bf8970254729abd4c3301ad5b38 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Fri, 17 Oct 2025 16:00:19 +0200 Subject: [PATCH 2/4] Removed react-copy-to-clipboard package Relaced the package with navigator.clipboard call Signed-off-by: Andrii Podriez --- package.json | 1 - .../scenarios/dialogs/result-python-dialog.js | 170 +++++++++--------- 2 files changed, 89 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index 2380a807..2736d04d 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "react-collapse": "^5.1.1", "react-color": "^2.19.3", "react-contexify": "^5.0.0", - "react-copy-to-clipboard": "^5.1.0", "react-datetime-picker": "^3.5.0", "react-dnd": "^14.0.5", "react-dnd-html5-backend": "^14.1.0", diff --git a/src/pages/scenarios/dialogs/result-python-dialog.js b/src/pages/scenarios/dialogs/result-python-dialog.js index f86b53a0..3619f097 100644 --- a/src/pages/scenarios/dialogs/result-python-dialog.js +++ b/src/pages/scenarios/dialogs/result-python-dialog.js @@ -15,16 +15,15 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; -import { Button } from 'react-bootstrap'; -import Icon from '../../../common/icon'; -import Dialog from '../../../common/dialogs/dialog'; -import {CopyToClipboard} from 'react-copy-to-clipboard'; -import SyntaxHighlighter from 'react-syntax-highlighter'; -import { github } from 'react-syntax-highlighter/dist/esm/styles/hljs'; +import React from "react"; +import { Button } from "react-bootstrap"; +import Icon from "../../../common/icon"; +import Dialog from "../../../common/dialogs/dialog"; +import SyntaxHighlighter from "react-syntax-highlighter"; +import { github } from "react-syntax-highlighter/dist/esm/styles/hljs"; class ResultPythonDialog extends React.Component { - villasDataProcessingUrl = 'https://pypi.org/project/villas-dataprocessing/'; + villasDataProcessingUrl = "https://pypi.org/project/villas-dataprocessing/"; constructor(props) { super(props); @@ -38,11 +37,11 @@ class ResultPythonDialog extends React.Component { if (result) { const output = this.getJupyterNotebook(result); const blob = new Blob([JSON.stringify(output)], { - 'type': 'application/x-ipynb+json' + type: "application/x-ipynb+json", }); const url = URL.createObjectURL(blob); - this.setState({ fileDownloadUrl: url }) + this.setState({ fileDownloadUrl: url }); } } } @@ -51,26 +50,26 @@ class ResultPythonDialog extends React.Component { const result = this.props.results[this.props.resultId]; const output = this.getJupyterNotebook(result); const blob = new Blob([JSON.stringify(output)], { - 'type': 'application/x-ipynb+json' + type: "application/x-ipynb+json", }); var url = window.URL.createObjectURL(blob); - var a = document.createElement('a'); - a.style = 'display: none'; + var a = document.createElement("a"); + a.style = "display: none"; a.href = url; a.download = `villas_web_result_${result.id}.ipynb`; document.body.appendChild(a); a.click(); - setTimeout(function(){ + setTimeout(function () { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 100); } getPythonDependencies(notebook) { - let code = ''; + let code = ""; if (notebook) code += `import sys !{sys.executable} -m `; @@ -81,7 +80,7 @@ class ResultPythonDialog extends React.Component { } getPythonSnippets(notebook, result) { - let token = localStorage.getItem('token'); + let token = localStorage.getItem("token"); let files = []; for (let file of this.props.files) { @@ -95,17 +94,18 @@ class ResultPythonDialog extends React.Component { let code_snippets = []; /* Imports */ - let code_imports = ''; - if (notebook) - code_imports += 'from IPython.display import display\n' + let code_imports = ""; + if (notebook) code_imports += "from IPython.display import display\n"; - code_imports += `from villas.web.result import Result\n` - code_imports += `from pprint import pprint` + code_imports += `from villas.web.result import Result\n`; + code_imports += `from pprint import pprint`; - code_snippets.push(code_imports) + code_snippets.push(code_imports); /* Result object */ - code_snippets.push(`r = Result(${result.id}, '${token}', endpoint='https://slew.k8s.eonerc.rwth-aachen.de')`); + code_snippets.push( + `r = Result(${result.id}, '${token}', endpoint='https://slew.k8s.eonerc.rwth-aachen.de')` + ); /* Examples */ code_snippets.push(`# Get result metadata @@ -139,22 +139,22 @@ f${file.id} = r.get_file_by_name('${file.name}')`; display(f${file.id})\n`; switch (file.type) { - case 'application/zip': + case "application/zip": code += `\n# Open a file within the zipped results with f${file.id}.open_zip('file_in_zip.csv') as f: f${file.id} = pandas.read_csv(f)`; break; - case 'text/csv': - case 'application/vnd.ms-excel': - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': - case 'application/x-hdf5': - case 'application/x-matlab-data': + case "text/csv": + case "application/vnd.ms-excel": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": + case "application/x-hdf5": + case "application/x-matlab-data": code += `\n# Load tables as Pandas dataframe f${file.id} = f${file.id}.load()`; break; - case 'application/json': + case "application/json": code += `\n# Load JSON file as Python dictionary f${file.id} = f${file.id}.load()`; break; @@ -176,107 +176,115 @@ f${file.id} = f${file.id}.load()`; * See: https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html */ getCellId() { - var result = []; - var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var result = []; + var characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var charactersLength = characters.length; - for ( var i = 0; i < 8; i++ ) - result.push(characters.charAt(Math.floor(Math.random() * charactersLength))); + for (var i = 0; i < 8; i++) + result.push( + characters.charAt(Math.floor(Math.random() * charactersLength)) + ); - return result.join(''); + return result.join(""); } getJupyterNotebook(result) { let ipynb_cells = []; - let cells = [ this.getPythonDependencies(true) ]; + let cells = [this.getPythonDependencies(true)]; cells = cells.concat(this.getPythonSnippets(true, result)); for (let cell of cells) { - let lines = cell.split('\n'); + let lines = cell.split("\n"); - for (let i = 0; i < lines.length -1; i++) - lines[i] += '\n' + for (let i = 0; i < lines.length - 1; i++) lines[i] += "\n"; ipynb_cells.push({ - cell_type: 'code', + cell_type: "code", execution_count: null, id: this.getCellId(), metadata: {}, outputs: [], - source: lines - }) + source: lines, + }); } return { cells: ipynb_cells, metadata: { kernelspec: { - display_name: 'Python 3', - language: 'python', - name: 'python3' + display_name: "Python 3", + language: "python", + name: "python3", }, language_info: { codemirror_mode: { - name: 'ipython', - version: 3 + name: "ipython", + version: 3, }, - file_extension: '.py', - mimetype: 'text/x-python', - name: 'python', - nbconvert_exporter: 'python', - pygments_lexer: 'ipython3', - version: '3.9.5' - } + file_extension: ".py", + mimetype: "text/x-python", + name: "python", + nbconvert_exporter: "python", + pygments_lexer: "ipython3", + version: "3.9.5", + }, }, nbformat: 4, - nbformat_minor: 5 - } + nbformat_minor: 5, + }; } render() { let result = this.props.results[this.props.resultId]; - if (!result) - return null; + if (!result) return null; let snippets = this.getPythonSnippets(true, result); - let code = snippets.join('\n\n'); + let code = snippets.join("\n\n"); return ( this.props.onClose()} valid={true} - size='lg' + size="lg" blendOutCancel={true} > -

Use the following Python code-snippet to fetch and load your results as a Pandas dataframe.

- -

1) Please install the villas-controller Python package:

- +

+ Use the following Python code-snippet to fetch and load your results + as a Pandas dataframe. +

+ +

+ 1) Please install the{" "} + villas-controller Python + package: +

+ {this.getPythonDependencies(false)} -

2a) Insert the following snippet your Python code:

- +

+ 2a) Insert the following snippet your Python code: +

+ {code} - - - -

2b) Or alternatively, download the following generated Jupyter notebook to get started:

+ + +

+ 2b) Or alternatively, download the following generated Jupyter + notebook to get started: +

); From 74305ba682a1c00ff613b7ad7535e3d3db1872c4 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Fri, 17 Oct 2025 16:49:49 +0200 Subject: [PATCH 3/4] Migration from react-json-view to json-edit-react Replaced ReactJson components from previous package with new JsonEditor components Removed react-json-view dependency Signed-off-by: Andrii Podriez --- package.json | 2 +- src/common/parameters-editor.js | 82 +++++----- src/common/rawDataTable.js | 38 ++--- src/pages/infrastructure/ic-params-table.js | 147 +++++++++--------- .../dialogs/result-configs-dialog.js | 22 +-- 5 files changed, 149 insertions(+), 142 deletions(-) diff --git a/package.json b/package.json index 2736d04d..ca505d2a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "gaugeJS": "^1.3.7", "handlebars": "^4.7.7", "isomorphic-form-data": "^2.0.0", + "json-edit-react": "^1.29.0", "jsonwebtoken": "^8.5.1", "jszip": "^3.9.1", "lodash": "^4.17.21", @@ -42,7 +43,6 @@ "react-dnd": "^14.0.5", "react-dnd-html5-backend": "^14.1.0", "react-dom": "^17.0.2", - "react-json-view": "^1.21.3", "react-notification-system": "^0.4.0", "react-redux": "^7.2.8", "react-rnd": "^10.3.7", diff --git a/src/common/parameters-editor.js b/src/common/parameters-editor.js index aca6b052..a0e7959e 100644 --- a/src/common/parameters-editor.js +++ b/src/common/parameters-editor.js @@ -15,64 +15,64 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; -import PropTypes from 'prop-types'; -import JsonView from 'react-json-view'; +import React from "react"; +import PropTypes from "prop-types"; +import { JsonEditor } from "json-edit-react"; class ParametersEditor extends React.Component { - onAdd = event => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } + onAdd = (event) => { + if (this.props.onChange != null) { + this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); } + }; - onEdit = event => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } + onEdit = (event) => { + if (this.props.onChange != null) { + this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); } + }; - onDelete = event => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } + onDelete = (event) => { + if (this.props.onChange != null) { + this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); } + }; - render() { - const containerStyle = { - minHeight: '100px', + render() { + const containerStyle = { + minHeight: "100px", - paddingTop: '5px', - paddingBottom: '5px', - paddingLeft: '8px', + paddingTop: "5px", + paddingBottom: "5px", + paddingLeft: "8px", - border: '1px solid lightgray' - }; + border: "1px solid lightgray", + }; - return
- -
; - } + return ( +
+ +
+ ); + } } ParametersEditor.propTypes = { - content: PropTypes.object, - onChange: PropTypes.func, - disabled: PropTypes.bool + content: PropTypes.object, + onChange: PropTypes.func, + disabled: PropTypes.bool, }; ParametersEditor.defaultProps = { - content: {}, - disabled: false + content: {}, + disabled: false, }; export default ParametersEditor; diff --git a/src/common/rawDataTable.js b/src/common/rawDataTable.js index 87ff5bab..92ebcd18 100644 --- a/src/common/rawDataTable.js +++ b/src/common/rawDataTable.js @@ -1,23 +1,23 @@ import { isJSON } from "../utils/isJson"; -import ReactJson from "react-json-view"; +import { JsonEditor } from "json-edit-react"; const RawDataTable = (props) => { - if(props.rawData !== null && isJSON(props.rawData)){ - return ( - - ) - } else { - return ( -
No valid JSON raw data available.
- ) - } -} + if (props.rawData !== null && isJSON(props.rawData)) { + return ( + + ); + } else { + return
No valid JSON raw data available.
; + } +}; -export default RawDataTable; \ No newline at end of file +export default RawDataTable; diff --git a/src/pages/infrastructure/ic-params-table.js b/src/pages/infrastructure/ic-params-table.js index a134c5b2..50ff8532 100644 --- a/src/pages/infrastructure/ic-params-table.js +++ b/src/pages/infrastructure/ic-params-table.js @@ -1,74 +1,81 @@ -import { Table, } from 'react-bootstrap'; -import moment from 'moment'; -import { isJSON } from '../../utils/isJson'; -import ReactJson from 'react-json-view'; +import { Table } from "react-bootstrap"; +import moment from "moment"; +import { isJSON } from "../../utils/isJson"; +import { JsonEditor } from "json-edit-react"; const ICParamsTable = (props) => { - const ic = props.ic; + const ic = props.ic; - return( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyValue
Name{ic.name}
Description{ic.description}
UUID{ic.uuid}
State{ic.state}
Category{ic.category}
Type{ic.type}
Uptime{moment.duration(ic.uptime, "seconds").humanize()}
Location{ic.location}
Websocket URL{ic.websocketurl}
API URL{ic.apiurl}
Start parameter schema - {isJSON(ic.startparameterschema) ? - :
No Start parameter schema JSON available.
} -
- ) -} + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyValue
Name{ic.name}
Description{ic.description}
UUID{ic.uuid}
State{ic.state}
Category{ic.category}
Type{ic.type}
Uptime{moment.duration(ic.uptime, "seconds").humanize()}
Location{ic.location}
Websocket URL{ic.websocketurl}
API URL{ic.apiurl}
Start parameter schema + {isJSON(ic.startparameterschema) ? ( + + ) : ( +
No Start parameter schema JSON available.
+ )} +
+ ); +}; -export default ICParamsTable; \ No newline at end of file +export default ICParamsTable; diff --git a/src/pages/scenarios/dialogs/result-configs-dialog.js b/src/pages/scenarios/dialogs/result-configs-dialog.js index 0e62b138..25d2de86 100644 --- a/src/pages/scenarios/dialogs/result-configs-dialog.js +++ b/src/pages/scenarios/dialogs/result-configs-dialog.js @@ -14,11 +14,10 @@ * 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 Dialog from '../../../common/dialogs/dialog'; -import ReactJson from 'react-json-view'; - +import React from "react"; +import { Form } from "react-bootstrap"; +import Dialog from "../../../common/dialogs/dialog"; +import { JsonEditor } from "json-edit-react"; class ResultConfigDialog extends React.Component { valid = true; @@ -28,7 +27,7 @@ class ResultConfigDialog extends React.Component { this.state = { confirmCommand: false, - command: '', + command: "", }; } @@ -48,13 +47,14 @@ class ResultConfigDialog extends React.Component { blendOutCancel={true} > - From a7b546d74944898af9c8e91f123d0fbcbc2c33e9 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Thu, 30 Oct 2025 11:04:07 +0100 Subject: [PATCH 4/4] Fixed json editor in edit config dialogue Replaced data handling with a single function Updated validation function in edit-config dialogue Adjusted the size of the json editor box Signed-off-by: Andrii Podriez --- src/common/parameters-editor.js | 49 +++++++++------------- src/pages/scenarios/dialogs/edit-config.js | 15 +------ 2 files changed, 21 insertions(+), 43 deletions(-) diff --git a/src/common/parameters-editor.js b/src/common/parameters-editor.js index a0e7959e..872a5436 100644 --- a/src/common/parameters-editor.js +++ b/src/common/parameters-editor.js @@ -20,45 +20,34 @@ import PropTypes from "prop-types"; import { JsonEditor } from "json-edit-react"; class ParametersEditor extends React.Component { - onAdd = (event) => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } - }; - - onEdit = (event) => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } - }; - - onDelete = (event) => { - if (this.props.onChange != null) { - this.props.onChange(JSON.parse(JSON.stringify(event.updated_src))); - } + handleJsonUpdate = ({ newData }) => { + this.props.onChange(JSON.parse(JSON.stringify(newData))); }; render() { const containerStyle = { + width: "100%", minHeight: "100px", - - paddingTop: "5px", - paddingBottom: "5px", - paddingLeft: "8px", - + padding: "5px", border: "1px solid lightgray", + display: "flex", }; return ( -
- +
+
+ +
); } diff --git a/src/pages/scenarios/dialogs/edit-config.js b/src/pages/scenarios/dialogs/edit-config.js index 6801857a..a4ba089f 100644 --- a/src/pages/scenarios/dialogs/edit-config.js +++ b/src/pages/scenarios/dialogs/edit-config.js @@ -55,7 +55,7 @@ class EditConfigDialog extends React.Component { data.icID = parseInt(this.state.icID, 10); } if ( - Object.keys(this.state.startParameters).length === 0 && + Object.keys(this.state.startParameters).length !== 0 && this.state.startParameters.constructor === Object && JSON.stringify(this.props.config.startParameters) !== JSON.stringify(this.state.startParameters) @@ -67,18 +67,7 @@ class EditConfigDialog extends React.Component { for (let e of this.state.selectedFiles) { IDs.push(e.id); } - if ( - this.props.config.fileIDs !== null && - this.props.config.fileIDs !== undefined - ) { - if ( - JSON.stringify(IDs) !== JSON.stringify(this.props.config.fileIDs) - ) { - data.fileIDs = IDs; - } - } else { - data.fileIDs = IDs; - } + data.fileIDs = IDs; //forward modified config to callback function this.props.onClose(data);