From ec277b27aed2d09dffa7b1caf2e075f9a6f3bdae Mon Sep 17 00:00:00 2001 From: Tristan Teufel Date: Wed, 27 Dec 2023 19:44:05 +0100 Subject: [PATCH] new card layout --- src/app/BlindsControl.tsx | 89 +++++----- src/app/LightControl.tsx | 54 +++--- src/app/RainDetectionTransmitter.tsx | 0 src/app/Room.tsx | 160 +++++++++++------- src/app/ThermostatControl.tsx | 58 +++---- .../components/CircularProgressWithLabel.tsx | 27 +++ src/hooks/useApi.ts | 2 + 7 files changed, 232 insertions(+), 158 deletions(-) create mode 100644 src/app/RainDetectionTransmitter.tsx create mode 100644 src/app/components/CircularProgressWithLabel.tsx diff --git a/src/app/BlindsControl.tsx b/src/app/BlindsControl.tsx index bd7886d..d6d721a 100644 --- a/src/app/BlindsControl.tsx +++ b/src/app/BlindsControl.tsx @@ -1,11 +1,4 @@ -import { - Box, - CircularProgress, - CircularProgressProps, - IconButton, - ListItemText, - Typography, -} from '@mui/material'; +import { Box, IconButton, ListItemText } from '@mui/material'; import { BlindVirtualReceiverChannel, useSetValueMutation, @@ -15,45 +8,31 @@ import BlindsClosedIcon from '@mui/icons-material/BlindsClosed'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import StopIcon from '@mui/icons-material/Stop'; +import { CircularProgressWithLabel } from './components/CircularProgressWithLabel'; interface ControlProps { channel: BlindVirtualReceiverChannel; } -const CircularProgressWithLabel = ( - props: CircularProgressProps & { value: number } -) => { - return ( - - - - {`${Math.round(props.value)}%`} - - - ); -}; - export const BlindsControl = ({ channel }: ControlProps) => { const setValueMutation = useSetValueMutation(); const { datapoints, name, address, interfaceName } = channel; const blindValue = Number(datapoints.LEVEL) * 100; return ( - + - {blindValue === 0 ? : } + {blindValue === 0 ? ( + + ) : ( + + )} { }, }} /> - - + setValueMutation.mutateAsync({ interface: interfaceName, address, - valueKey: 'STOP', - type: 'boolean', - value: true, + valueKey: 'LEVEL', + type: 'double', + value: 0, }) } > - + setValueMutation.mutateAsync({ interface: interfaceName, address, - valueKey: 'LEVEL', - type: 'double', - value: 0, + valueKey: 'STOP', + type: 'boolean', + value: true, }) } > - + @@ -106,10 +99,10 @@ export const BlindsControl = ({ channel }: ControlProps) => { }) } > - + - + diff --git a/src/app/LightControl.tsx b/src/app/LightControl.tsx index 8073419..d6a7675 100644 --- a/src/app/LightControl.tsx +++ b/src/app/LightControl.tsx @@ -1,4 +1,4 @@ -import { Box, ListItemText, Switch, styled } from '@mui/material'; +import { Box, CardContent, CardHeader, ListItemText, Switch, styled } from '@mui/material'; import { SwitchVirtualReceiverChannel, useSetValueMutation, @@ -20,26 +20,36 @@ export const LightControl = ({ channel, refetch }: ControlProps) => { const checked = datapoints.STATE === 'true'; return ( - - - {checked ? ( - - ) : ( - - )} - - - + + + {checked ? ( + + ) : ( + + )} + + + } + /> + + { }} checked={checked} /> - + ); }; diff --git a/src/app/RainDetectionTransmitter.tsx b/src/app/RainDetectionTransmitter.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/Room.tsx b/src/app/Room.tsx index 51ca7d8..7ffb7d1 100644 --- a/src/app/Room.tsx +++ b/src/app/Room.tsx @@ -1,10 +1,13 @@ import { Box, + Card, + CardContent, + CardHeader, + Collapse, Container, - Divider, + IconButton, + IconButtonProps, LinearProgress, - List, - ListItem, Typography, } from '@mui/material'; import { useParams } from 'react-router-dom'; @@ -13,32 +16,60 @@ import { BlindsControl } from './BlindsControl'; import { LightControl } from './LightControl'; import { ThermostatControl } from './ThermostatControl'; import { FloorControl } from './FloorControl'; -import { useMemo } from 'react'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { useMemo, useState } from 'react'; +import styled from '@emotion/styled'; + +interface ExpandMoreProps extends IconButtonProps { + expand: boolean; +} + +const ExpandMore = styled((props: ExpandMoreProps) => { + const { expand, ...other } = props; + return ; +})(({ expand }) => ({ + transform: !expand ? 'rotate(0deg)' : 'rotate(180deg)', + marginLeft: 'auto', + transition: 'transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', +})); export const Room = () => { + const [expanded, setExpanded] = useState>( + Object.fromEntries( + Object.values(ChannelType).map((type) => [type, true]) + ) as Record + ); + + const handleExpandClick = (channelType: ChannelType) => { + setExpanded((prevExpanded) => ({ + ...prevExpanded, + [channelType]: !prevExpanded[channelType], + })); + }; + const { roomId } = useParams<{ roomId: string }>(); - const { data: channelsForRoom, refetch, isLoading } = useGetChannelsForRoom(Number(roomId)); + const { + data: channelsForRoom, + refetch, + isLoading, + } = useGetChannelsForRoom(Number(roomId)); - console.log('channelsForRoom', channelsForRoom) + console.log('channelsForRoom', channelsForRoom); - const sorted = useMemo(() => [...channelsForRoom].sort((a, b) => { - const order = [ - ChannelType.SWITCH_VIRTUAL_RECEIVER, - ChannelType.BLIND_VIRTUAL_RECEIVER, - ChannelType.CLIMATECONTROL_FLOOR_TRANSCEIVER, - ChannelType.HEATING_CLIMATECONTROL_TRANSCEIVER, - ]; - - const indexA = order.indexOf(a.type); - const indexB = order.indexOf(b.type); - - if (indexA === -1 || indexB === -1) { - return 0; - } - - return indexA - indexB; - }), [channelsForRoom]); + const channelsPerType = useMemo(() => { + return channelsForRoom?.reduce((acc, channel) => { + const channels = acc.get(channel.type); + + if (channels) { + channels.push(channel); + } else { + acc.set(channel.type, [channel]); + } + + return acc; + }, new Map()); + }, [channelsForRoom]); const getControlComponent = (channel: Channel, refetch: () => void) => { switch (channel.type) { @@ -51,43 +82,56 @@ export const Room = () => { case ChannelType.BLIND_VIRTUAL_RECEIVER: return ; default: - return {(channel as Channel).type} no implemented; + return ( + + {(channel as Channel).type} no implemented + + ); } - } + }; - if(isLoading) { - return + if (isLoading) { + return ; } - return ( - - - {sorted.map((channel, index) => { - - return ( - - - - {getControlComponent(channel, refetch)} - - - - {channelsForRoom?.length === index + 1 ? null : ( - - )} - - ); - })} - - - ); + return ( + + {Array.from(channelsPerType).map(([channelType, channels]) => { + return channels.length ? ( + + + + {channelType} + + handleExpandClick(channelType)} + aria-expanded={expanded[channelType]} + aria-label="show more" + > + + + + } + /> + + + {channels.map((channel, index) => { + return ( + + {getControlComponent(channel, refetch)} + + ); + })} + + + + ) : null; + })} + + ); }; diff --git a/src/app/ThermostatControl.tsx b/src/app/ThermostatControl.tsx index b350e08..39e228d 100644 --- a/src/app/ThermostatControl.tsx +++ b/src/app/ThermostatControl.tsx @@ -16,6 +16,7 @@ import ThermostatAutoIcon from '@mui/icons-material/ThermostatAuto'; import WaterDamageOutlinedIcon from '@mui/icons-material/WaterDamageOutlined'; import { useEffect, useState } from 'react'; import { Icon } from '@iconify/react'; +import { CircularProgressWithLabel } from './components/CircularProgressWithLabel'; interface ControlProps { channel: HeatingClimateControlTransceiverChannel; @@ -139,13 +140,20 @@ export const ThermostatControl = ({ channel }: ControlProps) => { alignItems: 'center', }} > - + {Number(datapoints?.HUMIDITY)}% ) : null} + {datapoints?.LEVEL ? ( + + ) : null} + { }} > {pointMode ? ( - - - - - - - + ) : ( - - - - - + )} + {datapoints?.ACTUAL_TEMPERATURE ? Number(datapoints?.ACTUAL_TEMPERATURE).toLocaleString( @@ -207,7 +190,22 @@ export const ThermostatControl = ({ channel }: ControlProps) => { - + + + + + + + + {pointTemp.toLocaleString('de-DE', { maximumFractionDigits: 2, diff --git a/src/app/components/CircularProgressWithLabel.tsx b/src/app/components/CircularProgressWithLabel.tsx new file mode 100644 index 0000000..e1e82e1 --- /dev/null +++ b/src/app/components/CircularProgressWithLabel.tsx @@ -0,0 +1,27 @@ +import { Box, CircularProgress, CircularProgressProps, Typography } from "@mui/material"; + +export const CircularProgressWithLabel = ( + props: CircularProgressProps & { value: number } + ) => { + return ( + + + + {`${Math.round(props.value)}%`} + + + ); + }; \ No newline at end of file diff --git a/src/hooks/useApi.ts b/src/hooks/useApi.ts index 2ef1784..e4389ea 100644 --- a/src/hooks/useApi.ts +++ b/src/hooks/useApi.ts @@ -56,6 +56,8 @@ export type HeatingClimateControlTransceiverDatapoint = { SET_POINT_TEMPERATURE: string; SWITCH_POINT_OCCURED: string; WINDOW_STATE: string; + LEVEL?: string; + LEVEL_STATUS?: string; }; export type FloorClimateControlTransceiverDatapoint = {