This commit is contained in:
MichaelDvP
2026-04-14 08:09:50 +02:00
55 changed files with 577 additions and 393 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
import { memo } from 'react';
import type { IconType } from 'react-icons';
import { AiOutlineAlert, AiOutlineControl, AiOutlineGateway } from 'react-icons/ai';
import { CgSmartHomeBoiler } from 'react-icons/cg';
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 { VscVmConnect } from 'react-icons/vsc';
import type { SvgIconProps } from '@mui/material';
import { DeviceType } from './types';
const deviceIconLookup: Record<
DeviceType,
React.ComponentType<SvgIconProps> | null
> = {
const deviceIconLookup: Record<DeviceType, IconType | null> = {
[DeviceType.TEMPERATURESENSOR]: TiThermometer,
[DeviceType.ANALOGSENSOR]: PiGauge,
[DeviceType.BOILER]: CgSmartHomeBoiler,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -591,7 +591,14 @@ const Sensors = () => {
/>
)}
{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
variant="outlined"
color="primary"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -240,12 +240,16 @@ const ManageUsers = () => {
</Table>
{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 && (
<Box flexGrow={1} sx={{ '& button': { mt: 2 } }}>
<Box sx={{ flexGrow: 1, '& button': { mt: 2 } }}>
<ButtonRow>
<Button
startIcon={<CancelIcon />}
@@ -270,7 +274,7 @@ const ManageUsers = () => {
</ButtonRow>
</Box>
)}
<Box flexWrap="nowrap" whiteSpace="nowrap">
<Box sx={{ flexWrap: 'nowrap', whiteSpace: 'nowrap' }}>
<ButtonRow>
<Button
startIcon={<PersonAddIcon />}

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ const LayoutDrawerComponent = ({ mobileOpen, onClose }: LayoutDrawerProps) => {
() => (
<>
<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} />
<Typography variant="h6">{PROJECT_NAME}</Typography>
</Box>

View File

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

View File

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

View File

@@ -15,12 +15,14 @@ const circularProgressStyles: SxProps<Theme> = (theme: Theme) => ({
const LoadingSpinner = ({ height = '100%' }: LoadingSpinnerProps) => {
return (
<Box
display="flex"
alignItems="center"
justifyContent="center"
flexDirection="column"
padding={2}
height={height}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
padding: 2,
height
}}
>
<CircularProgress sx={circularProgressStyles} size={100} />
</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 {
type ChangeEvent,
type DragEvent,
@@ -6,12 +6,27 @@ import {
useRef,
useState
} from 'react';
import { toast } from 'react-toastify';
import CancelIcon from '@mui/icons-material/Cancel';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
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';
const DocumentUploader = styled(Box)<{ active?: boolean }>(({ theme, active }) => ({
@@ -58,6 +73,23 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const [dragged, setDragged] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
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 validExtensions = ['.json', '.bin', '.md5'];
@@ -97,9 +129,8 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
const handleUploadClick = (event: MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (file) {
onFileSelected(file);
}
void checkUpgradeImportantMessages(file?.name || '');
setShowUpgradeDialog(true);
};
const handleBrowseClick = () => {
@@ -158,6 +189,46 @@ const DragNdrop = ({ text, onFileSelected }: DragNdropProps) => {
{LL.UPLOAD()}
</Button>
</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>

View File

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

View File

@@ -188,8 +188,12 @@ const cz: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Vytvořte zálohu svého nastavení a konfigurace',
DOWNLOAD_SETTINGS_TEXT2: 'Exportovat všechny hodnoty',
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_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',
TIME_SET: 'Čas nastaven',
MANAGE_USERS: 'Spravovat uživatele',
@@ -347,18 +351,17 @@ const cz: Translation = {
BITMASK: 'Bit Mask',
DUPLICATE: 'Duplikát',
DASHBOARD_1: 'Všechny aktivní entity EMS jsou označené jako oblíbené. Všechny vlastní entity, harmonogramy a externí sensory jsou zobrazeny níže.',
NO_DATA: 'No data',
NO_DATA: 'žádné údaje',
NO_DATA_1: 'Nebyly nalezeny žádné oblíbené entity. Použijte modul',
NO_DATA_2: 'pro jejich výběr.',
NO_DATA_3: 'Pro zobrazení všech dostupných entit navštivte stránku',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Nebylo nalezeno žádné volné GPIO',
THIS_VERSION: 'Tato verze',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ sestavení',
INTERNET_CONNECTION_REQUIRED: 'Pro automatickou kontrolu a instalaci aktualizací je třeba internetové připojení',
SWITCH_RELEASE_TYPE: 'Přepnout na {0} verzi',
FIRMWARE_VERSION_INFO: 'Informace o verzi firmwaru',
NO_DATA: 'Žádná data',
USER_PROFILE: 'Uživatelský profil',
STORED_VERSIONS: 'Uložené verze',
ONLINE_HELP: 'online nápověda'

View File

@@ -188,8 +188,12 @@ const de: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Erstellen Sie eine Sicherung Ihrer Konfigurationen und Einstellungen',
DOWNLOAD_SETTINGS_TEXT2: 'Exportiere alle Werte',
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_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.',
TIME_SET: 'Zeit gesetzt',
MANAGE_USERS: 'Nutzerverwaltung',
@@ -347,7 +351,6 @@ const de: Translation = {
BITMASK: 'Bit Maske',
DUPLICATE: 'Kopieren',
DASHBOARD_1: 'Alle EMS-Entitäten, die aktiv und als Favorit markiert sind, sowie alle benutzerdefinierten Entitäten, Zeitpläne und externen Sensordaten werden unten angezeigt.',
NO_DATA: 'Keine Daten',
NO_DATA_1: 'Keine favorisierten EMS-Entitäten gefunden! Verwenden Sie das Modul',
NO_DATA_2: ', um sie zu markieren.',
NO_DATA_3: 'Um alle verfügbaren Entitäten anzuzeigen, gehen Sie zu',

View File

@@ -188,8 +188,12 @@ const en: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
DOWNLOAD_SETTINGS_TEXT2: 'Export all values',
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_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',
TIME_SET: 'Time set',
MANAGE_USERS: 'Manage Users',
@@ -347,7 +351,6 @@ const en: Translation = {
BYTES: 'Bytes',
BITMASK: 'Bit Mask',
DUPLICATE: 'Duplicate',
NO_DATA: 'No data',
NO_DATA_1: 'No favorite EMS entities found yet. Use the',
NO_DATA_2: 'module to mark them.',
NO_DATA_3: 'To see all available entities go to',

View File

@@ -188,8 +188,12 @@ const fr: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Créer une sauvegarde de vos paramètres et configurations',
DOWNLOAD_SETTINGS_TEXT2: 'Exporter toutes les valeurs',
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_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',
TIME_SET: 'Time set',
MANAGE_USERS: 'Gérer les utilisateurs',
@@ -347,11 +351,10 @@ const fr: Translation = {
BITMASK: 'Masque de bits',
DUPLICATE: 'Dupliquer',
DASHBOARD_1: 'Toutes les entités EMS actives et marquées comme favoris, plus toutes les entités personnalisées, les programmes et les données des capteurs externes sont affichées ci-dessous.',
NO_DATA: 'No data',
NO_DATA_1: 'Aucune entité EMS favorite trouvée. Utilisez le',
NO_DATA_2: 'module pour les marquer.',
NO_DATA_3: 'Pour voir toutes les entités disponibles, aller à',
NO_GPIO:'No available GPIO found',
NO_GPIO:"Aucun GPIO disponible n'a été détecté",
THIS_VERSION: 'Cette version',
PLATFORM: 'Plateforme',
RELEASE_TYPE: 'Type de version',

View File

@@ -188,8 +188,12 @@ const it: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Create a backup of your configuration and settings',
DOWNLOAD_SETTINGS_TEXT2: 'Esporta tutti i valori',
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_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',
TIME_SET: 'Imposta Ora',
MANAGE_USERS: 'Gestione Utenti',
@@ -347,11 +351,10 @@ const it: Translation = {
BITMASK: 'Bitmask',
DUPLICATE: 'Duplicato',
DASHBOARD_1: 'Tutte le entità EMS che sono attive e marcate come preferite, più tutte le entità personalizzate, piani di programmazione e dati dei sensori esterni sono visualizzati di seguito.',
NO_DATA: 'No data',
NO_DATA_1: 'Nessuna entità EMS preferita trovata. Usa il',
NO_DATA_2: 'modulo per marcarle.',
NO_DATA_3: 'Per vedere tutte le entità disponibili vai a',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Non è stato trovato alcun GPIO disponibile',
THIS_VERSION: 'Questa versione',
PLATFORM: 'Piattaforma',
RELEASE_TYPE: 'Tipo di rilascio',

View File

@@ -188,8 +188,12 @@ const nl: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Maak een back-up van uw configuratie en instellingen',
DOWNLOAD_SETTINGS_TEXT2: 'Exporteer alle waarden',
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_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',
TIME_SET: 'Tijd ingesteld',
MANAGE_USERS: 'Gebruikersbeheer',
@@ -347,11 +351,10 @@ const nl: Translation = {
BITMASK: 'Bit Mask',
DUPLICATE: 'Duplicaat',
DASHBOARD_1: 'Alle EMS-entiteiten die actief zijn en als favoriet zijn gemarkeerd, plus alle aangepaste entiteiten en externe sensorgegevens worden hieronder weergegeven.',
NO_DATA: 'No data',
NO_DATA_1: 'Er zijn nog geen favoriete EMS-entiteiten gevonden. Gebruik de',
NO_DATA_2: 'module om ze te markeren.',
NO_DATA_3: 'Om alle beschikbare entiteiten te zien, ga naar',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Er is geen beschikbare GPIO gevonden',
THIS_VERSION: 'Deze Versie',
PLATFORM: 'Platform',
RELEASE_TYPE: 'Release Typ',

View File

@@ -188,8 +188,12 @@ const no: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Lag en sikkerhetskopi av dine konfigurasjon og innstillinger',
DOWNLOAD_SETTINGS_TEXT2: 'Eksporter alle verdier',
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_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',
TIME_SET: 'Still in tid',
MANAGE_USERS: 'Administrer Brukere',
@@ -347,11 +351,10 @@ const no: Translation = {
BITMASK: 'Bitmask',
DUPLICATE: 'Duplikat',
DASHBOARD_1: 'Alle EMS enheter som er aktive og merket som favoritt, pluss alle personlige enheter, planlegg og eksterne sensor data er vist nedenfor.',
NO_DATA: 'No data',
NO_DATA_1: 'Ingen favoritte EMS enheter funnet enda. Bruk',
NO_DATA_2: 'modul for å markere dem.',
NO_DATA_3: 'For å se alle tilgjengelige enheter, gå til',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Det ble ikke funnet noen tilgjengelige GPIO-porter',
THIS_VERSION: 'Denne versjonen',
PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelses type',

View File

@@ -188,8 +188,12 @@ const pl: BaseTranslation = {
DOWNLOAD_SETTINGS_TEXT: 'Utwórz kopię swoich ustawień i konfiguracji',
DOWNLOAD_SETTINGS_TEXT2: 'Eksportuj wszystkie wartości',
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_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!',
TIME_SET: 'Zegar został ustawiony.',
MANAGE_USERS: 'Zarządzanie użytkownikami',
@@ -347,11 +351,10 @@ const pl: BaseTranslation = {
BITMASK: 'Bit Mask',
DUPLICATE: 'Duplicate',
DASHBOARD_1: 'All EMS entities that are active and marked as Favorite, plus all Custom Entities, Schedules and external Sensors data are displayed below.',
NO_DATA: 'No data',
NO_DATA_1: 'Brak ulubionych encji EMS. Użyj',
NO_DATA_2: 'moduł do ich oznaczenia.',
NO_DATA_3: 'Aby zobaczyć wszystkie dostępne encje przejdź do',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Nie znaleziono dostępnych pinów GPIO',
THIS_VERSION: 'Ta wersja',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ wydania',

View File

@@ -188,8 +188,12 @@ const sk: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Vytvorte zálohu svojej konfigurácie a nastavení',
DOWNLOAD_SETTINGS_TEXT2: 'Exportovať všetky hodnoty',
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_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',
TIME_SET: 'Nastavený čas',
MANAGE_USERS: 'Správa používateľov',
@@ -347,11 +351,10 @@ const sk: Translation = {
BITMASK: 'Bitová maska',
DUPLICATE: 'Duplicitné',
DASHBOARD_1: 'Všetky entity EMS, ktoré sú aktívne a označené ako obľúbené, plus všetky vlastné entity, plány a údaje externých senzorov sú zobrazené nižšie.',
NO_DATA: 'No data',
NO_DATA_1: 'Nenašli sa žiadne obľúbené entity EMS. Použite',
NO_DATA_2: 'modul na ich označenie.',
NO_DATA_3: 'Ak chcete zobraziť všetky dostupné entity, prejdite na',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Nebol nájdený žiadny dostupný GPIO',
THIS_VERSION: 'Táto verzia',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Typ vydania',

View File

@@ -188,8 +188,12 @@ const sv: Translation = {
DOWNLOAD_SETTINGS_TEXT: 'Skapa en säkerhetskopia av din konfiguration och inställningar',
DOWNLOAD_SETTINGS_TEXT2: 'Exportera alla värden',
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_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',
TIME_SET: 'Ställ in tid',
MANAGE_USERS: 'Användare',
@@ -347,11 +351,10 @@ const sv: Translation = {
BITMASK: 'Bitmask',
DUPLICATE: 'Dublett',
DASHBOARD_1: 'Alla EMS-enheter som är aktiva och markerade som favorit, plus alla anpassade entiteter, scheman och externa sensor-data visas nedan.',
NO_DATA: 'No data',
NO_DATA_1: 'Inga favorit EMS enheter hittade än. Använd',
NO_DATA_2: 'modul för att markera dem.',
NO_DATA_3: 'För att se alla tillgängliga enheter, gå till',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Inga tillgängliga GPIO-portar hittades',
THIS_VERSION: 'Denna version',
PLATFORM: 'Plattform',
RELEASE_TYPE: 'Utgivelsestyp',

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_TEXT2: 'Tüm değerleri dışarı al',
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_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.',
TIME_SET: 'Zaman ayarı',
MANAGE_USERS: 'Kullanıcıları yönet',
@@ -347,11 +351,10 @@ const tr: Translation = {
BITMASK: 'Bit Maskesi',
DUPLICATE: 'Çift',
DASHBOARD_1: 'Tüm aktif ve Favori olarak işaretlenmiş EMS varlıkları, artı tüm özel varlıklar, zamanlayıcılar ve harici sensör verileri aşağıda görüntüleniyor.',
NO_DATA: 'No data',
NO_DATA_1: 'Henüz bir favori EMS varlığı bulunamadı. Kullanın',
NO_DATA_2: 'modülünü kullanın.',
NO_DATA_3: 'Tüm kullanılabilir varlıkları görmek için git',
NO_GPIO:'No available GPIO found',
NO_GPIO:'Kullanılabilir GPIO bulunamadı',
THIS_VERSION: 'Bu Sürüm',
PLATFORM: 'Platforma',
RELEASE_TYPE: 'Sürüm Tipi',