Files
VirtWeb/virtweb_frontend/src/widgets/forms/VMDisksList.tsx
Pierre HUBERT 3bf8859ff9
All checks were successful
continuous-integration/drone/push Build is passing
WIP ESLint
2025-03-28 12:12:11 +01:00

183 lines
4.6 KiB
TypeScript

import { mdiHarddisk } from "@mdi/js";
import Icon from "@mdi/react";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import DeleteIcon from "@mui/icons-material/Delete";
import {
Avatar,
Button,
IconButton,
ListItem,
ListItemAvatar,
ListItemText,
Paper,
Tooltip,
} from "@mui/material";
import { filesize } from "filesize";
import { ServerApi } from "../../api/ServerApi";
import { VMDisk, VMInfo } from "../../api/VMApi";
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
import { SelectInput } from "./SelectInput";
import { TextInput } from "./TextInput";
export function VMDisksList(p: {
vm: VMInfo;
onChange?: () => void;
editable: boolean;
}): React.ReactElement {
const addNewDisk = () => {
p.vm.disks.push({
alloc_type: "Sparse",
size: 10000,
delete: false,
name: `disk${p.vm.disks.length}`,
new: true,
});
p.onChange?.();
};
return (
<>
{/* disks list */}
{p.vm.disks.map((d, num) => (
<DiskInfo
// eslint-disable-next-line react-x/no-array-index-key
key={num}
editable={p.editable}
disk={d}
onChange={p.onChange}
removeFromList={() => {
p.vm.disks.splice(num, 1);
p.onChange?.();
}}
/>
))}
{p.editable && <Button onClick={addNewDisk}>Add new disk</Button>}
</>
);
}
function DiskInfo(p: {
editable: boolean;
disk: VMDisk;
onChange?: () => void;
removeFromList: () => void;
}): React.ReactElement {
const confirm = useConfirm();
const deleteDisk = async () => {
if (p.disk.deleteType) {
p.disk.deleteType = undefined;
p.onChange?.();
return;
}
const keepFile = await confirm(
`You asked to delete the disk ${p.disk.name}. Do you want to keep the block file or not ? `,
"Delete disk",
"Keep the file",
"Delete the file"
);
if (!(await confirm("Do you really want to delete this disk?"))) return;
p.disk.deleteType = keepFile ? "keepfile" : "deletefile";
p.onChange?.();
};
if (!p.editable || !p.disk.new)
return (
<ListItem
secondaryAction={
p.editable && (
<IconButton
edge="end"
aria-label="delete disk"
onClick={deleteDisk}
>
{p.disk.deleteType ? (
<Tooltip title="Cancel disk removal">
<CheckCircleIcon />
</Tooltip>
) : (
<Tooltip title="Remove disk">
<DeleteIcon />
</Tooltip>
)}
</IconButton>
)
}
>
<ListItemAvatar>
<Avatar>
<Icon path={mdiHarddisk} />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={
<>
{p.disk.name}{" "}
{p.disk.deleteType && (
<span style={{ color: "red" }}>
{p.disk.deleteType === "deletefile"
? "Remove, DELETING block file"
: "Remove, keeping block file"}
</span>
)}
</>
}
secondary={`${filesize(p.disk.size * 1000 * 1000)} - ${
p.disk.alloc_type
}`}
/>
</ListItem>
);
return (
<Paper elevation={3} style={{ margin: "10px", padding: "10px" }}>
<TextInput
editable={true}
label="Disk name"
size={ServerApi.Config.constraints.disk_name_size}
checkValue={(v) => /^[a-zA-Z0-9]+$/.test(v)}
value={p.disk.name}
onValueChange={(v) => {
p.disk.name = v ?? "";
p.onChange?.();
}}
/>
<TextInput
editable={true}
label="Disk size (MB)"
size={ServerApi.Config.constraints.disk_size}
value={p.disk.size.toString()}
onValueChange={(v) => {
p.disk.size = Number(v ?? "0");
p.onChange?.();
}}
type="number"
/>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<SelectInput
editable={true}
label="File allocation type"
options={[
{ label: "Sparse allocation", value: "Sparse" },
{ label: "Fixed allocation", value: "Fixed" },
]}
value={p.disk.alloc_type}
onValueChange={(v) => {
p.disk.alloc_type = v as any;
p.onChange?.();
}}
/>
<IconButton onClick={p.removeFromList}>
<DeleteIcon />
</IconButton>
</div>
</Paper>
);
}