mui upgrade

This commit is contained in:
proddy
2026-04-13 23:30:36 +02:00
parent 0f30c81554
commit 7dd13bcab7
54 changed files with 922 additions and 735 deletions

View File

@@ -26,8 +26,8 @@
"@alova/adapter-xhr": "2.3.1", "@alova/adapter-xhr": "2.3.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.9", "@mui/icons-material": "^9.0.0",
"@mui/material": "^7.3.9", "@mui/material": "^9.0.0",
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@table-library/react-table-library": "4.1.15", "@table-library/react-table-library": "4.1.15",
"alova": "3.5.1", "alova": "3.5.1",
@@ -37,11 +37,11 @@
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"magic-string": "^0.30.21", "magic-string": "^0.30.21",
"mime-types": "^3.0.2", "mime-types": "^3.0.2",
"preact": "^10.29.0", "preact": "^10.29.1",
"react": "^19.2.4", "react": "^19.2.5",
"react-dom": "^19.2.4", "react-dom": "^19.2.5",
"react-icons": "^5.6.0", "react-icons": "^5.6.0",
"react-router": "^7.13.2", "react-router": "^7.14.1",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"typesafe-i18n": "^5.27.1", "typesafe-i18n": "^5.27.1",
"typescript": "^6.0.2" "typescript": "^6.0.2"
@@ -52,18 +52,18 @@
"@preact/compat": "^18.3.2", "@preact/compat": "^18.3.2",
"@preact/preset-vite": "^2.10.5", "@preact/preset-vite": "^2.10.5",
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0", "@types/node": "^25.6.0",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"axe-core": "^4.11.1", "axe-core": "^4.11.3",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"eslint": "^10.1.0", "eslint": "^10.2.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.1", "prettier": "^3.8.2",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"terser": "^5.46.1", "terser": "^5.46.1",
"typescript-eslint": "^8.58.0", "typescript-eslint": "^8.58.2",
"vite": "^8.0.3", "vite": "^8.0.8",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^6.1.1" "vite-tsconfig-paths": "^6.1.1"
}, },

