Reorganize network tab
This commit is contained in:
parent
0175726696
commit
085deff4f7
@ -2,23 +2,27 @@ import { Grid, Paper, Typography } from "@mui/material";
|
|||||||
import React, { PropsWithChildren } from "react";
|
import React, { PropsWithChildren } from "react";
|
||||||
|
|
||||||
export function EditSection(
|
export function EditSection(
|
||||||
p: { title: string; actions?: React.ReactElement } & PropsWithChildren
|
p: { title?: string; actions?: React.ReactElement } & PropsWithChildren
|
||||||
): React.ReactElement {
|
): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<Grid item sm={12} md={6}>
|
<Grid item sm={12} md={6}>
|
||||||
<Paper style={{ margin: "10px", padding: "10px" }}>
|
<Paper style={{ margin: "10px", padding: "10px" }}>
|
||||||
<span
|
{(p.title || p.actions) && (
|
||||||
style={{
|
<span
|
||||||
display: "flex",
|
style={{
|
||||||
justifyContent: "space-between",
|
display: "flex",
|
||||||
alignItems: "center",
|
justifyContent: "space-between",
|
||||||
}}
|
alignItems: "center",
|
||||||
>
|
}}
|
||||||
<Typography variant="h5" style={{ marginBottom: "15px" }}>
|
>
|
||||||
{p.title}
|
{p.title && (
|
||||||
</Typography>
|
<Typography variant="h5" style={{ marginBottom: "15px" }}>
|
||||||
{p.actions}
|
{p.title}
|
||||||
</span>
|
</Typography>
|
||||||
|
)}
|
||||||
|
{p.actions}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{p.children}
|
{p.children}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -4,6 +4,7 @@ import DeleteIcon from "@mui/icons-material/Delete";
|
|||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
|
Grid,
|
||||||
IconButton,
|
IconButton,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
@ -19,6 +20,7 @@ import { randomMacAddress } from "../../utils/RandUtils";
|
|||||||
import { MACInput } from "./MACInput";
|
import { MACInput } from "./MACInput";
|
||||||
import { SelectInput } from "./SelectInput";
|
import { SelectInput } from "./SelectInput";
|
||||||
import { VMNetworkFilterParameters } from "./VMNetworkFilterParameters";
|
import { VMNetworkFilterParameters } from "./VMNetworkFilterParameters";
|
||||||
|
import { EditSection } from "./EditSection";
|
||||||
|
|
||||||
export function VMNetworksList(p: {
|
export function VMNetworksList(p: {
|
||||||
vm: VMInfo;
|
vm: VMInfo;
|
||||||
@ -37,22 +39,28 @@ export function VMNetworksList(p: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* networks list */}
|
|
||||||
{p.vm.networks.map((n, num) => (
|
|
||||||
<NetworkInfoWidget
|
|
||||||
key={num}
|
|
||||||
network={n}
|
|
||||||
removeFromList={() => {
|
|
||||||
p.vm.networks.splice(num, 1);
|
|
||||||
p.onChange?.();
|
|
||||||
}}
|
|
||||||
{...p}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{p.editable && (
|
{p.editable && (
|
||||||
<Button onClick={addNew}>Add a new network interface</Button>
|
<div style={{ textAlign: "right", marginTop: "5px" }}>
|
||||||
|
<Button onClick={addNew}>Add a new network interface</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{/* networks list */}
|
||||||
|
{p.vm.networks.map((n, num) => (
|
||||||
|
<EditSection key={num}>
|
||||||
|
<NetworkInfoWidget
|
||||||
|
key={num}
|
||||||
|
network={n}
|
||||||
|
removeFromList={() => {
|
||||||
|
p.vm.networks.splice(num, 1);
|
||||||
|
p.onChange?.();
|
||||||
|
}}
|
||||||
|
{...p}
|
||||||
|
/>
|
||||||
|
</EditSection>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -141,64 +149,66 @@ function NetworkInfoWidget(p: {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{p.network.type === "DefinedNetwork" && (
|
{p.network.type === "DefinedNetwork" && (
|
||||||
<SelectInput
|
<>
|
||||||
editable={p.editable}
|
<SelectInput
|
||||||
label="Defined network"
|
editable={p.editable}
|
||||||
options={p.networksList.map((n) => {
|
label="Defined network"
|
||||||
const chars = [n.forward_mode.toString()];
|
options={p.networksList.map((n) => {
|
||||||
if (n.ip_v4) chars.push("IPv4");
|
const chars = [n.forward_mode.toString()];
|
||||||
if (n.ip_v6) chars.push("IPv6");
|
if (n.ip_v4) chars.push("IPv4");
|
||||||
if (n.description) chars.push(n.description);
|
if (n.ip_v6) chars.push("IPv6");
|
||||||
|
if (n.description) chars.push(n.description);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: n.name,
|
label: n.name,
|
||||||
value: n.name,
|
value: n.name,
|
||||||
description: chars.join(" - "),
|
description: chars.join(" - "),
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
value={p.network.network}
|
value={p.network.network}
|
||||||
onValueChange={(v) => {
|
onValueChange={(v) => {
|
||||||
if (p.network.type === "DefinedNetwork")
|
if (p.network.type === "DefinedNetwork")
|
||||||
p.network.network = v as any;
|
p.network.network = v as any;
|
||||||
p.onChange?.();
|
p.onChange?.();
|
||||||
}}
|
}}
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Network Filter */}
|
|
||||||
<SelectInput
|
|
||||||
editable={p.editable}
|
|
||||||
label="Network filter"
|
|
||||||
value={p.network.nwfilterref?.name}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
if (v && !p.network.nwfilterref) {
|
|
||||||
p.network.nwfilterref = { name: v, parameters: [] };
|
|
||||||
} else if (v) {
|
|
||||||
p.network.nwfilterref!.name = v;
|
|
||||||
} else {
|
|
||||||
p.network.nwfilterref = undefined;
|
|
||||||
}
|
|
||||||
p.onChange?.();
|
|
||||||
}}
|
|
||||||
options={[
|
|
||||||
{ label: "No network filer", value: undefined },
|
|
||||||
...p.networkFiltersList.map((v) => {
|
|
||||||
return {
|
|
||||||
value: v.name,
|
|
||||||
label: `${v.name} (${v.chain?.protocol ?? "unspecified"})`,
|
|
||||||
description: `${v.rules.length} rules - ${v.join_filters.length} joint filters`,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{p.network.nwfilterref && (
|
|
||||||
<div style={{ margin: "10px" }}>
|
|
||||||
<VMNetworkFilterParameters
|
|
||||||
filterref={p.network.nwfilterref}
|
|
||||||
{...p}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
{/* Network Filter */}
|
||||||
|
<SelectInput
|
||||||
|
editable={p.editable}
|
||||||
|
label="Network filter"
|
||||||
|
value={p.network.nwfilterref?.name}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
if (v && !p.network.nwfilterref) {
|
||||||
|
p.network.nwfilterref = { name: v, parameters: [] };
|
||||||
|
} else if (v) {
|
||||||
|
p.network.nwfilterref!.name = v;
|
||||||
|
} else {
|
||||||
|
p.network.nwfilterref = undefined;
|
||||||
|
}
|
||||||
|
p.onChange?.();
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{ label: "No network filer", value: undefined },
|
||||||
|
...p.networkFiltersList.map((v) => {
|
||||||
|
return {
|
||||||
|
value: v.name,
|
||||||
|
label: `${v.name} (${v.chain?.protocol ?? "unspecified"})`,
|
||||||
|
description: `${v.rules.length} rules - ${v.join_filters.length} joint filters`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{p.network.nwfilterref && (
|
||||||
|
<div style={{ margin: "10px" }}>
|
||||||
|
<VMNetworkFilterParameters
|
||||||
|
filterref={p.network.nwfilterref}
|
||||||
|
{...p}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -288,14 +288,7 @@ function VMDetailsTabStorage(p: DetailsInnerProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function VMDetailsTabNetwork(p: DetailsInnerProps): React.ReactElement {
|
function VMDetailsTabNetwork(p: DetailsInnerProps): React.ReactElement {
|
||||||
return (
|
return <VMNetworksList {...p} />;
|
||||||
<Grid container spacing={2}>
|
|
||||||
{/* Networks section */}
|
|
||||||
<EditSection title="Networks">
|
|
||||||
<VMNetworksList {...p} />
|
|
||||||
</EditSection>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function VMDetailsTabDanger(p: DetailsInnerProps): React.ReactElement {
|
function VMDetailsTabDanger(p: DetailsInnerProps): React.ReactElement {
|
||||||
|
Loading…
Reference in New Issue
Block a user