Wizard: add segment tracking

This commit is contained in:
Katarina Sieklova 2025-03-26 16:30:38 +01:00 committed by Klara Simickova
parent c99157216f
commit d18f25e331
9 changed files with 344 additions and 54 deletions

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import {
Dropdown, Dropdown,
@ -15,10 +15,12 @@ import {
Button, Button,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/MenuToggle/MenuToggle'; 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 { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
import { ChromeUser } from '@redhat-cloud-services/types';
import { skipToken } from '@reduxjs/toolkit/query'; import { skipToken } from '@reduxjs/toolkit/query';
import { targetOptions } from '../../constants'; import { AMPLITUDE_MODULE_NAME, targetOptions } from '../../constants';
import { import {
useGetBlueprintQuery, useGetBlueprintQuery,
useComposeBlueprintMutation, useComposeBlueprintMutation,
@ -38,6 +40,16 @@ export const BuildImagesButton = ({ children }: BuildImagesButtonPropTypes) => {
const [buildBlueprint, { isLoading: imageBuildLoading }] = const [buildBlueprint, { isLoading: imageBuildLoading }] =
useComposeBlueprintMutation(); useComposeBlueprintMutation();
const dispatch = useAppDispatch(); 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 () => { const onBuildHandler = async () => {
if (selectedBlueprintId) { 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) { } catch (imageBuildError) {
dispatch( dispatch(
addNotification({ addNotification({

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { import {
ActionGroup, ActionGroup,
@ -6,8 +6,14 @@ import {
Modal, Modal,
ModalVariant, ModalVariant,
} from '@patternfly/react-core'; } 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 { import {
backendApi, backendApi,
useDeleteBlueprintMutation, useDeleteBlueprintMutation,
@ -36,6 +42,15 @@ export const DeleteBlueprintModal: React.FunctionComponent<
const blueprintsOffset = useAppSelector(selectOffset) || PAGINATION_OFFSET; const blueprintsOffset = useAppSelector(selectOffset) || PAGINATION_OFFSET;
const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT; const blueprintsLimit = useAppSelector(selectLimit) || PAGINATION_LIMIT;
const dispatch = useAppDispatch(); 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 searchParams: GetBlueprintsApiArg = { const searchParams: GetBlueprintsApiArg = {
limit: blueprintsLimit, limit: blueprintsLimit,
@ -59,6 +74,10 @@ export const DeleteBlueprintModal: React.FunctionComponent<
}); });
const handleDelete = async () => { const handleDelete = async () => {
if (selectedBlueprintId) { if (selectedBlueprintId) {
analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Deleted`, {
module: AMPLITUDE_MODULE_NAME,
account_id: userData?.identity.internal?.account_id || 'Not found',
});
setShowDeleteModal(false); setShowDeleteModal(false);
await deleteBlueprint({ id: selectedBlueprintId }); await deleteBlueprint({ id: selectedBlueprintId });
dispatch(setBlueprintId(undefined)); dispatch(setBlueprintId(undefined));

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import {
DropdownList, DropdownList,
@ -11,6 +11,7 @@ import {
Button, Button,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { ChromeUser } from '@redhat-cloud-services/types';
import { AMPLITUDE_MODULE_NAME } from '../../../../../constants'; import { AMPLITUDE_MODULE_NAME } from '../../../../../constants';
import { useCreateBlueprintMutation } from '../../../../../store/backendApi'; import { useCreateBlueprintMutation } from '../../../../../store/backendApi';
@ -21,6 +22,7 @@ import {
useComposeBlueprintMutation, useComposeBlueprintMutation,
} from '../../../../../store/imageBuilderApi'; } from '../../../../../store/imageBuilderApi';
import { selectPackages } from '../../../../../store/wizardSlice'; import { selectPackages } from '../../../../../store/wizardSlice';
import { createAnalytics } from '../../../../../Utilities/analytics';
import { useGetEnvironment } from '../../../../../Utilities/useGetEnvironment'; import { useGetEnvironment } from '../../../../../Utilities/useGetEnvironment';
type CreateDropdownProps = { type CreateDropdownProps = {
@ -34,7 +36,15 @@ export const CreateSaveAndBuildBtn = ({
setIsOpen, setIsOpen,
isDisabled, isDisabled,
}: CreateDropdownProps) => { }: CreateDropdownProps) => {
const { analytics, isBeta } = useChrome(); 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 packages = useAppSelector(selectPackages);
const [buildBlueprint] = useComposeBlueprintMutation(); const [buildBlueprint] = useComposeBlueprintMutation();
@ -47,15 +57,21 @@ export const CreateSaveAndBuildBtn = ({
const requestBody = await getBlueprintPayload(); const requestBody = await getBlueprintPayload();
setIsOpen(false); setIsOpen(false);
if (!process.env.IS_ON_PREMISE && !isFedoraEnv) { if (!process.env.IS_ON_PREMISE && !isFedoraEnv && requestBody) {
analytics.track(`${AMPLITUDE_MODULE_NAME}-blueprintCreated`, { const analyticsData = createAnalytics(requestBody, packages, isBeta);
module: AMPLITUDE_MODULE_NAME, analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, {
isPreview: isBeta(), ...analyticsData,
type: 'createBlueprintAndBuildImages', 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 = const blueprint =
requestBody && requestBody &&
(await createBlueprint({ (await createBlueprint({
@ -86,7 +102,15 @@ export const CreateSaveButton = ({
getBlueprintPayload, getBlueprintPayload,
isDisabled, isDisabled,
}: CreateDropdownProps) => { }: CreateDropdownProps) => {
const { analytics, isBeta } = useChrome(); 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 packages = useAppSelector(selectPackages);
const { isFedoraEnv } = useGetEnvironment(); const { isFedoraEnv } = useGetEnvironment();
@ -141,12 +165,12 @@ export const CreateSaveButton = ({
const requestBody = await getBlueprintPayload(); const requestBody = await getBlueprintPayload();
setIsOpen(false); setIsOpen(false);
if (!process.env.IS_ON_PREMISE && !isFedoraEnv) { if (!process.env.IS_ON_PREMISE && !isFedoraEnv && requestBody) {
analytics.track(`${AMPLITUDE_MODULE_NAME}-blueprintCreated`, { const analyticsData = createAnalytics(requestBody, packages, isBeta);
module: AMPLITUDE_MODULE_NAME, analytics.track(`${AMPLITUDE_MODULE_NAME} - Blueprint Created`, {
isPreview: isBeta(), ...analyticsData,
type: 'createBlueprint', type: 'createBlueprint',
packages: packages.map((pkg) => pkg.name), account_id: userData?.identity.internal?.account_id || 'Not found',
}); });
} }
const blueprint = const blueprint =

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { import {
DropdownList, DropdownList,
@ -8,12 +8,18 @@ import {
Flex, Flex,
FlexItem, FlexItem,
} from '@patternfly/react-core'; } 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 { useUpdateBlueprintMutation } from '../../../../../store/backendApi';
import { useAppSelector } from '../../../../../store/hooks';
import { import {
CreateBlueprintRequest, CreateBlueprintRequest,
useComposeBlueprintMutation, useComposeBlueprintMutation,
} from '../../../../../store/imageBuilderApi'; } from '../../../../../store/imageBuilderApi';
import { selectPackages } from '../../../../../store/wizardSlice';
import { createAnalytics } from '../../../../../Utilities/analytics';
type EditDropdownProps = { type EditDropdownProps = {
getBlueprintPayload: () => Promise<'' | CreateBlueprintRequest | undefined>; getBlueprintPayload: () => Promise<'' | CreateBlueprintRequest | undefined>;
@ -28,13 +34,40 @@ export const EditSaveAndBuildBtn = ({
blueprintId, blueprintId,
isDisabled, isDisabled,
}: EditDropdownProps) => { }: EditDropdownProps) => {
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth, isBeta } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const [buildBlueprint] = useComposeBlueprintMutation(); const [buildBlueprint] = useComposeBlueprintMutation();
const packages = useAppSelector(selectPackages);
const [updateBlueprint] = useUpdateBlueprintMutation({ const [updateBlueprint] = useUpdateBlueprintMutation({
fixedCacheKey: 'updateBlueprintKey', fixedCacheKey: 'updateBlueprintKey',
}); });
const onSaveAndBuild = async () => { const onSaveAndBuild = async () => {
const requestBody = await getBlueprintPayload(); 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); setIsOpen(false);
if (requestBody) { if (requestBody) {
await updateBlueprint({ await updateBlueprint({
@ -64,11 +97,31 @@ export const EditSaveButton = ({
blueprintId, blueprintId,
isDisabled, isDisabled,
}: EditDropdownProps) => { }: EditDropdownProps) => {
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 [updateBlueprint, { isLoading }] = useUpdateBlueprintMutation({ const [updateBlueprint, { isLoading }] = useUpdateBlueprintMutation({
fixedCacheKey: 'updateBlueprintKey', fixedCacheKey: 'updateBlueprintKey',
}); });
const onSave = async () => { const onSave = async () => {
const requestBody = await getBlueprintPayload(); 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); setIsOpen(false);
if (requestBody) { if (requestBody) {
updateBlueprint({ id: blueprintId, createBlueprintRequest: requestBody }); updateBlueprint({ id: blueprintId, createBlueprintRequest: requestBody });

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { import {
ClipboardCopy, ClipboardCopy,
@ -12,9 +12,12 @@ import {
Skeleton, Skeleton,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons'; 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 ClonesTable from './ClonesTable';
import { AMPLITUDE_MODULE_NAME } from '../../constants';
import { useGetComposeStatusQuery } from '../../store/backendApi'; import { useGetComposeStatusQuery } from '../../store/backendApi';
import { extractProvisioningList } from '../../store/helpers'; import { extractProvisioningList } from '../../store/helpers';
import { import {
@ -131,7 +134,15 @@ type AwsDetailsPropTypes = {
export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => { export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => {
const options = compose.request.image_requests[0].upload_request.options; const options = compose.request.image_requests[0].upload_request.options;
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
if (!isAwsUploadRequestOptions(options)) { if (!isAwsUploadRequestOptions(options)) {
throw TypeError( throw TypeError(
`Error: options must be of type AwsUploadRequestOptions, not ${typeof options}.` `Error: options must be of type AwsUploadRequestOptions, not ${typeof options}.`
@ -152,6 +163,15 @@ export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => {
clickTip="Copied" clickTip="Copied"
variant="inline-compact" variant="inline-compact"
ouiaId="aws-uuid" 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} {compose.id}
</ClipboardCopy> </ClipboardCopy>
@ -183,6 +203,18 @@ export const AwsDetails = ({ compose }: AwsDetailsPropTypes) => {
// the format of an account link is taken from // the format of an account link is taken from
// https://docs.aws.amazon.com/signin/latest/userguide/sign-in-urls-defined.html // 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/`} 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]} {options.share_with_accounts[0]}
</Button> </Button>

View file

@ -23,6 +23,8 @@ import {
Thead, Thead,
Tr, Tr,
} from '@patternfly/react-table'; } 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 { useDispatch } from 'react-redux';
import { NavigateFunction, useNavigate } from 'react-router-dom'; import { NavigateFunction, useNavigate } from 'react-router-dom';
@ -47,6 +49,7 @@ import { ExpiringStatus, CloudStatus, LocalStatus } from './Status';
import { AwsTarget, Target } from './Target'; import { AwsTarget, Target } from './Target';
import { import {
AMPLITUDE_MODULE_NAME,
AWS_S3_EXPIRATION_TIME_IN_HOURS, AWS_S3_EXPIRATION_TIME_IN_HOURS,
OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS,
PAGINATION_LIMIT, PAGINATION_LIMIT,
@ -441,7 +444,14 @@ type AwsRowPropTypes = {
const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => { const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const target = <AwsTarget compose={compose} />; const target = <AwsTarget compose={compose} />;
const status = <CloudStatus compose={compose} />; const status = <CloudStatus compose={compose} />;
@ -451,7 +461,15 @@ const AwsRow = ({ compose, composeStatus, rowIndex }: AwsRowPropTypes) => {
const details = <AwsDetails compose={compose} />; const details = <AwsDetails compose={compose} />;
const actions = ( const actions = (
<ActionsColumn items={awsActions(compose, composeStatus, navigate)} /> <ActionsColumn
items={awsActions(
compose,
composeStatus,
navigate,
analytics,
userData?.identity.internal?.account_id
)}
/>
); );
return ( return (
@ -497,6 +515,10 @@ type RowPropTypes = {
details: JSX.Element; details: JSX.Element;
}; };
type Analytics = {
track: (event: string, props?: Record<string, unknown>) => void;
};
const Row = ({ const Row = ({
compose, compose,
rowIndex, rowIndex,
@ -506,6 +528,14 @@ const Row = ({
details, details,
instance, instance,
}: RowPropTypes) => { }: RowPropTypes) => {
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
const handleToggle = () => setIsExpanded(!isExpanded); const handleToggle = () => setIsExpanded(!isExpanded);
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -568,7 +598,13 @@ const Row = ({
{actions ? ( {actions ? (
actions actions
) : ( ) : (
<ActionsColumn items={defaultActions(compose)} /> <ActionsColumn
items={defaultActions(
compose,
analytics,
userData?.identity.internal?.account_id
)}
/>
)} )}
</Td> </Td>
</Tr> </Tr>
@ -581,33 +617,53 @@ const Row = ({
); );
}; };
const defaultActions = (compose: ComposesResponseItem) => [ const defaultActions = (
{ compose: ComposesResponseItem,
title: ( analytics: Analytics,
<a account_id: string | undefined
className="ib-subdued-link" ) => {
href={`data:text/plain;charset=utf-8,${encodeURIComponent( const name = `request-${compose.id}.json`;
JSON.stringify(compose.request, null, ' ')
)}`} return [
download={`request-${compose.id}.json`} {
> title: (
Download compose request (.json) <a
</a> className="ib-subdued-link"
), href={`data:text/plain;charset=utf-8,${encodeURIComponent(
}, JSON.stringify(compose.request, null, ' ')
]; )}`}
download={name}
onClick={() => {
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)
</a>
),
},
];
};
const awsActions = ( const awsActions = (
compose: ComposesResponseItem, compose: ComposesResponseItem,
status: ComposeStatus | undefined, status: ComposeStatus | undefined,
navigate: NavigateFunction navigate: NavigateFunction,
) => [ analytics: Analytics,
{ account_id: string | undefined
title: 'Share to new region', ) => {
onClick: () => navigate(resolveRelPath(`share/${compose.id}`)), return [
isDisabled: status?.image_status.status === 'success' ? false : true, {
}, title: 'Share to new region',
...defaultActions(compose), onClick: () => navigate(resolveRelPath(`share/${compose.id}`)),
]; isDisabled: status?.image_status.status === 'success' ? false : true,
},
...defaultActions(compose, analytics, account_id),
];
};
export default ImagesTable; export default ImagesTable;

View file

@ -1,4 +1,4 @@
import React, { Suspense, useState } from 'react'; import React, { Suspense, useEffect, useState } from 'react';
import path from 'path'; import path from 'path';
@ -20,11 +20,13 @@ import {
} from '@patternfly/react-core/dist/esm/components/List/List'; } from '@patternfly/react-core/dist/esm/components/List/List';
import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome';
import { ChromeUser } from '@redhat-cloud-services/types';
import { useLoadModule, useScalprum } from '@scalprum/react-core'; import { useLoadModule, useScalprum } from '@scalprum/react-core';
import cockpit from 'cockpit'; import cockpit from 'cockpit';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
AMPLITUDE_MODULE_NAME,
FILE_SYSTEM_CUSTOMIZATION_URL, FILE_SYSTEM_CUSTOMIZATION_URL,
MODAL_ANCHOR, MODAL_ANCHOR,
SEARCH_INPUT, SEARCH_INPUT,
@ -93,6 +95,15 @@ const ProvisioningLink = ({
compose, compose,
composeStatus, composeStatus,
}: ProvisioningLinkPropTypes) => { }: ProvisioningLinkPropTypes) => {
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
const [wizardOpen, setWizardOpen] = useState(false); const [wizardOpen, setWizardOpen] = useState(false);
const [exposedScalprumModule, error] = useLoadModule( const [exposedScalprumModule, error] = useLoadModule(
{ {
@ -155,7 +166,16 @@ const ProvisioningLink = ({
isLoading={isLoadingPermission} isLoading={isLoadingPermission}
variant="link" variant="link"
isInline 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 Launch
</Button> </Button>

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import './ImageBuildStatus.scss'; import './ImageBuildStatus.scss';
import { import {
@ -23,8 +23,11 @@ import {
OffIcon, OffIcon,
PendingIcon, PendingIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { ChromeUser } from '@redhat-cloud-services/types';
import { import {
AMPLITUDE_MODULE_NAME,
AWS_S3_EXPIRATION_TIME_IN_HOURS, AWS_S3_EXPIRATION_TIME_IN_HOURS,
OCI_STORAGE_EXPIRATION_TIME_IN_DAYS, OCI_STORAGE_EXPIRATION_TIME_IN_DAYS,
} from '../../constants'; } from '../../constants';
@ -74,13 +77,21 @@ export const AwsDetailsStatus = ({ compose }: ComposeStatusPropTypes) => {
const { data, isSuccess } = useGetComposeStatusQuery({ const { data, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id, composeId: compose.id,
}); });
const { analytics } = useChrome();
if (!isSuccess) { if (!isSuccess) {
return <></>; return <></>;
} }
switch (data?.image_status.status) { 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 ( return (
<ErrorStatus <ErrorStatus
icon={statuses[data.image_status.status].icon} icon={statuses[data.image_status.status].icon}
@ -88,6 +99,8 @@ export const AwsDetailsStatus = ({ compose }: ComposeStatusPropTypes) => {
error={data.image_status.error || ''} error={data.image_status.error || ''}
/> />
); );
}
default: default:
return ( return (
<Status <Status
@ -106,13 +119,28 @@ export const CloudStatus = ({ compose }: CloudStatusPropTypes) => {
const { data, isSuccess } = useGetComposeStatusQuery({ const { data, isSuccess } = useGetComposeStatusQuery({
composeId: compose.id, composeId: compose.id,
}); });
const [userData, setUserData] = useState<ChromeUser | void>(undefined);
const { analytics, auth } = useChrome();
useEffect(() => {
(async () => {
const data = await auth?.getUser();
setUserData(data);
})();
}, [auth]);
if (!isSuccess) { if (!isSuccess) {
return <Skeleton />; return <Skeleton />;
} }
switch (data?.image_status.status) { 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 ( return (
<ErrorStatus <ErrorStatus
icon={statuses['failure'].icon} icon={statuses['failure'].icon}
@ -120,6 +148,7 @@ export const CloudStatus = ({ compose }: CloudStatusPropTypes) => {
error={data.image_status.error || ''} error={data.image_status.error || ''}
/> />
); );
}
default: default:
return ( return (
<Status <Status
@ -135,8 +164,17 @@ type AzureStatusPropTypes = {
}; };
export const AzureStatus = ({ status }: AzureStatusPropTypes) => { export const AzureStatus = ({ status }: AzureStatusPropTypes) => {
const { analytics } = useChrome();
switch (status.image_status.status) { 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 ( return (
<ErrorStatus <ErrorStatus
icon={statuses[status.image_status.status].icon} icon={statuses[status.image_status.status].icon}
@ -144,6 +182,7 @@ export const AzureStatus = ({ status }: AzureStatusPropTypes) => {
error={status.image_status.error || ''} error={status.image_status.error || ''}
/> />
); );
}
default: default:
return ( return (
<Status <Status

View file

@ -0,0 +1,30 @@
import { IBPackageWithRepositoryInfo } from '../Components/CreateImageWizard/steps/Packages/Packages';
import { AMPLITUDE_MODULE_NAME } from '../constants';
import { CreateBlueprintRequest } from '../store/imageBuilderApi';
export const createAnalytics = (
requestBody: CreateBlueprintRequest,
packages: IBPackageWithRepositoryInfo[],
isBeta: () => 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;
};