868
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ import { toast } from 'react-toastify';
import ForwardIcon from '@mui/icons-material/Forward'; import ForwardIcon from '@mui/icons-material/Forward';
import { Box, Button, Paper, Typography } from '@mui/material'; import { Box, Button, Paper, Typography } from '@mui/material';
import type { Theme } from '@mui/material/styles';
import * as AuthenticationApi from 'components/routing/authentication'; import * as AuthenticationApi from 'components/routing/authentication';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
@@ -36,7 +37,7 @@ const SignIn = memo(() => {
{ {
immediate: false immediate: false
} }
).onSuccess((response) => { ).onSuccess((response: { data: { access_token: string } }) => {
if (response.data) { if (response.data) {
authenticationContext.signIn(response.data.access_token); authenticationContext.signIn(response.data.access_token);
} }
@@ -78,7 +79,6 @@ const SignIn = memo(() => {
} }
}, [signInRequest, signIn, LL]); }, [signInRequest, signIn, LL]);
// Memoize callback to prevent recreation on every render
const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]); const submitOnEnter = useMemo(() => onEnterCallback(signIn), [signIn]);
// get rid of scrollbar // get rid of scrollbar
@@ -92,13 +92,15 @@ const SignIn = memo(() => {
return ( return (
<Box <Box
display="flex" sx={(theme: Theme) => ({
height="100vh" display: 'flex',
margin="auto" height: '100vh',
padding={2} margin: 'auto',
justifyContent="center" padding: 2,
flexDirection="column" justifyContent: 'center',
maxWidth={(theme) => theme.breakpoints.values.sm} flexDirection: 'column',
maxWidth: theme.breakpoints.values.sm
})}
> >
<Paper <Paper
sx={(theme) => ({ sx={(theme) => ({
@@ -111,16 +113,18 @@ const SignIn = memo(() => {
width: '100%' width: '100%'
})} })}
> >
<Typography mb={1} variant="h4"> <Typography sx={{ mb: 1 }} variant="h4">
{PROJECT_NAME} {PROJECT_NAME}
</Typography> </Typography>
<LanguageSelector /> <LanguageSelector />
<Box <Box
mt={1} sx={{
display="flex" mt: 1,
flexDirection="column" display: 'flex',
gap={1} flexDirection: 'column',
alignItems="center" gap: 1,
alignItems: 'center'
}}
> >
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors || {}} fieldErrors={fieldErrors || {}}

View File

@@ -343,9 +343,9 @@ const CustomEntities = () => {
return ( return (
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.ENTITIES_HELP_1()}.</Typography> {LL.ENTITIES_HELP_1()}.
</Box> </Typography>
{renderEntity()} {renderEntity()}
@@ -361,8 +361,8 @@ const CustomEntities = () => {
/> />
)} )}
<Box mt={2} display="flex" flexWrap="wrap"> <Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges > 0 && ( {numChanges > 0 && (
<ButtonRow> <ButtonRow>
<Button <Button
@@ -384,7 +384,7 @@ const CustomEntities = () => {
</ButtonRow> </ButtonRow>
)} )}
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<Button <Button
startIcon={<AddIcon />} startIcon={<AddIcon />}
variant="outlined" variant="outlined"

View File

@@ -7,7 +7,7 @@ import DoneIcon from '@mui/icons-material/Done';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import { import {
Box, Box,
Button, Button,
@@ -178,7 +178,7 @@ const CustomEntitiesDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
</Grid> </Grid>
<Grid mt={3}> <Grid sx={{ mt: 3 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox
@@ -238,7 +238,7 @@ const CustomEntitiesDialog = ({
)} )}
{editItem.ram === 0 && ( {editItem.ram === 0 && (
<> <>
<Grid mt={3}> <Grid sx={{ mt: 3 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox
@@ -404,7 +404,7 @@ const CustomEntitiesDialog = ({
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
variant="outlined" variant="outlined"

View File

@@ -470,10 +470,10 @@ const Customizations = () => {
const renderDeviceList = () => ( const renderDeviceList = () => (
<> <>
<Box mb={1} color="warning.main"> <Typography sx={{ mb: 1 }} color="warning" variant="body1">
<Typography variant="body1">{LL.CUSTOMIZATIONS_HELP_1()}.</Typography> {LL.CUSTOMIZATIONS_HELP_1()}.
</Box> </Typography>
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}> <Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2 }}>
{rename ? ( {rename ? (
<> <>
<TextField <TextField
@@ -570,27 +570,22 @@ const Customizations = () => {
const renderDeviceData = () => { const renderDeviceData = () => {
return ( return (
<> <>
<Box color="warning.main"> <Typography sx={{ mt: 1, mb: 1 }} color="warning" variant="body2">
<Typography variant="body2" mt={1} mb={1}> <OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()}
<OptionIcon type="favorite" isSet={true} />={LL.CUSTOMIZATIONS_HELP_2()} &nbsp;&nbsp;
&nbsp;&nbsp; <OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()}
<OptionIcon type="readonly" isSet={true} />={LL.CUSTOMIZATIONS_HELP_3()} &nbsp;&nbsp;
&nbsp;&nbsp; <OptionIcon type="api_mqtt_exclude" isSet={true} />=
<OptionIcon type="api_mqtt_exclude" isSet={true} />= {LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp;
{LL.CUSTOMIZATIONS_HELP_4()}&nbsp;&nbsp; <OptionIcon type="web_exclude" isSet={true} />={LL.CUSTOMIZATIONS_HELP_5()}
<OptionIcon type="web_exclude" isSet={true} />= &nbsp;&nbsp;
{LL.CUSTOMIZATIONS_HELP_5()}&nbsp;&nbsp; <OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()}
<OptionIcon type="deleted" isSet={true} />={LL.CUSTOMIZATIONS_HELP_6()} </Typography>
</Typography>
</Box>
<Grid <Grid
container container
mb={1}
mt={0}
spacing={2} spacing={2}
direction="row" direction="row"
justifyContent="flex-start" sx={{ mb: 1, mt: 0, justifyContent: 'flex-start', alignItems: 'center' }}
alignItems="center"
> >
<Grid> <Grid>
<TextField <TextField
@@ -779,8 +774,8 @@ const Customizations = () => {
</Button> </Button>
</MessageBox> </MessageBox>
) : ( ) : (
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button

View File

@@ -37,7 +37,7 @@ interface LabelValueProps {
const LabelValue = memo(({ label, value }: LabelValueProps) => ( const LabelValue = memo(({ label, value }: LabelValueProps) => (
<Grid container direction="row"> <Grid container direction="row">
<Typography variant="body2" color="warning.main"> <Typography variant="body2" color="warning">
{label}:&nbsp; {label}:&nbsp;
</Typography> </Typography>
<Typography variant="body2">{value}</Typography> <Typography variant="body2">{value}</Typography>
@@ -131,7 +131,7 @@ const CustomizationsDialog = ({
/> />
<LabelValue label={LL.WRITEABLE()} value={writeableIcon} /> <LabelValue label={LL.WRITEABLE()} value={writeableIcon} />
<Box mt={1} mb={2}> <Box sx={{ mt: 1, mb: 2 }}>
<EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} /> <EntityMaskToggle onUpdate={updateDeviceEntity} de={editItem} />
</Box> </Box>
@@ -172,7 +172,7 @@ const CustomizationsDialog = ({
</Grid> </Grid>
{error && ( {error && (
<Typography variant="body2" color="error" mt={2}> <Typography sx={{ mt: 2 }} variant="body2" color="error">
Error: Check min and max values Error: Check min and max values
</Typography> </Typography>
)} )}

View File

@@ -6,7 +6,7 @@ import { toast } from 'react-toastify';
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; import HelpOutlineIcon from '@mui/icons-material/HelpOutlined';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
import { import {
@@ -263,7 +263,7 @@ const Dashboard = memo(() => {
return ( return (
<> <>
{data.connected && data.nodes.length > 0 && !hasFavEntities && ( {data.connected && data.nodes.length > 0 && !hasFavEntities && (
<MessageBox mb={2} level="warning"> <MessageBox sx={{ mb: 2 }} level="warning">
<Typography> <Typography>
{LL.NO_DATA_1()}&nbsp; {LL.NO_DATA_1()}&nbsp;
<Link to="/customizations" style={{ color: 'white' }}> <Link to="/customizations" style={{ color: 'white' }}>
@@ -280,10 +280,12 @@ const Dashboard = memo(() => {
)} )}
<Box <Box
display="flex" sx={{
justifyContent="flex-end" display: 'flex',
flexWrap="nowrap" justifyContent: 'flex-end',
whiteSpace="nowrap" flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
> >
<ToggleButtonGroup <ToggleButtonGroup
size="small" size="small"
@@ -306,7 +308,7 @@ const Dashboard = memo(() => {
</Box> </Box>
{data.nodes.length > 0 ? ( {data.nodes.length > 0 ? (
<Box mt={1} justifyContent="center" flexDirection="column"> <Box sx={{ mt: 1, justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider <IconContext.Provider
value={{ value={{
color: 'lightblue', color: 'lightblue',
@@ -373,13 +375,8 @@ const Dashboard = memo(() => {
</IconContext.Provider> </IconContext.Provider>
</Box> </Box>
) : ( ) : (
<Box <Box sx={{ display: 'flex' }}>
display="flex" <Typography sx={{ mt: 1 }} color="warning" variant="body1">
// justifyContent="flex-end"
// flexWrap="nowrap"
// whiteSpace="nowrap"
>
<Typography mt={1} color="warning.main" variant="body1">
no data no data
</Typography> </Typography>
<Tooltip title={LL.DASHBOARD_1()}> <Tooltip title={LL.DASHBOARD_1()}>

View File

@@ -1,4 +1,5 @@
import { memo } from 'react'; import { memo } from 'react';
import type { IconType } from 'react-icons';
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai'; import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg'; import { CgSmartHomeBoiler } from 'react-icons/cg';
import { FaSolarPanel } from 'react-icons/fa'; import { FaSolarPanel } from 'react-icons/fa';
@@ -15,14 +16,9 @@ import { PiFan, PiGauge } from 'react-icons/pi';
import { TiFlowSwitch, TiThermometer } from 'react-icons/ti'; import { TiFlowSwitch, TiThermometer } from 'react-icons/ti';
import { VscVmConnect } from 'react-icons/vsc'; import { VscVmConnect } from 'react-icons/vsc';
import type { SvgIconProps } from '@mui/material';
import { DeviceType } from './types'; import { DeviceType } from './types';
const deviceIconLookup: Record< const deviceIconLookup: Record<DeviceType, IconType | null> = {
DeviceType,
React.ComponentType<SvgIconProps> | null
> = {
[DeviceType.TEMPERATURESENSOR]: TiThermometer, [DeviceType.TEMPERATURESENSOR]: TiThermometer,
[DeviceType.ANALOGSENSOR]: PiGauge, [DeviceType.ANALOGSENSOR]: PiGauge,
[DeviceType.BOILER]: CgSmartHomeBoiler, [DeviceType.BOILER]: CgSmartHomeBoiler,

View File

@@ -546,7 +546,7 @@ const Devices = memo(() => {
) )
</MessageBox> </MessageBox>
) : ( ) : (
<Box justifyContent="center" flexDirection="column"> <Box sx={{ justifyContent: 'center', flexDirection: 'column' }}>
<IconContext.Provider <IconContext.Provider
value={{ value={{
color: 'lightblue', color: 'lightblue',
@@ -670,12 +670,12 @@ const Devices = memo(() => {
}} }}
> >
<Box sx={{ p: 1 }}> <Box sx={{ p: 1 }}>
<Grid container justifyContent="space-between"> <Grid container sx={{ justifyContent: 'space-between' }}>
<Typography noWrap variant="subtitle1" color="warning.main"> <Typography noWrap variant="subtitle1" color="warning">
{deviceInfo.n}&nbsp;( {deviceInfo.n}&nbsp;(
{deviceInfo.tn}) {deviceInfo.tn})
</Typography> </Typography>
<Grid justifyContent="flex-end"> <Grid sx={{ justifyContent: 'flex-end' }}>
<ButtonTooltip title={LL.CLOSE()}> <ButtonTooltip title={LL.CLOSE()}>
<IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}> <IconButton onClick={resetDeviceSelect} aria-label={LL.CLOSE()}>
<HighlightOffIcon color="primary" sx={{ fontSize: 18 }} /> <HighlightOffIcon color="primary" sx={{ fontSize: 18 }} />

View File

@@ -128,9 +128,9 @@ const DevicesDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={onClose}> <Dialog sx={dialogStyle} open={open} onClose={onClose}>
<DialogTitle>{dialogTitle}</DialogTitle> <DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" mb={2}> <Typography sx={{ mb: 2 }} color="warning" variant="body2">
<Typography variant="body2">{editItem.id.slice(2)}</Typography> {editItem.id.slice(2)}
</Box> </Typography>
<Grid container> <Grid container>
<Grid size={12}> <Grid size={12}>
{editItem.l ? ( {editItem.l ? (

View File

@@ -71,7 +71,6 @@ const HelpComponent = () => {
}); });
const [imgError, setImgError] = useState<boolean>(false); const [imgError, setImgError] = useState<boolean>(false);
// Memoize the request method to prevent re-creation on every render
const getCustomSupportMethod = useMemo( const getCustomSupportMethod = useMemo(
() => callAction({ action: 'getCustomSupport' }), () => callAction({ action: 'getCustomSupport' }),
[] []
@@ -146,11 +145,9 @@ const HelpComponent = () => {
<SectionContent> <SectionContent>
{customSupport.html && ( {customSupport.html && (
<Stack <Stack
padding={1}
mb={2}
direction="row" direction="row"
divider={<Divider orientation="vertical" flexItem />} divider={<Divider orientation="vertical" flexItem />}
sx={SUPPORT_BOX_STYLES} sx={{ padding: 1, mb: 2, ...SUPPORT_BOX_STYLES }}
> >
<Typography variant="subtitle1"> <Typography variant="subtitle1">
<div dangerouslySetInnerHTML={{ __html: customSupport.html }} /> <div dangerouslySetInnerHTML={{ __html: customSupport.html }} />
@@ -185,23 +182,21 @@ const HelpComponent = () => {
</List> </List>
)} )}
<Box p={2} color="warning.main"> <Typography sx={{ mb: 1 }} color="warning" variant="body1">
<Typography mb={1} variant="body1"> {LL.HELP_INFORMATION_4()}.
{LL.HELP_INFORMATION_4()}. </Typography>
</Typography> <Button
<Button startIcon={<DownloadIcon />}
startIcon={<DownloadIcon />} variant="outlined"
variant="outlined" color="primary"
color="primary" onClick={handleDownloadSystemInfo}
onClick={handleDownloadSystemInfo} >
> {LL.SUPPORT_INFORMATION(0)}
{LL.SUPPORT_INFORMATION(0)} </Button>
</Button>
</Box>
<Divider sx={{ mt: 4 }} /> <Divider sx={{ mt: 4 }} />
<Typography color="white" variant="subtitle1" align="center" mt={1}> <Typography color="white" variant="subtitle1" align="center" sx={{ mt: 1 }}>
&copy;&nbsp; &copy;&nbsp;
<Link <Link
target="_blank" target="_blank"

View File

@@ -186,9 +186,9 @@ const Modules = () => {
return ( return (
<> <>
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.MODULES_DESCRIPTION()}.</Typography> {LL.MODULES_DESCRIPTION()}.
</Box> </Typography>
<Table <Table
data={{ nodes: modules }} data={{ nodes: modules }}
theme={modules_theme} theme={modules_theme}
@@ -236,8 +236,8 @@ const Modules = () => {
)} )}
</Table> </Table>
<Box mt={1} display="flex" flexWrap="wrap"> <Box sx={{ mt: 1, display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button

View File

@@ -79,7 +79,7 @@ const ModulesDialog = ({
label="Enabled" label="Enabled"
/> />
</Grid> </Grid>
<Box mt={2} mb={1}> <Box sx={{ mt: 2, mb: 1 }}>
<TextField <TextField
name="license" name="license"
label="License Key" label="License Key"

View File

@@ -2,12 +2,12 @@ import { memo } from 'react';
import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined'; import CommentsDisabledOutlinedIcon from '@mui/icons-material/CommentsDisabledOutlined';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutlined';
import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined'; import EditOffOutlinedIcon from '@mui/icons-material/EditOffOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined'; import InsertCommentOutlinedIcon from '@mui/icons-material/InsertCommentOutlined';
import StarIcon from '@mui/icons-material/Star'; import StarIcon from '@mui/icons-material/Star';
import StarOutlineIcon from '@mui/icons-material/StarOutline'; import StarOutlineIcon from '@mui/icons-material/StarOutlined';
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'; import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import type { SvgIconProps } from '@mui/material'; import type { SvgIconProps } from '@mui/material';

View File

@@ -358,9 +358,9 @@ const Scheduler = () => {
return ( return (
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<Box mb={2} color="warning.main"> <Typography sx={{ mb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.SCHEDULER_HELP_1()}.</Typography> {LL.SCHEDULER_HELP_1()}.
</Box> </Typography>
{renderSchedule()} {renderSchedule()}
{selectedScheduleItem && ( {selectedScheduleItem && (
@@ -375,8 +375,8 @@ const Scheduler = () => {
/> />
)} )}
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
{numChanges !== 0 && ( {numChanges !== 0 && (
<ButtonRow> <ButtonRow>
<Button <Button
@@ -398,7 +398,7 @@ const Scheduler = () => {
</ButtonRow> </ButtonRow>
)} )}
</Box> </Box>
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<AddIcon />} startIcon={<AddIcon />}

View File

@@ -4,7 +4,7 @@ import AddIcon from '@mui/icons-material/Add';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import { import {
Box, Box,
Button, Button,
@@ -338,11 +338,13 @@ const SchedulerDialog = ({
onChange={updateFormValue} onChange={updateFormValue}
/> />
{isTimerSchedule && ( {isTimerSchedule && (
<Box color="warning.main" ml={2} mt={4}> <Typography
<Typography variant="body2"> sx={{ ml: 2, mt: 4 }}
{LL.SCHEDULER_HELP_2()} color="warning"
</Typography> variant="body2"
</Box> >
{LL.SCHEDULER_HELP_2()}
</Typography>
)} )}
</> </>
) : ( ) : (
@@ -391,7 +393,7 @@ const SchedulerDialog = ({
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1}> <Box sx={{ flexGrow: 1 }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
variant="outlined" variant="outlined"

View File

@@ -591,7 +591,14 @@ const Sensors = () => {
/> />
)} )}
{sensorData?.analog_enabled === true && me.admin && ( {sensorData?.analog_enabled === true && me.admin && (
<Box mt={2} display="flex" flexWrap="wrap" justifyContent="flex-end"> <Box
sx={{
mt: 2,
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'flex-end'
}}
>
<Button <Button
variant="outlined" variant="outlined"
color="primary" color="primary"

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutlined';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { import {
Box, Box,
@@ -479,7 +479,7 @@ const SensorsAnalogDialog = ({
)} )}
</Grid> </Grid>
{fieldErrors && Object.keys(fieldErrors).length > 0 && ( {fieldErrors && Object.keys(fieldErrors).length > 0 && (
<Box mt={1}> <Box sx={{ mt: 1 }}>
{Object.values(fieldErrors).map((errArr, idx) => {Object.values(fieldErrors).map((errArr, idx) =>
Array.isArray(errArr) Array.isArray(errArr)
? errArr.map((err, j) => ( ? errArr.map((err, j) => (
@@ -487,7 +487,7 @@ const SensorsAnalogDialog = ({
key={`${idx}-${j}`} key={`${idx}-${j}`}
color="error" color="error"
variant="caption" variant="caption"
display="block" sx={{ display: 'block' }}
> >
{err.message} {err.message}
</Typography> </Typography>
@@ -498,7 +498,7 @@ const SensorsAnalogDialog = ({
)} )}
{editItem.s && ( {editItem.s && (
<Grid> <Grid>
<Typography mt={1} color="warning.main" variant="body2"> <Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon <WarningIcon
fontSize="small" fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }} sx={{ mr: 1, verticalAlign: 'middle' }}
@@ -511,7 +511,7 @@ const SensorsAnalogDialog = ({
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{!creating && ( {!creating && (
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}> <Box sx={{ flexGrow: 1, '& button': { mt: 0 } }}>
<Button <Button
startIcon={<RemoveIcon />} startIcon={<RemoveIcon />}
disabled={editItem.s} disabled={editItem.s}

View File

@@ -4,7 +4,6 @@ import CancelIcon from '@mui/icons-material/Cancel';
import DoneIcon from '@mui/icons-material/Done'; import DoneIcon from '@mui/icons-material/Done';
import WarningIcon from '@mui/icons-material/Warning'; import WarningIcon from '@mui/icons-material/Warning';
import { import {
Box,
Button, Button,
Dialog, Dialog,
DialogActions, DialogActions,
@@ -111,11 +110,9 @@ const SensorsTemperatureDialog = ({
<Dialog sx={dialogStyle} open={open} onClose={handleClose}> <Dialog sx={dialogStyle} open={open} onClose={handleClose}>
<DialogTitle>{dialogTitle}</DialogTitle> <DialogTitle>{dialogTitle}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" mb={2}> <Typography sx={{ mb: 2 }} color="warning" variant="body2">
<Typography variant="body2"> {LL.ID_OF(LL.SENSOR(0))}: {editItem.id}
{LL.ID_OF(LL.SENSOR(0))}: {editItem.id} </Typography>
</Typography>
</Box>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid> <Grid>
<ValidatedTextField <ValidatedTextField
@@ -142,7 +139,7 @@ const SensorsTemperatureDialog = ({
</Grid> </Grid>
{editItem.s && ( {editItem.s && (
<Grid> <Grid>
<Typography mt={1} color="warning.main" variant="body2"> <Typography sx={{ mt: 1 }} color="warning" variant="body2">
<WarningIcon <WarningIcon
fontSize="small" fontSize="small"
sx={{ mr: 1, verticalAlign: 'middle' }} sx={{ mr: 1, verticalAlign: 'middle' }}

View File

@@ -41,8 +41,12 @@ const UserProfileComponent = () => {
/> />
</ListItem> </ListItem>
</List> </List>
<Box mt={2} mb={2} display="flex" alignItems="center"> <Box sx={{ mt: 2, mb: 2, display: 'flex', alignItems: 'center' }}>
<Typography mr={2} variant="body1" align="center"> <Typography
sx={{ mr: 2, textAlign: 'center' }}
color="warning"
variant="body1"
>
{LL.LANGUAGE()}: {LL.LANGUAGE()}:
</Typography> </Typography>
<LanguageSelector /> <LanguageSelector />

View File

@@ -771,7 +771,7 @@ const ApplicationSettings = () => {
label={LL.REMOTE_TIMEOUT_EN()} label={LL.REMOTE_TIMEOUT_EN()}
/> />
{data.remote_timeout_en && ( {data.remote_timeout_en && (
<Box mt={2}> <Box sx={{ mt: 2 }}>
<ValidatedTextField <ValidatedTextField
fieldErrors={fieldErrors || {}} fieldErrors={fieldErrors || {}}
name="remote_timeout" name="remote_timeout"

View File

@@ -1,12 +1,23 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import DownloadIcon from '@mui/icons-material/GetApp'; import DownloadIcon from '@mui/icons-material/GetApp';
import { Box, Button, Grid, Typography } from '@mui/material'; import WarningIcon from '@mui/icons-material/Warning';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Grid,
Typography
} from '@mui/material';
import * as SystemApi from 'api/system'; import * as SystemApi from 'api/system';
import { API, callAction } from 'api/app'; import { API, callAction } from 'api/app';
import { dialogStyle } from '@/CustomTheme';
import { useRequest } from 'alova/client'; import { useRequest } from 'alova/client';
import type { APIcall } from 'app/main/types'; import type { APIcall } from 'app/main/types';
import SystemMonitor from 'app/status/SystemMonitor'; import SystemMonitor from 'app/status/SystemMonitor';
@@ -19,15 +30,11 @@ import {
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { saveFile } from 'utils'; import { saveFile } from 'utils';
interface DownloadButton {
type: string;
label: string | number;
isGridButton: boolean;
}
const DownloadUpload = () => { const DownloadUpload = () => {
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [confirmBackup, setConfirmBackup] = useState<boolean>(false);
const [restarting, setRestarting] = useState<boolean>(false); const [restarting, setRestarting] = useState<boolean>(false);
const { send: sendExportData } = useRequest( const { send: sendExportData } = useRequest(
@@ -62,40 +69,44 @@ const DownloadUpload = () => {
useLayoutTitle(LL.DOWNLOAD_UPLOAD()); useLayoutTitle(LL.DOWNLOAD_UPLOAD());
const downloadButtons: DownloadButton[] = useMemo( const handleCloseBackupDialog = useCallback(() => {
() => [ setConfirmBackup(false);
{ }, []);
type: 'settings',
label: LL.SETTINGS_OF(LL.APPLICATION()), const renderBackupDialog = useMemo(
isGridButton: true () => (
}, <Dialog
{ sx={dialogStyle}
type: 'customizations', open={confirmBackup}
label: LL.CUSTOMIZATIONS(), onClose={handleCloseBackupDialog}
isGridButton: true >
}, <DialogTitle>{LL.DOWNLOAD_SYSTEM_BACKUP()}</DialogTitle>
{ <DialogContent dividers>
type: 'entities', <WarningIcon color="warning" sx={{ fontSize: 18 }} />
label: LL.CUSTOM_ENTITIES(0), &nbsp;
isGridButton: true {LL.WARNING_SYSTEM_BACKUP()}
}, </DialogContent>
{ <DialogActions>
type: 'schedule', <Button
label: LL.SCHEDULE(0), startIcon={<CancelIcon />}
isGridButton: true variant="outlined"
}, onClick={handleCloseBackupDialog}
{ color="secondary"
type: 'systembackup', >
label: LL.DOWNLOAD_SYSTEM_BACKUP(), {LL.CANCEL()}
isGridButton: true </Button>
}, <Button
{ startIcon={<DownloadIcon />}
type: 'allvalues', variant="outlined"
label: LL.ALLVALUES(), onClick={() => handleDownload('systembackup')()}
isGridButton: false color="primary"
} >
], {LL.DOWNLOAD(0)}
[LL] </Button>
</DialogActions>
</Dialog>
),
[confirmBackup, handleCloseBackupDialog, LL]
); );
const handleDownload = useCallback( const handleDownload = useCallback(
@@ -117,58 +128,57 @@ const DownloadUpload = () => {
); );
} }
const gridButtons = downloadButtons.filter((btn) => btn.isGridButton);
const standaloneButton = downloadButtons.find((btn) => !btn.isGridButton);
return ( return (
<SectionContent> <SectionContent>
{renderBackupDialog}
<Typography sx={{ pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pb: 2 }} variant="h6" color="primary">
{LL.DOWNLOAD(0)} {LL.DOWNLOAD(0)}
</Typography> </Typography>
<Typography mb={1} variant="body1" color="warning"> <Grid
{LL.DOWNLOAD_SETTINGS_TEXT()}. container
</Typography> spacing={2}
sx={{
<Grid container spacing={2}> alignItems: 'center'
{gridButtons.map((button) => ( }}
<Grid key={button.type}> >
<Button <Typography variant="body1" color="warning">
startIcon={<DownloadIcon />} {LL.DOWNLOAD_SETTINGS_TEXT()}:
variant="outlined" </Typography>
color="primary"
onClick={handleDownload(button.type)}
>
{button.label}
</Button>
</Grid>
))}
</Grid>
<Typography mt={2} mb={1} variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT2()}.
</Typography>
{standaloneButton && (
<Button <Button
startIcon={<DownloadIcon />} startIcon={<DownloadIcon />}
variant="outlined" variant="outlined"
color="primary" color="primary"
onClick={handleDownload(standaloneButton.type)} onClick={() => setConfirmBackup(true)}
> >
{standaloneButton.label} {LL.DOWNLOAD_SYSTEM_BACKUP()}
</Button> </Button>
)} </Grid>
<Grid container spacing={2} sx={{ mt: 2, alignItems: 'center' }}>
<Typography variant="body1" color="warning">
{LL.DOWNLOAD_SETTINGS_TEXT2()}:
</Typography>
<Button
startIcon={<DownloadIcon />}
variant="outlined"
color="primary"
onClick={handleDownload('allvalues')}
>
{LL.ALLVALUES()}
</Button>
</Grid>
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()} {LL.UPLOAD()}
</Typography> </Typography>
<Box color="warning.main" sx={{ pb: 2 }}> <Typography sx={{ pb: 2 }} color="warning" variant="body1">
<Typography variant="body1">{LL.UPLOAD_TEXT()}.</Typography> {LL.UPLOAD_TEXT()}:
</Box> </Typography>
<SingleUpload text={LL.UPLOAD_DRAG()} doRestart={doRestart} /> <SingleUpload doRestart={doRestart} />
</SectionContent> </SectionContent>
); );
}; };

View File

@@ -129,7 +129,7 @@ const MqttSettings = () => {
<SectionContent> <SectionContent>
{blocker ? <BlockNavigation blocker={blocker} /> : null} {blocker ? <BlockNavigation blocker={blocker} /> : null}
<> <>
<Box display="flex" gap={2} mb={1}> <Box sx={{ display: 'flex', gap: 2, mb: 1 }}>
<BlockFormControlLabel <BlockFormControlLabel
control={ control={
<Checkbox <Checkbox

View File

@@ -193,9 +193,9 @@ const NTPSettings = () => {
{timeZoneItems} {timeZoneItems}
</ValidatedTextField> </ValidatedTextField>
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{!data.enabled && !dirtyFlags.length && ( {!data.enabled && !dirtyFlags.length && (
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
onClick={openSetTime} onClick={openSetTime}
@@ -259,9 +259,9 @@ const NTPSettings = () => {
<Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}> <Dialog sx={dialogStyle} open={settingTime} onClose={handleCloseSetTime}>
<DialogTitle>{LL.SET_TIME(1)}</DialogTitle> <DialogTitle>{LL.SET_TIME(1)}</DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Box color="warning.main" p={0} pl={0} pr={0} mt={0} mb={2}> <Typography color="warning" variant="body2">
<Typography variant="body2">{LL.SET_TIME_TEXT()}</Typography> {LL.SET_TIME_TEXT()}
</Box> </Typography>
<TextField <TextField
label={LL.LOCAL_TIME(0)} label={LL.LOCAL_TIME(0)}
type="datetime-local" type="datetime-local"

View File

@@ -156,11 +156,13 @@ const Settings = () => {
<Divider /> <Divider />
<Box <Box
mt={2} sx={{
display="flex" mt: 2,
justifyContent="flex-end" display: 'flex',
flexWrap="nowrap" justifyContent: 'flex-end',
whiteSpace="nowrap" flexWrap: 'nowrap',
whiteSpace: 'nowrap'
}}
> >
<Button <Button
startIcon={<SettingsBackupRestoreIcon />} startIcon={<SettingsBackupRestoreIcon />}

View File

@@ -54,19 +54,27 @@ const GenerateToken = ({ username, onClose }: GenerateTokenProps) => {
<DialogContent dividers> <DialogContent dividers>
{token ? ( {token ? (
<> <>
<MessageBox message={LL.ACCESS_TOKEN_TEXT()} level="info" my={2} /> <MessageBox
<Box mt={2} mb={2}> message={LL.ACCESS_TOKEN_TEXT()}
level="info"
sx={{ mt: 2, mb: 2 }}
/>
<Box sx={{ mt: 2, mb: 2 }}>
<TextField <TextField
label="Token" label="Token"
multiline multiline
value={token.token} value={token.token}
fullWidth fullWidth
contentEditable={false} slotProps={{
input: {
readOnly: true
}
}}
/> />
</Box> </Box>
</> </>
) : ( ) : (
<Box m={4} textAlign="center"> <Box sx={{ m: 4, textAlign: 'center' }}>
<LinearProgress /> <LinearProgress />
<Typography variant="h6">{LL.GENERATING_TOKEN()}&hellip;</Typography> <Typography variant="h6">{LL.GENERATING_TOKEN()}&hellip;</Typography>
</Box> </Box>

View File

@@ -240,12 +240,16 @@ const ManageUsers = () => {
</Table> </Table>
{noAdminConfigured() && ( {noAdminConfigured() && (
<MessageBox level="warning" message={LL.USER_WARNING()} my={2} /> <MessageBox
level="warning"
message={LL.USER_WARNING()}
sx={{ mt: 2, mb: 2 }}
/>
)} )}
<Box display="flex" flexWrap="wrap"> <Box sx={{ display: 'flex', flexWrap: 'wrap' }}>
{changed !== 0 && ( {changed !== 0 && (
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}> <Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<CancelIcon />} startIcon={<CancelIcon />}
@@ -270,7 +274,7 @@ const ManageUsers = () => {
</ButtonRow> </ButtonRow>
</Box> </Box>
)} )}
<Box flexWrap="nowrap" whiteSpace="nowrap"> <Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow> <ButtonRow>
<Button <Button
startIcon={<PersonAddIcon />} startIcon={<PersonAddIcon />}

View File

@@ -266,7 +266,7 @@ const SystemLog = () => {
return ( return (
<> <>
<Grid container spacing={2} alignItems="center"> <Grid container spacing={2} sx={{ alignItems: 'center' }}>
<Grid> <Grid>
<TextField <TextField
name="level" name="level"

View File

@@ -118,17 +118,15 @@ const SystemMonitor = () => {
p: 3 p: 3
}} }}
> >
<Box display="flex" alignItems="center" flexDirection="column"> <Box sx={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<img <img
src="/app/icon.png" src="/app/icon.png"
alt="EMS-ESP" alt="EMS-ESP"
style={{ width: '40px', height: '40px', marginBottom: '16px' }} style={{ width: '40px', height: '40px', marginBottom: '16px' }}
/> />
<Typography <Typography
color="secondary" sx={{ color: 'secondary', fontWeight: 400, textAlign: 'center' }}
variant="h6" variant="h6"
fontWeight={400}
textAlign="center"
> >
{statusMessage} {statusMessage}
</Typography> </Typography>
@@ -148,11 +146,14 @@ const SystemMonitor = () => {
</MessageBox> </MessageBox>
) : ( ) : (
<> <>
<Typography mt={2} variant="h6" fontWeight={400} textAlign="center"> <Typography
sx={{ mt: 2, fontWeight: 400, textAlign: 'center' }}
variant="h6"
>
{LL.PLEASE_WAIT()}&hellip; {LL.PLEASE_WAIT()}&hellip;
</Typography> </Typography>
{isUploading && ( {isUploading && (
<Box width="100%" pl={2} pr={2} py={2}> <Box sx={{ width: '100%', pl: 2, pr: 2, py: 2 }}>
<LinearProgressWithLabel value={progressValue} /> <LinearProgressWithLabel value={progressValue} />
</Box> </Box>
)} )}

View File

@@ -307,7 +307,7 @@ const InstallDialog = memo(
{`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`} {`${LL.INSTALL()} ${fetchDevVersion ? LL.DEVELOPMENT() : LL.STABLE()} Firmware`}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Typography mb={2}> <Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION( {LL.INSTALL_VERSION(
downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(), downloadOnly ? LL.DOWNLOAD(1) : LL.INSTALL(),
fetchDevVersion ? latestDevVersion?.name : latestVersion?.name fetchDevVersion ? latestDevVersion?.name : latestVersion?.name
@@ -371,7 +371,9 @@ const InstallPartitionDialog = memo(
{LL.INSTALL()} {LL.STORED_VERSIONS()} {LL.INSTALL()} {LL.STORED_VERSIONS()}
</DialogTitle> </DialogTitle>
<DialogContent dividers> <DialogContent dividers>
<Typography mb={2}>{LL.INSTALL_VERSION(LL.INSTALL(), version)}</Typography> <Typography sx={{ mb: 2 }}>
{LL.INSTALL_VERSION(LL.INSTALL(), version)}
</Typography>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
@@ -637,8 +639,8 @@ const Version = () => {
return ( return (
<> <>
<Box p={2} border="1px solid grey" borderRadius={2}> <Box sx={{ p: 2, border: '1px solid #565656', borderRadius: 2 }}>
<Typography mb={1} variant="h6" color="primary"> <Typography sx={{ mb: 1 }} variant="h6" color="primary">
{LL.THIS_VERSION()} {LL.THIS_VERSION()}
</Typography> </Typography>
@@ -703,7 +705,7 @@ const Version = () => {
{internetLive ? ( {internetLive ? (
<> <>
<Typography mt={4} mb={1} variant="h6" color="primary"> <Typography sx={{ mt: 4, mb: 1 }} variant="h6" color="primary">
{LL.AVAILABLE_VERSION()} {LL.AVAILABLE_VERSION()}
</Typography> </Typography>
@@ -725,7 +727,7 @@ const Version = () => {
</Grid> </Grid>
<Grid size={{ xs: 8, md: 10 }}> <Grid size={{ xs: 8, md: 10 }}>
{otherPartitions.map((partition) => ( {otherPartitions.map((partition) => (
<Typography key={partition.partition} mb={1}> <Typography key={partition.partition} sx={{ mb: 1 }}>
{partition.version} {partition.version}
<IconButton <IconButton
onClick={() => onClick={() =>
@@ -791,7 +793,7 @@ const Version = () => {
</Grid> </Grid>
</> </>
) : ( ) : (
<Typography mt={2} color="warning"> <Typography sx={{ mt: 2 }} color="warning">
<WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} /> <WarningIcon color="warning" sx={{ verticalAlign: 'middle', mr: 2 }} />
{LL.INTERNET_CONNECTION_REQUIRED()} {LL.INTERNET_CONNECTION_REQUIRED()}
</Typography> </Typography>
@@ -832,7 +834,7 @@ const Version = () => {
<Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary"> <Typography sx={{ pt: 2, pb: 2 }} variant="h6" color="primary">
{LL.UPLOAD()} {LL.UPLOAD()}
</Typography> </Typography>
<SingleUpload text={LL.UPLOAD_DROP_TEXT()} doRestart={doRestart} /> <SingleUpload doRestart={doRestart} />
</> </>
)} )}
</Box> </Box>

View File

@@ -53,12 +53,16 @@ const MessageBox: FC<PropsWithChildren<MessageBoxProps>> = ({
return ( return (
<Box <Box
p={2}
display="flex"
alignItems="center"
borderRadius={1}
sx={{ backgroundColor, color: 'white', ...sx }}
{...rest} {...rest}
sx={{
display: 'flex',
alignItems: 'center',
borderRadius: 1,
backgroundColor,
color: 'white',
p: 2,
...sx
}}
> >
<Icon /> <Icon />
{(message || children) && ( {(message || children) && (

View File

@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
() => ( () => (
<> <>
<Toolbar disableGutters> <Toolbar disableGutters>
<Box display="flex" alignItems="center" px={2}> <Box sx={{ display: 'flex', alignItems: 'center', p: 2 }}>
<LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} /> <LayoutDrawerLogo src="/app/icon.png" alt={PROJECT_NAME} />
<Typography variant="h6">{PROJECT_NAME}</Typography> <Typography variant="h6">{PROJECT_NAME}</Typography>
</Box> </Box>

View File

@@ -51,9 +51,7 @@ const LayoutMenuComponent = () => {
sx={{ my: 0 }} sx={{ my: 0 }}
slotProps={{ slotProps={{
primary: { primary: {
fontWeight: '600', sx: { fontWeight: 600, mb: '2px', color: 'lightblue' }
mb: '2px',
color: 'lightblue'
} }
}} }}
/> />

View File

@@ -32,8 +32,16 @@ const FormLoaderComponent = ({ errorMessage, onRetry }: FormLoaderProps) => {
); );
} }
return ( return (
<Box m={2} py={2} display="flex" alignItems="center" flexDirection="column"> <Box
<Box py={2}> sx={{
m: 2,
py: 2,
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
}}
>
<Box sx={{ p: 2 }}>
<CircularProgress size={100} /> <CircularProgress size={100} />
</Box> </Box>
</Box> </Box>

View File

@@ -15,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => { const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
return ( return (
<Box <Box
display="flex" sx={{
alignItems="center" display: 'flex',
justifyContent="center" alignItems: 'center',
flexDirection="column" justifyContent: 'center',
padding={2} flexDirection: 'column',
height={height} padding: 2,
height
}}
> >
<CircularProgress sx={circularProgressStyles} size={100} /> <CircularProgress sx={circularProgressStyles} size={100} />
</Box> </Box>

View File

@@ -1,4 +1,4 @@
// Code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0 // drag/drop code inspired by Prince Azubuike from https://medium.com/@dprincecoder/creating-a-drag-and-drop-file-upload-component-in-react-a-step-by-step-guide-4d93b6cc21e0
import { import {
type ChangeEvent, type ChangeEvent,
type DragEvent, type DragEvent,
@@ -6,12 +6,27 @@ import {
useRef, useRef,
useState useState
} from 'react'; } from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload'; import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import UploadIcon from '@mui/icons-material/Upload'; import UploadIcon from '@mui/icons-material/Upload';
import { Box, Button, Typography, styled } from '@mui/material'; import WarningIcon from '@mui/icons-material/Warning';
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography,
styled
} from '@mui/material';
import { callAction } from 'api/app';
import { dialogStyle } from '@/CustomTheme';
import { useRequest } from 'alova/client';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({ const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
@@ -58,6 +73,23 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const [dragged, setDragged] = useState(false); const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
const { LL } = useI18nContext(); const { LL } = useI18nContext();
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
const [upgradeImportantMessageType, setUpgradeImportantMessageType] =
useState<number>(0);
const { send: checkUpgradeImportantMessages } = useRequest(
(type: string) =>
callAction({ action: 'upgradeImportantMessages', param: type }),
{
immediate: false
}
)
.onSuccess((event: { data: number }) => {
setUpgradeImportantMessageType(event.data);
})
.onError((error: { error?: { message?: string } }) => {
toast.error(String(error.error?.message || 'An error occurred'));
});
const checkFileExtension = (file: File) => { const checkFileExtension = (file: File) => {
const validExtensions = ['.json', '.bin', '.md5']; const validExtensions = ['.json', '.bin', '.md5'];
@@ -97,9 +129,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => { const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
if (file) { void checkUpgradeImportantMessages(file?.name || '');
onFileSelected(file); setShowUpgradeDialog(true);
}
}; };
const handleBrowseClick = () => { const handleBrowseClick = () => {
@@ -158,6 +189,46 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
{LL.UPLOAD()} {LL.UPLOAD()}
</Button> </Button>
</Box> </Box>
{showUpgradeDialog && (
<Dialog
sx={dialogStyle}
open={showUpgradeDialog}
onClose={() => setShowUpgradeDialog(false)}
>
<DialogTitle>
<WarningIcon
color="warning"
sx={{ fontSize: 18, verticalAlign: 'middle' }}
/>
&nbsp;&nbsp;
{LL.UPGRADE_IMPORTANT_MESSAGES()}
</DialogTitle>
<DialogContent dividers>
{upgradeImportantMessageType === 1 &&
LL.UPGRADE_IMPORTANT_MESSAGES_1()}
{upgradeImportantMessageType === 2 &&
LL.UPGRADE_IMPORTANT_MESSAGES_2()}
</DialogContent>
<DialogActions>
<Button
startIcon={<CancelIcon />}
variant="outlined"
onClick={() => setShowUpgradeDialog(false)}
color="secondary"
>
{LL.CANCEL()}
</Button>
<Button
startIcon={<UploadIcon />}
variant="outlined"
onClick={() => onFileSelected(file)}
color="primary"
>
{LL.UPLOAD()}
</Button>
</DialogActions>
</Dialog>
)}
</> </>
)} )}
</DocumentUploader> </DocumentUploader>

View File

@@ -13,11 +13,10 @@ import DragNdrop from './DragNdrop';
import { LinearProgressWithLabel } from './LinearProgressWithLabel'; import { LinearProgressWithLabel } from './LinearProgressWithLabel';
interface SingleUploadProps { interface SingleUploadProps {
text: string;
doRestart: () => void; doRestart: () => void;
} }
const SingleUpload = ({ text, doRestart }: SingleUploadProps) => { const SingleUpload = ({ doRestart }: SingleUploadProps) => {
const [md5, setMd5] = useState<string>(); const [md5, setMd5] = useState<string>();
const [file, setFile] = useState<File>(); const [file, setFile] = useState<File>();
const { LL } = useI18nContext(); const { LL } = useI18nContext();
@@ -58,7 +57,7 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
<> <>
{isUploading ? ( {isUploading ? (
<> <>
<Box width="100%" pl={2} pr={2}> <Box sx={{ width: '100%', pl: 2, pr: 2 }}>
<LinearProgressWithLabel <LinearProgressWithLabel
value={ value={
progress.total === 0 || progress.loaded === 0 progress.total === 0 || progress.loaded === 0
@@ -81,11 +80,11 @@ const SingleUpload = ({ text, doRestart }: SingleUploadProps) => {
</Button> </Button>
</> </>
) : ( ) : (
<DragNdrop text={text} onFileSelected={setFile} /> <DragNdrop text={LL.UPLOAD_DROP_TEXT()} onFileSelected={setFile} />
)} )}
{md5 && ( {md5 && (
<Box mt={2}> <Box sx={{ mt: 2 }}>
<Typography variant="body2">{'MD5: ' + md5}</Typography> <Typography variant="body2">{'MD5: ' + md5}</Typography>
</Box> </Box>
)} )}

View File

@@ -188,8 +188,12 @@ const cz: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace', DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty', DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha', DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
WARNING_SYSTEM_BACKUP: 'Toto vytvoří zálohu vašich nastavení a konfigurace. Všechna hesla budou v zálohovém souboru čitelná. Opravdu chcete pokračovat?',
UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)', UPLOAD_TEXT: 'Nahrajte nový soubor firmwaru (.bin) nebo záložní soubor (.json)',
UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr', UPLOAD_DROP_TEXT: 'Přetáhněte soubor sem nebo klikněte pro výběr',
UPGRADE_IMPORTANT_MESSAGES: 'Důležité zprávy pro aktualizaci',
UPGRADE_IMPORTANT_MESSAGES_1: 'Aktualizujete se z hlavní verze. Vezměte prosím na vědomí, že to může způsobit problémy s vaší konfigurací.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete se z dílčí verze. Vezměte prosím na vědomí, že to může způsobit problémy s vaší konfigurací.',
ERROR: 'Neočekávaná chyba, zkuste to prosím znovu', ERROR: 'Neočekávaná chyba, zkuste to prosím znovu',
TIME_SET: 'Čas nastaven', TIME_SET: 'Čas nastaven',
MANAGE_USERS: 'Spravovat uživatele', MANAGE_USERS: 'Spravovat uživatele',

View File

@@ -188,8 +188,12 @@ const de: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen', DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte', DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung', DOWNLOAD_SYSTEM_BACKUP: 'System Sicherung',
WARNING_SYSTEM_BACKUP: 'Dies wird eine Sicherung Ihrer Konfigurationen und Einstellungen erstellen. Alle Passwörter werden im Sicherungsdatei lesbar. Möchten Sie fortfahren?',
UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch', UPLOAD_TEXT: 'Laden Sie eine neue Firmware-Datei (.bin) oder eine Sicherungsdatei (.json) hoch',
UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier', UPLOAD_DROP_TEXT: 'Legen Sie eine Firmware-Datei (.bin) ab oder klicken Sie hier',
UPGRADE_IMPORTANT_MESSAGES: 'Wichtige Nachrichten für das Upgrade',
UPGRADE_IMPORTANT_MESSAGES_1: 'Sie sind von einer Major-Version auf eine neue Version aktualisiert. Bitte beachten Sie, dass dies zu Problemen mit Ihrer Konfiguration führen kann.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Sie sind von einer Minor-Version auf eine neue Version aktualisiert. Bitte beachten Sie, dass dies zu Problemen mit Ihrer Konfiguration führen kann.',
ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.', ERROR: 'Unerwarteter Fehler, bitte versuchen Sie es erneut.',
TIME_SET: 'Zeit gesetzt', TIME_SET: 'Zeit gesetzt',
MANAGE_USERS: 'Nutzerverwaltung', MANAGE_USERS: 'Nutzerverwaltung',

View File

@@ -188,8 +188,12 @@ const en: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
DOWNLOAD_SETTINGS_TEXT2: 'Export all values', DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
DOWNLOAD_SYSTEM_BACKUP: 'System Backup', DOWNLOAD_SYSTEM_BACKUP: 'System Backup',
WARNING_SYSTEM_BACKUP: 'This will create a backup of your configuration and settings. All passwords will be readable in the backup file. Do you want to continue?',
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
UPGRADE_IMPORTANT_MESSAGES: 'Upgrade Important Messages',
UPGRADE_IMPORTANT_MESSAGES_1: 'You are upgrading from a major version. Please be aware that this may cause issues with your configuration.',
UPGRADE_IMPORTANT_MESSAGES_2: 'You are upgrading from a minor version. Please be aware that this may cause issues with your configuration.',
ERROR: 'Unexpected Error, please try again', ERROR: 'Unexpected Error, please try again',
TIME_SET: 'Time set', TIME_SET: 'Time set',
MANAGE_USERS: 'Manage Users', MANAGE_USERS: 'Manage Users',

View File

@@ -188,8 +188,12 @@ const fr: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations', DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs', DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système', DOWNLOAD_SYSTEM_BACKUP: 'Sauvegarde système',
WARNING_SYSTEM_BACKUP: 'Cela créera une sauvegarde de vos paramètres et configurations. Tous les mots de passe seront lisibles dans le fichier de sauvegarde. Voulez-vous continuer ?',
UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)', UPLOAD_TEXT: 'Télécharger un nouveau fichier firmware (.bin) ou une sauvegarde (.json)',
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
UPGRADE_IMPORTANT_MESSAGES: 'Messages importants pour la mise à jour',
UPGRADE_IMPORTANT_MESSAGES_1: 'Vous mettez à jour à partir d\'une version majeure. Veuillez prendre en compte que cela peut causer des problèmes avec votre configuration.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Vous mettez à jour à partir d\'une version mineure. Veuillez prendre en compte que cela peut causer des problèmes avec votre configuration.',
ERROR: 'Erreur inattendue, veuillez réessayer', ERROR: 'Erreur inattendue, veuillez réessayer',
TIME_SET: 'Time set', TIME_SET: 'Time set',
MANAGE_USERS: 'Gérer les utilisateurs', MANAGE_USERS: 'Gérer les utilisateurs',

View File

@@ -188,8 +188,12 @@ const it: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings', DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori', DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema', DOWNLOAD_SYSTEM_BACKUP: 'Backup sistema',
WARNING_SYSTEM_BACKUP: 'Questo creerà un backup delle tue configurazioni e impostazioni. Tutte le password saranno leggibili nel file di backup. Vuoi continuare?',
UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)', UPLOAD_TEXT: 'Upload a new firmware file (.bin) or a backup file (.json)',
UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here', UPLOAD_DROP_TEXT: 'Drop a firmware .bin file or click here',
UPGRADE_IMPORTANT_MESSAGES: 'Messaggi importanti per l\'aggiornamento',
UPGRADE_IMPORTANT_MESSAGES_1: 'Stai aggiornando da una versione principale. Si prega di essere consapevoli che questo può causare problemi con la tua configurazione.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Stai aggiornando da una versione secondaria. Si prega di essere consapevoli che questo può causare problemi con la tua configurazione.',
ERROR: 'Errore Inaspettato, prego tenta ancora', ERROR: 'Errore Inaspettato, prego tenta ancora',
TIME_SET: 'Imposta Ora', TIME_SET: 'Imposta Ora',
MANAGE_USERS: 'Gestione Utenti', MANAGE_USERS: 'Gestione Utenti',

View File

@@ -188,8 +188,12 @@ const nl: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen', DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden', DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup', DOWNLOAD_SYSTEM_BACKUP: 'Systeem Backup',
WARNING_SYSTEM_BACKUP: 'Dit zal een backup maken van uw configuratie en instellingen. Alle wachtwoorden zullen leesbaar zijn in het backup bestand. Weet je zeker dat je wilt doorgaan?',
UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)', UPLOAD_TEXT: 'Upload een nieuw firmwarebestand (.bin) of een back-upbestand (.json)',
UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier', UPLOAD_DROP_TEXT: 'Sleep en firmware .bin bestand hierheen of klik hier',
UPGRADE_IMPORTANT_MESSAGES: 'Belangrijke berichten voor upgrade',
UPGRADE_IMPORTANT_MESSAGES_1: 'U updatet van een grote versie. Wees bewust dat dit problemen met uw configuratie kan veroorzaken.',
UPGRADE_IMPORTANT_MESSAGES_2: 'U updatet van een kleine versie. Wees bewust dat dit problemen met uw configuratie kan veroorzaken.',
ERROR: 'Onverwachte fout, probeer opnieuw', ERROR: 'Onverwachte fout, probeer opnieuw',
TIME_SET: 'Tijd ingesteld', TIME_SET: 'Tijd ingesteld',
MANAGE_USERS: 'Gebruikersbeheer', MANAGE_USERS: 'Gebruikersbeheer',

View File

@@ -188,8 +188,12 @@ const no: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger', DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier', DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi', DOWNLOAD_SYSTEM_BACKUP: 'System Sikkerhetskopi',
WARNING_SYSTEM_BACKUP: 'Dette vil lage en sikkerhetskopi av dine konfigurasjon og innstillinger. Alle passord vil være lesbare i sikkerhetskopien. Er du sikker på at du vil fortsette?',
UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)', UPLOAD_TEXT: 'Last opp en ny firmware fil (.bin) eller en sikkerhetskopi fil (.json)',
UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her', UPLOAD_DROP_TEXT: 'Dropp en firmware fil (.bin) eller klikk her',
UPGRADE_IMPORTANT_MESSAGES: 'Viktige meldinger for oppgradering',
UPGRADE_IMPORTANT_MESSAGES_1: 'Du oppgraderer fra en stor versjon. Vær oppmerksom på at dette kan føre til problemer med din konfigurasjon.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Du oppgraderer fra en liten versjon. Vær oppmerksom på at dette kan føre til problemer med din konfigurasjon.',
ERROR: 'Ukjent feil, prøv igjen', ERROR: 'Ukjent feil, prøv igjen',
TIME_SET: 'Still in tid', TIME_SET: 'Still in tid',
MANAGE_USERS: 'Administrer Brukere', MANAGE_USERS: 'Administrer Brukere',

View File

@@ -188,8 +188,12 @@ const pl: BaseTranslation = {
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji', DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości', DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu', DOWNLOAD_SYSTEM_BACKUP: 'Kopia zapasowa systemu',
WARNING_SYSTEM_BACKUP: 'To utworzy kopię swoich ustawień i konfiguracji. Wszystkie hasła będą widoczne w kopii zapasowej. Czy na pewno chcesz kontynuować?',
UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)', UPLOAD_TEXT: 'Wgraj nowy plik firmware (.bin) lub kopię ustawień (.json)',
UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj', UPLOAD_DROP_TEXT: 'Upuść plik firmware .bin lub kliknij tutaj',
UPGRADE_IMPORTANT_MESSAGES: 'Ważne informacje dotyczące aktualizacji',
UPGRADE_IMPORTANT_MESSAGES_1: 'Aktualizujesz się z głównej wersji. Proszę mieć świadomość, że to może spowodować problemy z konfiguracją.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujesz się z drugorzędnej wersji. Proszę mieć świadomość, że to może spowodować problemy z konfiguracją.',
ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!', ERROR: 'Nieoczekiwany błąd, spróbuj ponownie!',
TIME_SET: 'Zegar został ustawiony.', TIME_SET: 'Zegar został ustawiony.',
MANAGE_USERS: 'Zarządzanie użytkownikami', MANAGE_USERS: 'Zarządzanie użytkownikami',

View File

@@ -188,8 +188,12 @@ const sk: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení', DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty', DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha', DOWNLOAD_SYSTEM_BACKUP: 'Systémová záloha',
WARNING_SYSTEM_BACKUP: 'Toto vytvorí zálohu vašich nastavení a konfigurácií. Všetky hesla budú v zálohovom súbore čitateľné. Naozaj chcete pokračovať?',
UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)', UPLOAD_TEXT: 'Nahrajte nový súbor firmvéru (.bin) alebo súbor zálohy (.json)',
UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem', UPLOAD_DROP_TEXT: 'Presuňte súbor .bin firmvéru alebo kliknite sem',
UPGRADE_IMPORTANT_MESSAGES: 'Dôležité informácie pre aktualizáciu',
UPGRADE_IMPORTANT_MESSAGES_1: 'Aktualizujete sa z hlavného verzie. Prosím, buďte si vedomí, že to môže spôsobiť problémy s vašou konfiguráciou.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Aktualizujete sa z menšieho verzie. Prosím, buďte si vedomí, že to môže spôsobiť problémy s vašou konfiguráciou.',
ERROR: 'Neočakávaná chyba, prosím skúste to znova', ERROR: 'Neočakávaná chyba, prosím skúste to znova',
TIME_SET: 'Nastavený čas', TIME_SET: 'Nastavený čas',
MANAGE_USERS: 'Správa používateľov', MANAGE_USERS: 'Správa používateľov',

View File

@@ -188,8 +188,12 @@ const sv: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar', DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden', DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia', DOWNLOAD_SYSTEM_BACKUP: 'System säkerhetskopia',
WARNING_SYSTEM_BACKUP: 'Detta kommer att skapa en säkerhetskopia av dina inställningar och konfiguration. Alla lösenord kommer att vara läsbara i säkerhetskopian. Är du säker på att du vill fortsätta?',
UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)', UPLOAD_TEXT: 'Ladda upp en ny firmwarefil (.bin) eller en säkerhetskopiafil (.json)',
UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här', UPLOAD_DROP_TEXT: 'Droppa en firmware .bin fil eller klicka här',
UPGRADE_IMPORTANT_MESSAGES: 'Viktiga meddelanden för uppgradering',
UPGRADE_IMPORTANT_MESSAGES_1: 'Du uppgraderar från en major version. Vänligen var medveten om att detta kan orsaka problem med din konfiguration.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Du uppgraderar från en minor version. Vänligen var medveten om att detta kan orsaka problem med din konfiguration.',
ERROR: 'Okänt fel, var god försök igen', ERROR: 'Okänt fel, var god försök igen',
TIME_SET: 'Ställ in tid', TIME_SET: 'Ställ in tid',
MANAGE_USERS: 'Användare', MANAGE_USERS: 'Användare',

View File

@@ -188,8 +188,12 @@ const tr: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın', DOWNLOAD_SETTINGS_TEXT: 'Yapılandırma ve ayarlarınızın yedekleme yapın',
DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al', DOWNLOAD_SETTINGS_TEXT2: 'Tüm değerleri dışarı al',
DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme', DOWNLOAD_SYSTEM_BACKUP: 'Sistem yedekleme',
WARNING_SYSTEM_BACKUP: 'Bu, yapılandırma ve ayarlarınızın bir yedeklemesi oluşturacaktır. Tüm şifreler yedekleme dosyasında okunabilir olacaktır. Devam etmek istediğinize emin misiniz?',
UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle', UPLOAD_TEXT: 'Yeni bir firmware dosyası (.bin) veya yedek dosyası (.json) yükle',
UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın', UPLOAD_DROP_TEXT: 'Bir firmware .bin dosyası veya buraya tıklayın',
UPGRADE_IMPORTANT_MESSAGES: 'Yükseltme Önemli Mesajları',
UPGRADE_IMPORTANT_MESSAGES_1: 'Bir ana sürümden yükselteysiniz. Lütfen bu, yapılandırmanızın sorunlarına neden olabileceğini unutmayın.',
UPGRADE_IMPORTANT_MESSAGES_2: 'Bir küçük sürümden yükselteysiniz. Lütfen bu, yapılandırmanızın sorunlarına neden olabileceğini unutmayın.',
ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.', ERROR: 'Beklenemedik hata, lütfen tekrar deneyin.',
TIME_SET: 'Zaman ayarı', TIME_SET: 'Zaman ayarı',
MANAGE_USERS: 'Kullanıcıları yönet', MANAGE_USERS: 'Kullanıcıları yönet',

View File

@@ -13,7 +13,7 @@
"@trivago/prettier-plugin-sort-imports": "^6.0.2", "@trivago/prettier-plugin-sort-imports": "^6.0.2",
"formidable": "^3.5.4", "formidable": "^3.5.4",
"itty-router": "^5.0.23", "itty-router": "^5.0.23",
"prettier": "^3.8.1" "prettier": "^3.8.2"
}, },
"packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319" "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319"
} }

View File

@@ -13,7 +13,7 @@ importers:
version: 3.1.3 version: 3.1.3
'@trivago/prettier-plugin-sort-imports': '@trivago/prettier-plugin-sort-imports':
specifier: ^6.0.2 specifier: ^6.0.2
version: 6.0.2(prettier@3.8.1) version: 6.0.2(prettier@3.8.2)
formidable: formidable:
specifier: ^3.5.4 specifier: ^3.5.4
version: 3.5.4 version: 3.5.4
@@ -21,8 +21,8 @@ importers:
specifier: ^5.0.23 specifier: ^5.0.23
version: 5.0.23 version: 5.0.23
prettier: prettier:
specifier: ^3.8.1 specifier: ^3.8.2
version: 3.8.1 version: 3.8.2
packages: packages:
@@ -112,8 +112,8 @@ packages:
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.0.3: brace-expansion@2.1.0:
resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
@@ -145,8 +145,8 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
hasBin: true hasBin: true
lodash-es@4.17.23: lodash-es@4.18.1:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==}
minimatch@9.0.9: minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
@@ -167,8 +167,8 @@ packages:
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
prettier@3.8.1: prettier@3.8.2:
resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} resolution: {integrity: sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -246,17 +246,17 @@ snapshots:
dependencies: dependencies:
'@noble/hashes': 1.8.0 '@noble/hashes': 1.8.0
'@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.1)': '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.8.2)':
dependencies: dependencies:
'@babel/generator': 7.29.1 '@babel/generator': 7.29.1
'@babel/parser': 7.29.2 '@babel/parser': 7.29.2
'@babel/traverse': 7.29.0 '@babel/traverse': 7.29.0
'@babel/types': 7.29.0 '@babel/types': 7.29.0
javascript-natural-sort: 0.7.1 javascript-natural-sort: 0.7.1
lodash-es: 4.17.23 lodash-es: 4.18.1
minimatch: 9.0.9 minimatch: 9.0.9
parse-imports-exports: 0.2.4 parse-imports-exports: 0.2.4
prettier: 3.8.1 prettier: 3.8.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -264,7 +264,7 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
brace-expansion@2.0.3: brace-expansion@2.1.0:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -291,11 +291,11 @@ snapshots:
jsesc@3.1.0: {} jsesc@3.1.0: {}
lodash-es@4.17.23: {} lodash-es@4.18.1: {}
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.0.3 brace-expansion: 2.1.0
ms@2.1.3: {} ms@2.1.3: {}
@@ -311,6 +311,6 @@ snapshots:
picocolors@1.1.1: {} picocolors@1.1.1: {}
prettier@3.8.1: {} prettier@3.8.2: {}
wrappy@1.0.2: {} wrappy@1.0.2: {}

View File

@@ -5170,6 +5170,32 @@ router
// set partition // set partition
console.log('setting partition to', content.param); console.log('setting partition to', content.param);
return status(200); return status(200);
} else if (action === 'upgradeImportantMessages') {
// check upgrade important messages
console.log(
'checking upgrade important messages for version ',
content.param
);
// determine message based on if we're upgrading a minor or major version
// TODO finish this
let message = 0;
const version = content.param;
const majorVersion = version.split('.')[0];
const minorVersion = version.split('.')[1];
const currentMajorVersion = THIS_VERSION.split('.')[0];
const currentMinorVersion = THIS_VERSION.split('.')[1];
if (majorVersion > currentMajorVersion) {
message = 1;
} else if (majorVersion < currentMajorVersion) {
message = 2;
} else if (minorVersion > currentMinorVersion) {
message = 1;
} else if (minorVersion < currentMinorVersion) {
message = 2;
} else {
message = 0;
}
return message;
} }
} }
return status(404); // cmd not found return status(404); // cmd not found

View File

@@ -216,6 +216,8 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
} else if (action == "resetMQTT" && is_admin) { } else if (action == "resetMQTT" && is_admin) {
EMSESP::mqtt_.reset_mqtt(); EMSESP::mqtt_.reset_mqtt();
ok = true; ok = true;
} else if (action == "upgradeImportantMessages") {
ok = upgradeImportantMessages(param.c_str());
} }
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY) #if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)
@@ -237,6 +239,20 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json)
request->send(response); request->send(response);
} }
// action = upgradeImportantMessages
// returns the type of upgrade important message to show\
// 0 = no message
// 1 = major version upgrade
// 2 = minor version upgrade
// TODO finish this
bool WebStatusService::upgradeImportantMessages(const char * version) {
version::Semver200_version current_version(current_version_s);
version::Semver200_version latest_version(version);
if (latest_version > current_version) {
return 1;
}
}
// action = checkUpgrade // action = checkUpgrade
// versions holds the latest development version and stable version in one string, comma separated // versions holds the latest development version and stable version in one string, comma separated
bool WebStatusService::checkUpgrade(JsonObject root, std::string & versions) { bool WebStatusService::checkUpgrade(JsonObject root, std::string & versions) {

View File

@@ -36,6 +36,7 @@ class WebStatusService {
bool uploadURL(const char * url); bool uploadURL(const char * url);
bool setSystemStatus(const char * status); bool setSystemStatus(const char * status);
void allvalues(JsonObject output); void allvalues(JsonObject output);
bool upgradeImportantMessages(const char * version);
std::string current_version_s = EMSESP_APP_VERSION; std::string current_version_s = EMSESP_APP_VERSION;
}; };