chore: add event tracking for billing (#7277)

fix AFF-1226
This commit is contained in:
pengx17
2024-06-20 03:33:07 +00:00
parent ddf72733e1
commit a085e507b4
8 changed files with 127 additions and 9 deletions

View File

@@ -224,7 +224,16 @@ const SubscriptionSettings = () => {
> >
<SettingRow <SettingRow
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
onClick={() => setOpenCancelModal(true)} onClick={() => {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'billing subscription list',
control: 'plan cancel action',
type: proSubscription.plan,
category: proSubscription.recurring,
});
setOpenCancelModal(true);
}}
className="dangerous-setting" className="dangerous-setting"
name={t[ name={t[
'com.affine.payment.billing-setting.cancel-subscription' 'com.affine.payment.billing-setting.cancel-subscription'
@@ -369,10 +378,21 @@ const PaymentMethodUpdater = () => {
const ResumeSubscription = () => { const ResumeSubscription = () => {
const t = useI18n(); const t = useI18n();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const subscription = useService(SubscriptionService).subscription;
const handleClick = useCallback(() => {
setOpen(true);
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'plan resume action',
type: subscription.pro$.value?.plan,
category: subscription.pro$.value?.recurring,
});
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
return ( return (
<ResumeAction open={open} onOpenChange={setOpen}> <ResumeAction open={open} onOpenChange={setOpen}>
<Button className={styles.button} onClick={() => setOpen(true)}> <Button className={styles.button} onClick={handleClick}>
{t['com.affine.payment.billing-setting.resume-subscription']()} {t['com.affine.payment.billing-setting.resume-subscription']()}
</Button> </Button>
</ResumeAction> </ResumeAction>

View File

@@ -1,4 +1,5 @@
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { mixpanel } from '@affine/core/utils';
import { useService } from '@toeverything/infra'; import { useService } from '@toeverything/infra';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
@@ -33,6 +34,13 @@ export const CancelAction = ({
// refresh idempotency key // refresh idempotency key
setIdempotencyKey(nanoid()); setIdempotencyKey(nanoid());
onOpenChange(false); onOpenChange(false);
mixpanel.track('ChangePlanSucceeded', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'plan cancel action',
type: subscription.pro$.value?.plan,
category: subscription.pro$.value?.recurring,
});
} finally { } finally {
setIsMutating(false); setIsMutating(false);
} }
@@ -78,6 +86,13 @@ export const ResumeAction = ({
// refresh idempotency key // refresh idempotency key
setIdempotencyKey(nanoid()); setIdempotencyKey(nanoid());
onOpenChange(false); onOpenChange(false);
mixpanel.track('ChangePlanSucceeded', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'plan resume action',
type: subscription.pro$.value?.plan,
category: subscription.pro$.value?.recurring,
});
} finally { } finally {
setIsMutating(false); setIsMutating(false);
} }

View File

@@ -1,6 +1,7 @@
import { Button, type ButtonProps, useConfirmModal } from '@affine/component'; import { Button, type ButtonProps, useConfirmModal } from '@affine/component';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { SubscriptionService } from '@affine/core/modules/cloud'; import { SubscriptionService } from '@affine/core/modules/cloud';
import { mixpanel } from '@affine/core/utils';
import { SubscriptionPlan } from '@affine/graphql'; import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { useService } from '@toeverything/infra'; import { useService } from '@toeverything/infra';
@@ -17,6 +18,12 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
const { openConfirmModal } = useConfirmModal(); const { openConfirmModal } = useConfirmModal();
const cancel = useAsyncCallback(async () => { const cancel = useAsyncCallback(async () => {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
control: 'plan cancel action',
type: subscription.ai$.value?.plan,
category: subscription.ai$.value?.recurring,
});
openConfirmModal({ openConfirmModal({
title: t['com.affine.payment.ai.action.cancel.confirm.title'](), title: t['com.affine.payment.ai.action.cancel.confirm.title'](),
description: description:
@@ -40,6 +47,10 @@ export const AICancel = ({ ...btnProps }: AICancelProps) => {
SubscriptionPlan.AI SubscriptionPlan.AI
); );
setIdempotencyKey(nanoid()); setIdempotencyKey(nanoid());
mixpanel.track('ChangePlanSucceeded', {
segment: 'settings panel',
control: 'plan cancel action',
});
} finally { } finally {
setMutating(false); setMutating(false);
} }

View File

@@ -6,6 +6,7 @@ import {
} from '@affine/component'; } from '@affine/component';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { SubscriptionService } from '@affine/core/modules/cloud'; import { SubscriptionService } from '@affine/core/modules/cloud';
import { mixpanel } from '@affine/core/utils';
import { SubscriptionPlan } from '@affine/graphql'; import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons/rc'; import { SingleSelectSelectSolidIcon } from '@blocksuite/icons/rc';
@@ -26,6 +27,13 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
const { openConfirmModal } = useConfirmModal(); const { openConfirmModal } = useConfirmModal();
const resume = useAsyncCallback(async () => { const resume = useAsyncCallback(async () => {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
control: 'plan resume action',
type: subscription.ai$.value?.plan,
category: subscription.ai$.value?.recurring,
});
openConfirmModal({ openConfirmModal({
title: t['com.affine.payment.ai.action.resume.confirm.title'](), title: t['com.affine.payment.ai.action.resume.confirm.title'](),
description: description:
@@ -43,6 +51,10 @@ export const AIResume = ({ ...btnProps }: AIResumeProps) => {
idempotencyKey, idempotencyKey,
SubscriptionPlan.AI SubscriptionPlan.AI
); );
mixpanel.track('ChangePlanSucceeded', {
segment: 'settings panel',
control: 'plan resume action',
});
notify({ notify({
icon: <SingleSelectSelectSolidIcon />, icon: <SingleSelectSelectSolidIcon />,
iconColor: cssVar('processingColor'), iconColor: cssVar('processingColor'),

View File

@@ -42,7 +42,7 @@ export const AISubscribe = ({ ...btnProps }: AISubscribeProps) => {
const subscribe = useAsyncCallback(async () => { const subscribe = useAsyncCallback(async () => {
setMutating(true); setMutating(true);
mixpanel.track('plan upgrade started', { mixpanel.track('PlanUpgradeStarted', {
category: SubscriptionRecurring.Yearly, category: SubscriptionRecurring.Yearly,
type: SubscriptionPlan.AI, type: SubscriptionPlan.AI,
}); });

View File

@@ -167,11 +167,23 @@ const CurrentPlan = () => {
const Downgrade = ({ disabled }: { disabled?: boolean }) => { const Downgrade = ({ disabled }: { disabled?: boolean }) => {
const t = useI18n(); const t = useI18n();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const subscription = useService(SubscriptionService).subscription;
const tooltipContent = disabled const tooltipContent = disabled
? t['com.affine.payment.downgraded-tooltip']() ? t['com.affine.payment.downgraded-tooltip']()
: null; : null;
const handleClick = useCallback(() => {
setOpen(true);
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'billing cancel action',
type: subscription.pro$.value?.plan,
category: subscription.pro$.value?.recurring,
});
}, [subscription.pro$.value?.plan, subscription.pro$.value?.recurring]);
return ( return (
<CancelAction open={open} onOpenChange={setOpen}> <CancelAction open={open} onOpenChange={setOpen}>
<Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}> <Tooltip content={tooltipContent} rootOptions={{ delayDuration: 0 }}>
@@ -179,7 +191,7 @@ const Downgrade = ({ disabled }: { disabled?: boolean }) => {
<Button <Button
className={styles.planAction} className={styles.planAction}
type="primary" type="primary"
onClick={() => setOpen(true)} onClick={handleClick}
disabled={disabled} disabled={disabled}
> >
{t['com.affine.payment.downgrade']()} {t['com.affine.payment.downgrade']()}
@@ -257,6 +269,13 @@ const Upgrade = ({ recurring }: { recurring: SubscriptionRecurring }) => {
} }
setMutating(true); setMutating(true);
mixpanel.track('PlanUpgradeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'pricing plan action',
type: 'cloud pro subscription',
category: recurring,
});
const link = await subscriptionService.createCheckoutSession({ const link = await subscriptionService.createCheckoutSession({
recurring, recurring,
idempotencyKey, idempotencyKey,
@@ -268,7 +287,6 @@ const Upgrade = ({ recurring }: { recurring: SubscriptionRecurring }) => {
setIdempotencyKey(nanoid()); setIdempotencyKey(nanoid());
popupWindow(link); popupWindow(link);
setOpenedExternalWindow(true); setOpenedExternalWindow(true);
mixpanel.track_forms('Subscription', SubscriptionPlan.Pro);
}, [openPaymentDisableModal, subscriptionService, recurring, idempotencyKey]); }, [openPaymentDisableModal, subscriptionService, recurring, idempotencyKey]);
return ( return (
@@ -302,6 +320,17 @@ const ChangeRecurring = ({
const [idempotencyKey, setIdempotencyKey] = useState(nanoid()); const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const subscription = useService(SubscriptionService).subscription; const subscription = useService(SubscriptionService).subscription;
const onStartChange = useCallback(() => {
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'plan resume action',
type: 'cloud pro subscription',
category: to,
});
setOpen(true);
}, [to]);
const change = useAsyncCallback(async () => { const change = useAsyncCallback(async () => {
setIsMutating(true); setIsMutating(true);
await subscription.setSubscriptionRecurring(idempotencyKey, to); await subscription.setSubscriptionRecurring(idempotencyKey, to);
@@ -327,7 +356,7 @@ const ChangeRecurring = ({
<Button <Button
className={styles.planAction} className={styles.planAction}
type="primary" type="primary"
onClick={() => setOpen(true)} onClick={onStartChange}
disabled={disabled || isMutating} disabled={disabled || isMutating}
loading={isMutating} loading={isMutating}
> >
@@ -371,6 +400,18 @@ const ResumeButton = () => {
const t = useI18n(); const t = useI18n();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const subscription = useService(SubscriptionService).subscription;
const handleClick = useCallback(() => {
setOpen(true);
mixpanel.track('PlanChangeStarted', {
segment: 'settings panel',
module: 'pricing plan list',
control: 'pricing plan action',
type: 'cloud pro subscription',
category: subscription.pro$.value?.recurring,
});
}, [subscription.pro$.value?.recurring]);
return ( return (
<ResumeAction open={open} onOpenChange={setOpen}> <ResumeAction open={open} onOpenChange={setOpen}>
@@ -378,7 +419,7 @@ const ResumeButton = () => {
className={styles.planAction} className={styles.planAction}
onMouseEnter={() => setHovered(true)} onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)} onMouseLeave={() => setHovered(false)}
onClick={() => setOpen(true)} onClick={handleClick}
> >
{hovered {hovered
? t['com.affine.payment.resume-renewal']() ? t['com.affine.payment.resume-renewal']()

View File

@@ -1,8 +1,10 @@
import { AuthPageContainer } from '@affine/component/auth-components'; import { AuthPageContainer } from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { SubscriptionPlan } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n'; import { Trans, useI18n } from '@affine/i18n';
import { type ReactNode, useCallback } from 'react'; import mixpanel from 'mixpanel-browser';
import { type ReactNode, useCallback, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import * as styles from './styles.css'; import * as styles from './styles.css';
@@ -56,6 +58,13 @@ const UpgradeSuccessLayout = ({
export const CloudUpgradeSuccess = () => { export const CloudUpgradeSuccess = () => {
const t = useI18n(); const t = useI18n();
useEffect(() => {
mixpanel.track('PlanUpgradeSucceeded', {
segment: 'settings panel',
control: 'plan upgrade action',
plan: SubscriptionPlan.Pro,
});
}, []);
return ( return (
<UpgradeSuccessLayout <UpgradeSuccessLayout
title={t['com.affine.payment.upgrade-success-page.title']()} title={t['com.affine.payment.upgrade-success-page.title']()}
@@ -66,7 +75,13 @@ export const CloudUpgradeSuccess = () => {
export const AIUpgradeSuccess = () => { export const AIUpgradeSuccess = () => {
const t = useI18n(); const t = useI18n();
useEffect(() => {
mixpanel.track('PlanUpgradeSucceeded', {
segment: 'settings panel',
control: 'plan upgrade action',
plan: SubscriptionPlan.Pro,
});
}, []);
return ( return (
<UpgradeSuccessLayout <UpgradeSuccessLayout
title={t['com.affine.payment.ai-upgrade-success-page.title']()} title={t['com.affine.payment.ai-upgrade-success-page.title']()}

View File

@@ -53,6 +53,10 @@ export const Component = () => {
: !!subscriptionService.subscription.pro$.value; : !!subscriptionService.subscription.pro$.value;
if (!subscribed) { if (!subscribed) {
setMessage('Creating checkout...'); setMessage('Creating checkout...');
mixpanel.track('PlanUpgradeStarted', {
type: plan,
category: recurring,
});
try { try {
const checkout = await subscriptionService.createCheckoutSession({ const checkout = await subscriptionService.createCheckoutSession({
idempotencyKey, idempotencyKey,