Skip to content

Commit

Permalink
feat: [IOBP-870] Add bonus & payments profile and super properties on…
Browse files Browse the repository at this point in the history
… mixpanel (#6391)

## Short description
This PR adds tracking for super properties and profile properties in the
bonus and payments domains. This tracks the number of saved payment
methods, whether the user has an active CGN card, and the list of
enabled welfare (idpay) names. The tracking is triggered upon opening
the user’s wallet section.

## List of changes proposed in this pull request
- Added the property `CGN_STATUS`, `WELFARE_STATUS` and
`SAVED_PAYMENT_METHOD`;
- Added a trigger to update the profile and super properties on mixpanel
inside the ts/features/newWallet/saga/index.ts saga that fires the
update when the all three requests are done (both in failure or in
success);

## How to test
Open the wallet screen and check that the profile and super properties
that are sent to mixpanel are populated as expected

---------

Co-authored-by: Emanuele Dall'Ara <[email protected]>
  • Loading branch information
Hantex9 and LeleDallas authored Nov 15, 2024
1 parent c9efcfe commit 3bba229
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 7 deletions.
2 changes: 2 additions & 0 deletions ts/features/bonus/cgn/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { cgnCodeFromBucket } from "../store/actions/bucket";
import { cgnUnsubscribe } from "../store/actions/unsubscribe";
import { cgnCategories } from "../store/actions/categories";

export type TrackCgnStatus = "active" | "not_active";

const trackCgnAction =
(mp: NonNullable<typeof mixpanel>) =>
// eslint-disable-next-line complexity
Expand Down
50 changes: 49 additions & 1 deletion ts/features/newWallet/saga/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import { Millisecond } from "@pagopa/ts-commons/lib/units";
import { SagaIterator } from "redux-saga";
import { put, select, takeLatest, takeLeading } from "typed-redux-saga/macro";
import {
all,
call,
put,
select,
take,
takeLatest,
takeLeading
} from "typed-redux-saga/macro";
// We need to disable the eslint rule because the types are not available for the following imports
// eslint-disable-next-line @jambit/typed-redux-saga/use-typed-effects
import { StrictEffect } from "redux-saga/effects";
import { walletAddCards } from "../store/actions/cards";
import { walletToggleLoadingState } from "../store/actions/placeholders";
import {
selectWalletCards,
selectWalletPlaceholders
} from "../store/selectors";
import { getPaymentsWalletUserMethods } from "../../payments/wallet/store/actions";
import { idPayWalletGet } from "../../idpay/wallet/store/actions";
import { cgnDetails } from "../../bonus/cgn/store/actions/details";
import { GlobalState } from "../../../store/reducers/types";
import { isIdPayEnabledSelector } from "../../../store/reducers/backendStatus/remoteConfig";
import { updateMixpanelProfileProperties } from "../../../mixpanelConfig/profileProperties";
import { updateMixpanelSuperProperties } from "../../../mixpanelConfig/superProperties";
import { handleWalletPlaceholdersTimeout } from "./handleWalletLoadingPlaceholdersTimeout";
import { handleWalletLoadingStateSaga } from "./handleWalletLoadingStateSaga";

Expand All @@ -30,4 +48,34 @@ export function* watchWalletSaga(): SagaIterator {
handleWalletPlaceholdersTimeout,
LOADING_STATE_TIMEOUT
);

const isIdPayEnabled = yield* select(isIdPayEnabledSelector);

const bonusAndPaymentsSagas = [
call(waitForPaymentMethods),
call(waitForCgnDetails),
...(isIdPayEnabled ? [call(waitForIdPay)] : [])
] as unknown as Array<StrictEffect>;

yield* all(bonusAndPaymentsSagas);

const state: GlobalState = yield* select();

void updateMixpanelProfileProperties(state);
void updateMixpanelSuperProperties(state);
}

function* waitForPaymentMethods(): SagaIterator {
yield* take([
getPaymentsWalletUserMethods.success,
getPaymentsWalletUserMethods.failure
]);
}

function* waitForIdPay(): SagaIterator {
yield* take([idPayWalletGet.success, idPayWalletGet.failure]);
}

function* waitForCgnDetails(): SagaIterator {
yield* take([cgnDetails.success, cgnDetails.failure]);
}
14 changes: 14 additions & 0 deletions ts/features/newWallet/store/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,17 @@ export const selectWalletCategoryFilter = createSelector(
selectWalletFeature,
wallet => wallet.preferences.categoryFilter
);

export const selectWalletPaymentMethods = createSelector(
selectSortedWalletCards,
cards => cards.filter(({ category }) => category === "payment")
);

export const selectWalletCgnCard = createSelector(
selectSortedWalletCards,
cards => cards.filter(({ category }) => category === "cgn")
);

export const selectBonusCards = createSelector(selectSortedWalletCards, cards =>
cards.filter(({ category }) => category === "bonus")
);
28 changes: 28 additions & 0 deletions ts/mixpanelConfig/mixpanelPropertyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import {
} from "../store/reducers/profile";
import { fastLoginOptInSelector } from "../features/fastLogin/store/selectors";
import { ServicesPreferencesModeEnum } from "../../definitions/backend/ServicesPreferencesMode";
import {
selectBonusCards,
selectWalletCgnCard,
selectWalletPaymentMethods
} from "../features/newWallet/store/selectors";
import { TrackCgnStatus } from "../features/bonus/cgn/analytics";
import { WalletCardBonus } from "../features/newWallet/types";
import { isMixpanelEnabled } from "./../store/reducers/persistedPreferences";

