Display the list of devices
This commit is contained in:
		@@ -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(
 | 
			
		||||
      <Route path="*" element={<BaseAuthenticatedPage />}>
 | 
			
		||||
        <Route path="" element={<HomeRoute />} />
 | 
			
		||||
        <Route path="devices" element={<DevicesRoute />} />
 | 
			
		||||
        <Route path="pending_devices" element={<PendingDevicesRoute />} />
 | 
			
		||||
        <Route path="*" element={<NotFoundRoute />} />
 | 
			
		||||
      </Route>
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,19 @@ export class DeviceApi {
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the list of validated devices
 | 
			
		||||
   */
 | 
			
		||||
  static async ValidatedList(): Promise<Device[]> {
 | 
			
		||||
    return (
 | 
			
		||||
      await APIClient.exec({
 | 
			
		||||
        uri: "/devices/list_validated",
 | 
			
		||||
        method: "GET",
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Validate a device
 | 
			
		||||
   */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								central_frontend/src/routes/DevicesRoute.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								central_frontend/src/routes/DevicesRoute.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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<Device[] | undefined>();
 | 
			
		||||
 | 
			
		||||
  const load = async () => {
 | 
			
		||||
    setList(await DeviceApi.ValidatedList());
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const reload = () => {
 | 
			
		||||
    loadKey.current += 1;
 | 
			
		||||
    setList(undefined);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SolarEnergyRouteContainer
 | 
			
		||||
      label="Devices"
 | 
			
		||||
      actions={
 | 
			
		||||
        <Tooltip title="Refresh table">
 | 
			
		||||
          <IconButton onClick={reload}>
 | 
			
		||||
            <RefreshIcon />
 | 
			
		||||
          </IconButton>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      <AsyncWidget
 | 
			
		||||
        loadKey={loadKey.current}
 | 
			
		||||
        ready={!!list}
 | 
			
		||||
        errMsg="Failed to load the list of validated devices!"
 | 
			
		||||
        load={load}
 | 
			
		||||
        build={() => <ValidatedDevicesList onReload={reload} list={list!} />}
 | 
			
		||||
      />
 | 
			
		||||
    </SolarEnergyRouteContainer>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 <p>There is no device validated yet.</p>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <TableContainer component={Paper}>
 | 
			
		||||
      <Table sx={{ minWidth: 650 }} aria-label="simple table">
 | 
			
		||||
        <TableHead>
 | 
			
		||||
          <TableRow>
 | 
			
		||||
            <TableCell>#</TableCell>
 | 
			
		||||
            <TableCell>Model</TableCell>
 | 
			
		||||
            <TableCell>Version</TableCell>
 | 
			
		||||
            <TableCell>Max number of relays</TableCell>
 | 
			
		||||
            <TableCell>Created</TableCell>
 | 
			
		||||
            <TableCell>Updated</TableCell>
 | 
			
		||||
            <TableCell></TableCell>
 | 
			
		||||
          </TableRow>
 | 
			
		||||
        </TableHead>
 | 
			
		||||
        <TableBody>
 | 
			
		||||
          {p.list.map((dev) => (
 | 
			
		||||
            <TableRow key={dev.id}>
 | 
			
		||||
              <TableCell component="th" scope="row">
 | 
			
		||||
                {dev.id}
 | 
			
		||||
              </TableCell>
 | 
			
		||||
              <TableCell>{dev.info.reference}</TableCell>
 | 
			
		||||
              <TableCell>{dev.info.version}</TableCell>
 | 
			
		||||
              <TableCell>{dev.info.max_relays}</TableCell>
 | 
			
		||||
              <TableCell>
 | 
			
		||||
                <TimeWidget time={dev.time_create} />
 | 
			
		||||
              </TableCell>
 | 
			
		||||
              <TableCell>
 | 
			
		||||
                <TimeWidget time={dev.time_update} />
 | 
			
		||||
              </TableCell>
 | 
			
		||||
              <TableCell>
 | 
			
		||||
                <Tooltip title="Delete device">
 | 
			
		||||
                  <IconButton onClick={() => deleteDevice(dev)}>
 | 
			
		||||
                    <DeleteIcon />
 | 
			
		||||
                  </IconButton>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
              </TableCell>
 | 
			
		||||
            </TableRow>
 | 
			
		||||
          ))}
 | 
			
		||||
        </TableBody>
 | 
			
		||||
      </Table>
 | 
			
		||||
    </TableContainer>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -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 {
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <NavLink label="Home" uri="/" icon={<Icon path={mdiHome} size={1} />} />
 | 
			
		||||
      <NavLink
 | 
			
		||||
        label="Devices"
 | 
			
		||||
        uri="/devices"
 | 
			
		||||
        icon={<Icon path={mdiChip} size={1} />}
 | 
			
		||||
      />
 | 
			
		||||
      <NavLink
 | 
			
		||||
        label="Pending devices"
 | 
			
		||||
        uri="/pending_devices"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user