From d18f25e331dc09aed50c09f90f3b2bd75112a75c Mon Sep 17 00:00:00 2001 From: Katarina Sieklova Date: Wed, 26 Mar 2025 16:30:38 +0100 Subject: [PATCH] Wizard: add segment tracking --- .../Blueprints/BuildImagesButton.tsx | 21 +++- .../Blueprints/DeleteBlueprintModal.tsx | 23 +++- .../steps/Review/Footer/CreateDropdown.tsx | 52 ++++++--- .../steps/Review/Footer/EditDropdown.tsx | 55 ++++++++- src/Components/ImagesTable/ImageDetails.tsx | 34 +++++- src/Components/ImagesTable/ImagesTable.tsx | 110 +++++++++++++----- src/Components/ImagesTable/Instance.tsx | 24 +++- src/Components/ImagesTable/Status.tsx | 49 +++++++- src/Utilities/analytics.ts | 30 +++++ 9 files changed, 344 insertions(+), 54 deletions(-) create mode 100644 src/Utilities/analytics.ts diff --git a/src/Components/Blueprints/BuildImagesButton.tsx b/src/Components/Blueprints/BuildImagesButton.tsx index 9cd7b5c8..9fb31658 100644 --- a/src/Components/Blueprints/BuildImagesButton.tsx +++ b/src/Components/Blueprints/BuildImagesButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Dropdown, @@ -15,10 +15,12 @@ import { 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 { targetOptions } from '../../constants'; +import { AMPLITUDE_MODULE_NAME, targetOptions } from '../../constants'; import { useGetBlueprintQuery, useComposeBlueprintMutation, @@ -38,6 +40,16 @@ export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => { const [buildBlueprint, { isLoading: imageBuildLoading }] = useComposeBlueprintMutation(); const dispatch = useAppDispatch(); + const { analytics, auth } = useChrome(); + + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const onBuildHandler = async () => { if (selectedBlueprintId) { @@ -50,6 +62,11 @@ export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => { ), }, }); + 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({ diff --git a/src/Components/Blueprints/DeleteBlueprintModal.tsx b/src/Components/Blueprints/DeleteBlueprintModal.tsx index 841d69da..bc8a57e2 100644 --- a/src/Components/Blueprints/DeleteBlueprintModal.tsx +++ b/src/Components/Blueprints/DeleteBlueprintModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { ActionGroup, @@ -6,8 +6,14 @@ import { Modal, ModalVariant, } from '@patternfly/react-core'; +import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; -import { PAGINATION_LIMIT, PAGINATION_OFFSET } from '../../constants'; +import { + AMPLITUDE_MODULE_NAME, + PAGINATION_LIMIT, + PAGINATION_OFFSET, +} from '../../constants'; import { backendApi, useDeleteBlueprintMutation, @@ -36,6 +42,15 @@ export const DeleteBlueprintModal: React.FunctionComponent< const blueprintsOffset = useAppSelector(selectOffset) || PAGINATION_OFFSET; const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const dispatch = useAppDispatch(); + const { analytics, auth } = useChrome(); + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const searchParams: GetBlueprintsApiArg = { limit: blueprintsLimit, @@ -59,6 +74,10 @@ export const DeleteBlueprintModal: React.FunctionComponent< }); const handleDelete = async () => { if (selectedBlueprintId) { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Deleted`, { + module: AMPLITUDE_MODULE_NAME, + account_id: userData?.identity.internal?.account_id || 'Not found', + }); setShowDeleteModal(false); await deleteBlueprint({ id: selectedBlueprintId }); dispatch(setBlueprintId(undefined)); diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx index 8b77b073..8c4ac033 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/CreateDropdown.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { DropdownList, @@ -11,6 +11,7 @@ import { Button, } 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 { useCreateBlueprintMutation } from '../../../../../store/backendApi'; @@ -21,6 +22,7 @@ import { useComposeBlueprintMutation, } from '../../../../../store/imageBuilderApi'; import { selectPackages } from '../../../../../store/wizardSlice'; +import { createAnalytics } from '../../../../../Utilities/analytics'; import { useGetEnvironment } from '../../../../../Utilities/useGetEnvironment'; type CreateDropdownProps = { @@ -34,7 +36,15 @@ export const CreateSaveAndBuildBtn = ({ setIsOpen, isDisabled, }: CreateDropdownProps) => { - const { analytics, isBeta } = useChrome(); + const [userData, setUserData] = useState(undefined); + + const { analytics, auth, isBeta } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const packages = useAppSelector(selectPackages); const [buildBlueprint] = useComposeBlueprintMutation(); @@ -47,15 +57,21 @@ export const CreateSaveAndBuildBtn = ({ const requestBody = await getBlueprintPayload(); setIsOpen(false); - if (!process.env.IS_ON_PREMISE && !isFedoraEnv) { - analytics.track(`${AMPLITUDE_MODULE_NAME}-blueprintCreated`, { - module: AMPLITUDE_MODULE_NAME, - isPreview: isBeta(), + if (!process.env.IS_ON_PREMISE && !isFedoraEnv && requestBody) { + const analyticsData = createAnalytics(requestBody, packages, isBeta); + analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, { + ...analyticsData, type: 'createBlueprintAndBuildImages', - packages: packages.map((pkg) => pkg.name), + 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 + ), }); } - const blueprint = requestBody && (await createBlueprint({ @@ -86,7 +102,15 @@ export const CreateSaveButton = ({ getBlueprintPayload, isDisabled, }: CreateDropdownProps) => { - const { analytics, isBeta } = useChrome(); + const { analytics, auth, isBeta } = useChrome(); + const [userData, setUserData] = useState(undefined); + + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const packages = useAppSelector(selectPackages); const { isFedoraEnv } = useGetEnvironment(); @@ -141,12 +165,12 @@ export const CreateSaveButton = ({ const requestBody = await getBlueprintPayload(); setIsOpen(false); - if (!process.env.IS_ON_PREMISE && !isFedoraEnv) { - analytics.track(`${AMPLITUDE_MODULE_NAME}-blueprintCreated`, { - module: AMPLITUDE_MODULE_NAME, - isPreview: isBeta(), + if (!process.env.IS_ON_PREMISE && !isFedoraEnv && requestBody) { + const analyticsData = createAnalytics(requestBody, packages, isBeta); + analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, { + ...analyticsData, type: 'createBlueprint', - packages: packages.map((pkg) => pkg.name), + account_id: userData?.identity.internal?.account_id || 'Not found', }); } const blueprint = diff --git a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx index 094e7fc6..62913fae 100644 --- a/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx +++ b/src/Components/CreateImageWizard/steps/Review/Footer/EditDropdown.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { DropdownList, @@ -8,12 +8,18 @@ import { Flex, FlexItem, } 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 { useUpdateBlueprintMutation } from '../../../../../store/backendApi'; +import { useAppSelector } from '../../../../../store/hooks'; import { CreateBlueprintRequest, useComposeBlueprintMutation, } from '../../../../../store/imageBuilderApi'; +import { selectPackages } from '../../../../../store/wizardSlice'; +import { createAnalytics } from '../../../../../Utilities/analytics'; type EditDropdownProps = { getBlueprintPayload: () => Promise<'' | CreateBlueprintRequest | undefined>; @@ -28,13 +34,40 @@ export const EditSaveAndBuildBtn = ({ blueprintId, isDisabled, }: EditDropdownProps) => { + const [userData, setUserData] = useState(undefined); + + const { analytics, auth, isBeta } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const [buildBlueprint] = useComposeBlueprintMutation(); + const packages = useAppSelector(selectPackages); + const [updateBlueprint] = useUpdateBlueprintMutation({ fixedCacheKey: 'updateBlueprintKey', }); const onSaveAndBuild = async () => { const requestBody = await getBlueprintPayload(); + + if (!process.env.IS_ON_PREMISE && requestBody) { + const analyticsData = createAnalytics(requestBody, packages, isBeta); + analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Updated`, { + ...analyticsData, + type: 'editBlueprintAndBuildImages', + account_id: userData?.identity.internal?.account_id || 'Not found', + }); + analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Requested`, { + module: AMPLITUDE_MODULE_NAME, + trigger: 'blueprint_updated', + image_request_types: requestBody.image_requests.map( + (req) => req.image_type + ), + }); + } setIsOpen(false); if (requestBody) { await updateBlueprint({ @@ -64,11 +97,31 @@ export const EditSaveButton = ({ blueprintId, isDisabled, }: EditDropdownProps) => { + const [userData, setUserData] = useState(undefined); + + const { analytics, auth, isBeta } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); + const packages = useAppSelector(selectPackages); + const [updateBlueprint, { isLoading }] = useUpdateBlueprintMutation({ fixedCacheKey: 'updateBlueprintKey', }); const onSave = async () => { const requestBody = await getBlueprintPayload(); + + if (!process.env.IS_ON_PREMISE && requestBody) { + const analyticsData = createAnalytics(requestBody, packages, isBeta); + analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Updated`, { + ...analyticsData, + type: 'editBlueprint', + account_id: userData?.identity.internal?.account_id || 'Not found', + }); + } setIsOpen(false); if (requestBody) { updateBlueprint({ id: blueprintId, createBlueprintRequest: requestBody }); diff --git a/src/Components/ImagesTable/ImageDetails.tsx b/src/Components/ImagesTable/ImageDetails.tsx index 6d051e38..8e4a9837 100644 --- a/src/Components/ImagesTable/ImageDetails.tsx +++ b/src/Components/ImagesTable/ImageDetails.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { ClipboardCopy, @@ -12,9 +12,12 @@ import { Skeleton, } from '@patternfly/react-core'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; +import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import ClonesTable from './ClonesTable'; +import { AMPLITUDE_MODULE_NAME } from '../../constants'; import { useGetComposeStatusQuery } from '../../store/backendApi'; import { extractProvisioningList } from '../../store/helpers'; import { @@ -131,7 +134,15 @@ type AwsDetailsPropTypes = { export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => { const options = compose.request.image_requests[0].upload_request.options; + const [userData, setUserData] = useState(undefined); + const { analytics, auth } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); if (!isAwsUploadRequestOptions(options)) { throw TypeError( `Error: options must be of type AwsUploadRequestOptions, not ${typeof options}.` @@ -152,6 +163,15 @@ export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => { clickTip="Copied" variant="inline-compact" ouiaId="aws-uuid" + onClick={() => { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Button Clicked`, { + module: AMPLITUDE_MODULE_NAME, + link_name: compose.id, + current_path: window.location.pathname, + account_id: + userData?.identity.internal?.account_id || 'Not found', + }); + }} > {compose.id} @@ -183,6 +203,18 @@ export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => { // the format of an account link is taken from // https://docs.aws.amazon.com/signin/latest/userguide/sign-in-urls-defined.html href={`https://${options.share_with_accounts[0]}.signin.aws.amazon.com/console/`} + onClick={() => { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Link Clicked`, { + module: AMPLITUDE_MODULE_NAME, + + link_name: options.share_with_accounts + ? options.share_with_accounts[0] + : '', + current_path: window.location.pathname, + account_id: + userData?.identity.internal?.account_id || 'Not found', + }); + }} > {options.share_with_accounts[0]} diff --git a/src/Components/ImagesTable/ImagesTable.tsx b/src/Components/ImagesTable/ImagesTable.tsx index 757fe1ac..7cffd146 100644 --- a/src/Components/ImagesTable/ImagesTable.tsx +++ b/src/Components/ImagesTable/ImagesTable.tsx @@ -23,6 +23,8 @@ import { Thead, Tr, } from '@patternfly/react-table'; +import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { useDispatch } from 'react-redux'; import { NavigateFunction, useNavigate } from 'react-router-dom'; @@ -47,6 +49,7 @@ import { ExpiringStatus, CloudStatus, LocalStatus } from './Status'; import { AwsTarget, Target } from './Target'; import { + AMPLITUDE_MODULE_NAME, AWS_S3_EXPIRATION_TIME_IN_HOURS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, PAGINATION_LIMIT, @@ -441,7 +444,14 @@ type AwsRowPropTypes = { const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => { const navigate = useNavigate(); - + const [userData, setUserData] = useState(undefined); + const { analytics, auth } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const target = ; const status = ; @@ -451,7 +461,15 @@ const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => { const details = ; const actions = ( - + ); return ( @@ -497,6 +515,10 @@ type RowPropTypes = { details: JSX.Element; }; +type Analytics = { + track: (event: string, props?: Record) => void; +}; + const Row = ({ compose, rowIndex, @@ -506,6 +528,14 @@ const Row = ({ details, instance, }: RowPropTypes) => { + const [userData, setUserData] = useState(undefined); + const { analytics, auth } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const [isExpanded, setIsExpanded] = useState(false); const handleToggle = () => setIsExpanded(!isExpanded); const dispatch = useDispatch(); @@ -568,7 +598,13 @@ const Row = ({ {actions ? ( actions ) : ( - + )} @@ -581,33 +617,53 @@ const Row = ({ ); }; -const defaultActions = (compose: ComposesResponseItem) => [ - { - title: ( - - Download compose request (.json) - - ), - }, -]; +const defaultActions = ( + compose: ComposesResponseItem, + analytics: Analytics, + account_id: string | undefined +) => { + const name = `request-${compose.id}.json`; + + return [ + { + title: ( + { + analytics.track(`${AMPLITUDE_MODULE_NAME} - File Downloaded`, { + module: AMPLITUDE_MODULE_NAME, + link_name: name, + current_path: window.location.pathname, + account_id: account_id || 'Not found', + }); + }} + > + Download compose request (.json) + + ), + }, + ]; +}; const awsActions = ( compose: ComposesResponseItem, status: ComposeStatus | undefined, - navigate: NavigateFunction -) => [ - { - title: 'Share to new region', - onClick: () => navigate(resolveRelPath(`share/${compose.id}`)), - isDisabled: status?.image_status.status === 'success' ? false : true, - }, - ...defaultActions(compose), -]; + navigate: NavigateFunction, + analytics: Analytics, + account_id: string | undefined +) => { + return [ + { + title: 'Share to new region', + onClick: () => navigate(resolveRelPath(`share/${compose.id}`)), + isDisabled: status?.image_status.status === 'success' ? false : true, + }, + ...defaultActions(compose, analytics, account_id), + ]; +}; export default ImagesTable; diff --git a/src/Components/ImagesTable/Instance.tsx b/src/Components/ImagesTable/Instance.tsx index d76dec84..4c95b867 100644 --- a/src/Components/ImagesTable/Instance.tsx +++ b/src/Components/ImagesTable/Instance.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, useState } from 'react'; +import React, { Suspense, useEffect, useState } from 'react'; import path from 'path'; @@ -20,11 +20,13 @@ import { } from '@patternfly/react-core/dist/esm/components/List/List'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { useLoadModule, useScalprum } from '@scalprum/react-core'; import cockpit from 'cockpit'; import { useNavigate } from 'react-router-dom'; import { + AMPLITUDE_MODULE_NAME, FILE_SYSTEM_CUSTOMIZATION_URL, MODAL_ANCHOR, SEARCH_INPUT, @@ -93,6 +95,15 @@ const ProvisioningLink = ({ compose, composeStatus, }: ProvisioningLinkPropTypes) => { + const [userData, setUserData] = useState(undefined); + + const { analytics, auth } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); const [wizardOpen, setWizardOpen] = useState(false); const [exposedScalprumModule, error] = useLoadModule( { @@ -155,7 +166,16 @@ const ProvisioningLink = ({ isLoading={isLoadingPermission} variant="link" isInline - onClick={() => setWizardOpen(true)} + onClick={() => { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Link Clicked`, { + module: AMPLITUDE_MODULE_NAME, + image_name: compose.image_name, + current_path: window.location.pathname, + account_id: userData?.identity.internal?.account_id || 'Not found', + }); + + setWizardOpen(true); + }} > Launch diff --git a/src/Components/ImagesTable/Status.tsx b/src/Components/ImagesTable/Status.tsx index 99d251bd..a6d25e3b 100644 --- a/src/Components/ImagesTable/Status.tsx +++ b/src/Components/ImagesTable/Status.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import './ImageBuildStatus.scss'; import { @@ -23,8 +23,11 @@ import { OffIcon, PendingIcon, } from '@patternfly/react-icons'; +import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; +import { ChromeUser } from '@redhat-cloud-services/types'; import { + AMPLITUDE_MODULE_NAME, AWS_S3_EXPIRATION_TIME_IN_HOURS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, } from '../../constants'; @@ -74,13 +77,21 @@ export const AwsDetailsStatus = ({ compose }: ComposeStatusPropTypes) => { const { data, isSuccess } = useGetComposeStatusQuery({ composeId: compose.id, }); + const { analytics } = useChrome(); if (!isSuccess) { return <>; } switch (data?.image_status.status) { - case 'failure': + case 'failure': { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Created`, { + module: AMPLITUDE_MODULE_NAME, + error: true, + error_id: data.image_status.error?.id, + error_details: data.image_status.error?.details, + error_reason: data.image_status.error?.reason, + }); return ( { error={data.image_status.error || ''} /> ); + } + default: return ( { const { data, isSuccess } = useGetComposeStatusQuery({ composeId: compose.id, }); - + const [userData, setUserData] = useState(undefined); + const { analytics, auth } = useChrome(); + useEffect(() => { + (async () => { + const data = await auth?.getUser(); + setUserData(data); + })(); + }, [auth]); if (!isSuccess) { return ; } switch (data?.image_status.status) { - case 'failure': + case 'failure': { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Created`, { + module: AMPLITUDE_MODULE_NAME, + error: true, + error_id: data.image_status.error?.id, + error_details: data.image_status.error?.details, + error_reason: data.image_status.error?.reason, + account_id: userData?.identity.internal?.account_id || 'Not found', + }); return ( { error={data.image_status.error || ''} /> ); + } default: return ( { + const { analytics } = useChrome(); + switch (status.image_status.status) { - case 'failure': + case 'failure': { + analytics.track(`${AMPLITUDE_MODULE_NAME} - Image Created`, { + module: AMPLITUDE_MODULE_NAME, + error: true, + error_id: status.image_status.error?.id, + error_details: status.image_status.error?.details, + error_reason: status.image_status.error?.reason, + }); return ( { error={status.image_status.error || ''} /> ); + } default: return ( boolean +) => { + const analyticsData = { + image_name: requestBody.name, + description: requestBody.description, + distribution: requestBody.distribution, + openscap: requestBody.customizations.openscap, + image_request_types: requestBody.image_requests.map( + (req) => req.image_type + ), + image_request_architectures: requestBody.image_requests.map( + (req) => req.architecture + ), + image_requests: requestBody.image_requests, + organization: requestBody.customizations.subscription?.organization, + metadata: requestBody.metadata, + packages: packages.map((pkg) => pkg.name), + file_system_configuration: requestBody.customizations.filesystem, + module: AMPLITUDE_MODULE_NAME, + is_preview: isBeta(), + }; + return analyticsData; +};