Warn user in WebUI of unsaved changes #911

This commit is contained in:
proddy
2023-02-05 18:46:59 +01:00
parent 834eceab16
commit 71de48fd32
32 changed files with 12956 additions and 157 deletions

View File

@@ -13,7 +13,8 @@ import {
FormLoader,
SectionContent,
ValidatedPasswordField,
ValidatedTextField
ValidatedTextField,
BlockNavigation
} from '../../components';
import { APProvisionMode, APSettings } from '../../types';
@@ -27,7 +28,7 @@ export const isAPEnabled = ({ provision_mode }: APSettings) => {
};
const APSettingsForm: FC = () => {
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
useRest<APSettings>({
read: APApi.readAPSettings,
update: APApi.updateAPSettings
@@ -195,6 +196,7 @@ const APSettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF(LL.ACCESS_POINT(1))} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()}
</SectionContent>
);

View File

@@ -27,6 +27,7 @@ const AccessPoint: FC = () => {
</RouterTabs>
<Routes>
<Route path="status" element={<APStatusForm />} />
<Route index element={<Navigate to="status" />} />
<Route
path="settings"
element={
@@ -35,6 +36,7 @@ const AccessPoint: FC = () => {
</RequireAdmin>
}
/>
{/* <Route path="/*" element={<Navigate to="status" />} /> */}
<Route path="/*" element={<Navigate replace to="status" />} />
</Routes>
</>

View File

@@ -2,6 +2,7 @@ import { FC, useState } from 'react';
import { ValidateFieldsError } from 'async-validator';
import { Button, Checkbox, MenuItem, Grid, Typography, InputAdornment } from '@mui/material';
import WarningIcon from '@mui/icons-material/Warning';
import CancelIcon from '@mui/icons-material/Cancel';
@@ -12,7 +13,8 @@ import {
FormLoader,
SectionContent,
ValidatedPasswordField,
ValidatedTextField
ValidatedTextField,
BlockNavigation
} from '../../components';
import { MqttSettings } from '../../types';
import { numberValue, updateValueDirty, useRest } from '../../utils';
@@ -21,7 +23,7 @@ import * as MqttApi from '../../api/mqtt';
import { useI18nContext } from '../../i18n/i18n-react';
const MqttSettingsForm: FC = () => {
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
useRest<MqttSettings>({
read: MqttApi.readMqttSettings,
update: MqttApi.updateMqttSettings
@@ -405,6 +407,7 @@ const MqttSettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF('MQTT')} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()}
</SectionContent>
);

View File

@@ -29,7 +29,8 @@ import {
SectionContent,
ValidatedPasswordField,
ValidatedTextField,
MessageBox
MessageBox,
BlockNavigation
} from '../../components';
import { NetworkSettings } from '../../types';
import * as NetworkApi from '../../api/network';
@@ -61,6 +62,7 @@ const WiFiSettingsForm: FC = () => {
origData,
dirtyFlags,
setDirtyFlags,
blocker,
saveData,
errorMessage,
restartNeeded
@@ -330,6 +332,7 @@ const WiFiSettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF(LL.NETWORK(1))} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <RestartMonitor /> : content()}
</SectionContent>
);

View File

@@ -6,7 +6,14 @@ import WarningIcon from '@mui/icons-material/Warning';
import CancelIcon from '@mui/icons-material/Cancel';
import { validate } from '../../validators';
import { BlockFormControlLabel, ButtonRow, FormLoader, SectionContent, ValidatedTextField } from '../../components';
import {
BlockFormControlLabel,
ButtonRow,
FormLoader,
SectionContent,
ValidatedTextField,
BlockNavigation
} from '../../components';
import { NTPSettings } from '../../types';
import { updateValueDirty, useRest } from '../../utils';
import * as NTPApi from '../../api/ntp';
@@ -16,7 +23,7 @@ import { NTP_SETTINGS_VALIDATOR } from '../../validators/ntp';
import { useI18nContext } from '../../i18n/i18n-react';
const NTPSettingsForm: FC = () => {
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
useRest<NTPSettings>({
read: NTPApi.readNTPSettings,
update: NTPApi.updateNTPSettings
@@ -111,6 +118,7 @@ const NTPSettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF('NTP')} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()}
</SectionContent>
);

