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")}
);