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,
|
DeviceNotFound,
|
||||||
#[error("Requested device is not validated")]
|
#[error("Requested device is not validated")]
|
||||||
DeviceNotValidated,
|
DeviceNotValidated,
|
||||||
|
#[error("Failed to delete device: {0}")]
|
||||||
|
DeleteDeviceFailed(&'static str),
|
||||||
#[error("Failed to update relay configuration: {0}")]
|
#[error("Failed to update relay configuration: {0}")]
|
||||||
UpdateRelayFailed(&'static str),
|
UpdateRelayFailed(&'static str),
|
||||||
#[error("Failed to delete relay: {0}")]
|
#[error("Failed to delete relay: {0}")]
|
||||||
@ -178,6 +180,19 @@ impl DevicesList {
|
|||||||
|
|
||||||
/// Delete a device
|
/// Delete a device
|
||||||
pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> {
|
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);
|
let crt_path = AppConfig::get().device_cert_path(id);
|
||||||
if crt_path.is_file() {
|
if crt_path.is_file() {
|
||||||
let cert = self.get_cert(id)?;
|
let cert = self.get_cert(id)?;
|
||||||
|
@ -6,13 +6,14 @@ import {
|
|||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { AuthApi } from "./api/AuthApi";
|
import { AuthApi } from "./api/AuthApi";
|
||||||
import { ServerApi } from "./api/ServerApi";
|
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 { LoginRoute } from "./routes/LoginRoute";
|
||||||
import { NotFoundRoute } from "./routes/NotFoundRoute";
|
import { NotFoundRoute } from "./routes/NotFoundRoute";
|
||||||
import { HomeRoute } from "./routes/HomeRoute";
|
|
||||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
|
||||||
import { PendingDevicesRoute } from "./routes/PendingDevicesRoute";
|
import { PendingDevicesRoute } from "./routes/PendingDevicesRoute";
|
||||||
import { DevicesRoute } from "./routes/DevicesRoute";
|
import { RelaysListRoute } from "./routes/RelaysListRoute";
|
||||||
import { DeviceRoute } from "./routes/DeviceRoute/DeviceRoute";
|
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled)
|
if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled)
|
||||||
@ -25,6 +26,7 @@ export function App() {
|
|||||||
<Route path="pending_devices" element={<PendingDevicesRoute />} />
|
<Route path="pending_devices" element={<PendingDevicesRoute />} />
|
||||||
<Route path="devices" element={<DevicesRoute />} />
|
<Route path="devices" element={<DevicesRoute />} />
|
||||||
<Route path="dev/:id" element={<DeviceRoute />} />
|
<Route path="dev/:id" element={<DeviceRoute />} />
|
||||||
|
<Route path="relays" element={<RelaysListRoute />} />
|
||||||
<Route path="*" element={<NotFoundRoute />} />
|
<Route path="*" element={<NotFoundRoute />} />
|
||||||
</Route>
|
</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 Icon from "@mdi/react";
|
||||||
import {
|
import {
|
||||||
List,
|
List,
|
||||||
ListItemButton,
|
ListItemButton,
|
||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
ListItemSecondaryAction,
|
|
||||||
ListItemText,
|
ListItemText,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
@ -31,25 +30,29 @@ export function SolarEnergyNavList(): React.ReactElement {
|
|||||||
uri="/pending_devices"
|
uri="/pending_devices"
|
||||||
icon={<Icon path={mdiNewBox} size={1} />}
|
icon={<Icon path={mdiNewBox} size={1} />}
|
||||||
/>
|
/>
|
||||||
|
<NavLink
|
||||||
|
label="Relays"
|
||||||
|
uri="/relays"
|
||||||
|
icon={<Icon path={mdiElectricSwitch} size={1} />}
|
||||||
|
/>
|
||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NavLink(p: {
|
function NavLink(
|
||||||
|
p: Readonly<{
|
||||||
icon: React.ReactElement;
|
icon: React.ReactElement;
|
||||||
uri: string;
|
uri: string;
|
||||||
label: string;
|
label: string;
|
||||||
secondaryAction?: React.ReactElement;
|
secondaryAction?: React.ReactElement;
|
||||||
}): React.ReactElement {
|
}>
|
||||||
|
): React.ReactElement {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
return (
|
return (
|
||||||
<RouterLink to={p.uri}>
|
<RouterLink to={p.uri}>
|
||||||
<ListItemButton selected={p.uri === location.pathname}>
|
<ListItemButton selected={p.uri === location.pathname}>
|
||||||
<ListItemIcon>{p.icon}</ListItemIcon>
|
<ListItemIcon>{p.icon}</ListItemIcon>
|
||||||
<ListItemText primary={p.label} />
|
<ListItemText primary={p.label} />
|
||||||
{p.secondaryAction && (
|
|
||||||
<ListItemSecondaryAction>{p.secondaryAction}</ListItemSecondaryAction>
|
|
||||||
)}
|
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user