diff --git a/central_frontend/src/App.tsx b/central_frontend/src/App.tsx index c7e96d1..db625d8 100644 --- a/central_frontend/src/App.tsx +++ b/central_frontend/src/App.tsx @@ -11,6 +11,7 @@ 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"; export function App() { if (!AuthApi.SignedIn && !ServerApi.Config.auth_disabled) @@ -20,6 +21,7 @@ export function App() { createRoutesFromElements( }> } /> + } /> } /> } /> diff --git a/central_frontend/src/api/DeviceApi.ts b/central_frontend/src/api/DeviceApi.ts index 84d32c9..bd7cc69 100644 --- a/central_frontend/src/api/DeviceApi.ts +++ b/central_frontend/src/api/DeviceApi.ts @@ -49,6 +49,19 @@ export class DeviceApi { }) ).data; } + + /** + * Get the list of validated devices + */ + static async ValidatedList(): Promise { + return ( + await APIClient.exec({ + uri: "/devices/list_validated", + method: "GET", + }) + ).data; + } + /** * Validate a device */ diff --git a/central_frontend/src/routes/DevicesRoute.tsx b/central_frontend/src/routes/DevicesRoute.tsx new file mode 100644 index 0000000..9b25123 --- /dev/null +++ b/central_frontend/src/routes/DevicesRoute.tsx @@ -0,0 +1,137 @@ +import { + Tooltip, + IconButton, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, +} from "@mui/material"; +import React from "react"; +import { Device, DeviceApi } from "../api/DeviceApi"; +import { AsyncWidget } from "../widgets/AsyncWidget"; +import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer"; +import RefreshIcon from "@mui/icons-material/Refresh"; +import { TimeWidget } from "../widgets/TimeWidget"; +import DeleteIcon from "@mui/icons-material/Delete"; +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"; + +export function DevicesRoute(): React.ReactElement { + const loadKey = React.useRef(1); + + const [list, setList] = React.useState(); + + const load = async () => { + setList(await DeviceApi.ValidatedList()); + }; + + const reload = () => { + loadKey.current += 1; + setList(undefined); + }; + + return ( + + + + + + } + > + } + /> + + ); +} + +function ValidatedDevicesList(p: { + list: Device[]; + onReload: () => void; +}): React.ReactElement { + const alert = useAlert(); + const confirm = useConfirm(); + const snackbar = useSnackbar(); + const loadingMessage = useLoadingMessage(); + + const deleteDevice = async (d: Device) => { + try { + if ( + !(await confirm( + `Do you really want to delete the device ${d.id}? The operation cannot be reverted!` + )) + ) + return; + + loadingMessage.show("Deleting device..."); + await DeviceApi.Delete(d); + + snackbar("The device has been successfully deleted!"); + p.onReload(); + } catch (e) { + console.error(`Failed to delete device! ${e})`); + alert("Failed to delete device!"); + } finally { + loadingMessage.hide(); + } + }; + + if (p.list.length === 0) { + return

There is no device validated yet.

; + } + + return ( + + + + + # + Model + Version + Max number of relays + Created + Updated + + + + + {p.list.map((dev) => ( + + + {dev.id} + + {dev.info.reference} + {dev.info.version} + {dev.info.max_relays} + + + + + + + + + deleteDevice(dev)}> + + + + + + ))} + +
+
+ ); +} diff --git a/central_frontend/src/widgets/SolarEnergyNavList.tsx b/central_frontend/src/widgets/SolarEnergyNavList.tsx index 5b5a260..784d4d9 100644 --- a/central_frontend/src/widgets/SolarEnergyNavList.tsx +++ b/central_frontend/src/widgets/SolarEnergyNavList.tsx @@ -1,4 +1,4 @@ -import { mdiHome, mdiNewBox } from "@mdi/js"; +import { mdiChip, mdiHome, mdiNewBox } from "@mdi/js"; import Icon from "@mdi/react"; import { List, @@ -21,6 +21,11 @@ export function SolarEnergyNavList(): React.ReactElement { }} > } /> + } + />