debian-image-builder-frontend/src/Components/Blueprints/BuildImagesButton.tsx
regexowl e6b030ea4c src: Remove ouia-ids
Since we're moving away from IQE the `ouia-id` atributes should be no longer needed. This removes them from the entire code base.
2025-04-25 11:32:43 +02:00

204 lines
6.2 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import {
Dropdown,
MenuToggle,
Menu,
MenuContent,
MenuList,
MenuItem,
Flex,
FlexItem,
Spinner,
MenuToggleAction,
ButtonProps,
Button,
} from '@patternfly/react-core';
import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/MenuToggle/MenuToggle';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
import { ChromeUser } from '@redhat-cloud-services/types';
import { skipToken } from '@reduxjs/toolkit/query';
import { AMPLITUDE_MODULE_NAME, targetOptions } from '../../constants';
import {
useGetBlueprintQuery,
useComposeBlueprintMutation,
} from '../../store/backendApi';
import { selectSelectedBlueprintId } from '../../store/BlueprintSlice';
import { useAppDispatch, useAppSelector } from '../../store/hooks';
import { ImageTypes } from '../../store/imageBuilderApi';
type BuildImagesButtonPropTypes = {
// default children is 'Build images'
children?: React.ReactNode;
};
export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => {
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const [deselectedTargets, setDeselectedTargets] = useState<ImageTypes[]>([]);
const [buildBlueprint, { isLoading: imageBuildLoading }] =
useComposeBlueprintMutation();
const dispatch = useAppDispatch();
const { analytics, auth } = useChrome();
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const onBuildHandler = async () => {
if (selectedBlueprintId) {
try {
await buildBlueprint({
id: selectedBlueprintId,
body: {
image_types: blueprintImageType?.filter(
(target) => !deselectedTargets.includes(target)
),
},
});
analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Requested`, {
module: AMPLITUDE_MODULE_NAME,
trigger: 'synchronize images',
account_id: userData?.identity.internal?.account_id || 'Not found',
});
} catch (imageBuildError) {
dispatch(
addNotification({
variant: 'warning',
title: 'No blueprint was build',
description: imageBuildError?.data?.error?.message,
})
);
}
}
};
const [isOpen, setIsOpen] = useState(false);
const onToggleClick = () => {
setIsOpen(!isOpen);
};
const { data: blueprintDetails } = useGetBlueprintQuery(
selectedBlueprintId ? { id: selectedBlueprintId } : skipToken
);
const blueprintImageType = blueprintDetails?.image_requests.map(
(image) => image.image_type
);
const onSelect = (
_event: React.MouseEvent<Element, MouseEvent>,
itemId: number
) => {
const imageType = blueprintImageType?.[itemId];
if (imageType && deselectedTargets.includes(imageType)) {
setDeselectedTargets(
deselectedTargets.filter((target) => target !== imageType)
);
} else if (imageType) {
setDeselectedTargets([...deselectedTargets, imageType]);
}
};
return (
<Dropdown
isOpen={isOpen}
onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
variant="primary"
data-testid="blueprint-build-image-menu"
ref={toggleRef}
onClick={onToggleClick}
isExpanded={isOpen}
splitButtonOptions={{
variant: 'action',
items: [
<MenuToggleAction
data-testid="blueprint-build-image-menu-option"
key="split-action"
onClick={onBuildHandler}
id="wizard-build-image-btn"
isDisabled={
!selectedBlueprintId ||
deselectedTargets.length === blueprintImageType?.length
}
>
<Flex display={{ default: 'inlineFlex' }}>
{imageBuildLoading && (
<FlexItem>
<Spinner
style={
{
'--pf-v5-c-spinner--Color': '#fff',
} as React.CSSProperties
}
isInline
size="md"
/>
</FlexItem>
)}
<FlexItem>{children ? children : 'Build images'}</FlexItem>
</Flex>
</MenuToggleAction>,
],
}}
></MenuToggle>
)}
>
<Menu onSelect={onSelect} selected={blueprintImageType}>
<MenuContent>
<MenuList>
{blueprintImageType?.map((imageType, index) => (
<MenuItem
data-testid="blueprint-menu-items"
key={imageType}
hasCheckbox
itemId={index}
isSelected={
deselectedTargets.length === 0 ||
!deselectedTargets.includes(imageType)
}
>
{targetOptions[imageType]}
</MenuItem>
))}
</MenuList>
</MenuContent>
</Menu>
</Dropdown>
);
};
type BuildImagesButtonEmptyStatePropTypes = {
variant?: ButtonProps['variant'];
children?: React.ReactNode;
};
export const BuildImagesButtonEmptyState = ({
variant,
children,
}: BuildImagesButtonEmptyStatePropTypes) => {
const selectedBlueprintId = useAppSelector(selectSelectedBlueprintId);
const [buildBlueprint, { isLoading: imageBuildLoading }] =
useComposeBlueprintMutation();
const onBuildHandler = async () => {
if (selectedBlueprintId) {
await buildBlueprint({ id: selectedBlueprintId, body: {} });
}
};
return (
<Button
onClick={onBuildHandler}
isDisabled={!selectedBlueprintId}
isLoading={imageBuildLoading}
variant={variant || 'primary'}
>
{children ? children : 'Build images'}
</Button>
);
};