Skip to content

Commit

Permalink
Merge pull request #2392 from upalatucci/fix-instancetype-messages
Browse files Browse the repository at this point in the history
CNV-53272:Refactor instancetype vm creation create button
  • Loading branch information
openshift-merge-bot[bot] authored Jan 28, 2025
2 parents 791e5db + 866af2a commit 0076da8
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 122 deletions.
6 changes: 5 additions & 1 deletion locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"5 min": "5 min",
"8 min": "8 min",
"8xlarge": "8xlarge",
"A boot volume must be selected": "A boot volume must be selected",
"A ControllerRevision resource is cloned from the InstanceType when creating the VirtualMachine": "A ControllerRevision resource is cloned from the InstanceType when creating the VirtualMachine",
"A ControllerRevision resource is cloned from the Preference when creating the VirtualMachine": "A ControllerRevision resource is cloned from the Preference when creating the VirtualMachine",
"A list of matching Nodes will be provided on label input below.": "A list of matching Nodes will be provided on label input below.",
Expand Down Expand Up @@ -135,6 +136,7 @@
"An error occurred": "An error occurred",
"An error occurred during the cloning process": "An error occurred during the cloning process",
"An error occurred during the upload process": "An error occurred during the upload process",
"An InstanceType must be selected": "An InstanceType must be selected",
"An OpenShift project is an alternative representation of a Kubernetes namespace.": "An OpenShift project is an alternative representation of a Kubernetes namespace.",
"and copy the download link URL": "and copy the download link URL",
"and copy the download link URL for the cloud base image": "and copy the download link URL for the cloud base image",
Expand All @@ -157,7 +159,7 @@
"Are you sure you want to stop the VirtualMachine workloads?": "Are you sure you want to stop the VirtualMachine workloads?",
"As a default, the VirtualMachine CPU uses sockets to enable hotplug. You can also define the topology manually": "As a default, the VirtualMachine CPU uses sockets to enable hotplug. You can also define the topology manually",
"As new versions of a DataSource become available older versions will be replaced": "As new versions of a DataSource become available older versions will be replaced",
"Ask your cluster administrator for access permissions.": "Ask your cluster administrator for access permissions.",
"Ask your cluster administrator for access permissions or select a different project (current project: {{currentNamespace}})": "Ask your cluster administrator for access permissions or select a different project (current project: {{currentNamespace}})",
"Assigns an external IP address to the VirtualMachine. This option requires a LoadBalancer Service backend": "Assigns an external IP address to the VirtualMachine. This option requires a LoadBalancer Service backend",
"Attach a virtual function network device to the VirtualMachine for high performance": "Attach a virtual function network device to the VirtualMachine for high performance",
"Attach existing sysprep": "Attach existing sysprep",
Expand Down Expand Up @@ -1438,6 +1440,8 @@
"VirtualMachine live migration": "VirtualMachine live migration",
"VirtualMachine name": "VirtualMachine name",
"VirtualMachine name can not be empty.": "VirtualMachine name can not be empty.",
"VirtualMachine name field is mandatory": "VirtualMachine name field is mandatory",
"VirtualMachine name not valid": "VirtualMachine name not valid",
"VirtualMachine statuses": "VirtualMachine statuses",
"VirtualMachine storage": "VirtualMachine storage",
"VirtualMachine Templates": "VirtualMachine Templates",
Expand Down
27 changes: 27 additions & 0 deletions src/utils/components/HidableTooltip/HidableTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { FC, ReactNode } from 'react';

import { Tooltip, TooltipPosition } from '@patternfly/react-core';

type HidableTooltipProps = {
children: ReactNode;
content: ReactNode;
hidden: boolean;
position?: TooltipPosition;
};

const HidableTooltip: FC<HidableTooltipProps> = ({
children,
content,
hidden,
position = TooltipPosition.right,
}) => {
return hidden ? (
<>{children}</>
) : (
<Tooltip content={content} position={position}>
<>{children}</>
</Tooltip>
);
};

export default HidableTooltip;
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { FC, ReactElement, ReactNode } from 'react';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Tooltip, TooltipPosition } from '@patternfly/react-core';
import { TooltipPosition } from '@patternfly/react-core';

import HidableTooltip from '../HidableTooltip/HidableTooltip';

