Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SKFP-734 Add tags to Google Analytics 4 #3907

Merged
merged 2 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion src/components/Cavatica/AnalyzeButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BooleanOperators } from '@ferlab/ui/core/data/sqon/operators';
import { ISqonGroupFilter } from '@ferlab/ui/core/data/sqon/types';
import { CAVATICA_FILE_BATCH_SIZE } from 'views/DataExploration/utils/constant';

import { trackCavaticaAction } from 'services/analytics';
import { CavaticaApi } from 'services/api/cavatica';
import { ICavaticaCreateProjectBody } from 'services/api/cavatica/models';
import { fetchAllFencesAuthentificationStatus } from 'store/fences/thunks';
Expand All @@ -30,13 +31,15 @@ interface OwnProps {
sqon?: ISqonGroupFilter;
type?: 'default' | 'primary';
disabled?: boolean;
index: string;
}

const CavaticaAnalyzeButton: React.FC<OwnProps> = ({
fileIds,
sqon,
type = 'default',
disabled = false,
index,
}) => {
const dispatch = useDispatch();
const cavatica = useCavaticaPassport();
Expand All @@ -57,7 +60,7 @@ const CavaticaAnalyzeButton: React.FC<OwnProps> = ({

useEffect(() => {
dispatch(fetchAllFencesAuthentificationStatus());
}, []);
}, [dispatch]);

// If the user is not connected to cavatica
useEffect(() => {
Expand Down Expand Up @@ -88,6 +91,7 @@ const CavaticaAnalyzeButton: React.FC<OwnProps> = ({
dispatch(passportActions.setCavaticaBulkImportDataStatus(CAVATICA_ANALYSE_STATUS.unknow));
}}
handleImportBulkData={(value) => {
trackCavaticaAction(index);
dispatch(startBulkImportJob(value));
}}
createProjectModalProps={{
Expand Down
3 changes: 3 additions & 0 deletions src/components/Layout/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import LineStyleIcon from 'components/Icons/LineStyleIcon';
import HeaderLink from 'components/Layout/Header/HeaderLink';
import styles from 'components/Layout/Header/index.module.scss';
import GradientAccent from 'components/uiKit/GradientAccent';
import { trackLogout, trackVisitResources } from 'services/analytics';
import { usePersona } from 'store/persona';
import { personaActions } from 'store/persona/slice';
import { userActions } from 'store/user/slice';
Expand Down Expand Up @@ -117,6 +118,7 @@ const Header = () => {
trigger={['click']}
overlay={
<Menu
onClick={({ key }) => trackVisitResources(key)}
items={[
{
key: 'website',
Expand Down Expand Up @@ -234,6 +236,7 @@ const Header = () => {
</Space>
),
onClick: () => {
trackLogout();
dispatch(personaActions.cleanLogout());
dispatch(userActions.cleanLogout());
window.sessionStorage.clear();
Expand Down
3 changes: 3 additions & 0 deletions src/components/uiKit/FilterList/CustomFilterContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IExtendedMappingResults, IGqlResults } from '@ferlab/ui/core/graphql/ty
import { getFilters } from 'graphql/utils/Filters';
import { isUndefined } from 'lodash';

import { trackFacetSearch } from 'services/analytics';
import { getFacetsDictionary, getFiltersDictionary } from 'utils/translation';

import CustomFilterSelector from './CustomFilterSelector';
Expand Down Expand Up @@ -58,6 +59,8 @@ const CustomFilterContainer = ({
}, [filtersOpen]);

const onChange = (fg: IFilterGroup, f: IFilter[]) => {
trackFacetSearch(index, fg.field);

updateActiveQueryFilters({
queryBuilderId,
filterGroup: fg,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useCallback, useEffect, useState } from 'react';
import Empty from '@ferlab/ui/core/components/Empty';
import { Select, Tag } from 'antd';
import debounce from 'lodash/debounce';
import take from 'lodash/take';
import Empty from '@ferlab/ui/core/components/Empty';

import SearchLabel from 'components/uiKit/search/SearchLabel';

export type OptionsType = {
Expand Down
8 changes: 5 additions & 3 deletions src/components/uiKit/search/GlobalSearch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { get } from 'lodash';

import Search, { TCustomHandleSearch } from 'components/uiKit/search/GlobalSearch/Search';
import { OptionsType } from 'components/uiKit/search/GlobalSearch/Search/SearchAutocomplete';
import { trackFacetSearch } from 'services/analytics';

export interface ICustomSearchProps {
queryBuilderId: string;
Expand Down Expand Up @@ -42,15 +43,16 @@ const GlobalSearch = <T,>({
tooltipText,
}: OwnProps<T>) => (
<Search<T>
onSelect={(values) =>
onSelect={(values) => {
trackFacetSearch(index, field);
updateActiveQueryField({
queryBuilderId,
field,
value: values,
index,
merge_strategy: MERGE_VALUES_STRATEGIES.OVERRIDE_VALUES,
})
}
});
}}
index={index}
tooltipText={tooltipText}
emptyDescription={emptyDescription}
Expand Down
23 changes: 11 additions & 12 deletions src/provider/KeycloakProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import React, { ReactElement } from "react";
import {
AuthClientError,
AuthClientEvent,
} from "@react-keycloak/core/"
import EnvVariables from "helpers/EnvVariables";
import { ReactKeycloakProvider as KeycloakProvider } from "@react-keycloak/web";
import keycloak from "auth/keycloak-api/keycloak";
import { trackAuthSuccess } from "services/analytics";
import React, { ReactElement } from 'react';
import { AuthClientError, AuthClientEvent } from '@react-keycloak/core/';
import { ReactKeycloakProvider as KeycloakProvider } from '@react-keycloak/web';
import keycloak from 'auth/keycloak-api/keycloak';
import EnvVariables from 'helpers/EnvVariables';

import { trackAuthError, trackAuthSuccess } from 'services/analytics';

export interface IProvider {
children: React.ReactNode;
}

const eventLogger = (eventType: AuthClientEvent, error?: AuthClientError) => {
if (EnvVariables.configFor("ENV") === "development" && error) {
console.error("eventLogger ", "eventType ", eventType);
console.error("eventLogger ", error);
if (EnvVariables.configFor('ENV') === 'development' && error) {
trackAuthError();
console.error('eventLogger ', 'eventType ', eventType);
console.error('eventLogger ', error);
}

if (eventType === 'onAuthSuccess') {
Expand Down
151 changes: 150 additions & 1 deletion src/services/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import EnvironmentVariables from 'helpers/EnvVariables';
import ReactGA from 'react-ga4';
import EnvironmentVariables from 'helpers/EnvVariables';
import { capitalize } from 'lodash';
import { FilterActionType } from 'views/DataExploration/components/PageContent';
import { SetActionType } from 'views/DataExploration/components/SetsManagementDropdown';

import { SetType } from 'services/api/savedSet/models';

const measurementId = EnvironmentVariables.configFor('MEASUREMENT_ID');
const isDev = EnvironmentVariables.configFor('ENV') === 'development';
Expand All @@ -19,3 +24,147 @@ export const trackAuthSuccess = () => {
});
}
};

export const trackAuthError = () => {
if (isGaActive) {
ReactGA.event({
category: 'Authentication',
action: 'login failed',
});
}
};

export const trackLogout = () => {
if (isGaActive) {
ReactGA.event({
category: 'Authentication',
action: 'logout',
});
}
};

export const trackFacetSearch = (page: string, field: string) => {
if (isGaActive) {
ReactGA.event({
category: 'FacetSearch',
action: `${capitalize(page)} -- ${field}`,
});
}
};

export const trackReportDownload = (reportCategory: string) => {
if (isGaActive) {
ReactGA.event({
category: 'Reports',
action: `report download -- ${reportCategory}`,
});
}
};

/**
* DONE needs to be tested
**/
export const trackCavaticaAction = (page: string) => {
if (isGaActive) {
ReactGA.event({
category: 'Cavatica',
action: `Analyze -- ${capitalize(page)}`,
});
}
};

export const trackSetActions = (action: string, setType: SetType) => {
let message = '';
switch (action) {
case SetActionType.CREATE_SET:
message = 'Creating new Set';
break;
case SetActionType.UPDATE_SET:
message = 'Updating existing Set';
break;
case SetActionType.REMOVE_SET:
message = 'Removing existing Set';
break;
case SetActionType.ADD_IDS:
message = 'Adding file(s) to existing Set';
break;
case SetActionType.REMOVE_IDS:
message = 'Removing file(s) from existing Set';
break;
default:
message = 'Unknown Action';
break;
}
if (isGaActive) {
ReactGA.event({
category: 'Sets',
action: `${setType} - ${message}`,
});
}
};

export const trackFilterActions = (action: string, tabId: string) => {
let message = '';
switch (action) {
case FilterActionType.CREATE_FILTER:
message = 'Creating new Filter';
break;
aperron-ferlab marked this conversation as resolved.
Show resolved Hide resolved
case FilterActionType.UPDATE_FILTER:
message = 'Updating existing Filter';
break;
case FilterActionType.REMOVE_FILTER:
message = 'Removing existing Filter';
break;
case FilterActionType.FAVORITE_FILTER:
message = 'Adding Filter to favorites';
break;
case FilterActionType.SHARE_FILTER:
message = 'Sharing Filter';
break;
default:
message = 'Unknown Action';
break;
}
if (isGaActive) {
ReactGA.event({
category: 'Filters',
action: `${tabId} - ${message}`,
});
}
};

export const trackVisitResources = (resource: string) => {
if (isGaActive) {
ReactGA.event({
category: 'Resources',
action: `Visit -- ${resource}`,
});
}
};

export const trackRegistrationStarted = () => {
if (isGaActive) {
ReactGA.event({
category: 'Registration',
action: 'Registration started',
});
}
};

export const trackNCIConnection = (connected: boolean) => {
if (isGaActive) {
ReactGA.event({
category: 'FencesConnections',
action: `${connected ? 'Connected to' : 'Disconnected from'} NCI CRDC Framework Services`,
});
}
};

export const trackKFConnection = (connected: boolean) => {
if (isGaActive) {
ReactGA.event({
category: 'FencesConnections',
action: `${connected ? 'Connected to' : 'Disconnected from'} Kids First Framework Services`,
});
}
};
4 changes: 3 additions & 1 deletion src/services/api/reports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import EnvironmentVariables from 'helpers/EnvVariables';
import isEmpty from 'lodash/isEmpty';

import downloader from 'common/downloader';
import { trackReportDownload } from 'services/analytics';

import { ReportConfig, ReportType } from './models';

Expand Down Expand Up @@ -42,7 +43,8 @@ export const headers = () => ({
});

const generateReport = (config: ReportConfig) => {
//TODO do we need google analytics tracking?
trackReportDownload(config.name);

let reportSqon;

if (!config.sqon || isEmpty(config.sqon)) {
Expand Down
7 changes: 6 additions & 1 deletion src/store/report/thunks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { startCase } from 'lodash';
import { v4 } from 'uuid';

import { getDefaultContentType } from 'common/downloader';
import { trackReportDownload } from 'services/analytics';
import { ArrangerApi } from 'services/api/arranger';
import { ArrangerColumnStateResults } from 'services/api/arranger/models';
import { ReportApi } from 'services/api/reports';
Expand Down Expand Up @@ -61,7 +62,7 @@ const fetchReport = createAsyncThunk<
duration: 0,
}),
);
await ReportApi.generateReport(args.data).then((_) => {
await ReportApi.generateReport(args.data).then(() => {
thunkAPI.dispatch(globalActions.destroyMessages([messageKey]));
thunkAPI.dispatch(
globalActions.displayNotification({
Expand All @@ -82,6 +83,8 @@ const fetchTsvReport = createAsyncThunk<void, TFetchTSVArgs, { rejectValue: stri
async (args, thunkAPI) => {
const messageKey = 'report_pending';

trackReportDownload(`${args.index}Tsv`);

thunkAPI.dispatch(
globalActions.displayMessage({
type: 'loading',
Expand Down Expand Up @@ -150,6 +153,8 @@ const generateLocalTsvReport = createAsyncThunk<
// !! This function assumes that it is called only when the table is not empty. Said otherwise, data is never empty !!
const messageKey = 'report_pending';

trackReportDownload(`${args.index}-${args.fileName}-tsv`);

try {
const formattedDate = format(new Date(), 'yyyy-MM-dd');
const formattedFileName = `kidsfirst-${args.fileName ?? args.index}-table-${formattedDate}.tsv`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DATA_EXPLORATION_QB_ID } from 'views/DataExploration/utils/constant';
import { FENCE_NAMES } from 'common/fenceTypes';
import KidsFirstLoginIcon from 'components/Icons/KidsFirstLoginIcon';
import NciIcon from 'components/Icons/NciIcon';
import { trackKFConnection, trackNCIConnection } from 'services/analytics';
import { useFenceAuthentification, useFencesAuthorizedStudies } from 'store/fences';
import {
fenceDisconnection,
Expand All @@ -34,9 +35,11 @@ const AuthorizedStudies = ({ id, className = '' }: DashboardCardProps) => {
name: 'Kids First Framework Services',
icon: <KidsFirstLoginIcon width={45} height={45} />,
onConnectToFence: () => {
trackKFConnection(true);
dispatch(fenceOpenAuhentificationTab(FENCE_NAMES.gen3));
},
onDisconnectFromFence: () => {
trackKFConnection(false);
dispatch(fenceDisconnection(FENCE_NAMES.gen3));
},
},
Expand All @@ -45,9 +48,11 @@ const AuthorizedStudies = ({ id, className = '' }: DashboardCardProps) => {
name: 'NCI CRDC Framework Services',
icon: <NciIcon width={45} height={45} />,
onConnectToFence: () => {
trackNCIConnection(true);
dispatch(fenceOpenAuhentificationTab(FENCE_NAMES.dcf));
},
onDisconnectFromFence: () => {
trackNCIConnection(false);
dispatch(fenceDisconnection(FENCE_NAMES.dcf));
},
},
Expand Down
Loading
Loading