diff --git a/frontend/src/controls/Attachments.tsx b/frontend/src/controls/Attachments.tsx index 47a7c29e440..9835f7681ad 100644 --- a/frontend/src/controls/Attachments.tsx +++ b/frontend/src/controls/Attachments.tsx @@ -2,9 +2,10 @@ import { RobotAttributeClass, useRobotAttributeQuery, } from "../api"; -import {Box, Grid, Paper, Typography, ToggleButton, ToggleButtonGroup} from "@mui/material"; +import {Grid, Typography, ToggleButton, ToggleButtonGroup} from "@mui/material"; import React from "react"; -import LoadingFade from "../components/LoadingFade"; +import ControlsCard from "./ControlsCard"; +import {Extension} from "@mui/icons-material"; const Attachments = (): React.ReactElement | null => { const { @@ -31,44 +32,32 @@ const Attachments = (): React.ReactElement | null => { } return ( - - {attachments.map(({ type, attached }) => { - return ( - - {type} - - ); - })} - + + + + {attachments.map(({ type, attached }) => { + return ( + + {type} + + ); + })} + + + ); }, [attachments, isAttachmentError]); return ( - - - - - - - - Attachments - - - - - - - - {attachmentDetails} - - - - - + + + {attachmentDetails} + + ); }; diff --git a/frontend/src/controls/BasicControls.tsx b/frontend/src/controls/BasicControls.tsx index fe974fc6c5e..6b23c287898 100644 --- a/frontend/src/controls/BasicControls.tsx +++ b/frontend/src/controls/BasicControls.tsx @@ -5,6 +5,7 @@ import { DialogContentText, Grid, Paper, + Skeleton, Typography, } from "@mui/material"; import { @@ -21,7 +22,6 @@ import { SvgIconComponent, } from "@mui/icons-material"; import React from "react"; -import LoadingFade from "../components/LoadingFade"; import ConfirmationDialog from "../components/ConfirmationDialog"; import {usePendingMapAction} from "../map/Map"; @@ -62,7 +62,7 @@ const BasicControls = (): React.ReactElement => { - + @@ -114,7 +114,7 @@ const BasicControls = (): React.ReactElement => { <> - + { return ( - + {basicControls && } diff --git a/frontend/src/controls/ControlsCard.tsx b/frontend/src/controls/ControlsCard.tsx new file mode 100644 index 00000000000..5c6a8ceb3db --- /dev/null +++ b/frontend/src/controls/ControlsCard.tsx @@ -0,0 +1,40 @@ +import {Box, Grid, Paper, Skeleton, SvgIconProps, Typography} from "@mui/material"; +import React, {ReactNode} from "react"; + + +interface ControlsCardProps { + icon: React.ComponentType; + title: string; + children: ReactNode; + isLoading?: boolean +} + +const ControlsCard: React.FC = ({ icon: Icon, title, children, isLoading }) => ( + + + + + + + + + {title} + + + + + { + isLoading ? ( + + ) : ( + children + ) + } + + + + + +); + +export default ControlsCard; diff --git a/frontend/src/controls/CurrentStatistics.tsx b/frontend/src/controls/CurrentStatistics.tsx index 51f9ecf4c17..3f5a8ac55b6 100644 --- a/frontend/src/controls/CurrentStatistics.tsx +++ b/frontend/src/controls/CurrentStatistics.tsx @@ -2,8 +2,8 @@ import {useCurrentStatisticsQuery} from "../api"; import {Box, CircularProgress, Grid, Paper, Typography} from "@mui/material"; import {Equalizer as StatisticsIcon} from "@mui/icons-material"; import React from "react"; -import LoadingFade from "../components/LoadingFade"; import {getFriendlyStatName, getHumanReadableStatValue} from "../utils"; +import ControlsCard from "./ControlsCard"; const CurrentStatistics = (): React.ReactElement => { const { @@ -50,32 +50,11 @@ const CurrentStatistics = (): React.ReactElement => { ]); return ( - - - - - - - - - Current Statistics - - - - - - - - {body} - - - - - + + + {body} + + ); }; diff --git a/frontend/src/controls/Dock.tsx b/frontend/src/controls/Dock.tsx index bfe1a8aed4e..c555509bacd 100644 --- a/frontend/src/controls/Dock.tsx +++ b/frontend/src/controls/Dock.tsx @@ -8,14 +8,15 @@ import { useRobotStatusQuery } from "../api"; import {useCapabilitiesSupported} from "../CapabilitiesProvider"; -import {Box, Button, Grid, Icon, Paper, styled, Typography} from "@mui/material"; +import {Button, Grid, Icon, styled, Typography} from "@mui/material"; import { RestoreFromTrash as EmptyIcon, + Villa as DockIcon, Water as CleanMopIcon, WindPower as DryMopIcon, } from "@mui/icons-material"; import React from "react"; -import LoadingFade from "../components/LoadingFade"; +import ControlsCard from "./ControlsCard"; const Dock = (): React.ReactElement => { const { data: robotStatus, isPending: isRobotStatusPending } = useRobotStatusQuery(); @@ -60,108 +61,111 @@ const Dock = (): React.ReactElement => { } = useMopDockDryManualTriggerMutation(); - const dockStatusIsRelevant = mopDockCleanTriggerSupported || mopDockDryTriggerSupported; - const commandIsExecuting = emptyIsExecuting || mopDockCleanCommandExecuting || mopDockDryCommandExecuting; - const mopAttachmentAttached = attachments?.find(a => { - return a.type === "mop"; - })?.attached === true; + const body = React.useMemo(() => { + const dockStatusIsRelevant = mopDockCleanTriggerSupported || mopDockDryTriggerSupported; + const commandIsExecuting = emptyIsExecuting || mopDockCleanCommandExecuting || mopDockDryCommandExecuting; + const mopAttachmentAttached = attachments?.find(a => { + return a.type === "mop"; + })?.attached === true; - if (isPending) { - return ( - - - - - - - - ); - } + if (isPending) { + return <>; + } + + if (robotStatus === undefined || (dockStatusIsRelevant && dockStatus?.length !== 1)) { + return ( + Error loading dock controls + ); + } + + const { value: robotState } = robotStatus; + const { value: dockState } = dockStatus?.[0] ?? {value: "idle"}; - if (robotStatus === undefined || (dockStatusIsRelevant && dockStatus?.length !== 1)) { return ( - - - - Error loading dock controls - - + + { + mopDockCleanTriggerSupported && + + + + } + { + mopDockDryTriggerSupported && + + + + } + { + triggerEmptySupported && + + + + } ); - } + }, [ + StyledIcon, + attachments, + dockStatus, + emptyIsExecuting, + isPending, + mopDockCleanCommandExecuting, + mopDockCleanTriggerSupported, + mopDockDryCommandExecuting, + mopDockDryTriggerSupported, + robotStatus, + triggerDockEmpty, + triggerEmptySupported, + triggerMopDockCleanCommand, + triggerMopDockDryCommand + ]); - const { value: robotState } = robotStatus; - const { value: dockState } = dockStatus?.[0] ?? {value: "idle"}; return ( - - - - - - Dock - - - { - mopDockCleanTriggerSupported && - - - - } - { - mopDockDryTriggerSupported && - - - - } - { - triggerEmptySupported && - - - - } - - - - - + + {body} + ); }; diff --git a/frontend/src/controls/PresetSelection.tsx b/frontend/src/controls/PresetSelection.tsx index d59bd403c93..b1a2fb473c3 100644 --- a/frontend/src/controls/PresetSelection.tsx +++ b/frontend/src/controls/PresetSelection.tsx @@ -146,7 +146,7 @@ const PresetSelectionControl = (props: PresetSelectionProps): React.ReactElement - + { return Error loading robot state; } - if (isPending) { - return ( - - - - ); - } - if (status === undefined) { return null; } return ( - + {status.value} {status.flag !== "none" ? <> – {status.flag} : ""} ); - }, [isStatusError, status, isPending]); + }, [isStatusError, status]); const batteriesDetails = React.useMemo(() => { if (isBatteryError) { @@ -101,24 +92,19 @@ const RobotStatus = (): React.ReactElement => { return batteries.map((battery, index) => { return ( - - - - + - - - - - - {Math.round(battery.level)}% - - + + Battery{batteries.length > 1 ? ` ${index+1}`: ""}: {Math.round(battery.level)}% + + + + ); @@ -126,39 +112,24 @@ const RobotStatus = (): React.ReactElement => { }, [batteries, isBatteryError]); return ( - - - - - - - - State - - - {stateDetails} - - - {batteries !== undefined && batteries.length > 0 && ( - - - Battery - - {batteriesDetails} - - )} - + + + + + {stateDetails} + + + {batteries !== undefined && batteries.length > 0 && ( + + {batteriesDetails} - - - + )} + + ); };