diff --git a/node_modules b/node_modules index 63cc29c6f..cb783a959 160000 --- a/node_modules +++ b/node_modules @@ -1 +1 @@ -Subproject commit 63cc29c6fb850153aa596880efb51a11bdaccbe2 +Subproject commit cb783a959f1537f89128c73b4ba83ef3dbed59a2 diff --git a/package.json b/package.json index e19b07171..b7b27f713 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "react-dom": "18.2.0", "redux": "4.1.2", "redux-thunk": "2.4.2", + "remarkable": "^2.0.1", "throttle-debounce": "3.0.1", "xterm": "5.1.0", "xterm-addon-canvas": "0.3.0" diff --git a/src/components/vm/vmActions.jsx b/src/components/vm/vmActions.jsx index d0fa03ddc..8d48d2afc 100644 --- a/src/components/vm/vmActions.jsx +++ b/src/components/vm/vmActions.jsx @@ -20,9 +20,13 @@ import cockpit from 'cockpit'; import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { Button } from "@patternfly/react-core/dist/esm/components/Button"; +import { DescriptionList, DescriptionListDescription, DescriptionListGroup, DescriptionListTerm } from "@patternfly/react-core/dist/esm/components/DescriptionList"; import { Dropdown, DropdownItem, DropdownSeparator, KebabToggle } from "@patternfly/react-core/dist/esm/deprecated/components/Dropdown"; +import { Modal } from "@patternfly/react-core/dist/esm/components/Modal"; import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip"; +import { PowerOffIcon, RedoIcon } from '@patternfly/react-icons'; import { useDialogs } from 'dialogs.jsx'; +import { Remarkable } from "remarkable"; import { updateVm } from '../../actions/store-actions.js'; import { @@ -53,6 +57,7 @@ import { domainStart, } from '../../libvirtApi/domain.js'; import store from "../../store.js"; +import { distanceToNow } from 'timeformat.js'; const _ = cockpit.gettext; @@ -166,6 +171,45 @@ const onSendNMI = (vm) => domainSendNMI({ name: vm.name, id: vm.id, connectionNa ); }); +export const ConfirmModal = ({ idPrefix, actionsList, startTime, title, titleIcon }) => { + const Dialogs = useDialogs(); + + const actions = actionsList.map(action => + + ); + actions.push( + + ); + + return ( + + + + {_("Uptime")} + {distanceToNow(new Date(startTime))} + + + + ); +}; + const VmActions = ({ vm, onAddErrorNotification, isDetailsPage }) => { const Dialogs = useDialogs(); const [isActionOpen, setIsActionOpen] = useState(false); @@ -183,6 +227,8 @@ const VmActions = ({ vm, onAddErrorNotification, isDetailsPage }) => { setOperationInProgress(false); } + const remarkable = new Remarkable(); + const id = `${vmId(vm.name)}-${vm.connectionName}`; const state = vm.state; const hasInstallPhase = vm.metadata && vm.metadata.hasInstallPhase; @@ -219,21 +265,73 @@ const VmActions = ({ vm, onAddErrorNotification, isDetailsPage }) => { variant={isDetailsPage ? 'primary' : 'secondary'} isLoading={operationInProgress} isDisabled={operationInProgress} - onClick={() => { setOperationInProgress(true); onShutdown(vm, setOperationInProgress) }} id={`${id}-shutdown-button`}> + id={`${id}-shutdown-button`} + onClick={() => Dialogs.show( + } + titleIcon={PowerOffIcon} + startTime={vm.startTime} + actionsList={[ + { + variant: "primary", + handler: () => { + setOperationInProgress(true); + onShutdown(vm, setOperationInProgress); + }, + name: _("Shut down"), + id: "shutdown", + }, + { + variant: "secondary", + handler: () => { + setOperationInProgress(true); + onReboot(vm); + }, + name: _("Reboot"), + id: "reboot", + }, + ]} /> + )}> {_("Shut down")} ); dropdownItems.push( onShutdown(vm)}> + onClick={() => Dialogs.show( + } + titleIcon={PowerOffIcon} + startTime={vm.startTime} + actionsList={[ + { + variant: "primary", + handler: () => onShutdown(vm), + name: _("Shut down"), + id: "shutdown", + }, + ]} /> + )}> {_("Shut down")} ); dropdownItems.push( onForceoff(vm)}> + onClick={() => Dialogs.show( + } + titleIcon={PowerOffIcon} + startTime={vm.startTime} + actionsList={[ + { + variant: "primary", + handler: () => onForceoff(vm), + name: _("Force shut down"), + id: "forceoff", + }, + ]} /> + )}> {_("Force shut down")} ); @@ -241,7 +339,19 @@ const VmActions = ({ vm, onAddErrorNotification, isDetailsPage }) => { dropdownItems.push( onSendNMI(vm)}> + onClick={() => Dialogs.show( + } + startTime={vm.startTime} + actionsList={[ + { + variant: "primary", + handler: () => onSendNMI(vm), + name: _("Send non-maskable interrupt"), + id: "forceoff", + }, + ]} /> + )}> {_("Send non-maskable interrupt")} ); @@ -252,14 +362,40 @@ const VmActions = ({ vm, onAddErrorNotification, isDetailsPage }) => { dropdownItems.push( onReboot(vm)}> + onClick={() => Dialogs.show( + } + titleIcon={RedoIcon} + startTime={vm.startTime} + actionsList={[ + { + variant: "primary", + handler: onReboot(vm), + name: _("Reboot"), + id: "reboot", + }, + ]} /> + )}> {_("Reboot")} ); dropdownItems.push( onForceReboot(vm)}> + onClick={() => Dialogs.show( + } + startTime={vm.startTime} + titleIcon={RedoIcon} + actionsList={[ + { + variant: "primary", + handler: onForceReboot(vm), + name: _("Force reboot"), + id: "reboot", + }, + ]} /> + )}> {_("Force reboot")} );