type WithPermissionTooltipProps = {
allowed: boolean;
Expand All @@ -16,15 +18,14 @@ const WithPermissionTooltip: FC<WithPermissionTooltipProps> = ({
}): ReactElement => {
const { t } = useKubevirtTranslation();

return allowed ? (
<>{children}</>
) : (
<Tooltip
return (
<HidableTooltip
content={title || t(`You don't have permission to perform this action`)}
hidden={allowed}
position={TooltipPosition.right}
>
<>{children}</>
</Tooltip>
</HidableTooltip>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useCallback, useMemo, useState } from 'react';
import React, { FC, useState } from 'react';
import { useNavigate } from 'react-router-dom-v5-compat';

import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
Expand All @@ -18,12 +18,10 @@ import {
generateNewSysprepConfig,
UNATTEND,
} from '@kubevirt-utils/components/SysprepModal/sysprep-utils';
import { isValidVMName } from '@kubevirt-utils/components/VMNameValidationHelperText/utils/utils';
import { DEFAULT_NAMESPACE } from '@kubevirt-utils/constants/constants';
import { VirtualMachineDetailsTab } from '@kubevirt-utils/constants/tabs-constants';
import { logITFlowEvent } from '@kubevirt-utils/extensions/telemetry/telemetry';
import {
CANCEL_CREATE_VM_BUTTON_CLICKED,
CREATE_VM_BUTTON_CLICKED,
CREATE_VM_FAILED,
CREATE_VM_SUCCEEDED,
Expand All @@ -40,21 +38,13 @@ import useNamespaceUDN from '@kubevirt-utils/resources/udn/hooks/useNamespaceUDN
import { vmSignal } from '@kubevirt-utils/store/customizeInstanceType';
import { createHeadlessService } from '@kubevirt-utils/utils/headless-service';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { k8sCreate, K8sVerb, useAccessReview } from '@openshift-console/dynamic-plugin-sdk';
import { k8sCreate } from '@openshift-console/dynamic-plugin-sdk';
import { useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk';
import {
Button,
ButtonVariant,
Checkbox,
Split,
SplitItem,
Stack,
StackItem,
Tooltip,
} from '@patternfly/react-core';
import { Checkbox, Stack, StackItem } from '@patternfly/react-core';

import { AUTOMATIC_UPDATE_FEATURE_NAME } from '../../../../clusteroverview/SettingsTab/ClusterTab/components/GuestManagmentSection/AutomaticSubscriptionRHELGuests/utils/constants';

import ActionButtons from './components/ActionButtons/ActionButtons';
import YamlAndCLIViewerModal from './components/YamlAndCLIViewerModal/YamlAndCLIViewerModal';

import './CreateVMFooter.scss';
Expand All @@ -78,33 +68,10 @@ const CreateVMFooter: FC = () => {

const { instanceTypeVMState, setStartVM, setVM, startVM, vmNamespaceTarget } =
useInstanceTypeVMStore();
const {
selectedBootableVolume,
selectedInstanceType,
sshSecretCredentials,
sysprepConfigMapData,
vmName,
} = instanceTypeVMState;
const { sshSecretCredentials, sysprepConfigMapData, vmName } = instanceTypeVMState;
const { applyKeyToProject, secretOption, sshPubKey, sshSecretName } = sshSecretCredentials || {};
const isWindowsOSVolume = useIsWindowsBootableVolume();

const onCancel = useCallback(() => {
logITFlowEvent(CANCEL_CREATE_VM_BUTTON_CLICKED, null, { vmName: vmName });
navigate(getResourceUrl({ activeNamespace, model: VirtualMachineModel }));
}, [activeNamespace, navigate, vmName]);

const [canCreateVM] = useAccessReview({
group: VirtualMachineModel.apiGroup,
namespace: vmNamespaceTarget,
resource: VirtualMachineModel.plural,
verb: 'create' as K8sVerb,
});

const hasNameAndInstanceType = useMemo(
() => !isEmpty(vmName) && !isEmpty(selectedInstanceType),
[vmName, selectedInstanceType],
);

const handleSubmit = async () => {
setIsSubmitting(true);
setError(null);
Expand Down Expand Up @@ -204,15 +171,6 @@ const CreateVMFooter: FC = () => {
}
};

const isValidVmName = isValidVMName(vmName);

const isDisabled =
isSubmitting ||
isEmpty(selectedBootableVolume) ||
!canCreateVM ||
!hasNameAndInstanceType ||
!isValidVmName;

return (
<footer className="create-vm-instance-type-footer">
<Stack hasGutter>
Expand All @@ -230,74 +188,27 @@ const CreateVMFooter: FC = () => {
/>
</StackItem>
<StackItem>
<Split hasGutter>
<SplitItem>
<Tooltip
content={
<Stack className="cpu-helper-text__body-content">
{t('Ask your cluster administrator for access permissions.')}
</Stack>
}
hidden={!isDisabled}
>
<Button
isAriaDisabled={isDisabled}
isLoading={isSubmitting}
onClick={handleSubmit}
variant={ButtonVariant.primary}
>
{t('Create VirtualMachine')}
</Button>
</Tooltip>
</SplitItem>
<SplitItem>
<Button
isDisabled={
isSubmitting ||
isEmpty(selectedBootableVolume) ||
!canCreateVM ||
!hasNameAndInstanceType ||
!isValidVmName
}
isLoading={isSubmitting}
onClick={handleCustomize}
variant={ButtonVariant.secondary}
>
{t('Customize VirtualMachine')}
</Button>
</SplitItem>

<SplitItem>
<Button onClick={onCancel} variant={ButtonVariant.link}>
{t('Cancel')}
</Button>
</SplitItem>
<SplitItem isFilled />
<SplitItem>
<Button
onClick={() => {
logITFlowEvent(VIEW_YAML_AND_CLI_CLICKED, null, { vmName: vmName });
createModal((props) => (
<YamlAndCLIViewerModal
vm={generateVM({
autoUpdateEnabled,
instanceTypeState: instanceTypeVMState,
isUDNManagedNamespace,
startVM,
subscriptionData,
targetNamespace: vmNamespaceTarget,
})}
{...props}
/>
));
}}
isDisabled={isEmpty(selectedBootableVolume) || !hasNameAndInstanceType}
variant={ButtonVariant.secondary}
>
{t('View YAML & CLI')}
</Button>
</SplitItem>
</Split>
<ActionButtons
onViewYAML={() => {
logITFlowEvent(VIEW_YAML_AND_CLI_CLICKED, null, { vmName: vmName });
createModal((props) => (
<YamlAndCLIViewerModal
vm={generateVM({
autoUpdateEnabled,
instanceTypeState: instanceTypeVMState,
isUDNManagedNamespace,
startVM,
subscriptionData,
targetNamespace: vmNamespaceTarget,
})}
{...props}
/>
));
}}
isSubmitting={isSubmitting}
onCreate={handleSubmit}
onCustomize={handleCustomize}
/>
</StackItem>
</Stack>
</footer>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { FC, useCallback } from 'react';
import { useNavigate } from 'react-router-dom-v5-compat';
import { VirtualMachineModel } from 'src/views/dashboard-extensions/utils';

import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
import HidableTooltip from '@kubevirt-utils/components/HidableTooltip/HidableTooltip';
import { logITFlowEvent } from '@kubevirt-utils/extensions/telemetry/telemetry';
import { CANCEL_CREATE_VM_BUTTON_CLICKED } from '@kubevirt-utils/extensions/telemetry/utils/constants';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { getResourceUrl } from '@kubevirt-utils/resources/shared';
import { useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk';
import {
Button,
ButtonVariant,
Split,
SplitItem,
Stack,
TooltipPosition,
} from '@patternfly/react-core';

import useStatusActionButtons from './useStatusActionButtons';

type ActionButtonsProps = {
isSubmitting?: boolean;
onCreate: () => void;
onCustomize: () => void;
onViewYAML: () => void;
};

const ActionButtons: FC<ActionButtonsProps> = ({
isSubmitting = false,
onCreate,
onCustomize,
onViewYAML,
}) => {
const navigate = useNavigate();

const [activeNamespace] = useActiveNamespace();

const { t } = useKubevirtTranslation();

const {
instanceTypeVMState: { vmName },
} = useInstanceTypeVMStore();

const onCancel = useCallback(() => {
logITFlowEvent(CANCEL_CREATE_VM_BUTTON_CLICKED, null, { vmName: vmName });
navigate(getResourceUrl({ activeNamespace, model: VirtualMachineModel }));
}, [activeNamespace, navigate, vmName]);

const { disableButtonTooltipContent, isCreationDisabled, isViewYAMLDisabled } =
useStatusActionButtons(isSubmitting);

return (
<Split hasGutter>
<SplitItem>
<HidableTooltip
content={<Stack>{disableButtonTooltipContent}</Stack>}
hidden={!isCreationDisabled}
position={TooltipPosition.top}
>
<Button
isAriaDisabled={isCreationDisabled}
isLoading={isSubmitting}
onClick={onCreate}
variant={ButtonVariant.primary}
>
{t('Create VirtualMachine')}
</Button>
</HidableTooltip>
</SplitItem>
<SplitItem>
<HidableTooltip
content={<Stack>{disableButtonTooltipContent}</Stack>}
hidden={!isCreationDisabled}
position={TooltipPosition.top}
>
<Button
isDisabled={isCreationDisabled}
isLoading={isSubmitting}
onClick={onCustomize}
variant={ButtonVariant.secondary}
>
{t('Customize VirtualMachine')}
</Button>
</HidableTooltip>
</SplitItem>

<SplitItem>
<Button onClick={onCancel} variant={ButtonVariant.link}>
{t('Cancel')}
</Button>
</SplitItem>
<SplitItem isFilled />
<SplitItem>
<Button
isDisabled={isViewYAMLDisabled}
onClick={onViewYAML}
variant={ButtonVariant.secondary}
>
{t('View YAML & CLI')}
</Button>
</SplitItem>
</Split>
);
};

export default ActionButtons;
Loading

0 comments on commit 0076da8

Please sign in to comment.