Reorganize networks page
This commit is contained in:
		| @@ -17,66 +17,31 @@ import { NetworkApi, NetworkInfo, NetworkURL } from "../api/NetworksApi"; | |||||||
| import { AsyncWidget } from "../widgets/AsyncWidget"; | import { AsyncWidget } from "../widgets/AsyncWidget"; | ||||||
| import { RouterLink } from "../widgets/RouterLink"; | import { RouterLink } from "../widgets/RouterLink"; | ||||||
| import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer"; | import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer"; | ||||||
| import { useConfirm } from "../hooks/providers/ConfirmDialogProvider"; |  | ||||||
| import { useSnackbar } from "../hooks/providers/SnackbarProvider"; |  | ||||||
| import { useAlert } from "../hooks/providers/AlertDialogProvider"; |  | ||||||
| import { NetworkStatusWidget } from "../widgets/net/NetworkStatusWidget"; | import { NetworkStatusWidget } from "../widgets/net/NetworkStatusWidget"; | ||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
|  |  | ||||||
| export function NetworksListRoute(): React.ReactElement { | export function NetworksListRoute(): React.ReactElement { | ||||||
|   const confirm = useConfirm(); |  | ||||||
|   const snackbar = useSnackbar(); |  | ||||||
|   const alert = useAlert(); |  | ||||||
|  |  | ||||||
|   const [list, setList] = React.useState<NetworkInfo[] | undefined>(); |   const [list, setList] = React.useState<NetworkInfo[] | undefined>(); | ||||||
|  |  | ||||||
|   const [count, setCount] = React.useState(1); |   const [count] = React.useState(1); | ||||||
|  |  | ||||||
|   const load = async () => { |   const load = async () => { | ||||||
|     setList(await NetworkApi.GetList()); |     setList(await NetworkApi.GetList()); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const reload = () => { |  | ||||||
|     setList(undefined); |  | ||||||
|     setCount(count + 1); |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   const requestDelete = async (n: NetworkInfo) => { |  | ||||||
|     try { |  | ||||||
|       if ( |  | ||||||
|         !(await confirm( |  | ||||||
|           "Do you really want to delete this network?", |  | ||||||
|           `Delete network ${n.name}`, |  | ||||||
|           "Delete" |  | ||||||
|         )) |  | ||||||
|       ) |  | ||||||
|         return; |  | ||||||
|  |  | ||||||
|       await NetworkApi.Delete(n); |  | ||||||
|       reload(); |  | ||||||
|       snackbar("The network was successfully deleted!"); |  | ||||||
|     } catch (e) { |  | ||||||
|       console.error(e); |  | ||||||
|       alert(`Failed to delete the network!\n${e}`); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <AsyncWidget |     <AsyncWidget | ||||||
|       loadKey={count} |       loadKey={count} | ||||||
|       load={load} |       load={load} | ||||||
|       ready={list !== undefined} |       ready={list !== undefined} | ||||||
|       errMsg="Failed to load the list of networks!" |       errMsg="Failed to load the list of networks!" | ||||||
|       build={() => ( |       build={() => <NetworksListRouteInner list={list!} />} | ||||||
|         <NetworksListRouteInner list={list!} onRequestDelete={requestDelete} /> |  | ||||||
|       )} |  | ||||||
|     /> |     /> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| function NetworksListRouteInner(p: { | function NetworksListRouteInner(p: { | ||||||
|   list: NetworkInfo[]; |   list: NetworkInfo[]; | ||||||
|   onRequestDelete: (n: NetworkInfo) => void; |  | ||||||
| }): React.ReactElement { | }): React.ReactElement { | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
| @@ -130,9 +95,6 @@ function NetworksListRouteInner(p: { | |||||||
|                         <VisibilityIcon /> |                         <VisibilityIcon /> | ||||||
|                       </IconButton> |                       </IconButton> | ||||||
|                     </RouterLink> |                     </RouterLink> | ||||||
|                     <IconButton onClick={() => p.onRequestDelete(t)}> |  | ||||||
|                       <DeleteIcon /> |  | ||||||
|                     </IconButton> |  | ||||||
|                   </TableCell> |                   </TableCell> | ||||||
|                 </TableRow> |                 </TableRow> | ||||||
|               ); |               ); | ||||||
|   | |||||||
| @@ -1,16 +1,19 @@ | |||||||
| import { Checkbox, Grid, Paper } from "@mui/material"; | import { Box, Button, Checkbox, Grid, Paper, Tab, Tabs } from "@mui/material"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi"; | import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi"; | ||||||
| import { ServerApi } from "../../api/ServerApi"; | import { ServerApi } from "../../api/ServerApi"; | ||||||
|  | import { useAlert } from "../../hooks/providers/AlertDialogProvider"; | ||||||
|  | import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider"; | ||||||
|  | import { useSnackbar } from "../../hooks/providers/SnackbarProvider"; | ||||||
| import { AsyncWidget } from "../AsyncWidget"; | import { AsyncWidget } from "../AsyncWidget"; | ||||||
|  | import { CheckboxInput } from "../forms/CheckboxInput"; | ||||||
| import { EditSection } from "../forms/EditSection"; | import { EditSection } from "../forms/EditSection"; | ||||||
| import { IPInput } from "../forms/IPInput"; | import { IPInput } from "../forms/IPInput"; | ||||||
|  | import { ResAutostartInput } from "../forms/ResAutostartInput"; | ||||||
| import { SelectInput } from "../forms/SelectInput"; | import { SelectInput } from "../forms/SelectInput"; | ||||||
| import { TextInput } from "../forms/TextInput"; | import { TextInput } from "../forms/TextInput"; | ||||||
| import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider"; |  | ||||||
| import { CheckboxInput } from "../forms/CheckboxInput"; |  | ||||||
| import { ResAutostartInput } from "../forms/ResAutostartInput"; |  | ||||||
| import { DHCPHostReservations } from "./DHCPHostReservations"; | import { DHCPHostReservations } from "./DHCPHostReservations"; | ||||||
|  | import { useNavigate } from "react-router-dom"; | ||||||
|  |  | ||||||
| interface DetailsProps { | interface DetailsProps { | ||||||
|   net: NetworkInfo; |   net: NetworkInfo; | ||||||
| @@ -35,9 +38,44 @@ export function NetworkDetails(p: DetailsProps): React.ReactElement { | |||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| function NetworkDetailsInner( | enum VMTab { | ||||||
|   p: DetailsProps & { cardsList: string[] } |   General = 0, | ||||||
| ): React.ReactElement { |   IPv4, | ||||||
|  |   IPv6, | ||||||
|  |   Danger, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type DetailsInnerProps = DetailsProps & { cardsList: string[] }; | ||||||
|  |  | ||||||
|  | function NetworkDetailsInner(p: DetailsInnerProps): React.ReactElement { | ||||||
|  |   const [currTab, setCurrTab] = React.useState(VMTab.General); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <Box sx={{ borderBottom: 1, borderColor: "divider" }}> | ||||||
|  |         <Tabs value={currTab} onChange={(_ev, newVal) => setCurrTab(newVal)}> | ||||||
|  |           <Tab label="General" tabIndex={VMTab.General} /> | ||||||
|  |           <Tab label="IPv4" tabIndex={VMTab.IPv4} /> | ||||||
|  |           <Tab label="IPv6" tabIndex={VMTab.IPv6} /> | ||||||
|  |           {!p.editable && ( | ||||||
|  |             <Tab | ||||||
|  |               label="Danger zone" | ||||||
|  |               style={{ color: "red" }} | ||||||
|  |               tabIndex={VMTab.Danger} | ||||||
|  |             /> | ||||||
|  |           )} | ||||||
|  |         </Tabs> | ||||||
|  |       </Box> | ||||||
|  |  | ||||||
|  |       {currTab === VMTab.General && <NetworkDetailsTabGeneral {...p} />} | ||||||
|  |       {currTab === VMTab.IPv4 && <NetworkDetailsTabIPv4 {...p} />} | ||||||
|  |       {currTab === VMTab.IPv6 && <NetworkDetailsTabIPv6 {...p} />} | ||||||
|  |       {currTab === VMTab.Danger && <NetworkDetailsTabDanger {...p} />} | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function NetworkDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement { | ||||||
|   return ( |   return ( | ||||||
|     <Grid container spacing={2}> |     <Grid container spacing={2}> | ||||||
|       {/* Metadata section */} |       {/* Metadata section */} | ||||||
| @@ -161,7 +199,13 @@ function NetworkDetailsInner( | |||||||
|           multiline={true} |           multiline={true} | ||||||
|         /> |         /> | ||||||
|       </EditSection> |       </EditSection> | ||||||
|  |     </Grid> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function NetworkDetailsTabIPv4(p: DetailsInnerProps): React.ReactElement { | ||||||
|  |   return ( | ||||||
|  |     <Grid container spacing={2}> | ||||||
|       <IPSection |       <IPSection | ||||||
|         editable={p.editable} |         editable={p.editable} | ||||||
|         config={p.net.ip_v4} |         config={p.net.ip_v4} | ||||||
| @@ -171,7 +215,13 @@ function NetworkDetailsInner( | |||||||
|         }} |         }} | ||||||
|         version={4} |         version={4} | ||||||
|       /> |       /> | ||||||
|  |     </Grid> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function NetworkDetailsTabIPv6(p: DetailsInnerProps): React.ReactElement { | ||||||
|  |   return ( | ||||||
|  |     <Grid container spacing={2}> | ||||||
|       <IPSection |       <IPSection | ||||||
|         editable={p.editable} |         editable={p.editable} | ||||||
|         config={p.net.ip_v6} |         config={p.net.ip_v6} | ||||||
| @@ -309,3 +359,37 @@ function IPSection(p: { | |||||||
|     </EditSection> |     </EditSection> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function NetworkDetailsTabDanger(p: DetailsInnerProps): React.ReactElement { | ||||||
|  |   const confirm = useConfirm(); | ||||||
|  |   const snackbar = useSnackbar(); | ||||||
|  |   const alert = useAlert(); | ||||||
|  |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
|  |   const requestDelete = async () => { | ||||||
|  |     try { | ||||||
|  |       if ( | ||||||
|  |         !(await confirm( | ||||||
|  |           "Do you really want to delete this network?", | ||||||
|  |           `Delete network ${p.net.name}`, | ||||||
|  |           "Delete" | ||||||
|  |         )) | ||||||
|  |       ) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |       await NetworkApi.Delete(p.net); | ||||||
|  |  | ||||||
|  |       navigate("/net"); | ||||||
|  |       snackbar("The network was successfully deleted!"); | ||||||
|  |     } catch (e) { | ||||||
|  |       console.error(e); | ||||||
|  |       alert(`Failed to delete the network!\n${e}`); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Button color="error" onClick={requestDelete}> | ||||||
|  |       Delete the network | ||||||
|  |     </Button> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user