This commit is contained in:
proddy
2024-10-10 21:24:32 +01:00
parent ca8ed2d1a5
commit b7e6552557

View File

@@ -3,6 +3,7 @@ import { IconContext } from 'react-icons/lib';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import CircleIcon from '@mui/icons-material/Circle';
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 UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
@@ -12,8 +13,10 @@ import {
IconButton, IconButton,
ToggleButton, ToggleButton,
ToggleButtonGroup, ToggleButtonGroup,
Tooltip,
Typography Typography
} from '@mui/material'; } from '@mui/material';
import Grid from '@mui/material/Grid2';
import { Body, Cell, Row, Table } from '@table-library/react-table-library/table'; import { Body, Cell, Row, Table } from '@table-library/react-table-library/table';
import { useTheme } from '@table-library/react-table-library/theme'; import { useTheme } from '@table-library/react-table-library/theme';
@@ -24,11 +27,17 @@ import { AuthenticatedContext } from 'contexts/authentication';
import { useI18nContext } from 'i18n/i18n-react'; import { useI18nContext } from 'i18n/i18n-react';
import { useInterval } from 'utils'; import { useInterval } from 'utils';
import { readDashboard, writeDeviceValue } from '../../api/app'; import { readDashboard, writeDeviceValue, writeSchedule } from '../../api/app';
import DeviceIcon from './DeviceIcon'; import DeviceIcon from './DeviceIcon';
import DashboardDevicesDialog from './DevicesDialog'; import DevicesDialog from './DevicesDialog';
import { formatValue } from './deviceValue'; import { formatValue } from './deviceValue';
import { type DashboardItem, DeviceEntityMask, type DeviceValue } from './types'; import {
type DashboardItem,
DeviceEntityMask,
DeviceType,
type DeviceValue,
type Schedule
} from './types';
import { deviceValueItemValidation } from './validators'; import { deviceValueItemValidation } from './validators';
const Dashboard = () => { const Dashboard = () => {
@@ -54,6 +63,13 @@ const Dashboard = () => {
initialData: [] initialData: []
}); });
const { send: updateSchedule } = useRequest(
(data: Schedule) => writeSchedule(data),
{
immediate: false
}
);
const { loading: submitting, send: sendDeviceValue } = useRequest( const { loading: submitting, send: sendDeviceValue } = useRequest(
(data: { id: number; c: string; v: unknown }) => writeDeviceValue(data), (data: { id: number; c: string; v: unknown }) => writeDeviceValue(data),
{ {
@@ -104,14 +120,24 @@ const Dashboard = () => {
const tree = useTree( const tree = useTree(
{ nodes: data }, { nodes: data },
{ {
onChange: null // not used but needed onChange: undefined // not used but needed
}, },
{ {
treeIcon: { treeIcon: {
margin: '4px', margin: '4px',
iconDefault: null, iconDefault: null,
iconRight: <ChevronRightIcon color="primary" />, iconRight: (
iconDown: <ExpandMoreIcon color="primary" /> <ChevronRightIcon
sx={{ fontSize: 16, verticalAlign: 'middle' }}
color="info"
/>
),
iconDown: (
<ExpandMoreIcon
sx={{ fontSize: 16, verticalAlign: 'middle' }}
color="info"
/>
)
}, },
indentation: 50 indentation: 50
} }
@@ -131,27 +157,78 @@ const Dashboard = () => {
} }
}, [loading]); }, [loading]);
const showType = (n?: string, t?: number) => {
// if we have a name show it
if (n) {
return n;
}
if (t) {
// otherwise pick translation based on type
switch (t) {
case DeviceType.CUSTOM:
return LL.CUSTOM_ENTITIES(0);
case DeviceType.ANALOGSENSOR:
return LL.ANALOG_SENSOR(0);
case DeviceType.TEMPERATURESENSOR:
return LL.TEMP_SENSOR();
case DeviceType.SCHEDULER:
return LL.SCHEDULER();
default:
break;
}
}
return '';
};
const showName = (di: DashboardItem) => { const showName = (di: DashboardItem) => {
if (di.id < 100) { if (di.id < 100) {
// if its a device and has entities // if its a device (parent node) and has entities
if (di.nodes?.length) { if (di.nodes?.length) {
return ( return (
<> <>
<span style="font-size: 14px"> <span style="font-size: 14px">
<DeviceIcon type_id={di.t ?? 0} /> <DeviceIcon type_id={di.t ?? 0} />
&nbsp;&nbsp;{di.n} &nbsp;&nbsp;{showType(di.n, di.t)}
</span> </span>
<span style={{ color: 'lightblue' }}>&nbsp;({di.nodes?.length})</span> <span style={{ color: 'lightblue' }}>&nbsp;({di.nodes?.length})</span>
</> </>
); );
} }
} }
return <span style="color:lightgrey">{di.dv ? di.dv.id.slice(2) : ''}</span>; if (di.dv) {
return (
// ids for scheduler, and sensors are between 9600 and 9900
<span style="color:lightgrey">
{di.id >= 9600 && di.id < 9900 ? di.dv.id : di.dv.id.slice(2)}
</span>
);
}
}; };
const hasMask = (id: string, mask: number) => const hasMask = (id: string, mask: number) =>
(parseInt(id.slice(0, 2), 16) & mask) === mask; (parseInt(id.slice(0, 2), 16) & mask) === mask;
const toggleSchedule = async (di: DashboardItem) => {
// create a dummy record, the id=0 picks it up
await updateSchedule({
schedule: {
id: 0, // special number for only changing the active flag
active: !di.dv?.v,
flags: 0, // unused
time: '', // unused
cmd: '', // unused
value: '', // unused
name: di.dv?.id ?? ''
}
})
.catch((error: Error) => {
toast.error(error.message);
})
.finally(async () => {
await fetchDashboard();
});
};
const editDashboardValue = (di: DashboardItem) => { const editDashboardValue = (di: DashboardItem) => {
setSelectedDashboardItem(di); setSelectedDashboardItem(di);
setDeviceValueDialogOpen(true); setDeviceValueDialogOpen(true);
@@ -174,32 +251,37 @@ const Dashboard = () => {
return ( return (
<> <>
<Typography mb={2} variant="body1" color="warning"> <Grid container spacing={0} justifyContent="flex-start">
{LL.DASHBOARD_1()} <Grid size={11}>
</Typography> <Typography mb={2} variant="body1" color="warning">
{LL.DASHBOARD_1()}
</Typography>
</Grid>
<ToggleButtonGroup <Grid size={1} alignItems="end">
color="primary" <ToggleButtonGroup
size="small" color="primary"
value={showAll} size="small"
exclusive value={showAll}
onChange={handleShowAll} exclusive
> onChange={handleShowAll}
<ToggleButton value={true}> >
<UnfoldMoreIcon fontSize="small" /> <ToggleButton value={true}>
</ToggleButton> <UnfoldMoreIcon sx={{ fontSize: 14 }} />
<ToggleButton value={false}> </ToggleButton>
<UnfoldLessIcon fontSize="small" /> <ToggleButton value={false}>
</ToggleButton> <UnfoldLessIcon sx={{ fontSize: 14 }} />
</ToggleButtonGroup> </ToggleButton>
</ToggleButtonGroup>
</Grid>
</Grid>
<Box <Box
mt={2}
padding={1} padding={1}
justifyContent="center" justifyContent="center"
flexDirection="column" flexDirection="column"
sx={{ sx={{
borderRadius: 2, borderRadius: 1,
border: '1px solid grey' border: '1px solid grey'
}} }}
> >
@@ -225,30 +307,65 @@ const Dashboard = () => {
<Body> <Body>
{tableList.map((di: DashboardItem) => ( {tableList.map((di: DashboardItem) => (
<Row key={di.id} item={di}> <Row key={di.id} item={di}>
{/* TODO add a comment about the number 99 */}
{di.id > 99 ? ( {di.id > 99 ? (
<Cell>{showName(di)}</Cell> <>
) : ( <Cell>{showName(di)}</Cell>
<CellTree item={di}>{showName(di)}</CellTree> <Cell>
)} <Tooltip
<Cell pinRight> placement="left"
<span style={{ color: 'lightgrey' }}> title={
{di.dv && formatValue(LL, di.dv.v, di.dv.u)} di.dv ? formatValue(LL, di.dv.v, di.dv.u) : ''
</span> }
</Cell> arrow
<Cell stiff>
{me.admin &&
di.dv?.c &&
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && (
<IconButton
size="small"
onClick={() => editDashboardValue(di)}
> >
<EditIcon color="primary" sx={{ fontSize: 16 }} /> <span style={{ color: 'lightgrey' }}>
</IconButton> {di.dv ? formatValue(LL, di.dv.v, di.dv.u) : ''}
)} </span>
</Cell> </Tooltip>
</Cell>
<Cell stiff pinRight>
{me.admin && di.id < 9700 && di.id >= 9600 ? (
<IconButton
size="small"
onClick={() => toggleSchedule(di)}
>
{di.dv?.v ? (
<CircleIcon
color="success"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
) : (
<CircleIcon
color="error"
sx={{ fontSize: 16, verticalAlign: 'middle' }}
/>
)}
</IconButton>
) : (
me.admin &&
di.dv?.c &&
!hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && (
<IconButton
size="small"
onClick={() => editDashboardValue(di)}
>
<EditIcon
color="primary"
sx={{ fontSize: 16 }}
/>
</IconButton>
)
)}
</Cell>
</>
) : (
<>
<CellTree item={di}>{showName(di)}</CellTree>
<Cell />
<Cell />
</>
)}
</Row> </Row>
))} ))}
</Body> </Body>
@@ -265,7 +382,7 @@ const Dashboard = () => {
<SectionContent> <SectionContent>
{renderContent()} {renderContent()}
{selectedDashboardItem && selectedDashboardItem.dv && ( {selectedDashboardItem && selectedDashboardItem.dv && (
<DashboardDevicesDialog <DevicesDialog
open={deviceValueDialogOpen} open={deviceValueDialogOpen}
onClose={() => setDeviceValueDialogOpen(false)} onClose={() => setDeviceValueDialogOpen(false)}
onSave={deviceValueDialogSave} onSave={deviceValueDialogSave}