Check for dependencies conflict before deleting a device
This commit is contained in:
parent
bbe128e055
commit
78663854cc
@ -23,6 +23,8 @@ pub enum DevicesListError {
|
||||
DeviceNotFound,
|
||||
#[error("Requested device is not validated")]
|
||||
DeviceNotValidated,
|
||||
#[error("Failed to delete device: {0}")]
|
||||
DeleteDeviceFailed(&'static str),
|
||||
#[error("Failed to update relay configuration: {0}")]
|
||||
UpdateRelayFailed(&'static str),
|
||||
#[error("Failed to delete relay: {0}")]
|
||||
@ -178,6 +180,19 @@ impl DevicesList {
|
||||
|
||||
/// Delete a device
|
||||
pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> {
|
||||
// Check for conflicts
|
||||
let device = self
|
||||
.get_single(id)
|
||||
.ok_or(DevicesListError::DeleteDeviceFailed("Device not found!"))?;
|
||||
for r in &device.relays {
|
||||
if !self.relay_get_direct_dependencies(r.id).is_empty() {
|
||||
return Err(DevicesListError::DeleteDeviceFailed(
|
||||
"A relay of this device is required by another relay!",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let crt_path = AppConfig::get().device_cert_path(id);
|
||||
if crt_path.is_file() {
|
||||
let cert = self.get_cert(id)?;
|
||||
|
@ -6,13 +6,14 @@ import {
|
||||
} from "react-router-dom";
|
||||
import { AuthApi } from "./api/AuthApi";
|
||||
import { ServerApi } from "./api/ServerApi";
|
||||
import { DeviceRoute } from "./routes/DeviceRoute/DeviceRoute";
|
||||
import { DevicesRoute } from "./routes/DevicesRoute";
|
||||
import { HomeRoute } from "./routes/HomeRoute";
|
||||
import { LoginRoute } from "./routes/LoginRoute";
|
||||
import { NotFoundRoute } from "./routes/NotFoundRoute";
|
||||
import { HomeRoute } from "./routes/HomeRoute";
|
||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
||||
import { PendingDevicesRoute } from "./routes/PendingDevicesRoute";
|
||||
import { DevicesRoute } from "./routes/DevicesRoute";
|
||||
import { DeviceRoute } from "./routes/DeviceRoute/DeviceRoute";
|
||||
import { RelaysListRoute } from "./routes/RelaysListRoute";
|
||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
||||
|
||||
export function App() {
|
||||
if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled)
|
||||
@ -25,6 +26,7 @@ export function App() {
|
||||
<Route path="pending_devices" element={<PendingDevicesRoute />} />
|
||||
<Route path="devices" element={<DevicesRoute />} />
|
||||
<Route path="dev/:id" element={<DeviceRoute />} />
|
||||
<Route path="relays" element={<RelaysListRoute />} />
|
||||
<Route path="*" element={<NotFoundRoute />} />
|
||||
</Route>
|
||||
)
|
||||
|
67
central_frontend/src/routes/RelaysListRoute.tsx
Normal file
67
central_frontend/src/routes/RelaysListRoute.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import CheckIcon from "@mui/icons-material/Check";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import {
|
||||
IconButton,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { Device, DeviceApi, DeviceRelay } from "../api/DeviceApi";
|
||||
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
|
||||
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
|
||||
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
|
||||
import { AsyncWidget } from "../widgets/AsyncWidget";
|
||||
import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer";
|
||||
import { TimeWidget } from "../widgets/TimeWidget";
|
||||
import { RelayApi } from "../api/RelayApi";
|
||||
|
||||
export function RelaysListRoute(): React.ReactElement {
|
||||
const loadKey = React.useRef(1);
|
||||
|
||||
const [list, setList] = React.useState<DeviceRelay[] | undefined>();
|
||||
|
||||
const load = async () => {
|
||||
setList(await RelayApi.GetList());
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
loadKey.current += 1;
|
||||
setList(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<SolarEnergyRouteContainer
|
||||
label="Relays list"
|
||||
actions={
|
||||
<Tooltip title="Refresh list">
|
||||
<IconButton onClick={reload}>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<AsyncWidget
|
||||
loadKey={loadKey.current}
|
||||
ready={!!list}
|
||||
errMsg="Failed to load the list of relays!"
|
||||
load={load}
|
||||
build={() => <RelaysList onReload={reload} list={list!} />}
|
||||
/>
|
||||
</SolarEnergyRouteContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function RelaysList(p: {
|
||||
list: DeviceRelay[];
|
||||
onReload: () => void;
|
||||
}): React.ReactElement {
|
||||
return <>todo</>;
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
import { mdiChip, mdiHome, mdiNewBox } from "@mdi/js";
|
||||
import { mdiChip, mdiElectricSwitch, mdiHome, mdiNewBox } from "@mdi/js";
|
||||
import Icon from "@mdi/react";
|
||||
import {
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
} from "@mui/material";
|
||||
import { useLocation } from "react-router-dom";
|
||||
@ -31,25 +30,29 @@ export function SolarEnergyNavList(): React.ReactElement {
|
||||
uri="/pending_devices"
|
||||
icon={<Icon path={mdiNewBox} size={1} />}
|
||||
/>
|
||||
<NavLink
|
||||
label="Relays"
|
||||
uri="/relays"
|
||||
icon={<Icon path={mdiElectricSwitch} size={1} />}
|
||||
/>
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
function NavLink(p: {
|
||||
function NavLink(
|
||||
p: Readonly<{
|
||||
icon: React.ReactElement;
|
||||
uri: string;
|
||||
label: string;
|
||||
secondaryAction?: React.ReactElement;
|
||||
}): React.ReactElement {
|
||||
}>
|
||||
): React.ReactElement {
|
||||
const location = useLocation();
|
||||
return (
|
||||
<RouterLink to={p.uri}>
|
||||
<ListItemButton selected={p.uri === location.pathname}>
|
||||
<ListItemIcon>{p.icon}</ListItemIcon>
|
||||
<ListItemText primary={p.label} />
|
||||
{p.secondaryAction && (
|
||||
<ListItemSecondaryAction>{p.secondaryAction}</ListItemSecondaryAction>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</RouterLink>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user