Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,32 @@ import PropTypes from 'prop-types';
/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';
import { addQueryArgs } from '@wordpress/url';
import { useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { useSelect, useDispatch } from 'googlesitekit-data';
import { useSelect } from 'googlesitekit-data';
import {
BREAKPOINT_SMALL,
BREAKPOINT_TABLET,
useBreakpoint,
} from '@/js/hooks/useBreakpoint';
import { CORE_FORMS } from '@/js/googlesitekit/datastore/forms/constants';
import { CORE_SITE } from '@/js/googlesitekit/datastore/site/constants';
import { CORE_USER } from '@/js/googlesitekit/datastore/user/constants';
import {
AUDIENCE_TILE_CUSTOM_DIMENSION_CREATE,
CUSTOM_DIMENSION_DEFINITIONS,
EDIT_SCOPE,
MODULES_ANALYTICS_4,
} from '@/js/modules/analytics-4/datastore/constants';
import { ERROR_CODE_MISSING_REQUIRED_SCOPE } from '@/js/util/errors';
import BadgeWithTooltip from '@/js/components/BadgeWithTooltip';
import AudienceTilePagesMetricContent from './AudienceTilePagesMetricContent';
import AudienceErrorModal from '@/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceErrorModal';
import { AREA_MAIN_DASHBOARD_TRAFFIC_AUDIENCE_SEGMENTATION } from '@/js/googlesitekit/widgets/default-areas';
import useViewContext from '@/js/hooks/useViewContext';
import { trackEvent } from '@/js/util';
import useFormValue from '@/js/hooks/useFormValue';
import useCreateCustomDimension from '@/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceTilesWidget/hooks/useCreateCustomDimension';

export default function AudienceTilePagesMetric( {
// TODO: The prop `audienceTileNumber` is part of a temporary workaround to ensure `AudienceErrorModal` is only rendered once
// within `AudienceTilesWidget`. This should be removed once the `AudienceErrorModal` render is extracted
// from `AudienceTilePagesMetric` and it's rendered once at a higher level instead. See https://github.com/google/site-kit-wp/issues/9543.
audienceTileNumber,
audienceSlug,
TileIcon,
title,
Expand All @@ -80,48 +70,12 @@ export default function AudienceTilePagesMetric( {
)
);

const hasAnalyticsEditScope = useSelect( ( select ) =>
select( CORE_USER ).hasScope( EDIT_SCOPE )
);

const redirectURL = addQueryArgs( global.location.href, {
notification: 'audience_segmentation',
widgetArea: AREA_MAIN_DASHBOARD_TRAFFIC_AUDIENCE_SEGMENTATION,
} );
const errorRedirectURL = addQueryArgs( global.location.href, {
widgetArea: AREA_MAIN_DASHBOARD_TRAFFIC_AUDIENCE_SEGMENTATION,
} );

const isAutoCreatingCustomDimensionsForAudience = useFormValue(
AUDIENCE_TILE_CUSTOM_DIMENSION_CREATE,
'isAutoCreatingCustomDimensionsForAudience'
);

const isCreatingCustomDimension = useSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).isCreatingCustomDimension(
postTypeDimension
)
);

const isSyncingAvailableCustomDimensions = useSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).isFetchingSyncAvailableCustomDimensions()
);

const customDimensionError = useSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).getCreateCustomDimensionError(
postTypeDimension
)
);

const propertyID = useSelect( ( select ) =>
select( MODULES_ANALYTICS_4 ).getPropertyID()
);

const { clearError } = useDispatch( MODULES_ANALYTICS_4 );
const { setValues } = useDispatch( CORE_FORMS );
const { setPermissionScopeError, clearPermissionScopeError } =
useDispatch( CORE_USER );

