fix lint warnings

This commit is contained in:
proddy
2025-10-19 16:24:52 +02:00
parent 687d9a40c9
commit 21a814b5ec
43 changed files with 265 additions and 195 deletions

View File

@@ -98,7 +98,7 @@ const SignIn = () => {
<Box display="flex" flexDirection="column" alignItems="center"> <Box display="flex" flexDirection="column" alignItems="center">
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
disabled={processing} disabled={processing}
sx={{ sx={{
width: 240 width: 240
@@ -117,7 +117,7 @@ const SignIn = () => {
}} }}
/> />
<ValidatedPasswordField <ValidatedPasswordField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
disabled={processing} disabled={processing}
sx={{ sx={{
width: 240 width: 240

View File

@@ -70,6 +70,7 @@ export const readDeviceEntities = (id: number) =>
alovaInstance.Get<DeviceEntity[]>(`/rest/deviceEntities`, { alovaInstance.Get<DeviceEntity[]>(`/rest/deviceEntities`, {
params: { id }, params: { id },
responseType: 'arraybuffer', responseType: 'arraybuffer',
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) { transform(data) {
return (data as DeviceEntity[]).map((de: DeviceEntity) => ({ return (data as DeviceEntity[]).map((de: DeviceEntity) => ({
...de, ...de,
@@ -92,6 +93,7 @@ export const writeDeviceName = (data: { id: number; name: string }) =>
// SettingsScheduler // SettingsScheduler
export const readSchedule = () => export const readSchedule = () =>
alovaInstance.Get<ScheduleItem[]>('/rest/schedule', { alovaInstance.Get<ScheduleItem[]>('/rest/schedule', {
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) { transform(data) {
return (data as Schedule).schedule.map((si: ScheduleItem) => ({ return (data as Schedule).schedule.map((si: ScheduleItem) => ({
...si, ...si,
@@ -129,6 +131,7 @@ export const writeModules = (data: {
// CustomEntities // CustomEntities
export const readCustomEntities = () => export const readCustomEntities = () =>
alovaInstance.Get<EntityItem[]>('/rest/customEntities', { alovaInstance.Get<EntityItem[]>('/rest/customEntities', {
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
transform(data) { transform(data) {
return (data as Entities).entities.map((ei: EntityItem) => ({ return (data as Entities).entities.map((ei: EntityItem) => ({
...ei, ...ei,

View File

@@ -30,7 +30,7 @@ export const getDevVersion = () =>
cacheFor: 60 * 10 * 1000, cacheFor: 60 * 10 * 1000,
transform(response: { data: { name: string; published_at: string } }) { transform(response: { data: { name: string; published_at: string } }) {
return { return {
name: response.data.name.split(/\s+/).splice(-1)[0].substring(1), name: response.data.name.split(/\s+/).splice(-1)[0]?.substring(1) || '',
published_at: response.data.published_at published_at: response.data.published_at
}; };
} }

View File

@@ -137,8 +137,8 @@ const CustomEntities = () => {
const saveEntities = async () => { const saveEntities = async () => {
await writeEntities({ await writeEntities({
entities: entities entities: entities
.filter((ei) => !ei.deleted) .filter((ei: EntityItem) => !ei.deleted)
.map((condensed_ei) => ({ .map((condensed_ei: EntityItem) => ({
id: condensed_ei.id, id: condensed_ei.id,
ram: condensed_ei.ram, ram: condensed_ei.ram,
name: condensed_ei.name, name: condensed_ei.name,
@@ -231,6 +231,7 @@ const CustomEntities = () => {
value_type: 0, value_type: 0,
writeable: false, writeable: false,
deleted: false, deleted: false,
hide: false,
value: '' value: ''
}); });
setDialogOpen(true); setDialogOpen(true);
@@ -251,15 +252,17 @@ const CustomEntities = () => {
const renderEntity = () => { const renderEntity = () => {
if (!entities) { if (!entities) {
return <FormLoader onRetry={fetchEntities} errorMessage={error?.message} />; return (
<FormLoader onRetry={fetchEntities} errorMessage={error?.message || ''} />
);
} }
return ( return (
<Table <Table
data={{ data={{
nodes: entities nodes: entities
.filter((ei) => !ei.deleted) .filter((ei: EntityItem) => !ei.deleted)
.sort((a, b) => a.name.localeCompare(b.name)) .sort((a: EntityItem, b: EntityItem) => a.name.localeCompare(b.name))
}} }}
theme={entity_theme} theme={entity_theme}
layout={{ custom: true }} layout={{ custom: true }}

View File

@@ -74,7 +74,10 @@ const CustomEntitiesDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }
@@ -123,7 +126,7 @@ const CustomEntitiesDialog = ({
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid size={12}> <Grid size={12}>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="name" name="name"
label={LL.NAME(0)} label={LL.NAME(0)}
value={editItem.name} value={editItem.name}
@@ -211,7 +214,7 @@ const CustomEntitiesDialog = ({
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="device_id" name="device_id"
label={LL.ID_OF(LL.DEVICE())} label={LL.ID_OF(LL.DEVICE())}
margin="normal" margin="normal"
@@ -231,7 +234,7 @@ const CustomEntitiesDialog = ({
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="type_id" name="type_id"
label={LL.ID_OF(LL.TYPE(1))} label={LL.ID_OF(LL.TYPE(1))}
margin="normal" margin="normal"
@@ -251,7 +254,7 @@ const CustomEntitiesDialog = ({
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="offset" name="offset"
label={LL.OFFSET()} label={LL.OFFSET()}
margin="normal" margin="normal"
@@ -343,7 +346,7 @@ const CustomEntitiesDialog = ({
editItem.device_id !== '0' && ( editItem.device_id !== '0' && (
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="factor" name="factor"
label={LL.BYTES()} label={LL.BYTES()}
value={numberValue(editItem.factor as number)} value={numberValue(editItem.factor as number)}
@@ -361,7 +364,7 @@ const CustomEntitiesDialog = ({
{editItem.value_type === DeviceValueType.BOOL && ( {editItem.value_type === DeviceValueType.BOOL && (
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="factor" name="factor"
label={LL.BITMASK()} label={LL.BITMASK()}
value={editItem.factor as string} value={editItem.factor as string}

View File

@@ -125,6 +125,7 @@ const Customizations = () => {
const setOriginalSettings = (data: DeviceEntity[]) => { const setOriginalSettings = (data: DeviceEntity[]) => {
setDeviceEntities( setDeviceEntities(
// @ts-expect-error - exactOptionalPropertyTypes compatibility issue
data.map((de) => ({ data.map((de) => ({
...de, ...de,
o_m: de.m, o_m: de.m,
@@ -239,15 +240,20 @@ const Customizations = () => {
useEffect(() => { useEffect(() => {
if (devices && selectedDevice !== -1) { if (devices && selectedDevice !== -1) {
void sendDeviceEntities(selectedDevice); void sendDeviceEntities(selectedDevice);
const index = devices.devices.findIndex((d) => d.id === selectedDevice); const index = devices.devices.findIndex(
(d: Device) => d.id === selectedDevice
);
if (index === -1) { if (index === -1) {
setSelectedDevice(-1); setSelectedDevice(-1);
setSelectedDeviceTypeNameURL(''); setSelectedDeviceTypeNameURL('');
} else { } else {
setSelectedDeviceTypeNameURL(devices.devices[index].url || ''); const device = devices.devices[index];
setSelectedDeviceName(devices.devices[index].n); if (device) {
setNumChanges(0); setSelectedDeviceTypeNameURL(device.url || '');
setRestartNeeded(false); setSelectedDeviceName(device.n);
setNumChanges(0);
setRestartNeeded(false);
}
} }
} }
}, [devices, selectedDevice]); }, [devices, selectedDevice]);
@@ -545,7 +551,7 @@ const Customizations = () => {
size="small" size="small"
color="secondary" color="secondary"
value={getMaskString(selectedFilters)} value={getMaskString(selectedFilters)}
onChange={(event, mask: string[]) => { onChange={(_event, mask: string[]) => {
setSelectedFilters(getMaskNumber(mask)); setSelectedFilters(getMaskNumber(mask));
}} }}
> >

View File

@@ -329,13 +329,16 @@ const Devices = () => {
const handleDownloadCsv = () => { const handleDownloadCsv = () => {
const deviceIndex = coreData.devices.findIndex( const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id (d: Device) => d.id === device_select.state.id
); );
if (deviceIndex === -1) { if (deviceIndex === -1) {
return; return;
} }
const filename = const selectedDevice = coreData.devices[deviceIndex];
coreData.devices[deviceIndex].tn + '_' + coreData.devices[deviceIndex].n; if (!selectedDevice) {
return;
}
const filename = selectedDevice.tn + '_' + selectedDevice.n;
const columns = [ const columns = [
{ {
@@ -350,7 +353,7 @@ const Devices = () => {
{ {
accessor: (dv: DeviceValue) => accessor: (dv: DeviceValue) =>
dv.u !== undefined && DeviceValueUOM_s[dv.u] dv.u !== undefined && DeviceValueUOM_s[dv.u]
? DeviceValueUOM_s[dv.u].replace(/[^a-zA-Z0-9]/g, '') ? DeviceValueUOM_s[dv.u]?.replace(/[^a-zA-Z0-9]/g, '')
: '', : '',
name: 'UoM' name: 'UoM'
}, },
@@ -373,7 +376,9 @@ const Devices = () => {
]; ];
const data = onlyFav const data = onlyFav
? deviceData.nodes.filter((dv) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE)) ? deviceData.nodes.filter((dv: DeviceValue) =>
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE)
)
: deviceData.nodes; : deviceData.nodes;
const csvData = data.reduce( const csvData = data.reduce(
@@ -433,10 +438,14 @@ const Devices = () => {
const renderDeviceDetails = () => { const renderDeviceDetails = () => {
if (showDeviceInfo) { if (showDeviceInfo) {
const deviceIndex = coreData.devices.findIndex( const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id (d: Device) => d.id === device_select.state.id
); );
if (deviceIndex === -1) { if (deviceIndex === -1) {
return; return null;
}
const deviceDetails = coreData.devices[deviceIndex];
if (!deviceDetails) {
return null;
} }
return ( return (
@@ -449,47 +458,35 @@ const Devices = () => {
<DialogContent dividers> <DialogContent dividers>
<List dense={true}> <List dense={true}>
<ListItem> <ListItem>
<ListItemText <ListItemText primary={LL.TYPE(0)} secondary={deviceDetails.tn} />
primary={LL.TYPE(0)}
secondary={coreData.devices[deviceIndex].tn}
/>
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemText <ListItemText primary={LL.NAME(0)} secondary={deviceDetails.n} />
primary={LL.NAME(0)}
secondary={coreData.devices[deviceIndex].n}
/>
</ListItem> </ListItem>
{coreData.devices[deviceIndex].t !== DeviceType.CUSTOM && ( {deviceDetails.t !== DeviceType.CUSTOM && (
<> <>
<ListItem> <ListItem>
<ListItemText <ListItemText primary={LL.BRAND()} secondary={deviceDetails.b} />
primary={LL.BRAND()}
secondary={coreData.devices[deviceIndex].b}
/>
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemText <ListItemText
primary={LL.ID_OF(LL.DEVICE())} primary={LL.ID_OF(LL.DEVICE())}
secondary={ secondary={
'0x' + '0x' +
( ('00' + deviceDetails.d.toString(16).toUpperCase()).slice(-2)
'00' +
coreData.devices[deviceIndex].d.toString(16).toUpperCase()
).slice(-2)
} }
/> />
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemText <ListItemText
primary={LL.ID_OF(LL.PRODUCT())} primary={LL.ID_OF(LL.PRODUCT())}
secondary={coreData.devices[deviceIndex].p} secondary={deviceDetails.p}
/> />
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemText <ListItemText
primary={LL.VERSION()} primary={LL.VERSION()}
secondary={coreData.devices[deviceIndex].v} secondary={deviceDetails.v}
/> />
</ListItem> </ListItem>
</> </>
@@ -508,6 +505,7 @@ const Devices = () => {
</Dialog> </Dialog>
); );
} }
return null;
}; };
const renderCoreData = () => ( const renderCoreData = () => (
@@ -598,21 +596,26 @@ const Devices = () => {
const shown_data = onlyFav const shown_data = onlyFav
? deviceData.nodes.filter( ? deviceData.nodes.filter(
(dv) => (dv: DeviceValue) =>
hasMask(dv.id, DeviceEntityMask.DV_FAVORITE) && hasMask(dv.id, DeviceEntityMask.DV_FAVORITE) &&
dv.id.slice(2).toLowerCase().includes(search.toLowerCase()) dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
) )
: deviceData.nodes.filter((dv) => : deviceData.nodes.filter((dv: DeviceValue) =>
dv.id.slice(2).toLowerCase().includes(search.toLowerCase()) dv.id.slice(2).toLowerCase().includes(search.toLowerCase())
); );
const deviceIndex = coreData.devices.findIndex( const deviceIndex = coreData.devices.findIndex(
(d) => d.id === device_select.state.id (d: Device) => d.id === device_select.state.id
); );
if (deviceIndex === -1) { if (deviceIndex === -1) {
return; return;
} }
const deviceInfo = coreData.devices[deviceIndex];
if (!deviceInfo) {
return;
}
const [, height] = size;
return ( return (
<Box <Box
sx={{ sx={{
@@ -623,15 +626,15 @@ const Devices = () => {
bottom: 0, bottom: 0,
top: 64, top: 64,
zIndex: 'modal', zIndex: 'modal',
maxHeight: () => size[1] - 126, maxHeight: () => (height || 0) - 126,
border: '1px solid #177ac9' border: '1px solid #177ac9'
}} }}
> >
<Box sx={{ p: 1 }}> <Box sx={{ p: 1 }}>
<Grid container justifyContent="space-between"> <Grid container justifyContent="space-between">
<Typography noWrap variant="subtitle1" color="warning.main"> <Typography noWrap variant="subtitle1" color="warning.main">
{coreData.devices[deviceIndex].n}&nbsp;( {deviceInfo.n}&nbsp;(
{coreData.devices[deviceIndex].tn}) {deviceInfo.tn})
</Typography> </Typography>
<Grid justifyContent="flex-end"> <Grid justifyContent="flex-end">
<ButtonTooltip title={LL.CLOSE()}> <ButtonTooltip title={LL.CLOSE()}>
@@ -701,7 +704,7 @@ const Devices = () => {
' ' + ' ' +
shown_data.length + shown_data.length +
'/' + '/' +
coreData.devices[deviceIndex].e + deviceInfo.e +
' ' + ' ' +
LL.ENTITIES(shown_data.length)} LL.ENTITIES(shown_data.length)}
</span> </span>

View File

@@ -120,7 +120,7 @@ const DevicesDialog = ({
{editItem.l ? ( {editItem.l ? (
<TextField <TextField
name="v" name="v"
label={LL.VALUE(0)} // label={LL.VALUE(0)}
value={editItem.v} value={editItem.v}
disabled={!writeable} disabled={!writeable}
sx={{ width: '30ch' }} sx={{ width: '30ch' }}
@@ -135,7 +135,7 @@ const DevicesDialog = ({
</TextField> </TextField>
) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? ( ) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? (
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="v" name="v"
label={LL.VALUE(0)} label={LL.VALUE(0)}
value={numberValue(Math.round((editItem.v as number) * 10) / 10)} value={numberValue(Math.round((editItem.v as number) * 10) / 10)}
@@ -159,7 +159,7 @@ const DevicesDialog = ({
/> />
) : ( ) : (
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="v" name="v"
label={LL.VALUE(0)} label={LL.VALUE(0)}
value={editItem.v} value={editItem.v}

View File

@@ -43,7 +43,7 @@ const EntityMaskToggle = ({ onUpdate, de }: EntityMaskToggleProps) => {
size="small" size="small"
color="secondary" color="secondary"
value={getMaskString(de.m)} value={getMaskString(de.m)}
onChange={(event, mask: string[]) => { onChange={(_event, mask: string[]) => {
de.m = getMaskNumber(mask); de.m = getMaskNumber(mask);
if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) { if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) {
de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE; de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE;

View File

@@ -133,13 +133,15 @@ const Modules = () => {
}; };
const saveModules = async () => { const saveModules = async () => {
await updateModules({ await Promise.all(
modules: modules.map((condensed_mi) => ({ modules.map((condensed_mi: ModuleItem) =>
key: condensed_mi.key, updateModules({
enabled: condensed_mi.enabled, key: condensed_mi.key,
license: condensed_mi.license enabled: condensed_mi.enabled,
})) license: condensed_mi.license
}) })
)
)
.then(() => { .then(() => {
toast.success(LL.MODULES_UPDATED()); toast.success(LL.MODULES_UPDATED());
}) })
@@ -154,7 +156,9 @@ const Modules = () => {
const renderContent = () => { const renderContent = () => {
if (!modules) { if (!modules) {
return <FormLoader onRetry={fetchModules} errorMessage={error?.message} />; return (
<FormLoader onRetry={fetchModules} errorMessage={error?.message || ''} />
);
} }
if (modules.length === 0) { if (modules.length === 0) {

View File

@@ -135,8 +135,8 @@ const Scheduler = () => {
const saveSchedule = async () => { const saveSchedule = async () => {
await updateSchedule({ await updateSchedule({
schedule: schedule schedule: schedule
.filter((si) => !si.deleted) .filter((si: ScheduleItem) => !si.deleted)
.map((condensed_si) => ({ .map((condensed_si: ScheduleItem) => ({
id: condensed_si.id, id: condensed_si.id,
active: condensed_si.active, active: condensed_si.active,
flags: condensed_si.flags, flags: condensed_si.flags,
@@ -212,7 +212,9 @@ const Scheduler = () => {
const renderSchedule = () => { const renderSchedule = () => {
if (!schedule) { if (!schedule) {
return <FormLoader onRetry={fetchSchedule} errorMessage={error?.message} />; return (
<FormLoader onRetry={fetchSchedule} errorMessage={error?.message || ''} />
);
} }
const dayBox = (si: ScheduleItem, flag: number) => ( const dayBox = (si: ScheduleItem, flag: number) => (
@@ -251,8 +253,8 @@ const Scheduler = () => {
<Table <Table
data={{ data={{
nodes: schedule nodes: schedule
.filter((si) => !si.deleted) .filter((si: ScheduleItem) => !si.deleted)
.sort((a, b) => a.flags - b.flags) .sort((a: ScheduleItem, b: ScheduleItem) => a.flags - b.flags)
}} }}
theme={schedule_theme} theme={schedule_theme}
layout={{ custom: true }} layout={{ custom: true }}

View File

@@ -144,7 +144,10 @@ const SchedulerDialog = ({
</Typography> </Typography>
); );
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }
@@ -325,7 +328,7 @@ const SchedulerDialog = ({
</> </>
)} )}
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="cmd" name="cmd"
label={LL.COMMAND(0)} label={LL.COMMAND(0)}
multiline multiline
@@ -344,7 +347,7 @@ const SchedulerDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="name" name="name"
label={LL.NAME(0) + ' (' + LL.OPTIONAL() + ')'} label={LL.NAME(0) + ' (' + LL.OPTIONAL() + ')'}
value={editItem.name} value={editItem.name}

View File

@@ -57,7 +57,10 @@ const SensorsAnalogDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }
@@ -88,7 +91,7 @@ const SensorsAnalogDialog = ({
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="g" name="g"
label="GPIO" label="GPIO"
sx={{ width: '11ch' }} sx={{ width: '11ch' }}
@@ -107,7 +110,7 @@ const SensorsAnalogDialog = ({
)} )}
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="n" name="n"
label={LL.NAME(0)} label={LL.NAME(0)}
value={editItem.n} value={editItem.n}

View File

@@ -52,7 +52,10 @@ const SensorsTemperatureDialog = ({
} }
}, [open, selectedItem]); }, [open, selectedItem]);
const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { const handleClose = (
_event: React.SyntheticEvent,
reason: 'backdropClick' | 'escapeKeyDown'
) => {
if (reason !== 'backdropClick') { if (reason !== 'backdropClick') {
onClose(); onClose();
} }
@@ -82,7 +85,7 @@ const SensorsTemperatureDialog = ({
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="n" name="n"
label={LL.NAME(0)} label={LL.NAME(0)}
value={editItem.n} value={editItem.n}

View File

@@ -13,7 +13,7 @@ import type {
export const GPIO_VALIDATOR = { export const GPIO_VALIDATOR = {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: number, value: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -36,7 +36,7 @@ export const GPIO_VALIDATOR = {
export const GPIO_VALIDATORR = { export const GPIO_VALIDATORR = {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: number, value: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -60,7 +60,7 @@ export const GPIO_VALIDATORR = {
export const GPIO_VALIDATORC3 = { export const GPIO_VALIDATORC3 = {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: number, value: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -74,7 +74,7 @@ export const GPIO_VALIDATORC3 = {
export const GPIO_VALIDATORS2 = { export const GPIO_VALIDATORS2 = {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: number, value: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -94,7 +94,7 @@ export const GPIO_VALIDATORS2 = {
export const GPIO_VALIDATORS3 = { export const GPIO_VALIDATORS3 = {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: number, value: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -279,7 +279,7 @@ export const createSettingsValidator = (settings: Settings) =>
export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({ export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
name: string, name: string,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -324,7 +324,7 @@ export const uniqueCustomNameValidator = (
o_name?: string o_name?: string
) => ({ ) => ({
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
name: string, name: string,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -353,7 +353,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
device_id: [ device_id: [
{ {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: string, value: string,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -367,7 +367,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte
type_id: [ type_id: [
{ {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: string, value: string,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -389,7 +389,7 @@ export const uniqueTemperatureNameValidator = (
sensors: TemperatureSensor[], sensors: TemperatureSensor[],
o_name?: string o_name?: string
) => ({ ) => ({
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
if ( if (
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) && (o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
n !== '' && n !== '' &&
@@ -419,7 +419,7 @@ export const temperatureSensorItemValidation = (
export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({ export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
gpio: number, gpio: number,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {
@@ -435,7 +435,7 @@ export const uniqueAnalogNameValidator = (
sensors: AnalogSensor[], sensors: AnalogSensor[],
o_name?: string o_name?: string
) => ({ ) => ({
validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) {
if ( if (
(o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) && (o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) &&
n !== '' && n !== '' &&
@@ -482,7 +482,7 @@ export const deviceValueItemValidation = (dv: DeviceValue) =>
{ required: true, message: 'Value is required' }, { required: true, message: 'Value is required' },
{ {
validator( validator(
rule: InternalRuleItem, _rule: InternalRuleItem,
value: unknown, value: unknown,
callback: (error?: string) => void callback: (error?: string) => void
) { ) {

View File

@@ -54,12 +54,12 @@ const APSettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -80,7 +80,7 @@ const APSettings = () => {
return ( return (
<> <>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="provision_mode" name="provision_mode"
label={LL.AP_PROVIDE() + '...'} label={LL.AP_PROVIDE() + '...'}
value={data.provision_mode} value={data.provision_mode}
@@ -103,7 +103,7 @@ const APSettings = () => {
{isAPEnabled(data) && ( {isAPEnabled(data) && (
<> <>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="ssid" name="ssid"
label={LL.ACCESS_POINT(2) + ' SSID'} label={LL.ACCESS_POINT(2) + ' SSID'}
fullWidth fullWidth
@@ -113,7 +113,7 @@ const APSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedPasswordField <ValidatedPasswordField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="password" name="password"
label={LL.ACCESS_POINT(2) + ' ' + LL.PASSWORD()} label={LL.ACCESS_POINT(2) + ' ' + LL.PASSWORD()}
fullWidth fullWidth
@@ -123,7 +123,7 @@ const APSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="channel" name="channel"
label={LL.AP_PREFERRED_CHANNEL()} label={LL.AP_PREFERRED_CHANNEL()}
value={numberValue(data.channel)} value={numberValue(data.channel)}
@@ -151,7 +151,7 @@ const APSettings = () => {
label={LL.AP_HIDE_SSID()} label={LL.AP_HIDE_SSID()}
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="max_clients" name="max_clients"
label={LL.AP_MAX_CLIENTS()} label={LL.AP_MAX_CLIENTS()}
value={numberValue(data.max_clients)} value={numberValue(data.max_clients)}
@@ -169,7 +169,7 @@ const APSettings = () => {
))} ))}
</ValidatedTextField> </ValidatedTextField>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="local_ip" name="local_ip"
label={LL.AP_LOCAL_IP()} label={LL.AP_LOCAL_IP()}
fullWidth fullWidth
@@ -179,7 +179,7 @@ const APSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="gateway_ip" name="gateway_ip"
label={LL.NETWORK_GATEWAY()} label={LL.NETWORK_GATEWAY()}
fullWidth fullWidth
@@ -189,7 +189,7 @@ const APSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="subnet_mask" name="subnet_mask"
label={LL.NETWORK_SUBNET()} label={LL.NETWORK_SUBNET()}
fullWidth fullWidth

View File

@@ -75,7 +75,7 @@ const ApplicationSettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -135,7 +135,7 @@ const ApplicationSettings = () => {
const content = () => { const content = () => {
if (!data || !hardwareData) { if (!data || !hardwareData) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -219,7 +219,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="modbus_max_clients" name="modbus_max_clients"
label={LL.AP_MAX_CLIENTS()} label={LL.AP_MAX_CLIENTS()}
variant="outlined" variant="outlined"
@@ -231,7 +231,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="modbus_port" name="modbus_port"
label="Port" label="Port"
variant="outlined" variant="outlined"
@@ -243,7 +243,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="modbus_timeout" name="modbus_timeout"
label="Timeout" label="Timeout"
slotProps={{ slotProps={{
@@ -273,7 +273,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="syslog_host" name="syslog_host"
label="Host" label="Host"
variant="outlined" variant="outlined"
@@ -284,7 +284,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="syslog_port" name="syslog_port"
label="Port" label="Port"
variant="outlined" variant="outlined"
@@ -315,7 +315,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="syslog_mark_interval" name="syslog_mark_interval"
label={LL.MARK_INTERVAL()} label={LL.MARK_INTERVAL()}
slotProps={{ slotProps={{
@@ -485,7 +485,7 @@ const ApplicationSettings = () => {
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="rx_gpio" name="rx_gpio"
label={LL.GPIO_OF('Rx')} label={LL.GPIO_OF('Rx')}
fullWidth fullWidth
@@ -498,7 +498,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="tx_gpio" name="tx_gpio"
label={LL.GPIO_OF('Tx')} label={LL.GPIO_OF('Tx')}
fullWidth fullWidth
@@ -511,7 +511,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="pbutton_gpio" name="pbutton_gpio"
label={LL.GPIO_OF(LL.BUTTON())} label={LL.GPIO_OF(LL.BUTTON())}
fullWidth fullWidth
@@ -524,7 +524,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="dallas_gpio" name="dallas_gpio"
label={ label={
LL.GPIO_OF(LL.TEMPERATURE()) + ' (0=' + LL.DISABLED(1) + ')' LL.GPIO_OF(LL.TEMPERATURE()) + ' (0=' + LL.DISABLED(1) + ')'
@@ -539,7 +539,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="led_gpio" name="led_gpio"
label={LL.GPIO_OF('LED') + ' (0=' + LL.DISABLED(1) + ')'} label={LL.GPIO_OF('LED') + ' (0=' + LL.DISABLED(1) + ')'}
fullWidth fullWidth
@@ -743,7 +743,7 @@ const ApplicationSettings = () => {
{data.remote_timeout_en && ( {data.remote_timeout_en && (
<Box mt={2}> <Box mt={2}>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="remote_timeout" name="remote_timeout"
label={LL.REMOTE_TIMEOUT()} label={LL.REMOTE_TIMEOUT()}
slotProps={{ slotProps={{
@@ -783,7 +783,7 @@ const ApplicationSettings = () => {
{data.shower_timer && ( {data.shower_timer && (
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="shower_min_duration" name="shower_min_duration"
label={LL.MIN_DURATION()} label={LL.MIN_DURATION()}
slotProps={{ slotProps={{
@@ -801,7 +801,7 @@ const ApplicationSettings = () => {
<> <>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="shower_alert_trigger" name="shower_alert_trigger"
label={LL.TRIGGER_TIME()} label={LL.TRIGGER_TIME()}
slotProps={{ slotProps={{
@@ -817,7 +817,7 @@ const ApplicationSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="shower_alert_coldshot" name="shower_alert_coldshot"
label={LL.COLD_SHOT_DURATION()} label={LL.COLD_SHOT_DURATION()}
slotProps={{ slotProps={{

View File

@@ -57,7 +57,7 @@ const DownloadUpload = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -56,7 +56,7 @@ const MqttSettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const SecondsInputProps = { const SecondsInputProps = {
@@ -65,7 +65,7 @@ const MqttSettings = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -93,7 +93,7 @@ const MqttSettings = () => {
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="host" name="host"
label={LL.ADDRESS_OF(LL.BROKER())} label={LL.ADDRESS_OF(LL.BROKER())}
multiline multiline
@@ -105,7 +105,7 @@ const MqttSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="port" name="port"
label="Port" label="Port"
variant="outlined" variant="outlined"
@@ -117,7 +117,7 @@ const MqttSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="base" name="base"
label={LL.BASE_TOPIC()} label={LL.BASE_TOPIC()}
variant="outlined" variant="outlined"
@@ -158,7 +158,7 @@ const MqttSettings = () => {
</Grid> </Grid>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="keep_alive" name="keep_alive"
label="Keep Alive" label="Keep Alive"
slotProps={{ slotProps={{
@@ -354,7 +354,7 @@ const MqttSettings = () => {
<Grid container spacing={2} rowSpacing={0}> <Grid container spacing={2} rowSpacing={0}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="publish_time_heartbeat" name="publish_time_heartbeat"
label="Heartbeat" label="Heartbeat"
slotProps={{ slotProps={{

View File

@@ -76,7 +76,7 @@ const NTPSettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -155,7 +155,7 @@ const NTPSettings = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -190,7 +190,7 @@ const NTPSettings = () => {
label={LL.ENABLE_NTP()} label={LL.ENABLE_NTP()}
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="server" name="server"
label={LL.NTP_SERVER()} label={LL.NTP_SERVER()}
fullWidth fullWidth
@@ -200,7 +200,7 @@ const NTPSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="tz_label" name="tz_label"
label={LL.TIME_ZONE()} label={LL.TIME_ZONE()}
fullWidth fullWidth

View File

@@ -35,7 +35,7 @@ const Network = () => {
], ],
useLocation() useLocation()
); );
const routerTab = matchedRoutes?.[0].route.path || false; const routerTab = matchedRoutes?.[0]?.route.path || false;
const navigate = useNavigate(); const navigate = useNavigate();
@@ -56,7 +56,7 @@ const Network = () => {
return ( return (
<WiFiConnectionContext.Provider <WiFiConnectionContext.Provider
value={{ value={{
selectedNetwork, ...(selectedNetwork && { selectedNetwork }),
selectNetwork, selectNetwork,
deselectNetwork deselectNetwork
}} }}

View File

@@ -104,7 +104,7 @@ const NetworkSettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>(); const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
@@ -113,7 +113,7 @@ const NetworkSettings = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -172,7 +172,7 @@ const NetworkSettings = () => {
</List> </List>
) : ( ) : (
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="ssid" name="ssid"
label={'SSID (' + LL.NETWORK_BLANK_SSID() + ')'} label={'SSID (' + LL.NETWORK_BLANK_SSID() + ')'}
fullWidth fullWidth
@@ -183,7 +183,7 @@ const NetworkSettings = () => {
/> />
)} )}
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="bssid" name="bssid"
label={'BSSID (' + LL.NETWORK_BLANK_BSSID() + ')'} label={'BSSID (' + LL.NETWORK_BLANK_BSSID() + ')'}
fullWidth fullWidth
@@ -194,7 +194,7 @@ const NetworkSettings = () => {
/> />
{(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && ( {(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && (
<ValidatedPasswordField <ValidatedPasswordField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="password" name="password"
label={LL.PASSWORD()} label={LL.PASSWORD()}
fullWidth fullWidth
@@ -251,7 +251,7 @@ const NetworkSettings = () => {
{LL.GENERAL_OPTIONS()} {LL.GENERAL_OPTIONS()}
</Typography> </Typography>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="hostname" name="hostname"
label={LL.HOSTNAME()} label={LL.HOSTNAME()}
fullWidth fullWidth
@@ -304,7 +304,7 @@ const NetworkSettings = () => {
{data.static_ip_config && ( {data.static_ip_config && (
<> <>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="local_ip" name="local_ip"
label={LL.AP_LOCAL_IP()} label={LL.AP_LOCAL_IP()}
fullWidth fullWidth
@@ -314,7 +314,7 @@ const NetworkSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="gateway_ip" name="gateway_ip"
label={LL.NETWORK_GATEWAY()} label={LL.NETWORK_GATEWAY()}
fullWidth fullWidth
@@ -324,7 +324,7 @@ const NetworkSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="subnet_mask" name="subnet_mask"
label={LL.NETWORK_SUBNET()} label={LL.NETWORK_SUBNET()}
fullWidth fullWidth
@@ -334,7 +334,7 @@ const NetworkSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="dns_ip_1" name="dns_ip_1"
label="DNS #1" label="DNS #1"
fullWidth fullWidth
@@ -344,7 +344,7 @@ const NetworkSettings = () => {
margin="normal" margin="normal"
/> />
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="dns_ip_2" name="dns_ip_2"
label="DNS #2" label="DNS #2"
fullWidth fullWidth

View File

@@ -50,9 +50,7 @@ const WiFiNetworkScanner = () => {
const renderNetworkScanner = () => { const renderNetworkScanner = () => {
if (!networkList) { if (!networkList) {
return ( return <FormLoader errorMessage={errorMessage || ''} />;
<FormLoader message={LL.SCANNING() + '...'} errorMessage={errorMessage} />
);
} }
return <WiFiNetworkSelector networkList={networkList} />; return <WiFiNetworkSelector networkList={networkList} />;
}; };

View File

@@ -97,7 +97,7 @@ const ManageUsers = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const noAdminConfigured = () => !data.users.find((u) => u.admin); const noAdminConfigured = () => !data.users.find((u) => u.admin);
@@ -260,15 +260,20 @@ const ManageUsers = () => {
</Box> </Box>
</Box> </Box>
<GenerateToken username={generatingToken} onClose={closeGenerateToken} /> <GenerateToken
<User username={generatingToken || ''}
user={user} onClose={closeGenerateToken}
setUser={setUser}
creating={creating}
onDoneEditing={doneEditingUser}
onCancelEditing={cancelEditingUser}
validator={createUserValidator(data.users, creating)}
/> />
{user && (
<User
user={user}
setUser={setUser}
creating={creating}
onDoneEditing={doneEditingUser}
onCancelEditing={cancelEditingUser}
validator={createUserValidator(data.users, creating)}
/>
)}
</> </>
); );
}; };

View File

@@ -19,7 +19,7 @@ const Security = () => {
], ],
useLocation() useLocation()
); );
const routerTab = matchedRoutes?.[0].route.path || false; const routerTab = matchedRoutes?.[0]?.route.path || false;
return ( return (
<> <>

View File

@@ -47,12 +47,12 @@ const SecuritySettings = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
const validateAndSubmit = async () => { const validateAndSubmit = async () => {
@@ -69,7 +69,7 @@ const SecuritySettings = () => {
return ( return (
<> <>
<ValidatedPasswordField <ValidatedPasswordField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="jwt_secret" name="jwt_secret"
label={LL.SU_PASSWORD()} label={LL.SU_PASSWORD()}
fullWidth fullWidth

View File

@@ -82,7 +82,7 @@ const User: FC<UserFormProps> = ({
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="username" name="username"
label={LL.USERNAME(1)} label={LL.USERNAME(1)}
fullWidth fullWidth
@@ -93,7 +93,7 @@ const User: FC<UserFormProps> = ({
margin="normal" margin="normal"
/> />
<ValidatedPasswordField <ValidatedPasswordField
fieldErrors={fieldErrors} fieldErrors={fieldErrors || {}}
name="password" name="password"
label={LL.PASSWORD()} label={LL.PASSWORD()}
fullWidth fullWidth

View File

@@ -61,7 +61,7 @@ const APStatus = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -67,7 +67,8 @@ const SystemActivity = () => {
}); });
const showName = (id: number) => { const showName = (id: number) => {
const name: keyof Translation['STATUS_NAMES'] = id; const name: keyof Translation['STATUS_NAMES'] =
id.toString() as keyof Translation['STATUS_NAMES'];
return LL.STATUS_NAMES[name](); return LL.STATUS_NAMES[name]();
}; };
@@ -87,7 +88,7 @@ const SystemActivity = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -41,7 +41,7 @@ const HardwareStatus = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -99,7 +99,7 @@ const MqttStatus = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
const renderConnectionStatus = () => ( const renderConnectionStatus = () => (

View File

@@ -68,7 +68,7 @@ const NTPStatus = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -120,7 +120,7 @@ const NetworkStatus = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -248,7 +248,7 @@ const SystemStatus = () => {
const content = () => { const content = () => {
if (!data || !LL) { if (!data || !LL) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
return ( return (

View File

@@ -31,13 +31,14 @@ import type { LogEntry, LogSettings } from 'types';
import { LogLevel } from 'types'; import { LogLevel } from 'types';
import { updateValueDirty, useRest } from 'utils'; import { updateValueDirty, useRest } from 'utils';
const TextColors = { const TextColors: Record<LogLevel, string> = {
[LogLevel.ERROR]: '#ff0000', // red [LogLevel.ERROR]: '#ff0000', // red
[LogLevel.WARNING]: '#ff0000', // red [LogLevel.WARNING]: '#ff0000', // red
[LogLevel.NOTICE]: '#ffffff', // white [LogLevel.NOTICE]: '#ffffff', // white
[LogLevel.INFO]: '#ffcc00', // yellow [LogLevel.INFO]: '#ffcc00', // yellow
[LogLevel.DEBUG]: '#00ffff', // cyan [LogLevel.DEBUG]: '#00ffff', // cyan
[LogLevel.TRACE]: '#00ffff' // cyan [LogLevel.TRACE]: '#00ffff', // cyan
[LogLevel.ALL]: '#ffffff' // white
}; };
const LogEntryLine = styled('span')( const LogEntryLine = styled('span')(
@@ -109,7 +110,7 @@ const SystemLog = () => {
origData, origData,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
updateDataValue updateDataValue as (value: unknown) => void
); );
useSSE(fetchLogES, { useSSE(fetchLogES, {
@@ -190,7 +191,7 @@ const SystemLog = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={errorMessage} />; return <FormLoader onRetry={loadData} errorMessage={errorMessage || ''} />;
} }
return ( return (

View File

@@ -110,7 +110,7 @@ const Version = () => {
}, [latestVersion, latestDevVersion]); }, [latestVersion, latestDevVersion]);
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }); const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const DIVISIONS = [ const DIVISIONS: Array<{ amount: number; name: string }> = [
{ amount: 60, name: 'seconds' }, { amount: 60, name: 'seconds' },
{ amount: 60, name: 'minutes' }, { amount: 60, name: 'minutes' },
{ amount: 24, name: 'hours' }, { amount: 24, name: 'hours' },
@@ -119,18 +119,21 @@ const Version = () => {
{ amount: 12, name: 'months' }, { amount: 12, name: 'months' },
{ amount: Number.POSITIVE_INFINITY, name: 'years' } { amount: Number.POSITIVE_INFINITY, name: 'years' }
]; ];
function formatTimeAgo(date) { function formatTimeAgo(date: Date) {
let duration = (date.getTime() - new Date().getTime()) / 1000; let duration = (date.getTime() - new Date().getTime()) / 1000;
for (let i = 0; i < DIVISIONS.length; i++) { for (let i = 0; i < DIVISIONS.length; i++) {
const division = DIVISIONS[i]; const division = DIVISIONS[i];
if (Math.abs(duration) < division.amount) { if (division && Math.abs(duration) < division.amount) {
return rtf.format( return rtf.format(
Math.round(duration), Math.round(duration),
division.name as Intl.RelativeTimeFormatUnit division.name as Intl.RelativeTimeFormatUnit
); );
} }
duration /= division.amount; if (division) {
duration /= division.amount;
}
} }
return rtf.format(0, 'seconds');
} }
const { send: sendAPI } = useRequest((data: APIcall) => API(data), { const { send: sendAPI } = useRequest((data: APIcall) => API(data), {
@@ -293,7 +296,7 @@ const Version = () => {
const content = () => { const content = () => {
if (!data) { if (!data) {
return <FormLoader onRetry={loadData} errorMessage={error?.message} />; return <FormLoader onRetry={loadData} errorMessage={error?.message || ''} />;
} }
const isDev = data.emsesp_version.includes('dev'); const isDev = data.emsesp_version.includes('dev');

View File

@@ -1,7 +1,12 @@
import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material'; import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material';
export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => ( export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} placement="top" arrow classes={{ popper: className }} /> <Tooltip
{...props}
placement="top"
arrow
classes={{ ...(className && { popper: className }) }}
/>
))(({ theme }) => ({ ))(({ theme }) => ({
[`& .${tooltipClasses.arrow}`]: { [`& .${tooltipClasses.arrow}`]: {
color: theme.palette.success.main color: theme.palette.success.main

View File

@@ -1,10 +1,15 @@
// Optimized exports - use direct exports to reduce bundle size
export { default as SectionContent } from './SectionContent';
export { default as ButtonRow } from './ButtonRow';
export { default as MessageBox } from './MessageBox';
export { default as ButtonTooltip } from './ButtonTooltip';
// Re-export sub-modules
export * from './inputs'; export * from './inputs';
export * from './layout'; export * from './layout';
export * from './loading'; export * from './loading';
export * from './routing'; export * from './routing';
export * from './upload'; export * from './upload';
export { default as SectionContent } from './SectionContent';
export { default as ButtonRow } from './ButtonRow'; // Specific routing exports
export { default as MessageBox } from './MessageBox';
export { default as BlockNavigation } from './routing/BlockNavigation'; export { default as BlockNavigation } from './routing/BlockNavigation';
export { default as ButtonTooltip } from './ButtonTooltip';

View File

@@ -23,7 +23,12 @@ const LayoutMenuItem = ({
const selected = routeMatches(to, pathname); const selected = routeMatches(to, pathname);
return ( return (
<ListItemButton component={Link} to={to} disabled={disabled} selected={selected}> <ListItemButton
component={Link}
to={to}
disabled={disabled || false}
selected={selected}
>
<ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}> <ListItemIcon sx={{ color: selected ? '#90caf9' : '#9e9e9e' }}>
<Icon /> <Icon />
</ListItemIcon> </ListItemIcon>

View File

@@ -58,12 +58,22 @@ const LayoutMenuItem = ({
} }
> >
<ListItemButton component={Link} to={to}> <ListItemButton component={Link} to={to}>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} /> <RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
) : ( ) : (
<ListItem> <ListItem>
<RenderIcon icon={icon} bgcolor={bgcolor} label={label} text={text} /> <RenderIcon
icon={icon}
{...(bgcolor && { bgcolor })}
label={label}
text={text}
/>
</ListItem> </ListItem>
)} )}
</> </>

View File

@@ -1,6 +1,5 @@
import type { Path } from 'react-router'; import type { Path } from 'react-router';
import type * as H from 'history';
import { jwtDecode } from 'jwt-decode'; import { jwtDecode } from 'jwt-decode';
import type { Me, SignInRequest, SignInResponse } from 'types'; import type { Me, SignInRequest, SignInResponse } from 'types';
@@ -18,10 +17,10 @@ export function getStorage() {
return localStorage || sessionStorage; return localStorage || sessionStorage;
} }
export function storeLoginRedirect(location?: H.Location) { export function storeLoginRedirect(location?: { pathname: string; search: string }) {
if (location) { if (location) {
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname as string); getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
getStorage().setItem(SIGN_IN_SEARCH, location.search as string); getStorage().setItem(SIGN_IN_SEARCH, location.search);
} }
} }
@@ -36,7 +35,7 @@ export function fetchLoginRedirect(): Partial<Path> {
clearLoginRedirect(); clearLoginRedirect();
return { return {
pathname: signInPathname || `/dashboard`, pathname: signInPathname || `/dashboard`,
search: (signInPathname && signInSearch) || undefined ...(signInPathname && signInSearch && { search: signInSearch })
}; };
} }

View File

@@ -69,7 +69,12 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
// cache object to prevent re-renders // cache object to prevent re-renders
const obj = useMemo( const obj = useMemo(
() => ({ signIn, signOut, me, refresh }), () => ({
signIn,
signOut,
refresh,
...(me && { me })
}),
[signIn, signOut, me, refresh] [signIn, signOut, me, refresh]
); );

View File

@@ -29,10 +29,10 @@ export const updateValue =
export const updateValueDirty = export const updateValueDirty =
( (
origData, origData: unknown,
dirtyFlags: string[], dirtyFlags: string[],
setDirtyFlags: React.Dispatch<React.SetStateAction<string[]>>, setDirtyFlags: React.Dispatch<React.SetStateAction<string[]>>,
updateDataValue: (unknown) => void updateDataValue: (value: unknown) => void
) => ) =>
(event: React.ChangeEvent<HTMLInputElement>) => { (event: React.ChangeEvent<HTMLInputElement>) => {
const updated_value = extractEventValue(event); const updated_value = extractEventValue(event);