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}
-
-
-
+ )}
+
+
);
};