Improve VM screen

This commit is contained in:
Pierre HUBERT 2024-01-02 19:52:59 +01:00
parent d4ef389852
commit 767d2015df
2 changed files with 111 additions and 50 deletions

View File

@ -1,4 +1,3 @@
import DeleteIcon from "@mui/icons-material/Delete";
import VisibilityIcon from "@mui/icons-material/Visibility"; import VisibilityIcon from "@mui/icons-material/Visibility";
import { import {
Button, Button,
@ -14,15 +13,12 @@ import {
} from "@mui/material"; } from "@mui/material";
import { filesize } from "filesize"; import { filesize } from "filesize";
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom";
import { VMApi, VMInfo } from "../api/VMApi"; import { VMApi, VMInfo } from "../api/VMApi";
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 { VMStatusWidget } from "../widgets/vms/VMStatusWidget"; import { VMStatusWidget } from "../widgets/vms/VMStatusWidget";
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
import { useNavigate } from "react-router-dom";
import { useAlert } from "../hooks/providers/AlertDialogProvider";
export function VMListRoute(): React.ReactElement { export function VMListRoute(): React.ReactElement {
const [list, setList] = React.useState<VMInfo[] | undefined>(); const [list, setList] = React.useState<VMInfo[] | undefined>();
@ -66,39 +62,8 @@ function VMListWidget(p: {
list: VMInfo[]; list: VMInfo[];
onReload: () => void; onReload: () => void;
}): React.ReactElement { }): React.ReactElement {
const confirm = useConfirm();
const alert = useAlert();
const snackbar = useSnackbar();
const navigate = useNavigate(); const navigate = useNavigate();
const deleteVM = async (v: VMInfo) => {
try {
if (
!(await confirm(
`Do you really want to delete the vm ${v.name}? The operation CANNOT be undone!`,
"Delete a VM",
"DELETE"
))
)
return;
const keepData = !(await confirm(
"Do you want to delete the files of the VM?",
"Delete a VM",
"Delete the data",
"keep the data"
));
await VMApi.Delete(v, keepData);
snackbar("The VM was successfully deleted!");
p.onReload();
} catch (e) {
console.error(e);
alert(`Failed to delete VM!\n${e}`);
}
};
return ( return (
<TableContainer component={Paper}> <TableContainer component={Paper}>
<Table> <Table>
@ -135,11 +100,6 @@ function VMListWidget(p: {
</IconButton> </IconButton>
</RouterLink> </RouterLink>
</Tooltip> </Tooltip>
<Tooltip title="Delete this VM">
<IconButton onClick={() => deleteVM(row)}>
<DeleteIcon />
</IconButton>
</Tooltip>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}

View File

@ -1,4 +1,4 @@
import { Grid } from "@mui/material"; import { Box, Button, Grid, Tab, Tabs } from "@mui/material";
import React from "react"; import React from "react";
import { validate as validateUUID } from "uuid"; import { validate as validateUUID } from "uuid";
import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi"; import { IsoFile, IsoFilesApi } from "../../api/IsoFilesApi";
@ -16,6 +16,10 @@ import { ResAutostartInput } from "../forms/ResAutostartInput";
import { VMNetworksList } from "../forms/VMNetworksList"; import { VMNetworksList } from "../forms/VMNetworksList";
import { NetworkApi, NetworkInfo } from "../../api/NetworksApi"; import { NetworkApi, NetworkInfo } from "../../api/NetworksApi";
import { NWFilterApi, NWFilter } from "../../api/NWFilterApi"; import { NWFilterApi, NWFilter } from "../../api/NWFilterApi";
import { useNavigate } from "react-router-dom";
import { useAlert } from "../../hooks/providers/AlertDialogProvider";
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
interface DetailsProps { interface DetailsProps {
vm: VMInfo; vm: VMInfo;
@ -59,14 +63,49 @@ export function VMDetails(p: DetailsProps): React.ReactElement {
); );
} }
function VMDetailsInner( enum VMTab {
p: DetailsProps & { General = 0,
Storage,
Network,
Danger,
}
type DetailsInnerProps = DetailsProps & {
isoList: IsoFile[]; isoList: IsoFile[];
vcpuCombinations: number[]; vcpuCombinations: number[];
networksList: NetworkInfo[]; networksList: NetworkInfo[];
networkFiltersList: NWFilter[]; networkFiltersList: NWFilter[];
} };
): React.ReactElement {
function VMDetailsInner(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="Storage" tabIndex={VMTab.Storage} />
<Tab label="Network" tabIndex={VMTab.Network} />
{!p.editable && (
<Tab
label="Danger zone"
style={{ color: "red" }}
tabIndex={VMTab.Danger}
/>
)}
</Tabs>
</Box>
{currTab === VMTab.General && <VMDetailsTabGeneral {...p} />}
{currTab === VMTab.Storage && <VMDetailsTabStorage {...p} />}
{currTab === VMTab.Network && <VMDetailsTabNetwork {...p} />}
{currTab === VMTab.Danger && <VMDetailsTabDanger {...p} />}
</>
);
}
function VMDetailsTabGeneral(p: DetailsInnerProps): React.ReactElement {
return ( return (
<Grid container spacing={2}> <Grid container spacing={2}>
{ {
@ -215,7 +254,13 @@ function VMDetailsInner(
/> />
)} )}
</EditSection> </EditSection>
</Grid>
);
}
function VMDetailsTabStorage(p: DetailsInnerProps): React.ReactElement {
return (
<Grid container spacing={2}>
{/* Storage section */} {/* Storage section */}
<EditSection title="Storage"> <EditSection title="Storage">
<VMSelectIsoInput <VMSelectIsoInput
@ -229,7 +274,13 @@ function VMDetailsInner(
/> />
<VMDisksList {...p} /> <VMDisksList {...p} />
</EditSection> </EditSection>
</Grid>
);
}
function VMDetailsTabNetwork(p: DetailsInnerProps): React.ReactElement {
return (
<Grid container spacing={2}>
{/* Networks section */} {/* Networks section */}
<EditSection title="Networks"> <EditSection title="Networks">
<VMNetworksList {...p} /> <VMNetworksList {...p} />
@ -237,3 +288,53 @@ function VMDetailsInner(
</Grid> </Grid>
); );
} }
function VMDetailsTabDanger(p: DetailsInnerProps): React.ReactElement {
const confirm = useConfirm();
const alert = useAlert();
const snackbar = useSnackbar();
const navigate = useNavigate();
const deleteVM = async () => {
try {
if (
!(await confirm(
`Do you really want to delete the vm ${p.vm.name}? The operation CANNOT be undone!`,
"Delete a VM",
"DELETE"
))
)
return;
const keepData = !(await confirm(
"Do you want to delete the files of the VM?",
"Delete a VM",
"Delete the data",
"keep the data"
));
if (
!(await confirm(
`[LAST CALL] Do you really want to procede with removal? Again, the operation CANNOT be undone!`,
"Delete a VM",
"DELETE"
))
)
return;
await VMApi.Delete(p.vm, keepData);
snackbar("The VM was successfully deleted!");
navigate("/vms");
} catch (e) {
console.error(e);
alert(`Failed to delete VM!\n${e}`);
}
};
return (
<Button color="error" onClick={deleteVM}>
Delete the VM
</Button>
);
}