diff --git a/typescript/frontend-marios2/src/content/dashboards/Configuration/ConfigurationSodistoreGrid.tsx b/typescript/frontend-marios2/src/content/dashboards/Configuration/ConfigurationSodistoreGrid.tsx new file mode 100644 index 000000000..4d101e817 --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/Configuration/ConfigurationSodistoreGrid.tsx @@ -0,0 +1,256 @@ +import { ConfigurationValues, JSONRecordData } from '../Log/graph.util'; +import { + Alert, + Box, + CardContent, + CircularProgress, + Container, + Grid, + IconButton, + TextField, + useTheme +} from '@mui/material'; + +import React, { useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import Button from '@mui/material/Button'; +import { Close as CloseIcon } from '@mui/icons-material'; +import axiosConfig from '../../../Resources/axiosConfig'; + +interface ConfigurationSodistoreGridProps { + values: JSONRecordData; + id: number; +} + +function ConfigurationSodistoreGrid(props: ConfigurationSodistoreGridProps) { + const intl = useIntl(); + if (props.values === null) { + return null; + } + + const theme = useTheme(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + const [updated, setUpdated] = useState(false); + + const [errors, setErrors] = useState({ + minimumSoC: false, + gridSetPoint: false + }); + + const SetErrorForField = (field_name: string, state: boolean) => { + setErrors((prevErrors) => ({ + ...prevErrors, + [field_name]: state + })); + }; + + const [formValues, setFormValues] = useState>({ + minimumSoC: props.values.Config?.MinSoc, + gridSetPoint: + props.values.Config?.GridSetPoint != null + ? (props.values.Config.GridSetPoint as number) / 1000 + : undefined + }); + + const handleChange = (e) => { + const { name, value } = e.target; + + switch (name) { + case 'minimumSoC': + if ( + /[^0-9.]/.test(value) || + isNaN(parseFloat(value)) || + parseFloat(value) > 100 + ) { + SetErrorForField(name, true); + } else { + SetErrorForField(name, false); + } + break; + case 'gridSetPoint': + if (/[^0-9.]/.test(value) || isNaN(parseFloat(value))) { + SetErrorForField(name, true); + } else { + SetErrorForField(name, false); + } + break; + default: + return true; + } + + setFormValues({ + ...formValues, + [name]: value + }); + }; + + const handleSubmit = async () => { + const configurationToSend: Partial = { + minimumSoC: formValues.minimumSoC, + gridSetPoint: formValues.gridSetPoint + }; + + setLoading(true); + const res = await axiosConfig + .post( + `/EditInstallationConfig?installationId=${props.id}&product=4`, + configurationToSend + ) + .catch((err) => { + if (err.response) { + setError(true); + setLoading(false); + } + }); + + if (res) { + setUpdated(true); + setLoading(false); + } + }; + + return ( + + + + + +
+ + } + name="minimumSoC" + value={formValues.minimumSoC ?? ''} + onChange={handleChange} + helperText={ + errors.minimumSoC ? ( + + {intl.formatMessage({ id: 'valueBetween0And100' })} + + ) : ( + '' + ) + } + fullWidth + /> +
+ +
+ + } + name="gridSetPoint" + value={formValues.gridSetPoint ?? ''} + onChange={handleChange} + helperText={ + errors.gridSetPoint ? ( + + {intl.formatMessage({ id: 'pleaseProvideValidNumber' })} + + ) : ( + '' + ) + } + fullWidth + /> +
+ +
+ + + {loading && ( + + )} + + {updated && ( + + + setUpdated(false)} + sx={{ marginLeft: '4px' }} + > + + + + )} + + {error && ( + + + setError(false)} + sx={{ marginLeft: '4px' }} + > + + + + )} +
+
+
+
+
+
+ ); +} + +export default ConfigurationSodistoreGrid; diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx index 41c6c1e81..1e423f162 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Installations/Installation.tsx @@ -28,6 +28,7 @@ import Topology from '../Topology/Topology'; import TopologySodistoreGrid from '../Topology/TopologySodistoreGrid'; import BatteryView from '../BatteryView/BatteryView'; import Configuration from '../Configuration/Configuration'; +import ConfigurationSodistoreGrid from '../Configuration/ConfigurationSodistoreGrid'; import PvView from '../PvView/PvView'; import InstallationTicketsTab from '../Tickets/InstallationTicketsTab'; import DocumentsTab from '../Documents/DocumentsTab'; @@ -605,20 +606,10 @@ function Installation(props: singleInstallationProps) { path={routes.configuration} element={ props.current_installation.product === 4 ? ( - // TODO: SodistoreGrid — implement actual configuration - - - Configuration not yet available - - + ) : ( = new Set([2, 4, 5]); +export const CHECKLIST_ENABLED_PRODUCTS: ReadonlySet = new Set([2, 5]); export const UPLOADABLE_SUBTASK_KEYS: ReadonlySet = new Set([ 'checklistStep8Sub1',