const isRetryingCustomDimensionCreate = useFormValue(
AUDIENCE_TILE_CUSTOM_DIMENSION_CREATE,
'isRetrying'
Expand All @@ -135,71 +89,25 @@ export default function AudienceTilePagesMetric( {
const setupErrorCode = useSelect( ( select ) =>
select( CORE_SITE ).getSetupErrorCode()
);
const { setSetupErrorCode } = useDispatch( CORE_SITE );

const hasOAuthError = autoSubmit && setupErrorCode === 'access_denied';

const onCreateCustomDimension = useCallback(
( { isRetrying } = {} ) => {
setValues( AUDIENCE_TILE_CUSTOM_DIMENSION_CREATE, {
autoSubmit: true,
isRetrying,
} );

if ( ! hasAnalyticsEditScope ) {
setPermissionScopeError( {
code: ERROR_CODE_MISSING_REQUIRED_SCOPE,
message: __(
'Additional permissions are required to create new audiences in Analytics.',
'google-site-kit'
),
data: {
status: 403,
scopes: [ EDIT_SCOPE ],
skipModal: true,
skipDefaultErrorNotifications: true,
redirectURL,
errorRedirectURL,
},
} );
}
},
[
hasAnalyticsEditScope,
redirectURL,
errorRedirectURL,
setPermissionScopeError,
setValues,
]
);

const onCancel = useCallback( () => {
setValues( AUDIENCE_TILE_CUSTOM_DIMENSION_CREATE, {
autoSubmit: false,
isRetrying: false,
} );
setSetupErrorCode( null );
clearPermissionScopeError();
clearError( 'createCustomDimension', [
propertyID,
CUSTOM_DIMENSION_DEFINITIONS.googlesitekit_post_type,
] );
}, [
clearError,
clearPermissionScopeError,
propertyID,
setSetupErrorCode,
setValues,
] );

const isMobileBreakpoint = [ BREAKPOINT_SMALL, BREAKPOINT_TABLET ].includes(
breakpoint
);

const isSaving =
isAutoCreatingCustomDimensionsForAudience ||
isCreatingCustomDimension ||
isSyncingAvailableCustomDimensions;
const { onCreateCustomDimension, isSaving, setShowErrorModal } =
useCreateCustomDimension();

const shouldShowErrorModal =
( customDimensionError && ! isSaving ) ||
// I've deliberately removed the check for `! isAutoCreatingCustomDimensionsForAudience` to fix a bug where the error modal would disappear while retrying.
isRetryingCustomDimensionCreate ||
Copy link
Collaborator Author

@techanvil techanvil Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've deliberately removed the check for ! isAutoCreatingCustomDimensionsForAudience (see the current implementation, below) to fix a bug where the error modal would disappear while retrying.

( ( customDimensionError && ! isSaving ) ||
( isRetryingCustomDimensionCreate &&
! isAutoCreatingCustomDimensionsForAudience ) ||
hasOAuthError ) && (

hasOAuthError;

useEffect( () => {
setShowErrorModal( shouldShowErrorModal );
}, [ shouldShowErrorModal, setShowErrorModal ] );

return (
<div className="googlesitekit-audience-segmentation-tile-metric googlesitekit-audience-segmentation-tile-metric--top-content">
Expand Down Expand Up @@ -235,42 +143,12 @@ export default function AudienceTilePagesMetric( {
onCreateCustomDimension={ onCreateCustomDimension }
isSaving={ isSaving }
/>
{ /*
TODO: The `audienceTileNumber` check is part of a temporary workaround to ensure `AudienceErrorModal` is only rendered once
within `AudienceTilesWidget`. This should be removed, and the `AudienceErrorModal` render extracted
from here to be rendered once at a higher level instead. See https://github.com/google/site-kit-wp/issues/9543.
*/ }
{ audienceTileNumber === 0 &&
( ( customDimensionError && ! isSaving ) ||
( isRetryingCustomDimensionCreate &&
! isAutoCreatingCustomDimensionsForAudience ) ||
hasOAuthError ) && (
<AudienceErrorModal
apiErrors={ [ customDimensionError ] }
title={ __(
'Failed to enable metric',
'google-site-kit'
) }
description={ __(
'Oops! Something went wrong. Retry enabling the metric.',
'google-site-kit'
) }
onRetry={ () =>
onCreateCustomDimension( { isRetrying: true } )
}
onCancel={ onCancel }
inProgress={ isSaving }
hasOAuthError={ hasOAuthError }
trackEventCategory={ `${ viewContext }_audiences-top-content-cta` }
/>
) }
</div>
</div>
);
}

AudienceTilePagesMetric.propTypes = {
audienceTileNumber: PropTypes.number,
audienceSlug: PropTypes.string.isRequired,
TileIcon: PropTypes.elementType.isRequired,
title: PropTypes.string.isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ import BadgeWithTooltip from '@/js/components/BadgeWithTooltip';
import useViewContext from '@/js/hooks/useViewContext';
import AudienceTileZeroData from './AudienceTileZeroData';
export default function AudienceTile( {
// TODO: The prop `audienceTileNumber` is part of a temporary workaround to ensure `AudienceErrorModal` is only rendered once
// within `AudienceTilesWidget`. This should be removed once the `AudienceErrorModal` render is extracted
// from `AudienceTilePagesMetric` and it's rendered once at a higher level instead. See https://github.com/google/site-kit-wp/issues/9543.
audienceTileNumber = 0,
audienceSlug,
title,
infoTooltip,
Expand Down Expand Up @@ -284,7 +280,6 @@ export default function AudienceTile( {
( postTypeDimensionExists &&
! hasInvalidCustomDimensionError ) ) && (
<AudienceTilePagesMetric
audienceTileNumber={ audienceTileNumber }
audienceSlug={ audienceSlug }
TileIcon={ AudienceMetricIconTopContent }
title={ __(
Expand All @@ -303,7 +298,6 @@ export default function AudienceTile( {
}

AudienceTile.propTypes = {
audienceTileNumber: PropTypes.number,
audienceSlug: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
infoTooltip: PropTypes.oneOfType( [ PropTypes.string, PropTypes.element ] ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,6 @@ export default function Body( {
isViewOnly,
] );

// TODO: The variable `audienceTileNumber` is part of a temporary workaround to ensure `AudienceErrorModal` is only rendered once
// within `AudienceTilesWidget`. This should be removed once the `AudienceErrorModal` render is extracted
// from `AudienceTilePagesMetric` and it's rendered once at a higher level instead. See https://github.com/google/site-kit-wp/issues/9543.
let audienceTileNumber = 0;

return (
<div className="googlesitekit-widget-audience-tiles__body">
{ allTilesError && ! loading && (
Expand Down Expand Up @@ -362,7 +357,6 @@ export default function Body( {
return (
<AudienceTile
key={ audienceResourceName }
audienceTileNumber={ audienceTileNumber++ }
audienceSlug={ audienceSlug }
title={ audienceName }
infoTooltip={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
useEffect,
useMemo,
useRef,
Fragment,
} from '@wordpress/element';

/**
Expand All @@ -47,6 +48,8 @@ import Body from './Body';
import Header from './Header';
import useAudienceTilesReports from '@/js/modules/analytics-4/hooks/useAudienceTilesReports';
import { isInvalidCustomDimensionError } from '@/js/modules/analytics-4/utils/custom-dimensions';
import CustomDimensionErrorModal from '@/js/modules/analytics-4/components/audience-segmentation/dashboard/CustomDimensionErrorModal';
import useCreateCustomDimension from '@/js/modules/analytics-4/components/audience-segmentation/dashboard/AudienceTilesWidget/hooks/useCreateCustomDimension';

function hasZeroDataForAudience( report, dimensionName ) {
const audienceData = report?.rows?.find(
Expand Down Expand Up @@ -266,6 +269,8 @@ export default function AudienceTiles( { Widget, widgetLoading } ) {

const activeTileIndex = getAudienceTileIndex( activeTile );

const { showErrorModal } = useCreateCustomDimension();

// Determine loading state.
const loading =
widgetLoading ||
Expand All @@ -278,31 +283,34 @@ export default function AudienceTiles( { Widget, widgetLoading } ) {
isSyncingAvailableCustomDimensions;

return (
<Widget className="googlesitekit-widget-audience-tiles" noPadding>
{ allTilesError === false &&
! loading &&
isTabbedBreakpoint &&
visibleAudiences.length > 0 && (
<Header
activeTileIndex={ activeTileIndex }
setActiveTile={ setActiveTile }
visibleAudiences={ visibleAudiences }
/>
) }
<Body
activeTileIndex={ activeTileIndex }
allTilesError={ allTilesError }
individualTileErrors={ individualTileErrors }
loading={ loading }
topCitiesReportsLoaded={ topCitiesReportsLoaded }
topContentReportsLoaded={ topContentReportsLoaded }
topContentPageTitlesReportsLoaded={
topContentPageTitlesReportsLoaded
}
visibleAudiences={ visibleAudiences }
Widget={ Widget }
/>
</Widget>
<Fragment>
<Widget className="googlesitekit-widget-audience-tiles" noPadding>
{ allTilesError === false &&
! loading &&
isTabbedBreakpoint &&
visibleAudiences.length > 0 && (
<Header
activeTileIndex={ activeTileIndex }
setActiveTile={ setActiveTile }
visibleAudiences={ visibleAudiences }
/>
) }
<Body
activeTileIndex={ activeTileIndex }
allTilesError={ allTilesError }
individualTileErrors={ individualTileErrors }
loading={ loading }
topCitiesReportsLoaded={ topCitiesReportsLoaded }
topContentReportsLoaded={ topContentReportsLoaded }
topContentPageTitlesReportsLoaded={
topContentPageTitlesReportsLoaded
}
visibleAudiences={ visibleAudiences }
Widget={ Widget }
/>
</Widget>
{ showErrorModal && <CustomDimensionErrorModal /> }
</Fragment>
);
}

Expand Down
Loading
Loading