mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
rename SAVE to APPLY, show number of updates - #911
This commit is contained in:
@@ -4,6 +4,7 @@ import { range } from 'lodash';
|
|||||||
|
|
||||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import { createAPSettingsValidator, validate } from '../../validators';
|
import { createAPSettingsValidator, validate } from '../../validators';
|
||||||
import {
|
import {
|
||||||
@@ -16,7 +17,7 @@ import {
|
|||||||
} from '../../components';
|
} from '../../components';
|
||||||
|
|
||||||
import { APProvisionMode, APSettings } from '../../types';
|
import { APProvisionMode, APSettings } from '../../types';
|
||||||
import { numberValue, updateValue, useRest } from '../../utils';
|
import { numberValue, updateValueDirty, useRest } from '../../utils';
|
||||||
import * as APApi from '../../api/ap';
|
import * as APApi from '../../api/ap';
|
||||||
|
|
||||||
import { useI18nContext } from '../../i18n/i18n-react';
|
import { useI18nContext } from '../../i18n/i18n-react';
|
||||||
@@ -26,16 +27,17 @@ export const isAPEnabled = ({ provision_mode }: APSettings) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const APSettingsForm: FC = () => {
|
const APSettingsForm: FC = () => {
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<APSettings>({
|
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
|
||||||
read: APApi.readAPSettings,
|
useRest<APSettings>({
|
||||||
update: APApi.updateAPSettings
|
read: APApi.readAPSettings,
|
||||||
});
|
update: APApi.updateAPSettings
|
||||||
|
});
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -163,18 +165,30 @@ const APSettingsForm: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<ButtonRow>
|
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<Button
|
<ButtonRow>
|
||||||
startIcon={<SaveIcon />}
|
<Button
|
||||||
disabled={saving}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
disabled={saving}
|
||||||
color="primary"
|
variant="outlined"
|
||||||
type="submit"
|
color="primary"
|
||||||
onClick={validateAndSubmit}
|
type="submit"
|
||||||
>
|
onClick={() => loadData()}
|
||||||
{LL.SAVE()}
|
>
|
||||||
</Button>
|
{LL.CANCEL()}
|
||||||
</ButtonRow>
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={validateAndSubmit}
|
||||||
|
>
|
||||||
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ValidateFieldsError } from 'async-validator';
|
|||||||
|
|
||||||
import { Button, Checkbox, MenuItem, Grid, Typography, InputAdornment } from '@mui/material';
|
import { Button, Checkbox, MenuItem, Grid, Typography, InputAdornment } from '@mui/material';
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import { createMqttSettingsValidator, validate } from '../../validators';
|
import { createMqttSettingsValidator, validate } from '../../validators';
|
||||||
import {
|
import {
|
||||||
@@ -14,22 +15,23 @@ import {
|
|||||||
ValidatedTextField
|
ValidatedTextField
|
||||||
} from '../../components';
|
} from '../../components';
|
||||||
import { MqttSettings } from '../../types';
|
import { MqttSettings } from '../../types';
|
||||||
import { numberValue, updateValue, useRest } from '../../utils';
|
import { numberValue, updateValueDirty, useRest } from '../../utils';
|
||||||
import * as MqttApi from '../../api/mqtt';
|
import * as MqttApi from '../../api/mqtt';
|
||||||
|
|
||||||
import { useI18nContext } from '../../i18n/i18n-react';
|
import { useI18nContext } from '../../i18n/i18n-react';
|
||||||
|
|
||||||
const MqttSettingsForm: FC = () => {
|
const MqttSettingsForm: FC = () => {
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<MqttSettings>({
|
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
|
||||||
read: MqttApi.readMqttSettings,
|
useRest<MqttSettings>({
|
||||||
update: MqttApi.updateMqttSettings
|
read: MqttApi.readMqttSettings,
|
||||||
});
|
update: MqttApi.updateMqttSettings
|
||||||
|
});
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -372,18 +374,31 @@ const MqttSettingsForm: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ButtonRow>
|
|
||||||
<Button
|
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
startIcon={<SaveIcon />}
|
<ButtonRow>
|
||||||
disabled={saving}
|
<Button
|
||||||
variant="outlined"
|
startIcon={<CancelIcon />}
|
||||||
color="primary"
|
disabled={saving}
|
||||||
type="submit"
|
variant="outlined"
|
||||||
onClick={validateAndSubmit}
|
color="primary"
|
||||||
>
|
type="submit"
|
||||||
{LL.SAVE()}
|
onClick={() => loadData()}
|
||||||
</Button>
|
>
|
||||||
</ButtonRow>
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={validateAndSubmit}
|
||||||
|
>
|
||||||
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
|
|||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BlockFormControlLabel,
|
BlockFormControlLabel,
|
||||||
@@ -32,7 +33,7 @@ import {
|
|||||||
} from '../../components';
|
} from '../../components';
|
||||||
import { NetworkSettings } from '../../types';
|
import { NetworkSettings } from '../../types';
|
||||||
import * as NetworkApi from '../../api/network';
|
import * as NetworkApi from '../../api/network';
|
||||||
import { numberValue, updateValue, useRest } from '../../utils';
|
import { numberValue, updateValueDirty, useRest } from '../../utils';
|
||||||
import * as EMSESP from '../../project/api';
|
import * as EMSESP from '../../project/api';
|
||||||
|
|
||||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||||
@@ -52,7 +53,18 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
|
|
||||||
const [initialized, setInitialized] = useState(false);
|
const [initialized, setInitialized] = useState(false);
|
||||||
const [restarting, setRestarting] = useState(false);
|
const [restarting, setRestarting] = useState(false);
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage, restartNeeded } = useRest<NetworkSettings>({
|
const {
|
||||||
|
loadData,
|
||||||
|
saving,
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
origData,
|
||||||
|
dirtyFlags,
|
||||||
|
setDirtyFlags,
|
||||||
|
saveData,
|
||||||
|
errorMessage,
|
||||||
|
restartNeeded
|
||||||
|
} = useRest<NetworkSettings>({
|
||||||
read: NetworkApi.readNetworkSettings,
|
read: NetworkApi.readNetworkSettings,
|
||||||
update: NetworkApi.updateNetworkSettings
|
update: NetworkApi.updateNetworkSettings
|
||||||
});
|
});
|
||||||
@@ -78,7 +90,7 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
}
|
}
|
||||||
}, [initialized, setInitialized, data, setData, selectedNetwork]);
|
}, [initialized, setInitialized, data, setData, selectedNetwork]);
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
@@ -287,8 +299,19 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
)}
|
)}
|
||||||
{!restartNeeded && (
|
|
||||||
|
{!restartNeeded && dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => loadData()}
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SaveIcon />}
|
startIcon={<SaveIcon />}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
@@ -297,7 +320,7 @@ const WiFiSettingsForm: FC = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
onClick={validateAndSubmit}
|
onClick={validateAndSubmit}
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { ValidateFieldsError } from 'async-validator';
|
|||||||
|
|
||||||
import { Button, Checkbox, MenuItem } from '@mui/material';
|
import { Button, Checkbox, MenuItem } from '@mui/material';
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import { validate } from '../../validators';
|
import { validate } from '../../validators';
|
||||||
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedTextField } from '../../components';
|
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedTextField } from '../../components';
|
||||||
import { NTPSettings } from '../../types';
|
import { NTPSettings } from '../../types';
|
||||||
import { updateValue, useRest } from '../../utils';
|
import { updateValueDirty, useRest } from '../../utils';
|
||||||
import * as NTPApi from '../../api/ntp';
|
import * as NTPApi from '../../api/ntp';
|
||||||
import { selectedTimeZone, timeZoneSelectItems, TIME_ZONES } from './TZ';
|
import { selectedTimeZone, timeZoneSelectItems, TIME_ZONES } from './TZ';
|
||||||
import { NTP_SETTINGS_VALIDATOR } from '../../validators/ntp';
|
import { NTP_SETTINGS_VALIDATOR } from '../../validators/ntp';
|
||||||
@@ -15,14 +16,15 @@ import { NTP_SETTINGS_VALIDATOR } from '../../validators/ntp';
|
|||||||
import { useI18nContext } from '../../i18n/i18n-react';
|
import { useI18nContext } from '../../i18n/i18n-react';
|
||||||
|
|
||||||
const NTPSettingsForm: FC = () => {
|
const NTPSettingsForm: FC = () => {
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<NTPSettings>({
|
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
|
||||||
read: NTPApi.readNTPSettings,
|
useRest<NTPSettings>({
|
||||||
update: NTPApi.updateNTPSettings
|
read: NTPApi.readNTPSettings,
|
||||||
});
|
update: NTPApi.updateNTPSettings
|
||||||
|
});
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
@@ -79,18 +81,30 @@ const NTPSettingsForm: FC = () => {
|
|||||||
<MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem>
|
<MenuItem disabled>{LL.TIME_ZONE()}...</MenuItem>
|
||||||
{timeZoneSelectItems()}
|
{timeZoneSelectItems()}
|
||||||
</ValidatedTextField>
|
</ValidatedTextField>
|
||||||
<ButtonRow>
|
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<Button
|
<ButtonRow>
|
||||||
startIcon={<SaveIcon />}
|
<Button
|
||||||
disabled={saving}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
disabled={saving}
|
||||||
color="primary"
|
variant="outlined"
|
||||||
type="submit"
|
color="primary"
|
||||||
onClick={validateAndSubmit}
|
type="submit"
|
||||||
>
|
onClick={() => loadData()}
|
||||||
{LL.SAVE()}
|
>
|
||||||
</Button>
|
{LL.CANCEL()}
|
||||||
</ButtonRow>
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={validateAndSubmit}
|
||||||
|
>
|
||||||
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const NTPStatusForm: FC = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
autoFocus
|
autoFocus
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ const ManageUsersForm: FC = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ import { ValidateFieldsError } from 'async-validator';
|
|||||||
|
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import * as SecurityApi from '../../api/security';
|
import * as SecurityApi from '../../api/security';
|
||||||
import { SecuritySettings } from '../../types';
|
import { SecuritySettings } from '../../types';
|
||||||
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField } from '../../components';
|
import { ButtonRow, FormLoader, MessageBox, SectionContent, ValidatedPasswordField } from '../../components';
|
||||||
import { SECURITY_SETTINGS_VALIDATOR, validate } from '../../validators';
|
import { SECURITY_SETTINGS_VALIDATOR, validate } from '../../validators';
|
||||||
import { updateValue, useRest } from '../../utils';
|
import { updateValueDirty, useRest } from '../../utils';
|
||||||
import { AuthenticatedContext } from '../../contexts/authentication';
|
import { AuthenticatedContext } from '../../contexts/authentication';
|
||||||
|
|
||||||
import { useI18nContext } from '../../i18n/i18n-react';
|
import { useI18nContext } from '../../i18n/i18n-react';
|
||||||
@@ -17,13 +18,15 @@ const SecuritySettingsForm: FC = () => {
|
|||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<SecuritySettings>({
|
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
|
||||||
read: SecurityApi.readSecuritySettings,
|
useRest<SecuritySettings>({
|
||||||
update: SecurityApi.updateSecuritySettings
|
read: SecurityApi.readSecuritySettings,
|
||||||
});
|
update: SecurityApi.updateSecuritySettings
|
||||||
|
});
|
||||||
|
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
const authenticatedContext = useContext(AuthenticatedContext);
|
||||||
const updateFormValue = updateValue(setData);
|
|
||||||
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const content = () => {
|
const content = () => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -54,18 +57,30 @@ const SecuritySettingsForm: FC = () => {
|
|||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<MessageBox level="info" message={LL.SU_TEXT()} mt={1} />
|
<MessageBox level="info" message={LL.SU_TEXT()} mt={1} />
|
||||||
<ButtonRow>
|
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<Button
|
<ButtonRow>
|
||||||
startIcon={<SaveIcon />}
|
<Button
|
||||||
disabled={saving}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
disabled={saving}
|
||||||
color="primary"
|
variant="outlined"
|
||||||
type="submit"
|
color="primary"
|
||||||
onClick={validateAndSubmit}
|
type="submit"
|
||||||
>
|
onClick={() => loadData()}
|
||||||
{LL.SAVE()}
|
>
|
||||||
</Button>
|
{LL.CANCEL()}
|
||||||
</ButtonRow>
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={validateAndSubmit}
|
||||||
|
>
|
||||||
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { FC, useState } from 'react';
|
|||||||
|
|
||||||
import { Button, Checkbox } from '@mui/material';
|
import { Button, Checkbox } from '@mui/material';
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
import * as SystemApi from '../../api/system';
|
import * as SystemApi from '../../api/system';
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +15,7 @@ import {
|
|||||||
} from '../../components';
|
} from '../../components';
|
||||||
|
|
||||||
import { OTASettings } from '../../types';
|
import { OTASettings } from '../../types';
|
||||||
import { numberValue, updateValue, useRest } from '../../utils';
|
import { numberValue, updateValueDirty, useRest } from '../../utils';
|
||||||
|
|
||||||
import { ValidateFieldsError } from 'async-validator';
|
import { ValidateFieldsError } from 'async-validator';
|
||||||
import { validate } from '../../validators';
|
import { validate } from '../../validators';
|
||||||
@@ -23,14 +24,15 @@ import { OTA_SETTINGS_VALIDATOR } from '../../validators/system';
|
|||||||
import { useI18nContext } from '../../i18n/i18n-react';
|
import { useI18nContext } from '../../i18n/i18n-react';
|
||||||
|
|
||||||
const OTASettingsForm: FC = () => {
|
const OTASettingsForm: FC = () => {
|
||||||
const { loadData, saving, data, setData, saveData, errorMessage } = useRest<OTASettings>({
|
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
|
||||||
read: SystemApi.readOTASettings,
|
useRest<OTASettings>({
|
||||||
update: SystemApi.updateOTASettings
|
read: SystemApi.readOTASettings,
|
||||||
});
|
update: SystemApi.updateOTASettings
|
||||||
|
});
|
||||||
|
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
@@ -76,18 +78,30 @@ const OTASettingsForm: FC = () => {
|
|||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
<ButtonRow>
|
{dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<Button
|
<ButtonRow>
|
||||||
startIcon={<SaveIcon />}
|
<Button
|
||||||
disabled={saving}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
disabled={saving}
|
||||||
color="primary"
|
variant="outlined"
|
||||||
type="submit"
|
color="primary"
|
||||||
onClick={validateAndSubmit}
|
type="submit"
|
||||||
>
|
onClick={() => loadData()}
|
||||||
{LL.SAVE()}
|
>
|
||||||
</Button>
|
{LL.CANCEL()}
|
||||||
</ButtonRow>
|
</Button>
|
||||||
|
<Button
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={validateAndSubmit}
|
||||||
|
>
|
||||||
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const de: Translation = {
|
|||||||
DASHBOARD: 'Kontrollzentrum',
|
DASHBOARD: 'Kontrollzentrum',
|
||||||
SETTINGS_OF: '{0} Einstellungen',
|
SETTINGS_OF: '{0} Einstellungen',
|
||||||
SAVED: 'gespeichert',
|
SAVED: 'gespeichert',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
HELP_OF: '{0} Hilfe',
|
HELP_OF: '{0} Hilfe',
|
||||||
LOGGED_IN: 'Eingeloggt als {name}',
|
LOGGED_IN: 'Eingeloggt als {name}',
|
||||||
PLEASE_SIGNIN: 'Bitte einloggen, um fortzufahren',
|
PLEASE_SIGNIN: 'Bitte einloggen, um fortzufahren',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const en: Translation = {
|
|||||||
RESET: 'Reset',
|
RESET: 'Reset',
|
||||||
SEND: 'Send',
|
SEND: 'Send',
|
||||||
SAVE: 'Save',
|
SAVE: 'Save',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})',
|
||||||
|
UPDATE: 'Update',
|
||||||
REMOVE: 'Remove',
|
REMOVE: 'Remove',
|
||||||
PROBLEM_UPDATING: 'Problem updating',
|
PROBLEM_UPDATING: 'Problem updating',
|
||||||
PROBLEM_LOADING: 'Problem loading',
|
PROBLEM_LOADING: 'Problem loading',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const fr: Translation = {
|
|||||||
RESET: 'Réinitialiser',
|
RESET: 'Réinitialiser',
|
||||||
SEND: 'Envoyer',
|
SEND: 'Envoyer',
|
||||||
SAVE: 'Sauvegarder',
|
SAVE: 'Sauvegarder',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
REMOVE: 'Enlever',
|
REMOVE: 'Enlever',
|
||||||
PROBLEM_UPDATING: 'Problème lors de la mise à jour',
|
PROBLEM_UPDATING: 'Problème lors de la mise à jour',
|
||||||
PROBLEM_LOADING: 'Problème lors du chargement',
|
PROBLEM_LOADING: 'Problème lors du chargement',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const nl: Translation = {
|
|||||||
RESET: 'Reset',
|
RESET: 'Reset',
|
||||||
SEND: 'Verzenden',
|
SEND: 'Verzenden',
|
||||||
SAVE: 'Opslaan',
|
SAVE: 'Opslaan',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
REMOVE: 'Verwijderen',
|
REMOVE: 'Verwijderen',
|
||||||
PROBLEM_UPDATING: 'Probleem met updaten',
|
PROBLEM_UPDATING: 'Probleem met updaten',
|
||||||
PROBLEM_LOADING: 'Probleem met laden',
|
PROBLEM_LOADING: 'Probleem met laden',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const no: Translation = {
|
|||||||
RESET: 'Nullstill',
|
RESET: 'Nullstill',
|
||||||
SEND: 'Send',
|
SEND: 'Send',
|
||||||
SAVE: 'Lagre',
|
SAVE: 'Lagre',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
REMOVE: 'Fjern',
|
REMOVE: 'Fjern',
|
||||||
PROBLEM_UPDATING: 'Problem med oppdatering',
|
PROBLEM_UPDATING: 'Problem med oppdatering',
|
||||||
PROBLEM_LOADING: 'Problem med opplasting',
|
PROBLEM_LOADING: 'Problem med opplasting',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const pl: BaseTranslation = {
|
|||||||
RESET: 'Reset{{uj|owanie|}}',
|
RESET: 'Reset{{uj|owanie|}}',
|
||||||
SEND: 'Wyślij',
|
SEND: 'Wyślij',
|
||||||
SAVE: 'Zapisz',
|
SAVE: 'Zapisz',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
REMOVE: 'Usuń',
|
REMOVE: 'Usuń',
|
||||||
PROBLEM_UPDATING: 'Problem z aktualizacją!',
|
PROBLEM_UPDATING: 'Problem z aktualizacją!',
|
||||||
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
PROBLEM_LOADING: 'Problem z załadowaniem!',
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ const sv: Translation = {
|
|||||||
RESET: 'Nollsäll',
|
RESET: 'Nollsäll',
|
||||||
SEND: 'Skicka',
|
SEND: 'Skicka',
|
||||||
SAVE: 'Spara',
|
SAVE: 'Spara',
|
||||||
|
APPLY_CHANGES: 'Apply Changes ({0})', // TODO translate
|
||||||
|
UPDATE: 'Update', // TODO translate
|
||||||
REMOVE: 'Ta bort',
|
REMOVE: 'Ta bort',
|
||||||
PROBLEM_UPDATING: 'Problem vid uppdatering',
|
PROBLEM_UPDATING: 'Problem vid uppdatering',
|
||||||
PROBLEM_LOADING: 'Problem vid hämtning',
|
PROBLEM_LOADING: 'Problem vid hämtning',
|
||||||
|
|||||||
@@ -634,7 +634,7 @@ const DashboardData: FC = () => {
|
|||||||
onClick={() => sendSensor()}
|
onClick={() => sendSensor()}
|
||||||
color="warning"
|
color="warning"
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -1056,7 +1056,7 @@ const DashboardData: FC = () => {
|
|||||||
<ValidatedTextField
|
<ValidatedTextField
|
||||||
name="g"
|
name="g"
|
||||||
label="GPIO"
|
label="GPIO"
|
||||||
value={analog.g}
|
value={numberValue(analog.g)}
|
||||||
fullWidth
|
fullWidth
|
||||||
type="number"
|
type="number"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -1235,7 +1235,7 @@ const DashboardData: FC = () => {
|
|||||||
onClick={() => sendAnalog()}
|
onClick={() => sendAnalog()}
|
||||||
color="warning"
|
color="warning"
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useSnackbar } from 'notistack';
|
|||||||
import { Box, Button, Checkbox, MenuItem, Grid, Typography, Divider, InputAdornment } from '@mui/material';
|
import { Box, Button, Checkbox, MenuItem, Grid, Typography, Divider, InputAdornment } from '@mui/material';
|
||||||
|
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from '@mui/icons-material/Save';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||||
|
|
||||||
import { validate } from '../validators';
|
import { validate } from '../validators';
|
||||||
@@ -19,7 +20,7 @@ import {
|
|||||||
ButtonRow,
|
ButtonRow,
|
||||||
MessageBox
|
MessageBox
|
||||||
} from '../components';
|
} from '../components';
|
||||||
import { numberValue, extractErrorMessage, updateValue, useRest } from '../utils';
|
import { numberValue, extractErrorMessage, updateValueDirty, useRest } from '../utils';
|
||||||
|
|
||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
import { Settings, BOARD_PROFILES } from './types';
|
import { Settings, BOARD_PROFILES } from './types';
|
||||||
@@ -36,7 +37,18 @@ export function boardProfileSelectItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SettingsApplication: FC = () => {
|
const SettingsApplication: FC = () => {
|
||||||
const { loadData, saveData, saving, setData, data, errorMessage, restartNeeded } = useRest<Settings>({
|
const {
|
||||||
|
loadData,
|
||||||
|
saveData,
|
||||||
|
saving,
|
||||||
|
setData,
|
||||||
|
data,
|
||||||
|
origData,
|
||||||
|
dirtyFlags,
|
||||||
|
setDirtyFlags,
|
||||||
|
errorMessage,
|
||||||
|
restartNeeded
|
||||||
|
} = useRest<Settings>({
|
||||||
read: EMSESP.readSettings,
|
read: EMSESP.readSettings,
|
||||||
update: EMSESP.writeSettings
|
update: EMSESP.writeSettings
|
||||||
});
|
});
|
||||||
@@ -46,7 +58,7 @@ const SettingsApplication: FC = () => {
|
|||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const updateFormValue = updateValue(setData);
|
const updateFormValue = updateValueDirty(origData, dirtyFlags, setDirtyFlags, setData);
|
||||||
|
|
||||||
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
const [processingBoard, setProcessingBoard] = useState<boolean>(false);
|
const [processingBoard, setProcessingBoard] = useState<boolean>(false);
|
||||||
@@ -431,7 +443,7 @@ const SettingsApplication: FC = () => {
|
|||||||
endAdornment: <InputAdornment position="end">{LL.MINUTES()}</InputAdornment>
|
endAdornment: <InputAdornment position="end">{LL.MINUTES()}</InputAdornment>
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.shower_alert_trigger}
|
value={numberValue(data.shower_alert_trigger)}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
size="small"
|
size="small"
|
||||||
@@ -447,7 +459,7 @@ const SettingsApplication: FC = () => {
|
|||||||
endAdornment: <InputAdornment position="end">{LL.SECONDS()}</InputAdornment>
|
endAdornment: <InputAdornment position="end">{LL.SECONDS()}</InputAdornment>
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.shower_alert_coldshot}
|
value={numberValue(data.shower_alert_coldshot)}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
size="small"
|
size="small"
|
||||||
@@ -566,7 +578,7 @@ const SettingsApplication: FC = () => {
|
|||||||
label="Port"
|
label="Port"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.syslog_port}
|
value={numberValue(data.syslog_port)}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
@@ -603,7 +615,7 @@ const SettingsApplication: FC = () => {
|
|||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={data.syslog_mark_interval}
|
value={numberValue(data.syslog_mark_interval)}
|
||||||
type="number"
|
type="number"
|
||||||
onChange={updateFormValue}
|
onChange={updateFormValue}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
@@ -619,8 +631,19 @@ const SettingsApplication: FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</MessageBox>
|
</MessageBox>
|
||||||
)}
|
)}
|
||||||
{!restartNeeded && (
|
|
||||||
|
{!restartNeeded && dirtyFlags && dirtyFlags.length !== 0 && (
|
||||||
<ButtonRow>
|
<ButtonRow>
|
||||||
|
<Button
|
||||||
|
startIcon={<CancelIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => loadData()}
|
||||||
|
>
|
||||||
|
{LL.CANCEL()}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
startIcon={<SaveIcon />}
|
startIcon={<SaveIcon />}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
@@ -629,7 +652,7 @@ const SettingsApplication: FC = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
onClick={validateAndSubmit}
|
onClick={validateAndSubmit}
|
||||||
>
|
>
|
||||||
{LL.SAVE()}
|
{LL.APPLY_CHANGES(dirtyFlags.length)}
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -21,3 +21,29 @@ export const updateValue =
|
|||||||
[event.target.name]: extractEventValue(event)
|
[event.target.name]: extractEventValue(event)
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateValueDirty =
|
||||||
|
<S>(origData: any, dirtyFlags: any, setDirtyFlags: any, updateEntity: UpdateEntity<S>) =>
|
||||||
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const updated_value = extractEventValue(event);
|
||||||
|
const name = event.target.name;
|
||||||
|
updateEntity((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[name]: updated_value
|
||||||
|
}));
|
||||||
|
|
||||||
|
const arr: string[] = dirtyFlags;
|
||||||
|
|
||||||
|
if (origData[name] !== updated_value) {
|
||||||
|
if (!arr.includes(name)) {
|
||||||
|
arr.push(name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const startIndex = arr.indexOf(name);
|
||||||
|
if (startIndex !== -1) {
|
||||||
|
arr.splice(startIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setDirtyFlags(arr);
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,16 +16,22 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
|
|||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const [saving, setSaving] = useState<boolean>(false);
|
|
||||||
const [data, setData] = useState<D>();
|
const [data, setData] = useState<D>();
|
||||||
|
const [saving, setSaving] = useState<boolean>(false);
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
const [restartNeeded, setRestartNeeded] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [origData, setOrigData] = useState<D>();
|
||||||
|
const [dirtyFlags, setDirtyFlags] = useState<string[]>();
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
setData(undefined);
|
setData(undefined);
|
||||||
|
setDirtyFlags([]);
|
||||||
setErrorMessage(undefined);
|
setErrorMessage(undefined);
|
||||||
try {
|
try {
|
||||||
setData((await read()).data);
|
const fetch_data = (await read()).data;
|
||||||
|
setData(fetch_data);
|
||||||
|
setOrigData(fetch_data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = extractErrorMessage(error, LL.PROBLEM_LOADING());
|
const message = extractErrorMessage(error, LL.PROBLEM_LOADING());
|
||||||
enqueueSnackbar(message, { variant: 'error' });
|
enqueueSnackbar(message, { variant: 'error' });
|
||||||
@@ -66,5 +72,16 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
|
|||||||
loadData();
|
loadData();
|
||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
return { loadData, saveData, saving, setData, data, errorMessage, restartNeeded } as const;
|
return {
|
||||||
|
loadData,
|
||||||
|
saveData,
|
||||||
|
saving,
|
||||||
|
setData,
|
||||||
|
data,
|
||||||
|
origData,
|
||||||
|
dirtyFlags,
|
||||||
|
setDirtyFlags,
|
||||||
|
errorMessage,
|
||||||
|
restartNeeded
|
||||||
|
} as const;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user