debian-image-builder-frontend/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx
regexowl 25a5f140d8 ESLint: Add rule to sort imports alphabetically
The `import/order` rule isn't enough to sort import within a single import group alphabetically.

This adds `sort-imports` rule that handles the sorting within groups.
2025-07-15 16:52:45 +00:00

204 lines
5.9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import {
Button,
DropdownItem,
DropdownList,
Flex,
FlexItem,
MenuToggleAction,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
Spinner,
} from '@patternfly/react-core';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { ChromeUser } from '@redhat-cloud-services/types';
import { AMPLITUDE_MODULE_NAME } from '../../../../../constants';
import {
useComposeBPWithNotification as useComposeBlueprintMutation,
useCreateBPWithNotification as useCreateBlueprintMutation,
} from '../../../../../Hooks';
import { setBlueprintId } from '../../../../../store/BlueprintSlice';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
import {
CreateBlueprintRequest,
CreateBlueprintResponse,
} from '../../../../../store/imageBuilderApi';
import { selectPackages } from '../../../../../store/wizardSlice';
import { createAnalytics } from '../../../../../Utilities/analytics';
type CreateDropdownProps = {
getBlueprintPayload: () => Promise<'' | CreateBlueprintRequest | undefined>;
setIsOpen: (isOpen: boolean) => void;
isDisabled: boolean;
};
export const CreateSaveAndBuildBtn = ({
getBlueprintPayload,
setIsOpen,
isDisabled,
}: CreateDropdownProps) => {
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth, isBeta } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const packages = useAppSelector(selectPackages);
const { trigger: buildBlueprint } = useComposeBlueprintMutation();
const { trigger: createBlueprint } = useCreateBlueprintMutation({
fixedCacheKey: 'createBlueprintKey',
});
const dispatch = useAppDispatch();
const onSaveAndBuild = async () => {
const requestBody = await getBlueprintPayload();
setIsOpen(false);
if (!process.env.IS_ON_PREMISE && requestBody) {
const analyticsData = createAnalytics(requestBody, packages, isBeta);
analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, {
...analyticsData,
type: 'createBlueprintAndBuildImages',
account_id: userData?.identity.internal?.account_id || 'Not found',
});
analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Requested`, {
module: AMPLITUDE_MODULE_NAME,
trigger: 'blueprint_created',
image_request_types: requestBody.image_requests.map(
(req) => req.image_type
),
});
}
if (requestBody) {
const blueprint = (await createBlueprint({
createBlueprintRequest: requestBody,
})) as CreateBlueprintResponse;
buildBlueprint({ id: blueprint.id, body: {} });
dispatch(setBlueprintId(blueprint.id));
}
};
return (
<DropdownList>
<DropdownItem onClick={onSaveAndBuild} isDisabled={isDisabled}>
Create blueprint and build image(s)
</DropdownItem>
</DropdownList>
);
};
export const CreateSaveButton = ({
setIsOpen,
getBlueprintPayload,
isDisabled,
}: CreateDropdownProps) => {
const { analytics, auth, isBeta } = useChrome();
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const packages = useAppSelector(selectPackages);
const { trigger: createBlueprint, isLoading } = useCreateBlueprintMutation({
fixedCacheKey: 'createBlueprintKey',
});
const dispatch = useAppDispatch();
const [showModal, setShowModal] = useState(false);
const wasModalSeen = window.localStorage.getItem(
'imageBuilder.saveAndBuildModalSeen'
);
const SaveAndBuildImagesModal = () => {
const handleClose = () => {
setShowModal(false);
};
return (
<Modal isOpen={showModal} onClose={handleClose} width="50%">
<ModalHeader title="Save time by building images" />
<ModalBody>
Building blueprints and images doesn&apos;t need to be a two step
process. To build images simultaneously, use the dropdown arrow to the
right side of this button.
</ModalBody>
<ModalFooter>
<Button
key="back"
variant="primary"
data-testid="close-button-saveandbuild-modal"
onClick={handleClose}
>
Close
</Button>
</ModalFooter>
</Modal>
);
};
const onClick = () => {
if (!wasModalSeen) {
setShowModal(true);
window.localStorage.setItem('imageBuilder.saveAndBuildModalSeen', 'true');
} else {
onSave();
}
};
const onSave = async () => {
const requestBody = await getBlueprintPayload();
setIsOpen(false);
if (!process.env.IS_ON_PREMISE && requestBody) {
const analyticsData = createAnalytics(requestBody, packages, isBeta);
analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, {
...analyticsData,
type: 'createBlueprint',
account_id: userData?.identity.internal?.account_id || 'Not found',
});
}
if (requestBody) {
const blueprint = (await createBlueprint({
createBlueprintRequest: requestBody,
})) as CreateBlueprintResponse;
dispatch(setBlueprintId(blueprint.id));
}
};
return (
<>
{showModal && <SaveAndBuildImagesModal />}
<MenuToggleAction
onClick={onClick}
id="wizard-create-save-btn"
isDisabled={isDisabled}
>
<Flex display={{ default: 'inlineFlex' }}>
{isLoading && (
<FlexItem>
<Spinner
style={
{ '--pf-v6-c-spinner--Color': '#fff' } as React.CSSProperties
}
isInline
size="md"
/>
</FlexItem>
)}
<FlexItem>Create blueprint</FlexItem>
</Flex>
</MenuToggleAction>
</>
);
};