Can delete a device relay from UI

This commit is contained in:
Pierre HUBERT 2024-08-31 20:03:46 +02:00
parent f35aac04f6
commit b0023a5167
5 changed files with 107 additions and 25 deletions

View File

@ -39,4 +39,14 @@ export class RelayApi {
jsonData: relay, jsonData: relay,
}); });
} }
/**
* Delete a relay configuration
*/
static async Delete(relay: DeviceRelay): Promise<void> {
await APIClient.exec({
method: "DELETE",
uri: `/relay/${relay.id}`,
});
}
} }

View File

@ -4,9 +4,9 @@ import {
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
Grid,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import Grid from "@mui/material/Grid2";
import { TimePicker } from "@mui/x-date-pickers"; import { TimePicker } from "@mui/x-date-pickers";
import React from "react"; import React from "react";
import { Device, DeviceRelay } from "../api/DeviceApi"; import { Device, DeviceRelay } from "../api/DeviceApi";
@ -81,7 +81,7 @@ export function EditDeviceRelaysDialog(p: {
<DialogContent> <DialogContent>
<DialogFormTitle>General info</DialogFormTitle> <DialogFormTitle>General info</DialogFormTitle>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Relay name" label="Relay name"
@ -97,7 +97,7 @@ export function EditDeviceRelaysDialog(p: {
size={ServerApi.Config.constraints.dev_name_len} size={ServerApi.Config.constraints.dev_name_len}
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<CheckboxInput <CheckboxInput
editable editable
label="Enable relay" label="Enable relay"
@ -112,7 +112,7 @@ export function EditDeviceRelaysDialog(p: {
} }
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Priority" label="Priority"
@ -130,7 +130,7 @@ export function EditDeviceRelaysDialog(p: {
helperText="Relay priority when selecting relays to turn on. 0 = lowest priority" helperText="Relay priority when selecting relays to turn on. 0 = lowest priority"
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Consumption" label="Consumption"
@ -148,7 +148,7 @@ export function EditDeviceRelaysDialog(p: {
helperText="Estimated consumption of device powered by relay" helperText="Estimated consumption of device powered by relay"
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Minimal uptime" label="Minimal uptime"
@ -166,7 +166,7 @@ export function EditDeviceRelaysDialog(p: {
helperText="Minimal time this relay shall be left on before it can be turned off (in seconds)" helperText="Minimal time this relay shall be left on before it can be turned off (in seconds)"
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Minimal downtime" label="Minimal downtime"
@ -188,7 +188,7 @@ export function EditDeviceRelaysDialog(p: {
<DialogFormTitle>Daily runtime</DialogFormTitle> <DialogFormTitle>Daily runtime</DialogFormTitle>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<CheckboxInput <CheckboxInput
editable editable
label="Enable minimal runtime" label="Enable minimal runtime"
@ -208,7 +208,7 @@ export function EditDeviceRelaysDialog(p: {
{!!relay.daily_runtime && ( {!!relay.daily_runtime && (
<> <>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TextInput <TextInput
editable editable
label="Minimal daily runtime" label="Minimal daily runtime"
@ -231,7 +231,7 @@ export function EditDeviceRelaysDialog(p: {
helperText="Minimum time, in seconds, that this relay should run each day" helperText="Minimum time, in seconds, that this relay should run each day"
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<TimePicker <TimePicker
label="Reset time" label="Reset time"
value={timeOfDay(relay.daily_runtime!.reset_time)} value={timeOfDay(relay.daily_runtime!.reset_time)}
@ -248,7 +248,7 @@ export function EditDeviceRelaysDialog(p: {
} }
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<MultipleSelectInput <MultipleSelectInput
label="Catchup hours" label="Catchup hours"
helperText="The hours during which the relay should be turned on to reach expected runtime" helperText="The hours during which the relay should be turned on to reach expected runtime"
@ -278,7 +278,7 @@ export function EditDeviceRelaysDialog(p: {
<DialogFormTitle>Constraints</DialogFormTitle> <DialogFormTitle>Constraints</DialogFormTitle>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<SelectMultipleRelaysInput <SelectMultipleRelaysInput
label="Required relays" label="Required relays"
exclude={[relay.id]} exclude={[relay.id]}
@ -294,7 +294,7 @@ export function EditDeviceRelaysDialog(p: {
helperText="Relays that must be already up for this relay to be started" helperText="Relays that must be already up for this relay to be started"
/> />
</Grid> </Grid>
<Grid item xs={6}> <Grid size={{ xs: 6 }}>
<SelectMultipleRelaysInput <SelectMultipleRelaysInput
label="Conflicting relays" label="Conflicting relays"
exclude={[relay.id]} exclude={[relay.id]}

View File

@ -1,14 +1,32 @@
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import { IconButton, Tooltip } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import {
IconButton,
ListItem,
ListItemText,
Tooltip,
Typography,
} from "@mui/material";
import React from "react"; import React from "react";
import { Device, DeviceRelay } from "../../api/DeviceApi"; import { Device, DeviceRelay } from "../../api/DeviceApi";
import { DeviceRouteCard } from "./DeviceRouteCard";
import { EditDeviceRelaysDialog } from "../../dialogs/EditDeviceRelaysDialog"; import { EditDeviceRelaysDialog } from "../../dialogs/EditDeviceRelaysDialog";
import { DeviceRouteCard } from "./DeviceRouteCard";
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
import { RelayApi } from "../../api/RelayApi";
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
export function DeviceRelays(p: { export function DeviceRelays(p: {
device: Device; device: Device;
onReload: () => void; onReload: () => void;
}): React.ReactElement { }): React.ReactElement {
const confirm = useConfirm();
const loadingMessage = useLoadingMessage();
const snackbar = useSnackbar();
const alert = useAlert();
const [dialogOpen, setDialogOpen] = React.useState(false); const [dialogOpen, setDialogOpen] = React.useState(false);
const [currRelay, setCurrRelay] = React.useState<DeviceRelay | undefined>(); const [currRelay, setCurrRelay] = React.useState<DeviceRelay | undefined>();
@ -17,6 +35,25 @@ export function DeviceRelays(p: {
setCurrRelay(undefined); setCurrRelay(undefined);
}; };
const deleteRelay = async (r: DeviceRelay) => {
if (
!(await confirm("Do you really want to delete this relay configuration?"))
)
return;
try {
await RelayApi.Delete(r);
p.onReload();
snackbar("The relay configuration was successfully deleted!");
} catch (e) {
console.error("Failed to delete relay!", e);
alert(`Failed to delete device relay configuration! ${e}`);
} finally {
loadingMessage.hide();
}
};
return ( return (
<> <>
{dialogOpen && ( {dialogOpen && (
@ -43,7 +80,39 @@ export function DeviceRelays(p: {
</Tooltip> </Tooltip>
} }
> >
TODO : relays list ({p.device.relays.length}) relays now) {p.device.relays.length === 0 ? (
<Typography style={{ textAlign: "center" }}>
No relay configured yet.
</Typography>
) : (
<></>
)}
{p.device.relays.map((r, i) => (
<ListItem
alignItems="flex-start"
key={r.id}
secondaryAction={
<>
<Tooltip title="Edit the relay configuration">
<IconButton>
<EditIcon />
</IconButton>
</Tooltip>
{i === p.device.relays.length - 1 && (
<Tooltip title="Delete the relay configuration">
<IconButton onClick={() => deleteRelay(r)}>
<DeleteIcon />
</IconButton>
</Tooltip>
)}
</>
}
>
<ListItemText primary={r.name} secondary={"TODO: status"} />
</ListItem>
))}
</DeviceRouteCard> </DeviceRouteCard>
</> </>
); );

View File

@ -1,5 +1,5 @@
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import { Grid, IconButton, Tooltip } from "@mui/material"; import { IconButton, Tooltip } from "@mui/material";
import React from "react"; import React from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { Device, DeviceApi } from "../../api/DeviceApi"; import { Device, DeviceApi } from "../../api/DeviceApi";
@ -11,6 +11,7 @@ import { AsyncWidget } from "../../widgets/AsyncWidget";
import { SolarEnergyRouteContainer } from "../../widgets/SolarEnergyRouteContainer"; import { SolarEnergyRouteContainer } from "../../widgets/SolarEnergyRouteContainer";
import { GeneralDeviceInfo } from "./GeneralDeviceInfo"; import { GeneralDeviceInfo } from "./GeneralDeviceInfo";
import { DeviceRelays } from "./DeviceRelays"; import { DeviceRelays } from "./DeviceRelays";
import Grid from "@mui/material/Grid2";
export function DeviceRoute(): React.ReactElement { export function DeviceRoute(): React.ReactElement {
const { id } = useParams(); const { id } = useParams();
@ -81,10 +82,10 @@ function DeviceRouteInner(p: {
} }
> >
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12} md={6}> <Grid size={{ xs: 12, md: 6 }}>
<GeneralDeviceInfo {...p} /> <GeneralDeviceInfo {...p} />
</Grid> </Grid>
<Grid item xs={12} md={6}> <Grid size={{ xs: 12, md: 6 }}>
<DeviceRelays {...p} /> <DeviceRelays {...p} />
</Grid> </Grid>
</Grid> </Grid>

View File

@ -4,7 +4,6 @@ import Avatar from "@mui/material/Avatar";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import CssBaseline from "@mui/material/CssBaseline"; import CssBaseline from "@mui/material/CssBaseline";
import Grid from "@mui/material/Grid";
import Link from "@mui/material/Link"; import Link from "@mui/material/Link";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
@ -12,6 +11,7 @@ import Typography from "@mui/material/Typography";
import * as React from "react"; import * as React from "react";
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider"; import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
import { AuthApi } from "../api/AuthApi"; import { AuthApi } from "../api/AuthApi";
import Grid from "@mui/material/Grid2";
function Copyright(props: any) { function Copyright(props: any) {
return ( return (
@ -60,10 +60,7 @@ export function LoginRoute() {
<Grid container component="main" sx={{ height: "100vh" }}> <Grid container component="main" sx={{ height: "100vh" }}>
<CssBaseline /> <CssBaseline />
<Grid <Grid
item size={{ sm: 4, md: 7, xs: false }}
xs={false}
sm={4}
md={7}
sx={{ sx={{
backgroundImage: 'url("/sun.jpg")', backgroundImage: 'url("/sun.jpg")',
backgroundColor: (t) => backgroundColor: (t) =>
@ -74,7 +71,12 @@ export function LoginRoute() {
backgroundPosition: "left", backgroundPosition: "left",
}} }}
/> />
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square> <Grid
size={{ xs: 12, sm: 8, md: 5 }}
component={Paper}
elevation={6}
square
>
<Box <Box
sx={{ sx={{
my: 8, my: 8,