Wizard: add segment tracking
This commit is contained in:
parent
c99157216f
commit
d18f25e331
9 changed files with 344 additions and 54 deletions
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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 });
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
30
src/Utilities/analytics.ts
Normal file
30
src/Utilities/analytics.ts
Normal 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;
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue