This commit is contained in:
proddy
2024-03-17 19:08:03 +01:00
parent 2739712c5b
commit 9bf7fbfb2e
58 changed files with 631 additions and 646 deletions

View File

@@ -20,7 +20,8 @@ import {
ValidatedTextField,
ButtonRow,
MessageBox,
BlockNavigation
BlockNavigation,
useLayoutTitle
} from 'components';
import RestartMonitor from 'framework/system/RestartMonitor';
@@ -36,7 +37,7 @@ export function boardProfileSelectItems() {
));
}
const SettingsApplication: FC = () => {
const ApplicationSettings: FC = () => {
const {
loadData,
saveData,
@@ -97,6 +98,8 @@ const SettingsApplication: FC = () => {
});
};
useLayoutTitle(LL.APPLICATION_SETTINGS());
const content = () => {
if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />;
@@ -680,11 +683,11 @@ const SettingsApplication: FC = () => {
};
return (
<SectionContent title={LL.APPLICATION_SETTINGS()} titleGutter>
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <RestartMonitor /> : content()}
</SectionContent>
);
};
export default SettingsApplication;
export default ApplicationSettings;

View File

@@ -13,17 +13,17 @@ import { useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify';
import SettingsCustomEntitiesDialog from './SettingsCustomEntitiesDialog';
import SettingsCustomEntitiesDialog from './CustomEntitiesDialog';
import * as EMSESP from './api';
import { DeviceValueTypeNames, DeviceValueUOM_s } from './types';
import { entityItemValidation } from './validators';
import type { EntityItem } from './types';
import type { FC } from 'react';
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
const SettingsCustomEntities: FC = () => {
const CustomEntities: FC = () => {
const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
@@ -31,6 +31,8 @@ const SettingsCustomEntities: FC = () => {
const [creating, setCreating] = useState<boolean>(false);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
useLayoutTitle(LL.CUSTOM_ENTITIES(0));
const {
data: entities,
send: fetchEntities,
@@ -246,7 +248,7 @@ const SettingsCustomEntities: FC = () => {
};
return (
<SectionContent title={LL.CUSTOM_ENTITIES(0)} titleGutter>
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main">
<Typography variant="body2">{LL.ENTITIES_HELP_1()}</Typography>
@@ -298,4 +300,4 @@ const SettingsCustomEntities: FC = () => {
);
};
export default SettingsCustomEntities;
export default CustomEntities;

View File

@@ -30,7 +30,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
type SettingsCustomEntitiesDialogProps = {
type CustomEntitiesDialogProps = {
open: boolean;
creating: boolean;
onClose: () => void;
@@ -39,14 +39,14 @@ type SettingsCustomEntitiesDialogProps = {
validator: Schema;
};
const SettingsCustomEntitiesDialog = ({
const CustomEntitiesDialog = ({
open,
creating,
onClose,
onSave,
selectedItem,
validator
}: SettingsCustomEntitiesDialogProps) => {
}: CustomEntitiesDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<EntityItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -281,4 +281,4 @@ const SettingsCustomEntitiesDialog = ({
);
};
export default SettingsCustomEntitiesDialog;
export default CustomEntitiesDialog;

View File

@@ -26,9 +26,9 @@ import { useState, useEffect, useCallback } from 'react';
import { useBlocker, useLocation } from 'react-router-dom';
import { toast } from 'react-toastify';
import SettingsCustomizationDialog from './CustomizationDialog';
import EntityMaskToggle from './EntityMaskToggle';
import OptionIcon from './OptionIcon';
import SettingsCustomizationDialog from './SettingsCustomizationDialog';
import * as EMSESP from './api';
@@ -37,14 +37,14 @@ import type { DeviceShort, DeviceEntity } from './types';
import type { FC } from 'react';
import { dialogStyle } from 'CustomTheme';
import * as SystemApi from 'api/system';
import { ButtonRow, SectionContent, MessageBox, BlockNavigation } from 'components';
import { ButtonRow, SectionContent, MessageBox, BlockNavigation, useLayoutTitle } from 'components';
import RestartMonitor from 'framework/system/RestartMonitor';
import { useI18nContext } from 'i18n/i18n-react';
export const APIURL = window.location.origin + '/api/';
const SettingsCustomization: FC = () => {
const Customization: FC = () => {
const { LL } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
@@ -58,6 +58,8 @@ const SettingsCustomization: FC = () => {
const [selectedDeviceEntity, setSelectedDeviceEntity] = useState<DeviceEntity>();
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
useLayoutTitle(LL.CUSTOMIZATIONS());
// fetch devices first
const { data: devices } = useRequest(EMSESP.readDevices);
@@ -508,9 +510,6 @@ const SettingsCustomization: FC = () => {
const renderContent = () => (
<>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.DEVICE_ENTITIES()}
</Typography>
{devices && renderDeviceList()}
{deviceEntities && renderDeviceData()}
{restartNeeded && (
@@ -561,7 +560,7 @@ const SettingsCustomization: FC = () => {
);
return (
<SectionContent title={LL.CUSTOMIZATIONS()} titleGutter>
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
{restarting ? <RestartMonitor /> : renderContent()}
{selectedDeviceEntity && (
@@ -576,4 +575,4 @@ const SettingsCustomization: FC = () => {
);
};
export default SettingsCustomization;
export default Customization;

View File

@@ -31,7 +31,7 @@ type SettingsCustomizationDialogProps = {
selectedItem: DeviceEntity;
};
const SettingsCustomizationDialog = ({ open, onClose, onSave, selectedItem }: SettingsCustomizationDialogProps) => {
const CustomizationDialog = ({ open, onClose, onSave, selectedItem }: SettingsCustomizationDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<DeviceEntity>(selectedItem);
const [error, setError] = useState<boolean>(false);
@@ -152,4 +152,4 @@ const SettingsCustomizationDialog = ({ open, onClose, onSave, selectedItem }: Se
);
};
export default SettingsCustomizationDialog;
export default CustomizationDialog;

View File

@@ -1,37 +0,0 @@
import { Tab } from '@mui/material';
import { Navigate, Route, Routes } from 'react-router-dom';
import DashboardDevices from './DashboardDevices';
import DashboardSensors from './DashboardSensors';
import DashboardStatus from './DashboardStatus';
import type { FC } from 'react';
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
const Dashboard: FC = () => {
const { routerTab } = useRouterTab();
const { LL } = useI18nContext();
useLayoutTitle(LL.DASHBOARD());
return (
<>
<RouterTabs value={routerTab}>
<Tab value="/dashboard/devices" label={LL.DEVICES()} />
<Tab value="/dashboard/sensors" label={LL.SENSORS()} />
<Tab value="/dashboard/status" label="Status" />
</RouterTabs>
<Routes>
<Route path="devices" element={<DashboardDevices />} />
<Route path="sensors" element={<DashboardSensors />} />
<Route path="status" element={<DashboardStatus />} />
<Route path="*" element={<Navigate replace to="/dashboard/devices" />} />
</Routes>
</>
);
};
export default Dashboard;

View File

@@ -38,8 +38,8 @@ import { useState, useContext, useEffect, useCallback, useLayoutEffect } from 'r
import { IconContext } from 'react-icons';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import DashboardDevicesDialog from './DashboardDevicesDialog';
import DeviceIcon from './DeviceIcon';
import DashboardDevicesDialog from './DevicesDialog';
import * as EMSESP from './api';
import { formatValue } from './deviceValue';
@@ -49,12 +49,12 @@ import { deviceValueItemValidation } from './validators';
import type { Device, DeviceValue } from './types';
import type { FC } from 'react';
import { dialogStyle } from 'CustomTheme';
import { ButtonRow, SectionContent, MessageBox } from 'components';
import { ButtonRow, SectionContent, MessageBox, useLayoutTitle } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
const DashboardDevices: FC = () => {
const Devices: FC = () => {
const { me } = useContext(AuthenticatedContext);
const { LL } = useI18nContext();
const [size, setSize] = useState([0, 0]);
@@ -66,6 +66,8 @@ const DashboardDevices: FC = () => {
const navigate = useNavigate();
useLayoutTitle(LL.DEVICES());
const { data: coreData, send: readCoreData } = useRequest(() => EMSESP.readCoreData(), {
initialData: {
connected: true,
@@ -608,7 +610,7 @@ const DashboardDevices: FC = () => {
};
return (
<SectionContent title={LL.DEVICE_DATA()} titleGutter id="devices-window">
<SectionContent id="devices-window">
{renderCoreData()}
{renderDeviceData()}
{renderDeviceDetails()}
@@ -636,4 +638,4 @@ const DashboardDevices: FC = () => {
);
};
export default DashboardDevices;
export default Devices;

View File

@@ -40,7 +40,7 @@ type DashboardDevicesDialogProps = {
progress: boolean;
};
const DashboardDevicesDialog = ({
const DevicesDialog = ({
open,
onClose,
onSave,
@@ -204,4 +204,4 @@ const DashboardDevicesDialog = ({
);
};
export default DashboardDevicesDialog;
export default DevicesDialog;

View File

@@ -64,7 +64,7 @@ const showQuality = (stat: Stat) => {
}
};
const DashboardStatus: FC = () => {
const EMSStatus: FC = () => {
const { data: data, send: loadData, error } = useRequest(EMSESP.readStatus);
const { LL } = useI18nContext();
@@ -272,11 +272,7 @@ const DashboardStatus: FC = () => {
);
};
return (
<SectionContent title={LL.EMS_BUS_STATUS_TITLE()} titleGutter>
{content()}
</SectionContent>
);
return <SectionContent title={LL.EMS_BUS_STATUS_TITLE()}>{content()}</SectionContent>;
};
export default DashboardStatus;
export default EMSStatus;

View File

@@ -1,9 +1,19 @@
import CommentIcon from '@mui/icons-material/CommentTwoTone';
import EastIcon from '@mui/icons-material/East';
import DownloadIcon from '@mui/icons-material/GetApp';
import GitHubIcon from '@mui/icons-material/GitHub';
import MenuBookIcon from '@mui/icons-material/MenuBookTwoTone';
import { Box, List, ListItem, ListItemAvatar, ListItemText, Link, Typography, Button } from '@mui/material';
import {
Box,
List,
ListItem,
ListItemAvatar,
ListItemText,
Link,
Typography,
Button,
ListItemButton,
Avatar
} from '@mui/material';
import { useRequest } from 'alova';
import { toast } from 'react-toastify';
import type { FC } from 'react';
@@ -39,59 +49,56 @@ const Help: FC = () => {
};
return (
<SectionContent title={LL.SUPPORT_INFORMATION(0)} titleGutter>
<List>
<SectionContent>
<List sx={{ borderRadius: 3, border: '2px solid grey' }}>
<ListItem>
<ListItemAvatar>
<MenuBookIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
</ListItemAvatar>
<ListItemText>
{LL.HELP_INFORMATION_1()}&nbsp;
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
&nbsp;
<Link target="_blank" href="https://emsesp.github.io/docs" color="primary">
{LL.CLICK_HERE()}
</Link>
</ListItemText>
<ListItemButton component="a" href="https://emsesp.github.io/docs">
<ListItemAvatar>
<Avatar sx={{ bgcolor: '#72caf9' }}>
<MenuBookIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.HELP_INFORMATION_1()} />
</ListItemButton>
</ListItem>
<ListItem>
<ListItemAvatar>
<CommentIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
</ListItemAvatar>
<ListItemText>
{LL.HELP_INFORMATION_2()}&nbsp;
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
&nbsp;
<Link target="_blank" href="https://discord.gg/3J3GgnzpyT" color="primary">
{LL.CLICK_HERE()}
</Link>
</ListItemText>
<ListItemButton component="a" href="https://discord.gg/3J3GgnzpyT">
<ListItemAvatar>
<Avatar sx={{ bgcolor: '#72caf9' }}>
<CommentIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.HELP_INFORMATION_2()} />
</ListItemButton>
</ListItem>
<ListItem>
<ListItemAvatar>
<GitHubIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
</ListItemAvatar>
<ListItemText>
{LL.HELP_INFORMATION_3()}&nbsp;
<EastIcon style={{ fontSize: 24, color: 'lightblue', verticalAlign: 'middle' }} />
<Link target="_blank" href="https://github.com/emsesp/EMS-ESP32/issues/new/choose" color="primary">
{LL.CLICK_HERE()}
</Link>
<br />
</ListItemText>
<ListItemButton component="a" href="https://github.com/emsesp/EMS-ESP32/issues/new/choose">
<ListItemAvatar>
<Avatar sx={{ bgcolor: '#72caf9' }}>
<GitHubIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={LL.HELP_INFORMATION_3()} />
</ListItemButton>
</ListItem>
</List>
<Box color="warning.main">
<Box p={2} color="warning.main">
<Typography mb={1} variant="body2">
{LL.HELP_INFORMATION_4()}
</Typography>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={() => callAPI('system', 'info')}
>
{LL.SUPPORT_INFORMATION(0)}
</Button>
</Box>
<Button startIcon={<DownloadIcon />} variant="outlined" color="primary" onClick={() => callAPI('system', 'info')}>
{LL.SUPPORT_INFORMATION(0)}
</Button>
<Button
sx={{ ml: 2 }}
startIcon={<DownloadIcon />}
@@ -102,7 +109,7 @@ const Help: FC = () => {
All Values
</Button>
<Box border={1} p={1} mt={4} color="orange">
<Box border={1} p={1} mt={4}>
<Typography align="center" variant="subtitle1" color="orange">
<b>{LL.HELP_INFORMATION_5()}</b>
</Typography>

View File

@@ -11,18 +11,18 @@ import { updateState, useRequest } from 'alova';
import { useState, useEffect, useCallback } from 'react';
import { useBlocker } from 'react-router-dom';
import { toast } from 'react-toastify';
import SettingsSchedulerDialog from './SettingsSchedulerDialog';
import SettingsSchedulerDialog from './SchedulerDialog';
import * as EMSESP from './api';
import { ScheduleFlag } from './types';
import { schedulerItemValidation } from './validators';
import type { ScheduleItem } from './types';
import type { FC } from 'react';
import { ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components';
import { ButtonRow, FormLoader, SectionContent, BlockNavigation, useLayoutTitle } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
const SettingsScheduler: FC = () => {
const Scheduler: FC = () => {
const { LL, locale } = useI18nContext();
const [numChanges, setNumChanges] = useState<number>(0);
const blocker = useBlocker(numChanges !== 0);
@@ -194,6 +194,8 @@ const SettingsScheduler: FC = () => {
</>
);
useLayoutTitle(LL.SCHEDULER());
return (
<Table
data={{ nodes: schedule.filter((si) => !si.deleted).sort((a, b) => a.time.localeCompare(b.time)) }}
@@ -249,7 +251,7 @@ const SettingsScheduler: FC = () => {
};
return (
<SectionContent title={LL.SCHEDULER()} titleGutter>
<SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main">
<Typography variant="body2">{LL.SCHEDULER_HELP_1()}</Typography>
@@ -298,4 +300,4 @@ const SettingsScheduler: FC = () => {
);
};
export default SettingsScheduler;
export default Scheduler;

View File

@@ -32,7 +32,7 @@ import { useI18nContext } from 'i18n/i18n-react';
import { updateValue } from 'utils';
import { validate } from 'validators';
type SettingsSchedulerDialogProps = {
type SchedulerDialogProps = {
open: boolean;
creating: boolean;
onClose: () => void;
@@ -42,15 +42,7 @@ type SettingsSchedulerDialogProps = {
dow: string[];
};
const SettingsSchedulerDialog = ({
open,
creating,
onClose,
onSave,
selectedItem,
validator,
dow
}: SettingsSchedulerDialogProps) => {
const SchedulerDialog = ({ open, creating, onClose, onSave, selectedItem, validator, dow }: SchedulerDialogProps) => {
const { LL } = useI18nContext();
const [editItem, setEditItem] = useState<ScheduleItem>(selectedItem);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -246,4 +238,4 @@ const SettingsSchedulerDialog = ({
);
};
export default SettingsSchedulerDialog;
export default SchedulerDialog;

View File

@@ -12,20 +12,20 @@ import { useState, useContext, useEffect } from 'react';
import { toast } from 'react-toastify';
import DashboardSensorsAnalogDialog from './DashboardSensorsAnalogDialog';
import DashboardSensorsTemperatureDialog from './DashboardSensorsTemperatureDialog';
import DashboardSensorsAnalogDialog from './SensorsAnalogDialog';
import DashboardSensorsTemperatureDialog from './SensorsTemperatureDialog';
import * as EMSESP from './api';
import { DeviceValueUOM, DeviceValueUOM_s, AnalogTypeNames, AnalogType } from './types';
import { temperatureSensorItemValidation, analogSensorItemValidation } from './validators';
import type { TemperatureSensor, AnalogSensor } from './types';
import type { FC } from 'react';
import { ButtonRow, SectionContent } from 'components';
import { ButtonRow, SectionContent, useLayoutTitle } from 'components';
import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react';
const DashboardSensors: FC = () => {
const Sensors: FC = () => {
const { LL } = useI18nContext();
const { me } = useContext(AuthenticatedContext);
const [selectedTemperatureSensor, setSelectedTemperatureSensor] = useState<TemperatureSensor>();
@@ -170,6 +170,8 @@ const DashboardSensors: FC = () => {
};
});
useLayoutTitle(LL.SENSORS());
const formatDurationMin = (duration_min: number) => {
const days = Math.trunc((duration_min * 60000) / 86400000);
const hours = Math.trunc((duration_min * 60000) / 3600000) % 24;
@@ -406,7 +408,7 @@ const DashboardSensors: FC = () => {
);
return (
<SectionContent title={LL.SENSOR_DATA()} titleGutter>
<SectionContent>
{sensorData.ts.length > 0 && (
<>
<Typography sx={{ pt: 2, pb: 1 }} variant="h6" color="secondary">
@@ -467,4 +469,4 @@ const DashboardSensors: FC = () => {
);
};
export default DashboardSensors;
export default Sensors;

View File

@@ -38,7 +38,7 @@ type DashboardSensorsAnalogDialogProps = {
validator: Schema;
};
const DashboardSensorsAnalogDialog = ({
const SensorsAnalogDialog = ({
open,
onClose,
onSave,
@@ -296,4 +296,4 @@ const DashboardSensorsAnalogDialog = ({
);
};
export default DashboardSensorsAnalogDialog;
export default SensorsAnalogDialog;

View File

@@ -26,7 +26,7 @@ import { numberValue, updateValue } from 'utils';
import { validate } from 'validators';
type DashboardSensorsTemperatureDialogProps = {
type SensorsTemperatureDialogProps = {
open: boolean;
onClose: () => void;
onSave: (ts: TemperatureSensor) => void;
@@ -34,13 +34,13 @@ type DashboardSensorsTemperatureDialogProps = {
validator: Schema;
};
const DashboardSensorsTemperatureDialog = ({
const SensorsTemperatureDialog = ({
open,
onClose,
onSave,
selectedItem,
validator
}: DashboardSensorsTemperatureDialogProps) => {
}: SensorsTemperatureDialogProps) => {
const { LL } = useI18nContext();
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const [editItem, setEditItem] = useState<TemperatureSensor>(selectedItem);
@@ -119,4 +119,4 @@ const DashboardSensorsTemperatureDialog = ({
);
};
export default DashboardSensorsTemperatureDialog;
export default SensorsTemperatureDialog;

View File

@@ -1,37 +0,0 @@
import { Tab } from '@mui/material';
import { Navigate, Route, Routes } from 'react-router-dom';
import SettingsApplication from './SettingsApplication';
import SettingsCustomEntities from './SettingsCustomEntities';
import SettingsCustomization from './SettingsCustomization';
import SettingsScheduler from './SettingsScheduler';
import type { FC } from 'react';
import { RouterTabs, useRouterTab, useLayoutTitle } from 'components';
import { useI18nContext } from 'i18n/i18n-react';
const Settings: FC = () => {
const { LL } = useI18nContext();
const { routerTab } = useRouterTab();
useLayoutTitle(LL.SETTINGS_OF(''));
return (
<>
<RouterTabs value={routerTab}>
<Tab value="/settings/application" label={LL.APPLICATION_SETTINGS()} />
<Tab value="/settings/customization" label={LL.CUSTOMIZATIONS()} />
<Tab value="/settings/scheduler" label={LL.SCHEDULER()} />
<Tab value="/settings/customentities" label={LL.CUSTOM_ENTITIES(0)} />
</RouterTabs>
<Routes>
<Route path="application" element={<SettingsApplication />} />
<Route path="customization" element={<SettingsCustomization />} />
<Route path="scheduler" element={<SettingsScheduler />} />
<Route path="customentities" element={<SettingsCustomEntities />} />
<Route path="*" element={<Navigate replace to="/settings/application" />} />
</Routes>
</>
);
};
export default Settings;