View File

@@ -7,7 +7,14 @@ 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 {
ButtonRow,
FormLoader,
MessageBox,
SectionContent,
ValidatedPasswordField,
BlockNavigation
} from '../../components';
import { SECURITY_SETTINGS_VALIDATOR, validate } from '../../validators';
import { updateValueDirty, useRest } from '../../utils';
import { AuthenticatedContext } from '../../contexts/authentication';
@@ -18,7 +25,7 @@ const SecuritySettingsForm: FC = () => {
const { LL } = useI18nContext();
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
const { loadData, saving, data, setData, origData, dirtyFlags, blocker, setDirtyFlags, saveData, errorMessage } =
useRest<SecuritySettings>({
read: SecurityApi.readSecuritySettings,
update: SecurityApi.updateSecuritySettings
@@ -87,6 +94,7 @@ const SecuritySettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF(LL.SECURITY(1))} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()}
</SectionContent>
);

View File

@@ -11,7 +11,8 @@ import {
FormLoader,
SectionContent,
ValidatedPasswordField,
ValidatedTextField
ValidatedTextField,
BlockNavigation
} from '../../components';
import { OTASettings } from '../../types';
@@ -24,7 +25,7 @@ import { OTA_SETTINGS_VALIDATOR } from '../../validators/system';
import { useI18nContext } from '../../i18n/i18n-react';
const OTASettingsForm: FC = () => {
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, saveData, errorMessage } =
const { loadData, saving, data, setData, origData, dirtyFlags, setDirtyFlags, blocker, saveData, errorMessage } =
useRest<OTASettings>({
read: SystemApi.readOTASettings,
update: SystemApi.updateOTASettings
@@ -108,6 +109,7 @@ const OTASettingsForm: FC = () => {
return (
<SectionContent title={LL.SETTINGS_OF('OTA')} titleGutter>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{content()}
</SectionContent>
);

View File

@@ -5,7 +5,6 @@ import { Tab } from '@mui/material';
import { useRouterTab, RouterTabs, useLayoutTitle, RequireAdmin } from '../../components';
import { AuthenticatedContext } from '../../contexts/authentication';
import { FeaturesContext } from '../../contexts/features';
import UploadFileForm from './UploadFileForm';
import SystemStatusForm from './SystemStatusForm';
import OTASettingsForm from './OTASettingsForm';
@@ -20,7 +19,6 @@ const System: FC = () => {
useLayoutTitle(LL.SYSTEM(0));
const { me } = useContext(AuthenticatedContext);
const { features } = useContext(FeaturesContext);
const { routerTab } = useRouterTab();
return (
@@ -28,33 +26,28 @@ const System: FC = () => {
<RouterTabs value={routerTab}>
<Tab value="status" label={LL.STATUS_OF(LL.SYSTEM(1))} />
<Tab value="log" label={LL.LOG_OF(LL.SYSTEM(2))} />
{features.ota && <Tab value="ota" label={LL.SETTINGS_OF('OTA')} disabled={!me.admin} />}
{features.upload_firmware && <Tab value="upload" label={LL.UPLOAD_DOWNLOAD()} disabled={!me.admin} />}
<Tab value="ota" label={LL.SETTINGS_OF('OTA')} disabled={!me.admin} />
<Tab value="upload" label={LL.UPLOAD_DOWNLOAD()} disabled={!me.admin} />
</RouterTabs>
<Routes>
<Route path="status" element={<SystemStatusForm />} />
<Route path="log" element={<SystemLog />} />
{features.ota && (
<Route
path="ota"
element={
<RequireAdmin>
<OTASettingsForm />
</RequireAdmin>
}
/>
)}
{features.upload_firmware && (
<Route
path="upload"
element={
<RequireAdmin>
<UploadFileForm />
</RequireAdmin>
}
/>
)}
<Route
path="ota"
element={
<RequireAdmin>
<OTASettingsForm />
</RequireAdmin>
}
/>
<Route
path="upload"
element={
<RequireAdmin>
<UploadFileForm />
</RequireAdmin>
}
/>
<Route path="/*" element={<Navigate replace to="status" />} />
</Routes>
</>