0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(console): report LinkedIn conversion (#4251)

* feat(console): report linkedin conversion

* refactor(console): clean up code
This commit is contained in:
Gao Sun 2023-07-27 15:42:44 +08:00 committed by GitHub
parent 5a1c9d3a7b
commit db91da19bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 57 deletions

View file

@ -0,0 +1,57 @@
import { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import {
shouldReport,
lintrk,
gtagAwTrackingId,
linkedInConversionId,
gtag,
gtagSignUpConversionId,
} from './utils';
/**
* In cloud production environment, this component initiates gtag.js and LinkedIn
* Insight Tag, then reports a sign-up conversion to them.
*/
export default function ReportConversion() {
/**
* This `useEffect()` initiates Google Tag and report a sign-up conversion to it.
* It may run multiple times (e.g. a user visit multiple times to finish the onboarding process,
* which rarely happens), but it'll be okay since we've set conversion's "Count" to "One"
* which means only the first interaction is valuable.
*
* Track this conversion in the backend has been considered, but Google does not provide
* a clear guideline for it and marks the [Node library](https://developers.google.com/tag-platform/tag-manager/api/v2/libraries)
* as "alpha" which looks unreliable.
*/
useEffect(() => {
if (shouldReport) {
gtag('js', new Date());
gtag('config', gtagAwTrackingId);
gtag('event', 'conversion', {
send_to: gtagSignUpConversionId,
});
lintrk('track', { conversion_id: linkedInConversionId });
console.debug('Have a good day!');
} else {
console.debug("Not reporting conversion because it's not production");
}
}, []);
if (shouldReport) {
return (
<Helmet>
<script
async
crossOrigin="anonymous"
src={`https://www.googletagmanager.com/gtag/js?id=${gtagAwTrackingId}`}
/>
<script async src="https://snap.licdn.com/li.lms-analytics/insight.min.js" />
</Helmet>
);
}
return null;
}

View file

@ -0,0 +1,44 @@
export const gtagAwTrackingId = 'AW-11124811245';
/** This ID indicates a user has truly signed up for Logto Cloud. */
export const gtagSignUpConversionId = `AW-11192640559/ZuqUCLvNpasYEK_IiNkp`;
const logtoProductionHostname = 'logto.io';
const linkedInPartnerId = '5096172';
export const linkedInConversionId = '13374828';
/**
* Due to the special of conversion reporting, it should be `true` only in the
* Logto Cloud production environment.
* Add the leading '.' to make it safer (ignore hostnames like "foologto.io").
*/
export const shouldReport = window.location.hostname.endsWith('.' + logtoProductionHostname);
/* eslint-disable @silverhand/fp/no-mutation, @silverhand/fp/no-mutating-methods */
/** This function is edited from the Google Tag official code snippet. */
export function gtag(..._: unknown[]) {
if (!window.dataLayer) {
window.dataLayer = [];
}
// We cannot use rest params here since gtag has some internal logic about `arguments` for data transpiling
// eslint-disable-next-line prefer-rest-params
window.dataLayer.push(arguments);
}
/** This function is edited from the LinkedIn Tag official code snippet. */
export function lintrk(..._: unknown[]) {
// Init LinkedIn tag if needed
if (!window._linkedin_data_partner_ids) {
window._linkedin_data_partner_ids = [];
window._linkedin_data_partner_ids.push(linkedInPartnerId);
}
if (!window.lintrk) {
window.lintrk = { q: [] };
}
// eslint-disable-next-line prefer-rest-params
window.lintrk.q.push(arguments);
}
/* eslint-enable @silverhand/fp/no-mutation, @silverhand/fp/no-mutating-methods */

View file

@ -1,3 +0,0 @@
declare interface Window {
dataLayer?: unknown[];
}

View file

@ -0,0 +1,9 @@
declare interface Window {
// `gtag.js`
dataLayer?: unknown[];
// LinkedIn
_linkedin_data_partner_ids?: unknown[];
lintrk?: {
q: unknown[];
};
}

View file

@ -1,4 +0,0 @@
export const gtagAwTrackingId = 'AW-11124811245';
/** This ID indicates a user has truly signed up for Logto Cloud. */
export const gtagSignUpConversionId = `AW-11192640559/ZuqUCLvNpasYEK_IiNkp`;
export const logtoProductionHostname = 'logto.io';

View file

@ -2,10 +2,10 @@ import { Component, ConsoleEvent } from '@logto/app-insights/custom-event';
import { TrackOnce } from '@logto/app-insights/react'; import { TrackOnce } from '@logto/app-insights/react';
import { Theme } from '@logto/schemas'; import { Theme } from '@logto/schemas';
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { Route, Navigate, Outlet, Routes } from 'react-router-dom'; import { Route, Navigate, Outlet, Routes } from 'react-router-dom';
import { SWRConfig } from 'swr'; import { SWRConfig } from 'swr';
import ReportConversion from '@/components/ReportConversion';
import AppBoundary from '@/containers/AppBoundary'; import AppBoundary from '@/containers/AppBoundary';
import ProtectedRoutes from '@/containers/ProtectedRoutes'; import ProtectedRoutes from '@/containers/ProtectedRoutes';
import TenantAccess from '@/containers/TenantAccess'; import TenantAccess from '@/containers/TenantAccess';
@ -15,21 +15,15 @@ import useSwrOptions from '@/hooks/use-swr-options';
import useTenantPathname from '@/hooks/use-tenant-pathname'; import useTenantPathname from '@/hooks/use-tenant-pathname';
import NotFound from '@/pages/NotFound'; import NotFound from '@/pages/NotFound';
import { gtagAwTrackingId, gtagSignUpConversionId, logtoProductionHostname } from './constants';
import AppContent from './containers/AppContent'; import AppContent from './containers/AppContent';
import useUserOnboardingData from './hooks/use-user-onboarding-data'; import useUserOnboardingData from './hooks/use-user-onboarding-data';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
import SignInExperience from './pages/SignInExperience'; import SignInExperience from './pages/SignInExperience';
import Welcome from './pages/Welcome'; import Welcome from './pages/Welcome';
import { OnboardingPage, OnboardingRoute } from './types'; import { OnboardingPage, OnboardingRoute } from './types';
import { getOnboardingPage, gtag } from './utils'; import { getOnboardingPage } from './utils';
const welcomePathname = getOnboardingPage(OnboardingPage.Welcome); const welcomePathname = getOnboardingPage(OnboardingPage.Welcome);
/**
* Due to the special of Google Tag, it should be `true` only in the Logto Cloud production environment.
* Add the leading '.' to make it safer (ignore hostnames like "foologto.io").
*/
const shouldReportToGtag = window.location.hostname.endsWith('.' + logtoProductionHostname);
function Layout() { function Layout() {
const swrOptions = useSwrOptions(); const swrOptions = useSwrOptions();
@ -44,27 +38,6 @@ function Layout() {
}; };
}, [setThemeOverride]); }, [setThemeOverride]);
/**
* This `useEffect()` initiates Google Tag and report a sign-up conversion to it.
* It may run multiple times (e.g. a user visit multiple times to finish the onboarding process,
* which rarely happens), but it'll be okay since we've set conversion's "Count" to "One"
* which means only the first interaction is valuable.
*
* Track this conversion in the backend has been considered, but Google does not provide
* a clear guideline for it and marks the [Node library](https://developers.google.com/tag-platform/tag-manager/api/v2/libraries)
* as "alpha" which looks unreliable.
*/
useEffect(() => {
if (shouldReportToGtag) {
gtag('js', new Date());
gtag('config', gtagAwTrackingId);
gtag('event', 'conversion', {
send_to: gtagSignUpConversionId,
});
console.debug('Google Tag event fires');
}
}, []);
const { const {
data: { questionnaire }, data: { questionnaire },
} = useUserOnboardingData(); } = useUserOnboardingData();
@ -80,15 +53,7 @@ function Layout() {
<div className={styles.app}> <div className={styles.app}>
<SWRConfig value={swrOptions}> <SWRConfig value={swrOptions}>
<AppBoundary> <AppBoundary>
{shouldReportToGtag && ( <ReportConversion />
<Helmet>
<script
async
crossOrigin="anonymous"
src={`https://www.googletagmanager.com/gtag/js?id=${gtagAwTrackingId}`}
/>
</Helmet>
)}
<Toast /> <Toast />
<Outlet /> <Outlet />
</AppBoundary> </AppBoundary>

View file

@ -5,15 +5,3 @@ import { OnboardingRoute } from './types';
export const getOnboardingPage = (page: OnboardingPage) => export const getOnboardingPage = (page: OnboardingPage) =>
joinPath(OnboardingRoute.Onboarding, page); joinPath(OnboardingRoute.Onboarding, page);
/** This function is updated from the Google Tag official code snippet. */
export function gtag(..._: unknown[]) {
if (!window.dataLayer) {
// eslint-disable-next-line @silverhand/fp/no-mutation
window.dataLayer = [];
}
// We cannot use rest params here since gtag has some internal logic about `arguments` for data transpiling
// eslint-disable-next-line @silverhand/fp/no-mutating-methods, prefer-rest-params
window.dataLayer.push(arguments);
}