diff --git a/src/components/vm/vmActions.jsx b/src/components/vm/vmActions.jsx index dd731df30..b60360795 100644 --- a/src/components/vm/vmActions.jsx +++ b/src/components/vm/vmActions.jsx @@ -38,6 +38,7 @@ import { ConfirmDialog } from './confirmDialog.jsx'; import { DeleteDialog } from "./deleteDialog.jsx"; import { MigrateDialog } from './vmMigrateDialog.jsx'; import { RenameDialog } from './vmRenameDialog.jsx'; +import { EditDescriptionDialog } from './vmEditDescriptionDialog.jsx'; import { ReplaceSpiceDialog } from './vmReplaceSpiceDialog.jsx'; import { domainCanDelete, @@ -451,9 +452,21 @@ const VmActions = ({ vm, vms, onAddErrorNotification, isDetailsPage }) => { {_("Rename")} ); - dropdownItems.push(); } + if (isDetailsPage) { + dropdownItems.push( + Dialogs.show()}> + {_("Edit description")} + + ); + } + + if (domainCanRename(state) || isDetailsPage) + dropdownItems.push(); + if (state !== undefined && domainCanDelete(state, vm.id)) { if (!vm.persistent) { dropdownItems.push( diff --git a/src/components/vm/vmDetailsPage.jsx b/src/components/vm/vmDetailsPage.jsx index 98fbfeb12..5e4ef55e3 100644 --- a/src/components/vm/vmDetailsPage.jsx +++ b/src/components/vm/vmDetailsPage.jsx @@ -75,6 +75,12 @@ export const VmDetailsPage = ({ + { + vm.inactiveXML.description && +
+ {vm.inactiveXML.description.split("\n").map((p, i) =>

{p}

)} +
+ } ); diff --git a/src/components/vm/vmEditDescriptionDialog.jsx b/src/components/vm/vmEditDescriptionDialog.jsx new file mode 100644 index 000000000..c22966f5d --- /dev/null +++ b/src/components/vm/vmEditDescriptionDialog.jsx @@ -0,0 +1,82 @@ +/* + * This file is part of Cockpit. + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Cockpit is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * Cockpit is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Cockpit; If not, see . + */ + +import cockpit from 'cockpit'; +import React, { useState } from 'react'; +import { Button } from "@patternfly/react-core/dist/esm/components/Button"; +import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form"; +import { Modal } from "@patternfly/react-core/dist/esm/components/Modal"; +import { TextArea } from "@patternfly/react-core/dist/esm/components/TextArea"; + +import { ModalError } from 'cockpit-components-inline-notification.jsx'; +import { useDialogs } from 'dialogs.jsx'; + +import { isObjectEmpty } from '../../helpers.js'; +import { domainSetDescription } from '../../libvirtApi/domain.js'; + +const _ = cockpit.gettext; + +export const EditDescriptionDialog = ({ vm }) => { + const Dialogs = useDialogs(); + const [description, setDescription] = useState(vm.inactiveXML.description || ""); + const [error, dialogErrorSet] = useState({}); + + async function onSubmit() { + try { + await domainSetDescription(vm, description); + Dialogs.close(); + } catch (exc) { + dialogErrorSet({ + dialogError: cockpit.format(_("Failed to set description of VM $0"), vm.name), + dialogErrorDetail: exc.message + }); + } + } + + return ( + + + + + }> +
{ + e.preventDefault(); + onSubmit(); + }} + isHorizontal> + {!isObjectEmpty(error) && } + +