Reorganize networks page

This commit is contained in:
Pierre HUBERT 2024-01-02 23:40:38 +01:00
parent 085deff4f7
commit afe5db1fcd
2 changed files with 93 additions and 47 deletions

View File

@ -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>
); );

View File

@ -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>
);
}