From a1439689dd9ec0a967c27340128a2e25503965c8 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 9 Jun 2025 18:00:23 +0200 Subject: [PATCH] Can resize existing disks --- virtweb_frontend/src/api/VMApi.ts | 1 + .../src/widgets/forms/DiskSizeInput.tsx | 24 ++++ .../src/widgets/forms/VMDisksList.tsx | 130 +++++++++++------- 3 files changed, 109 insertions(+), 46 deletions(-) create mode 100644 virtweb_frontend/src/widgets/forms/DiskSizeInput.tsx diff --git a/virtweb_frontend/src/api/VMApi.ts b/virtweb_frontend/src/api/VMApi.ts index 5df788f..44c4647 100644 --- a/virtweb_frontend/src/api/VMApi.ts +++ b/virtweb_frontend/src/api/VMApi.ts @@ -36,6 +36,7 @@ export interface BaseFileVMDisk { // application attributes new?: boolean; + originalSize?: number; deleteType?: "keepfile" | "deletefile"; } diff --git a/virtweb_frontend/src/widgets/forms/DiskSizeInput.tsx b/virtweb_frontend/src/widgets/forms/DiskSizeInput.tsx new file mode 100644 index 0000000..d9ec6de --- /dev/null +++ b/virtweb_frontend/src/widgets/forms/DiskSizeInput.tsx @@ -0,0 +1,24 @@ +import { ServerApi } from "../../api/ServerApi"; +import { TextInput } from "./TextInput"; + +export function DiskSizeInput(p: { + label?: string; + value: number; + onChange: (size: number) => void; +}): React.ReactElement { + return ( + { + p.onChange?.(Number(v ?? "0") * 1000 * 1000 * 1000); + }} + type="number" + /> + ); +} diff --git a/virtweb_frontend/src/widgets/forms/VMDisksList.tsx b/virtweb_frontend/src/widgets/forms/VMDisksList.tsx index 4b69923..d3c121b 100644 --- a/virtweb_frontend/src/widgets/forms/VMDisksList.tsx +++ b/virtweb_frontend/src/widgets/forms/VMDisksList.tsx @@ -1,5 +1,6 @@ import { mdiHarddiskPlus } from "@mdi/js"; import Icon from "@mdi/react"; +import ExpandIcon from "@mui/icons-material/Expand"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import DeleteIcon from "@mui/icons-material/Delete"; import { Button, IconButton, Paper, Tooltip, Typography } from "@mui/material"; @@ -15,6 +16,7 @@ import { DiskBusSelect } from "./DiskBusSelect"; import { DiskImageSelect } from "./DiskImageSelect"; import { SelectInput } from "./SelectInput"; import { TextInput } from "./TextInput"; +import { DiskSizeInput } from "./DiskSizeInput"; export function VMDisksList(p: { vm: VMInfo; @@ -99,6 +101,19 @@ function DiskInfo(p: { diskImagesList: DiskImage[]; }): React.ReactElement { const confirm = useConfirm(); + + const expandDisk = () => { + if (p.disk.resize === true) { + p.disk.resize = false; + p.disk.size = p.disk.originalSize!; + } else { + p.disk.resize = true; + p.disk.originalSize = p.disk.size!; + } + + p.onChange?.(); + }; + const deleteDisk = async () => { if (p.disk.deleteType) { p.disk.deleteType = undefined; @@ -121,42 +136,74 @@ function DiskInfo(p: { if (!p.editable || !p.disk.new) return ( - - {p.editable && ( - - {p.disk.deleteType ? ( - - - - ) : ( - - - - )} - - )} - - {p.canBackup && ( - + <> + + {p.editable && ( { - p.onRequestBackup(p.disk); - }} + edge="end" + aria-label="expand disk" + onClick={expandDisk} > - + {p.disk.resize === true ? ( + + + + ) : ( + + + + )} - - )} - - } - /> + )} + + {p.editable && ( + + {p.disk.deleteType ? ( + + + + ) : ( + + + + )} + + )} + + {p.canBackup && ( + + { + p.onRequestBackup(p.disk); + }} + > + + + + )} + + } + /> + + {/* New disk size*/} + {p.disk.resize && ( + { + p.disk.size = v; + p.onChange?.(); + }} + /> + )} + ); return ( @@ -233,21 +280,12 @@ function DiskInfo(p: { {/* Disk size */} {(!p.disk.from_image || p.disk.resize === true) && ( - { - p.disk.size = Number(v ?? "0") * 1000 * 1000 * 1000; + { + p.disk.size = v; p.onChange?.(); }} - type="number" /> )}