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