export type Property<K, T extends keyof K> = {
Expand Down Expand Up @@ -72,3 +79,24 @@ export const mixpanelOptInHandler = (
? "accepted"
: "declined";
};

export const paymentMethodsHandler = (state: GlobalState): number => {
const walletPaymentMethods = selectWalletPaymentMethods(state);
return walletPaymentMethods.length ?? 0;
};

export const cgnStatusHandler = (state: GlobalState): TrackCgnStatus => {
const cgnCard = selectWalletCgnCard(state);
return cgnCard.length > 0 ? "active" : "not_active";
};

export const welfareStatusHandler = (
state: GlobalState
): ReadonlyArray<string> => {
const bonusCards = selectBonusCards(state);
const idPayCards = bonusCards.filter(
card => card.type === "idPay"
) as Array<WalletCardBonus>;

return idPayCards.map(card => card.name);
};
17 changes: 13 additions & 4 deletions ts/mixpanelConfig/profileProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { idpSelector } from "../store/reducers/authentication";
import { tosVersionSelector } from "../store/reducers/profile";
import { checkNotificationPermissions } from "../features/pushNotifications/utils";
import { getPaymentsAnalyticsConfiguration } from "../features/payments/common/store/selectors";
import {
ItwCed,
ItwId,
Expand All @@ -24,14 +23,18 @@ import {
itwCredentialsByTypeSelector,
itwCredentialsSelector
} from "../features/itwallet/credentials/store/selectors";
import { TrackCgnStatus } from "../features/bonus/cgn/analytics";
import {
cgnStatusHandler,
loginSessionConfigHandler,
mixpanelOptInHandler,
MixpanelOptInTrackingType,
notificationConfigurationHandler,
paymentMethodsHandler,
Property,
PropertyToUpdate,
serviceConfigHandler
serviceConfigHandler,
welfareStatusHandler
} from "./mixpanelPropertyUtils";

type ProfileProperties = {
Expand All @@ -49,6 +52,8 @@ type ProfileProperties = {
ITW_TS_V2: ItwTs;
ITW_CED_V2: ItwCed;
SAVED_PAYMENT_METHOD: number;
CGN_STATUS: TrackCgnStatus;
WELFARE_STATUS: ReadonlyArray<string>;
};

export const updateMixpanelProfileProperties = async (
Expand All @@ -71,7 +76,9 @@ export const updateMixpanelProfileProperties = async (
const ITW_PG_V2 = pgStatusHandler(state);
const ITW_TS_V2 = tsStatusHandler(state);
const ITW_CED_V2 = cedStatusHandler(state);
const paymentsAnalyticsData = getPaymentsAnalyticsConfiguration(state);
const SAVED_PAYMENT_METHOD = paymentMethodsHandler(state);
const CGN_STATUS = cgnStatusHandler(state);
const WELFARE_STATUS = welfareStatusHandler(state);

const profilePropertiesObject: ProfileProperties = {
LOGIN_SESSION,
Expand All @@ -88,7 +95,9 @@ export const updateMixpanelProfileProperties = async (
ITW_PG_V2,
ITW_TS_V2,
ITW_CED_V2,
SAVED_PAYMENT_METHOD: paymentsAnalyticsData.savedPaymentMethods || 0
SAVED_PAYMENT_METHOD,
CGN_STATUS,
WELFARE_STATUS
};

if (forceUpdateFor) {
Expand Down
17 changes: 15 additions & 2 deletions ts/mixpanelConfig/superProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ import {
itwCredentialsByTypeSelector,
itwCredentialsSelector
} from "../features/itwallet/credentials/store/selectors";
import { TrackCgnStatus } from "../features/bonus/cgn/analytics";
import {
cgnStatusHandler,
loginSessionConfigHandler,
notificationConfigurationHandler,
paymentMethodsHandler,
Property,
PropertyToUpdate,
serviceConfigHandler
serviceConfigHandler,
welfareStatusHandler
} from "./mixpanelPropertyUtils";

type SuperProperties = {
Expand All @@ -54,6 +58,9 @@ type SuperProperties = {
ITW_PG_V2: ItwPg;
ITW_TS_V2: ItwTs;
ITW_CED_V2: ItwCed;
SAVED_PAYMENT_METHOD: number;
CGN_STATUS: TrackCgnStatus;
WELFARE_STATUS: ReadonlyArray<string>;
};

export const updateMixpanelSuperProperties = async (
Expand All @@ -77,6 +84,9 @@ export const updateMixpanelSuperProperties = async (
const ITW_PG_V2 = pgStatusHandler(state);
const ITW_TS_V2 = tsStatusHandler(state);
const ITW_CED_V2 = cedStatusHandler(state);
const SAVED_PAYMENT_METHOD = paymentMethodsHandler(state);
const CGN_STATUS = cgnStatusHandler(state);
const WELFARE_STATUS = welfareStatusHandler(state);

const superPropertiesObject: SuperProperties = {
isScreenReaderEnabled: screenReaderEnabled,
Expand All @@ -94,7 +104,10 @@ export const updateMixpanelSuperProperties = async (
ITW_ID_V2,
ITW_PG_V2,
ITW_TS_V2,
ITW_CED_V2
ITW_CED_V2,
SAVED_PAYMENT_METHOD,
CGN_STATUS,
WELFARE_STATUS
};

if (forceUpdateFor) {
Expand Down

0 comments on commit 3bba229

Please sign in to comment.