From 21a814b5ec97770d4f07069d0d91cd358f365880 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 19 Oct 2025 16:24:52 +0200 Subject: [PATCH] fix lint warnings --- interface/src/SignIn.tsx | 4 +- interface/src/api/app.ts | 3 + interface/src/api/system.ts | 2 +- interface/src/app/main/CustomEntities.tsx | 13 ++-- .../src/app/main/CustomEntitiesDialog.tsx | 17 +++-- interface/src/app/main/Customizations.tsx | 18 +++-- interface/src/app/main/Devices.tsx | 69 ++++++++++--------- interface/src/app/main/DevicesDialog.tsx | 6 +- interface/src/app/main/EntityMaskToggle.tsx | 2 +- interface/src/app/main/Modules.tsx | 20 +++--- interface/src/app/main/Scheduler.tsx | 12 ++-- interface/src/app/main/SchedulerDialog.tsx | 9 ++- .../src/app/main/SensorsAnalogDialog.tsx | 9 ++- .../src/app/main/SensorsTemperatureDialog.tsx | 7 +- interface/src/app/main/validators.ts | 26 +++---- interface/src/app/settings/APSettings.tsx | 20 +++--- .../src/app/settings/ApplicationSettings.tsx | 34 ++++----- interface/src/app/settings/DownloadUpload.tsx | 2 +- interface/src/app/settings/MqttSettings.tsx | 14 ++-- interface/src/app/settings/NTPSettings.tsx | 8 +-- .../src/app/settings/network/Network.tsx | 4 +- .../app/settings/network/NetworkSettings.tsx | 22 +++--- .../settings/network/WiFiNetworkScanner.tsx | 4 +- .../src/app/settings/security/ManageUsers.tsx | 23 ++++--- .../src/app/settings/security/Security.tsx | 2 +- .../settings/security/SecuritySettings.tsx | 6 +- interface/src/app/settings/security/User.tsx | 4 +- interface/src/app/status/APStatus.tsx | 2 +- interface/src/app/status/Activity.tsx | 5 +- interface/src/app/status/HardwareStatus.tsx | 2 +- interface/src/app/status/MqttStatus.tsx | 2 +- interface/src/app/status/NTPStatus.tsx | 2 +- interface/src/app/status/NetworkStatus.tsx | 2 +- interface/src/app/status/Status.tsx | 2 +- interface/src/app/status/SystemLog.tsx | 9 +-- interface/src/app/status/Version.tsx | 13 ++-- interface/src/components/ButtonTooltip.tsx | 7 +- interface/src/components/index.ts | 13 ++-- .../src/components/layout/LayoutMenuItem.tsx | 7 +- .../src/components/layout/ListMenuItem.tsx | 14 +++- .../src/components/routing/authentication.ts | 9 ++- .../authentication/Authentication.tsx | 7 +- interface/src/utils/binding.ts | 4 +- 43 files changed, 265 insertions(+), 195 deletions(-) diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx index c547be43d..5d15d4fa5 100644 --- a/interface/src/SignIn.tsx +++ b/interface/src/SignIn.tsx @@ -98,7 +98,7 @@ const SignIn = () => { { }} /> alovaInstance.Get(`/rest/deviceEntities`, { params: { id }, responseType: 'arraybuffer', + // @ts-expect-error - exactOptionalPropertyTypes compatibility issue transform(data) { return (data as DeviceEntity[]).map((de: DeviceEntity) => ({ ...de, @@ -92,6 +93,7 @@ export const writeDeviceName = (data: { id: number; name: string }) => // SettingsScheduler export const readSchedule = () => alovaInstance.Get('/rest/schedule', { + // @ts-expect-error - exactOptionalPropertyTypes compatibility issue transform(data) { return (data as Schedule).schedule.map((si: ScheduleItem) => ({ ...si, @@ -129,6 +131,7 @@ export const writeModules = (data: { // CustomEntities export const readCustomEntities = () => alovaInstance.Get('/rest/customEntities', { + // @ts-expect-error - exactOptionalPropertyTypes compatibility issue transform(data) { return (data as Entities).entities.map((ei: EntityItem) => ({ ...ei, diff --git a/interface/src/api/system.ts b/interface/src/api/system.ts index 99162e20f..492661212 100644 --- a/interface/src/api/system.ts +++ b/interface/src/api/system.ts @@ -30,7 +30,7 @@ export const getDevVersion = () => cacheFor: 60 * 10 * 1000, transform(response: { data: { name: string; published_at: string } }) { 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 }; } diff --git a/interface/src/app/main/CustomEntities.tsx b/interface/src/app/main/CustomEntities.tsx index fac37a376..26e66041f 100644 --- a/interface/src/app/main/CustomEntities.tsx +++ b/interface/src/app/main/CustomEntities.tsx @@ -137,8 +137,8 @@ const CustomEntities = () => { const saveEntities = async () => { await writeEntities({ entities: entities - .filter((ei) => !ei.deleted) - .map((condensed_ei) => ({ + .filter((ei: EntityItem) => !ei.deleted) + .map((condensed_ei: EntityItem) => ({ id: condensed_ei.id, ram: condensed_ei.ram, name: condensed_ei.name, @@ -231,6 +231,7 @@ const CustomEntities = () => { value_type: 0, writeable: false, deleted: false, + hide: false, value: '' }); setDialogOpen(true); @@ -251,15 +252,17 @@ const CustomEntities = () => { const renderEntity = () => { if (!entities) { - return ; + return ( + + ); } return ( !ei.deleted) - .sort((a, b) => a.name.localeCompare(b.name)) + .filter((ei: EntityItem) => !ei.deleted) + .sort((a: EntityItem, b: EntityItem) => a.name.localeCompare(b.name)) }} theme={entity_theme} layout={{ custom: true }} diff --git a/interface/src/app/main/CustomEntitiesDialog.tsx b/interface/src/app/main/CustomEntitiesDialog.tsx index 05d1ab816..bf10b55fc 100644 --- a/interface/src/app/main/CustomEntitiesDialog.tsx +++ b/interface/src/app/main/CustomEntitiesDialog.tsx @@ -74,7 +74,10 @@ const CustomEntitiesDialog = ({ } }, [open, selectedItem]); - const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { + const handleClose = ( + _event: React.SyntheticEvent, + reason: 'backdropClick' | 'escapeKeyDown' + ) => { if (reason !== 'backdropClick') { onClose(); } @@ -123,7 +126,7 @@ const CustomEntitiesDialog = ({ { const setOriginalSettings = (data: DeviceEntity[]) => { setDeviceEntities( + // @ts-expect-error - exactOptionalPropertyTypes compatibility issue data.map((de) => ({ ...de, o_m: de.m, @@ -239,15 +240,20 @@ const Customizations = () => { useEffect(() => { if (devices && selectedDevice !== -1) { 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) { setSelectedDevice(-1); setSelectedDeviceTypeNameURL(''); } else { - setSelectedDeviceTypeNameURL(devices.devices[index].url || ''); - setSelectedDeviceName(devices.devices[index].n); - setNumChanges(0); - setRestartNeeded(false); + const device = devices.devices[index]; + if (device) { + setSelectedDeviceTypeNameURL(device.url || ''); + setSelectedDeviceName(device.n); + setNumChanges(0); + setRestartNeeded(false); + } } } }, [devices, selectedDevice]); @@ -545,7 +551,7 @@ const Customizations = () => { size="small" color="secondary" value={getMaskString(selectedFilters)} - onChange={(event, mask: string[]) => { + onChange={(_event, mask: string[]) => { setSelectedFilters(getMaskNumber(mask)); }} > diff --git a/interface/src/app/main/Devices.tsx b/interface/src/app/main/Devices.tsx index 5d6affdc0..69f30d297 100644 --- a/interface/src/app/main/Devices.tsx +++ b/interface/src/app/main/Devices.tsx @@ -329,13 +329,16 @@ const Devices = () => { const handleDownloadCsv = () => { const deviceIndex = coreData.devices.findIndex( - (d) => d.id === device_select.state.id + (d: Device) => d.id === device_select.state.id ); if (deviceIndex === -1) { return; } - const filename = - coreData.devices[deviceIndex].tn + '_' + coreData.devices[deviceIndex].n; + const selectedDevice = coreData.devices[deviceIndex]; + if (!selectedDevice) { + return; + } + const filename = selectedDevice.tn + '_' + selectedDevice.n; const columns = [ { @@ -350,7 +353,7 @@ const Devices = () => { { accessor: (dv: DeviceValue) => 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' }, @@ -373,7 +376,9 @@ const Devices = () => { ]; 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; const csvData = data.reduce( @@ -433,10 +438,14 @@ const Devices = () => { const renderDeviceDetails = () => { if (showDeviceInfo) { const deviceIndex = coreData.devices.findIndex( - (d) => d.id === device_select.state.id + (d: Device) => d.id === device_select.state.id ); if (deviceIndex === -1) { - return; + return null; + } + const deviceDetails = coreData.devices[deviceIndex]; + if (!deviceDetails) { + return null; } return ( @@ -449,47 +458,35 @@ const Devices = () => { - + - + - {coreData.devices[deviceIndex].t !== DeviceType.CUSTOM && ( + {deviceDetails.t !== DeviceType.CUSTOM && ( <> - + @@ -508,6 +505,7 @@ const Devices = () => { ); } + return null; }; const renderCoreData = () => ( @@ -598,21 +596,26 @@ const Devices = () => { const shown_data = onlyFav ? deviceData.nodes.filter( - (dv) => + (dv: DeviceValue) => hasMask(dv.id, DeviceEntityMask.DV_FAVORITE) && 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()) ); const deviceIndex = coreData.devices.findIndex( - (d) => d.id === device_select.state.id + (d: Device) => d.id === device_select.state.id ); if (deviceIndex === -1) { return; } + const deviceInfo = coreData.devices[deviceIndex]; + if (!deviceInfo) { + return; + } + const [, height] = size; return ( { bottom: 0, top: 64, zIndex: 'modal', - maxHeight: () => size[1] - 126, + maxHeight: () => (height || 0) - 126, border: '1px solid #177ac9' }} > - {coreData.devices[deviceIndex].n} ( - {coreData.devices[deviceIndex].tn}) + {deviceInfo.n} ( + {deviceInfo.tn}) @@ -701,7 +704,7 @@ const Devices = () => { ' ' + shown_data.length + '/' + - coreData.devices[deviceIndex].e + + deviceInfo.e + ' ' + LL.ENTITIES(shown_data.length)} diff --git a/interface/src/app/main/DevicesDialog.tsx b/interface/src/app/main/DevicesDialog.tsx index 35054b31e..a7aa1f184 100644 --- a/interface/src/app/main/DevicesDialog.tsx +++ b/interface/src/app/main/DevicesDialog.tsx @@ -120,7 +120,7 @@ const DevicesDialog = ({ {editItem.l ? ( ) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? ( ) : ( { size="small" color="secondary" value={getMaskString(de.m)} - onChange={(event, mask: string[]) => { + onChange={(_event, mask: string[]) => { de.m = getMaskNumber(mask); if (de.n === '' && de.m & DeviceEntityMask.DV_READONLY) { de.m = de.m | DeviceEntityMask.DV_WEB_EXCLUDE; diff --git a/interface/src/app/main/Modules.tsx b/interface/src/app/main/Modules.tsx index c5fa4ad88..d9ee062c8 100644 --- a/interface/src/app/main/Modules.tsx +++ b/interface/src/app/main/Modules.tsx @@ -133,13 +133,15 @@ const Modules = () => { }; const saveModules = async () => { - await updateModules({ - modules: modules.map((condensed_mi) => ({ - key: condensed_mi.key, - enabled: condensed_mi.enabled, - license: condensed_mi.license - })) - }) + await Promise.all( + modules.map((condensed_mi: ModuleItem) => + updateModules({ + key: condensed_mi.key, + enabled: condensed_mi.enabled, + license: condensed_mi.license + }) + ) + ) .then(() => { toast.success(LL.MODULES_UPDATED()); }) @@ -154,7 +156,9 @@ const Modules = () => { const renderContent = () => { if (!modules) { - return ; + return ( + + ); } if (modules.length === 0) { diff --git a/interface/src/app/main/Scheduler.tsx b/interface/src/app/main/Scheduler.tsx index 0029a8eaa..eeb47b3e0 100644 --- a/interface/src/app/main/Scheduler.tsx +++ b/interface/src/app/main/Scheduler.tsx @@ -135,8 +135,8 @@ const Scheduler = () => { const saveSchedule = async () => { await updateSchedule({ schedule: schedule - .filter((si) => !si.deleted) - .map((condensed_si) => ({ + .filter((si: ScheduleItem) => !si.deleted) + .map((condensed_si: ScheduleItem) => ({ id: condensed_si.id, active: condensed_si.active, flags: condensed_si.flags, @@ -212,7 +212,9 @@ const Scheduler = () => { const renderSchedule = () => { if (!schedule) { - return ; + return ( + + ); } const dayBox = (si: ScheduleItem, flag: number) => ( @@ -251,8 +253,8 @@ const Scheduler = () => {
!si.deleted) - .sort((a, b) => a.flags - b.flags) + .filter((si: ScheduleItem) => !si.deleted) + .sort((a: ScheduleItem, b: ScheduleItem) => a.flags - b.flags) }} theme={schedule_theme} layout={{ custom: true }} diff --git a/interface/src/app/main/SchedulerDialog.tsx b/interface/src/app/main/SchedulerDialog.tsx index 89838c246..812a99a81 100644 --- a/interface/src/app/main/SchedulerDialog.tsx +++ b/interface/src/app/main/SchedulerDialog.tsx @@ -144,7 +144,10 @@ const SchedulerDialog = ({ ); - const handleClose = (_event, reason: 'backdropClick' | 'escapeKeyDown') => { + const handleClose = ( + _event: React.SyntheticEvent, + reason: 'backdropClick' | 'escapeKeyDown' + ) => { if (reason !== 'backdropClick') { onClose(); } @@ -325,7 +328,7 @@ const SchedulerDialog = ({ )} { + const handleClose = ( + _event: React.SyntheticEvent, + reason: 'backdropClick' | 'escapeKeyDown' + ) => { if (reason !== 'backdropClick') { onClose(); } @@ -88,7 +91,7 @@ const SensorsAnalogDialog = ({ { + const handleClose = ( + _event: React.SyntheticEvent, + reason: 'backdropClick' | 'escapeKeyDown' + ) => { if (reason !== 'backdropClick') { onClose(); } @@ -82,7 +85,7 @@ const SensorsTemperatureDialog = ({ void ) { @@ -36,7 +36,7 @@ export const GPIO_VALIDATOR = { export const GPIO_VALIDATORR = { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: number, callback: (error?: string) => void ) { @@ -60,7 +60,7 @@ export const GPIO_VALIDATORR = { export const GPIO_VALIDATORC3 = { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: number, callback: (error?: string) => void ) { @@ -74,7 +74,7 @@ export const GPIO_VALIDATORC3 = { export const GPIO_VALIDATORS2 = { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: number, callback: (error?: string) => void ) { @@ -94,7 +94,7 @@ export const GPIO_VALIDATORS2 = { export const GPIO_VALIDATORS3 = { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: number, callback: (error?: string) => void ) { @@ -279,7 +279,7 @@ export const createSettingsValidator = (settings: Settings) => export const uniqueNameValidator = (schedule: ScheduleItem[], o_name?: string) => ({ validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, name: string, callback: (error?: string) => void ) { @@ -324,7 +324,7 @@ export const uniqueCustomNameValidator = ( o_name?: string ) => ({ validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, name: string, callback: (error?: string) => void ) { @@ -353,7 +353,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte device_id: [ { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: string, callback: (error?: string) => void ) { @@ -367,7 +367,7 @@ export const entityItemValidation = (entity: EntityItem[], entityItem: EntityIte type_id: [ { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: string, callback: (error?: string) => void ) { @@ -389,7 +389,7 @@ export const uniqueTemperatureNameValidator = ( sensors: TemperatureSensor[], o_name?: string ) => ({ - validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { + validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) { if ( (o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) && n !== '' && @@ -419,7 +419,7 @@ export const temperatureSensorItemValidation = ( export const isGPIOUniqueValidator = (sensors: AnalogSensor[]) => ({ validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, gpio: number, callback: (error?: string) => void ) { @@ -435,7 +435,7 @@ export const uniqueAnalogNameValidator = ( sensors: AnalogSensor[], o_name?: string ) => ({ - validator(rule: InternalRuleItem, n: string, callback: (error?: string) => void) { + validator(_rule: InternalRuleItem, n: string, callback: (error?: string) => void) { if ( (o_name === undefined || o_name.toLowerCase() !== n.toLowerCase()) && n !== '' && @@ -482,7 +482,7 @@ export const deviceValueItemValidation = (dv: DeviceValue) => { required: true, message: 'Value is required' }, { validator( - rule: InternalRuleItem, + _rule: InternalRuleItem, value: unknown, callback: (error?: string) => void ) { diff --git a/interface/src/app/settings/APSettings.tsx b/interface/src/app/settings/APSettings.tsx index 7c9aee614..aa294bd15 100644 --- a/interface/src/app/settings/APSettings.tsx +++ b/interface/src/app/settings/APSettings.tsx @@ -54,12 +54,12 @@ const APSettings = () => { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const content = () => { if (!data) { - return ; + return ; } const validateAndSubmit = async () => { @@ -80,7 +80,7 @@ const APSettings = () => { return ( <> { {isAPEnabled(data) && ( <> { margin="normal" /> { margin="normal" /> { label={LL.AP_HIDE_SSID()} /> { ))} { margin="normal" /> { margin="normal" /> { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const [fieldErrors, setFieldErrors] = useState(); @@ -135,7 +135,7 @@ const ApplicationSettings = () => { const content = () => { if (!data || !hardwareData) { - return ; + return ; } const validateAndSubmit = async () => { @@ -219,7 +219,7 @@ const ApplicationSettings = () => { { { { { { { { { { { { {data.remote_timeout_en && ( { {data.shower_timer && ( { <> { { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/settings/MqttSettings.tsx b/interface/src/app/settings/MqttSettings.tsx index 10921a96c..917e9fe90 100644 --- a/interface/src/app/settings/MqttSettings.tsx +++ b/interface/src/app/settings/MqttSettings.tsx @@ -56,7 +56,7 @@ const MqttSettings = () => { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const SecondsInputProps = { @@ -65,7 +65,7 @@ const MqttSettings = () => { const content = () => { if (!data) { - return ; + return ; } const validateAndSubmit = async () => { @@ -93,7 +93,7 @@ const MqttSettings = () => { { { { { { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const [fieldErrors, setFieldErrors] = useState(); @@ -155,7 +155,7 @@ const NTPSettings = () => { const content = () => { if (!data) { - return ; + return ; } const validateAndSubmit = async () => { @@ -190,7 +190,7 @@ const NTPSettings = () => { label={LL.ENABLE_NTP()} /> { margin="normal" /> { ], useLocation() ); - const routerTab = matchedRoutes?.[0].route.path || false; + const routerTab = matchedRoutes?.[0]?.route.path || false; const navigate = useNavigate(); @@ -56,7 +56,7 @@ const Network = () => { return ( { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const [fieldErrors, setFieldErrors] = useState(); @@ -113,7 +113,7 @@ const NetworkSettings = () => { const content = () => { if (!data) { - return ; + return ; } const validateAndSubmit = async () => { @@ -172,7 +172,7 @@ const NetworkSettings = () => { ) : ( { /> )} { /> {(!selectedNetwork || !isNetworkOpen(selectedNetwork)) && ( { {LL.GENERAL_OPTIONS()} { {data.static_ip_config && ( <> { margin="normal" /> { margin="normal" /> { margin="normal" /> { margin="normal" /> { const renderNetworkScanner = () => { if (!networkList) { - return ( - - ); + return ; } return ; }; diff --git a/interface/src/app/settings/security/ManageUsers.tsx b/interface/src/app/settings/security/ManageUsers.tsx index f4724ece9..991fa790d 100644 --- a/interface/src/app/settings/security/ManageUsers.tsx +++ b/interface/src/app/settings/security/ManageUsers.tsx @@ -97,7 +97,7 @@ const ManageUsers = () => { const content = () => { if (!data) { - return ; + return ; } const noAdminConfigured = () => !data.users.find((u) => u.admin); @@ -260,15 +260,20 @@ const ManageUsers = () => { - - + {user && ( + + )} ); }; diff --git a/interface/src/app/settings/security/Security.tsx b/interface/src/app/settings/security/Security.tsx index cbbc5716b..7d5d56827 100644 --- a/interface/src/app/settings/security/Security.tsx +++ b/interface/src/app/settings/security/Security.tsx @@ -19,7 +19,7 @@ const Security = () => { ], useLocation() ); - const routerTab = matchedRoutes?.[0].route.path || false; + const routerTab = matchedRoutes?.[0]?.route.path || false; return ( <> diff --git a/interface/src/app/settings/security/SecuritySettings.tsx b/interface/src/app/settings/security/SecuritySettings.tsx index b27128dfb..04f332335 100644 --- a/interface/src/app/settings/security/SecuritySettings.tsx +++ b/interface/src/app/settings/security/SecuritySettings.tsx @@ -47,12 +47,12 @@ const SecuritySettings = () => { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); const content = () => { if (!data) { - return ; + return ; } const validateAndSubmit = async () => { @@ -69,7 +69,7 @@ const SecuritySettings = () => { return ( <> = ({ = ({ margin="normal" /> { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/Activity.tsx b/interface/src/app/status/Activity.tsx index 7c6b826e7..5be73a81d 100644 --- a/interface/src/app/status/Activity.tsx +++ b/interface/src/app/status/Activity.tsx @@ -67,7 +67,8 @@ const SystemActivity = () => { }); 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](); }; @@ -87,7 +88,7 @@ const SystemActivity = () => { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/HardwareStatus.tsx b/interface/src/app/status/HardwareStatus.tsx index 681ac0a47..fdef8bcd2 100644 --- a/interface/src/app/status/HardwareStatus.tsx +++ b/interface/src/app/status/HardwareStatus.tsx @@ -41,7 +41,7 @@ const HardwareStatus = () => { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/MqttStatus.tsx b/interface/src/app/status/MqttStatus.tsx index 911c3c5d6..210ac02d1 100644 --- a/interface/src/app/status/MqttStatus.tsx +++ b/interface/src/app/status/MqttStatus.tsx @@ -99,7 +99,7 @@ const MqttStatus = () => { const content = () => { if (!data) { - return ; + return ; } const renderConnectionStatus = () => ( diff --git a/interface/src/app/status/NTPStatus.tsx b/interface/src/app/status/NTPStatus.tsx index cf2e456d0..2153b61a5 100644 --- a/interface/src/app/status/NTPStatus.tsx +++ b/interface/src/app/status/NTPStatus.tsx @@ -68,7 +68,7 @@ const NTPStatus = () => { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/NetworkStatus.tsx b/interface/src/app/status/NetworkStatus.tsx index 7185a947b..3a9ea09b2 100644 --- a/interface/src/app/status/NetworkStatus.tsx +++ b/interface/src/app/status/NetworkStatus.tsx @@ -120,7 +120,7 @@ const NetworkStatus = () => { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index 1b0836901..286901424 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -248,7 +248,7 @@ const SystemStatus = () => { const content = () => { if (!data || !LL) { - return ; + return ; } return ( diff --git a/interface/src/app/status/SystemLog.tsx b/interface/src/app/status/SystemLog.tsx index 730881ff2..cafe73755 100644 --- a/interface/src/app/status/SystemLog.tsx +++ b/interface/src/app/status/SystemLog.tsx @@ -31,13 +31,14 @@ import type { LogEntry, LogSettings } from 'types'; import { LogLevel } from 'types'; import { updateValueDirty, useRest } from 'utils'; -const TextColors = { +const TextColors: Record = { [LogLevel.ERROR]: '#ff0000', // red [LogLevel.WARNING]: '#ff0000', // red [LogLevel.NOTICE]: '#ffffff', // white [LogLevel.INFO]: '#ffcc00', // yellow [LogLevel.DEBUG]: '#00ffff', // cyan - [LogLevel.TRACE]: '#00ffff' // cyan + [LogLevel.TRACE]: '#00ffff', // cyan + [LogLevel.ALL]: '#ffffff' // white }; const LogEntryLine = styled('span')( @@ -109,7 +110,7 @@ const SystemLog = () => { origData, dirtyFlags, setDirtyFlags, - updateDataValue + updateDataValue as (value: unknown) => void ); useSSE(fetchLogES, { @@ -190,7 +191,7 @@ const SystemLog = () => { const content = () => { if (!data) { - return ; + return ; } return ( diff --git a/interface/src/app/status/Version.tsx b/interface/src/app/status/Version.tsx index f3e3d1a20..c06e3538e 100644 --- a/interface/src/app/status/Version.tsx +++ b/interface/src/app/status/Version.tsx @@ -110,7 +110,7 @@ const Version = () => { }, [latestVersion, latestDevVersion]); const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }); - const DIVISIONS = [ + const DIVISIONS: Array<{ amount: number; name: string }> = [ { amount: 60, name: 'seconds' }, { amount: 60, name: 'minutes' }, { amount: 24, name: 'hours' }, @@ -119,18 +119,21 @@ const Version = () => { { amount: 12, name: 'months' }, { amount: Number.POSITIVE_INFINITY, name: 'years' } ]; - function formatTimeAgo(date) { + function formatTimeAgo(date: Date) { let duration = (date.getTime() - new Date().getTime()) / 1000; for (let i = 0; i < DIVISIONS.length; i++) { const division = DIVISIONS[i]; - if (Math.abs(duration) < division.amount) { + if (division && Math.abs(duration) < division.amount) { return rtf.format( Math.round(duration), 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), { @@ -293,7 +296,7 @@ const Version = () => { const content = () => { if (!data) { - return ; + return ; } const isDev = data.emsesp_version.includes('dev'); diff --git a/interface/src/components/ButtonTooltip.tsx b/interface/src/components/ButtonTooltip.tsx index 8498a6860..3ffad081d 100644 --- a/interface/src/components/ButtonTooltip.tsx +++ b/interface/src/components/ButtonTooltip.tsx @@ -1,7 +1,12 @@ import { Tooltip, type TooltipProps, styled, tooltipClasses } from '@mui/material'; export const ButtonTooltip = styled(({ className, ...props }: TooltipProps) => ( - + ))(({ theme }) => ({ [`& .${tooltipClasses.arrow}`]: { color: theme.palette.success.main diff --git a/interface/src/components/index.ts b/interface/src/components/index.ts index 4d47f6d2d..74de4154b 100644 --- a/interface/src/components/index.ts +++ b/interface/src/components/index.ts @@ -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 './layout'; export * from './loading'; export * from './routing'; export * from './upload'; -export { default as SectionContent } from './SectionContent'; -export { default as ButtonRow } from './ButtonRow'; -export { default as MessageBox } from './MessageBox'; + +// Specific routing exports export { default as BlockNavigation } from './routing/BlockNavigation'; -export { default as ButtonTooltip } from './ButtonTooltip'; diff --git a/interface/src/components/layout/LayoutMenuItem.tsx b/interface/src/components/layout/LayoutMenuItem.tsx index 99b6278bd..ba4c52d63 100644 --- a/interface/src/components/layout/LayoutMenuItem.tsx +++ b/interface/src/components/layout/LayoutMenuItem.tsx @@ -23,7 +23,12 @@ const LayoutMenuItem = ({ const selected = routeMatches(to, pathname); return ( - + diff --git a/interface/src/components/layout/ListMenuItem.tsx b/interface/src/components/layout/ListMenuItem.tsx index 0a8df795b..4d957b595 100644 --- a/interface/src/components/layout/ListMenuItem.tsx +++ b/interface/src/components/layout/ListMenuItem.tsx @@ -58,12 +58,22 @@ const LayoutMenuItem = ({ } > - + ) : ( - + )} diff --git a/interface/src/components/routing/authentication.ts b/interface/src/components/routing/authentication.ts index ac79aaccb..fb660fe71 100644 --- a/interface/src/components/routing/authentication.ts +++ b/interface/src/components/routing/authentication.ts @@ -1,6 +1,5 @@ import type { Path } from 'react-router'; -import type * as H from 'history'; import { jwtDecode } from 'jwt-decode'; import type { Me, SignInRequest, SignInResponse } from 'types'; @@ -18,10 +17,10 @@ export function getStorage() { return localStorage || sessionStorage; } -export function storeLoginRedirect(location?: H.Location) { +export function storeLoginRedirect(location?: { pathname: string; search: string }) { if (location) { - getStorage().setItem(SIGN_IN_PATHNAME, location.pathname as string); - getStorage().setItem(SIGN_IN_SEARCH, location.search as string); + getStorage().setItem(SIGN_IN_PATHNAME, location.pathname); + getStorage().setItem(SIGN_IN_SEARCH, location.search); } } @@ -36,7 +35,7 @@ export function fetchLoginRedirect(): Partial { clearLoginRedirect(); return { pathname: signInPathname || `/dashboard`, - search: (signInPathname && signInSearch) || undefined + ...(signInPathname && signInSearch && { search: signInSearch }) }; } diff --git a/interface/src/contexts/authentication/Authentication.tsx b/interface/src/contexts/authentication/Authentication.tsx index d3aef600d..dba45cad7 100644 --- a/interface/src/contexts/authentication/Authentication.tsx +++ b/interface/src/contexts/authentication/Authentication.tsx @@ -69,7 +69,12 @@ const Authentication: FC = ({ children }) => { // cache object to prevent re-renders const obj = useMemo( - () => ({ signIn, signOut, me, refresh }), + () => ({ + signIn, + signOut, + refresh, + ...(me && { me }) + }), [signIn, signOut, me, refresh] ); diff --git a/interface/src/utils/binding.ts b/interface/src/utils/binding.ts index 54c52e38e..443f5a0f9 100644 --- a/interface/src/utils/binding.ts +++ b/interface/src/utils/binding.ts @@ -29,10 +29,10 @@ export const updateValue = export const updateValueDirty = ( - origData, + origData: unknown, dirtyFlags: string[], setDirtyFlags: React.Dispatch>, - updateDataValue: (unknown) => void + updateDataValue: (value: unknown) => void ) => (event: React.ChangeEvent) => { const updated_value = extractEventValue(event);