Skip to content

Commit d3dc678

Browse files
authored
Feature: Fathom event tracking (#961)
1 parent e90d924 commit d3dc678

File tree

13 files changed

+121
-13
lines changed

13 files changed

+121
-13
lines changed

frontend/src/components/common/CheckInStatusModal/index.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ export const CheckInStatusModal = ({
2424
}: CheckInStatusModalProps) => {
2525
const {data: checkInListsResponse, isLoading, ...rest} = useGetEventCheckInLists(eventId);
2626

27-
console.log('CheckInStatusModal - checkInListsResponse:', checkInListsResponse, 'isLoading:', isLoading, 'rest:', rest);
28-
2927
if (isLoading) {
3028
return (
3129
<Modal.Root opened={isOpen} onClose={onClose} size="md">

frontend/src/components/common/StatusToggle/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {useUpdateEventStatus} from '../../../mutations/useUpdateEventStatus';
88
import {useUpdateOrganizerStatus} from '../../../mutations/useUpdateOrganizerStatus';
99
import {IdParam} from '../../../types';
1010
import classes from './StatusToggle.module.scss';
11+
import {trackEvent, AnalyticsEvents} from '../../../utilites/analytics';
1112

1213
interface StatusToggleProps {
1314
entityType: 'event' | 'organizer';
@@ -47,6 +48,9 @@ export const StatusToggle: React.FC<StatusToggleProps> = ({
4748

4849
mutation.mutate(mutationParams as any, {
4950
onSuccess: () => {
51+
if (entityType === 'event' && newStatus === 'LIVE') {
52+
trackEvent(AnalyticsEvents.EVENT_PUBLISHED);
53+
}
5054
const successMessage = entityType === 'event'
5155
? t`Event status updated`
5256
: t`Organizer status updated`;

frontend/src/components/forms/OrganizerForm/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {useFormErrorResponseHandler} from "../../../hooks/useFormErrorResponseHa
1212
import {useGetMe} from "../../../queries/useGetMe.ts";
1313
import {IconBuilding} from "@tabler/icons-react";
1414
import classes from "../../routes/welcome/Welcome.module.scss";
15+
import {trackEvent, AnalyticsEvents} from "../../../utilites/analytics.ts";
1516

1617
interface OrganizerFormProps {
1718
onSuccess?: (organizer: Organizer) => void;
@@ -82,6 +83,7 @@ export const OrganizerCreateForm = ({onSuccess, onCancel}: OrganizerFormProps) =
8283
organizerData: values,
8384
}, {
8485
onSuccess: ({data: organizer}) => {
86+
trackEvent(AnalyticsEvents.ORGANIZER_CREATED);
8587
if (onSuccess) {
8688
onSuccess(organizer);
8789
}

frontend/src/components/modals/CreateWebhookModal/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export const CreateWebhookModal = ({onClose}: GenericModalProps) => {
3737
const createMutation = useCreateWebhook();
3838

3939
const handleSubmit = (requestData: WebhookRequest) => {
40-
console.log(eventId, requestData);
4140
createMutation.mutate({
4241
eventId: eventId as IdParam,
4342
webhook: requestData

frontend/src/components/routes/account/ManageAccount/sections/PaymentSettings/index.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {Anchor, Button, Grid, Group, Text, ThemeIcon, Title} from "@mantine/core
88
import {Account, StripeConnectAccountsResponse} from "../../../../../../types.ts";
99
import paymentClasses from "./PaymentSettings.module.scss";
1010
import classes from "../../ManageAccount.module.scss";
11-
import {useEffect, useState} from "react";
11+
import {useEffect, useRef, useState} from "react";
1212
import {IconAlertCircle, IconBrandStripe, IconCheck, IconExternalLink, IconInfoCircle} from '@tabler/icons-react';
1313
import {Card} from "../../../../../common/Card";
1414
import {formatCurrency} from "../../../../../../utilites/currency.ts";
@@ -19,6 +19,7 @@ import {VatSettings} from './VatSettings';
1919
import {VatSettingsModal} from './VatSettings/VatSettingsModal.tsx';
2020
import {VatNotice, getVatInfo} from './VatNotice';
2121
import {useGetAccountVatSetting} from '../../../../../../queries/useGetAccountVatSetting.ts';
22+
import {trackEvent, AnalyticsEvents} from "../../../../../../utilites/analytics.ts";
2223

2324
interface FeePlanDisplayProps {
2425
configuration?: {
@@ -725,6 +726,27 @@ const PaymentSettings = () => {
725726

726727
const [showVatModal, setShowVatModal] = useState(false);
727728
const [hasCheckedVatModal, setHasCheckedVatModal] = useState(false);
729+
const hasTrackedStripeConnection = useRef(false);
730+
731+
// Track Stripe connection when user returns with completed setup
732+
useEffect(() => {
733+
if (typeof window === 'undefined') return;
734+
if (hasTrackedStripeConnection.current) return;
735+
if (!stripeAccountsQuery.data) return;
736+
737+
const urlParams = new URLSearchParams(window.location.search);
738+
const isReturn = urlParams.get('is_return') === '1';
739+
if (!isReturn) return;
740+
741+
const completedAccount = stripeAccountsQuery.data.stripe_connect_accounts.find(
742+
acc => acc.is_setup_complete
743+
);
744+
745+
if (completedAccount) {
746+
hasTrackedStripeConnection.current = true;
747+
trackEvent(AnalyticsEvents.STRIPE_CONNECTED);
748+
}
749+
}, [stripeAccountsQuery.data]);
728750

729751
// Check if user is returning from Stripe and needs to fill VAT info
730752
// Only for Hi.Events Cloud - open-source doesn't have VAT handling

frontend/src/components/routes/event/EventDashboard/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {useEffect, useState} from 'react';
2222
import {StripePlatform} from "../../../../types.ts";
2323
import {isHiEvents} from "../../../../utilites/helpers.ts";
2424
import {StripeConnectButton} from "../../../common/StripeConnectButton";
25+
import {trackEvent, AnalyticsEvents} from "../../../../utilites/analytics.ts";
2526

2627
export const DashBoardSkeleton = () => {
2728
return (
@@ -67,16 +68,20 @@ export const EventDashboard = () => {
6768
};
6869

6970
const handleStatusToggle = () => {
71+
const newStatus = event?.status === 'LIVE' ? 'DRAFT' : 'LIVE';
7072
const message = event?.status === 'LIVE'
7173
? t`Are you sure you want to make this event draft? This will make the event invisible to the public`
7274
: t`Are you sure you want to make this event public? This will make the event visible to the public`;
7375

7476
confirmationDialog(message, () => {
7577
statusToggleMutation.mutate({
7678
eventId,
77-
status: event?.status === 'LIVE' ? 'DRAFT' : 'LIVE'
79+
status: newStatus
7880
}, {
7981
onSuccess: () => {
82+
if (newStatus === 'LIVE') {
83+
trackEvent(AnalyticsEvents.EVENT_PUBLISHED);
84+
}
8085
showSuccess(t`Event status updated`);
8186
},
8287
onError: (error: any) => {

frontend/src/components/routes/event/GettingStarted/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {getProductsFromEvent} from "../../../../utilites/helpers.ts";
1515
import {useEffect, useState} from 'react';
1616
import ConfettiAnimation from "./ConfettiAnimaiton";
1717
import {Browser, useBrowser} from "../../../../hooks/useGetBrowser.ts";
18+
import {trackEvent, AnalyticsEvents} from "../../../../utilites/analytics.ts";
1819

1920
const GettingStarted = () => {
2021
const {eventId} = useParams();
@@ -52,11 +53,15 @@ const GettingStarted = () => {
5253
const statusToggleMutation = useUpdateEventStatus();
5354

5455
const handleStatusToggle = () => {
56+
const newStatus = event?.status === 'LIVE' ? 'DRAFT' : 'LIVE';
5557
statusToggleMutation.mutate({
5658
eventId,
57-
status: event?.status === 'LIVE' ? 'DRAFT' : 'LIVE'
59+
status: newStatus
5860
}, {
5961
onSuccess: () => {
62+
if (newStatus === 'LIVE') {
63+
trackEvent(AnalyticsEvents.EVENT_PUBLISHED);
64+
}
6065
showSuccess(t`Event status updated`);
6166
},
6267
onError: (error: any) => {

frontend/src/components/routes/product-widget/CollectInformation/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {eventCheckoutPath, eventHomepagePath} from "../../../../utilites/urlHelp
3131
import {showInfo} from "../../../../utilites/notifications.tsx";
3232
import countries from "../../../../../data/countries.json";
3333
import classes from "./CollectInformation.module.scss";
34+
import {trackEvent, AnalyticsEvents} from "../../../../utilites/analytics.ts";
3435

3536
const LoadingSkeleton = () =>
3637
(
@@ -210,6 +211,9 @@ export const CollectInformation = () => {
210211

211212
onSuccess: (data) => {
212213
const nextPage = order?.is_payment_required ? 'payment' : 'summary';
214+
if (nextPage === 'summary') {
215+
trackEvent(AnalyticsEvents.PURCHASE_COMPLETED_FREE);
216+
}
213217
navigate(eventCheckoutPath(eventId, data.data.short_id, nextPage));
214218
},
215219

frontend/src/components/routes/product-widget/Payment/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {InlineOrderSummary} from "../../../common/InlineOrderSummary";
1818
import {showError} from "../../../../utilites/notifications.tsx";
1919
import {getConfig} from "../../../../utilites/config.ts";
2020
import classes from "./Payment.module.scss";
21+
import {trackEvent, AnalyticsEvents} from "../../../../utilites/analytics.ts";
2122

2223
const Payment = () => {
2324
const navigate = useNavigate();
@@ -67,6 +68,8 @@ const Payment = () => {
6768
orderShortId
6869
}, {
6970
onSuccess: () => {
71+
const totalCents = Math.round((order?.total_gross || 0) * 100);
72+
trackEvent(AnalyticsEvents.PURCHASE_COMPLETED_OFFLINE, { value: totalCents });
7073
navigate(`/checkout/${eventId}/${orderShortId}/summary`);
7174
},
7275
onError: (error: any) => {

frontend/src/components/routes/product-widget/PaymentReturn/index.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {usePollGetOrderPublic} from "../../../../queries/usePollGetOrderPublic.ts";
22
import {useNavigate, useParams} from "react-router";
3-
import {useEffect, useState} from "react";
3+
import {useEffect, useRef, useState} from "react";
44
import classes from './PaymentReturn.module.scss';
55
import {t} from "@lingui/macro";
66
import {useGetOrderStripePaymentIntentPublic} from "../../../../queries/useGetOrderStripePaymentIntentPublic.ts";
77
import {CheckoutContent} from "../../../layouts/Checkout/CheckoutContent";
88
import {eventCheckoutPath} from "../../../../utilites/urlHelper.ts";
99
import {HomepageInfoMessage} from "../../../common/HomepageInfoMessage";
1010
import {isSsr} from "../../../../utilites/helpers.ts";
11+
import {trackEvent, AnalyticsEvents} from "../../../../utilites/analytics.ts";
1112

1213
/**
1314
* This component is responsible for handling the return from the payment provider.
@@ -24,6 +25,7 @@ export const PaymentReturn = () => {
2425
const [attemptManualConfirmation, setAttemptManualConfirmation] = useState(false);
2526
const paymentIntentQuery = useGetOrderStripePaymentIntentPublic(eventId, orderShortId, attemptManualConfirmation);
2627
const [cannotConfirmPayment, setCannotConfirmPayment] = useState(false);
28+
const hasTrackedPurchase = useRef(false);
2729

2830
useEffect(
2931
() => {
@@ -44,6 +46,11 @@ export const PaymentReturn = () => {
4446
return;
4547
}
4648
if (paymentIntentQuery.data?.status === 'succeeded') {
49+
if (!hasTrackedPurchase.current && order) {
50+
hasTrackedPurchase.current = true;
51+
const totalCents = Math.round((order.total_gross || 0) * 100);
52+
trackEvent(AnalyticsEvents.PURCHASE_COMPLETED_PAID, { value: totalCents });
53+
}
4754
navigate(eventCheckoutPath(eventId, orderShortId, 'summary'));
4855
} else {
4956
// At this point we've tried multiple times to confirm the payment and failed.
@@ -59,6 +66,11 @@ export const PaymentReturn = () => {
5966
}
6067

6168
if (order?.status === 'COMPLETED') {
69+
if (!hasTrackedPurchase.current) {
70+
hasTrackedPurchase.current = true;
71+
const totalCents = Math.round((order.total_gross || 0) * 100);
72+
trackEvent(AnalyticsEvents.PURCHASE_COMPLETED_PAID, { value: totalCents });
73+
}
6274
navigate(eventCheckoutPath(eventId, orderShortId, 'summary'));
6375
}
6476
if (order?.payment_status === 'PAYMENT_FAILED' || (typeof window !== 'undefined' && window?.location.search.includes('failed'))) {

0 commit comments

Comments
 (0)