diff --git a/src/components/AuthenticationFlow/GitHubButton.tsx b/src/components/AuthenticationFlow/GitHubButton.tsx index 3deb4c569..b0001699a 100644 --- a/src/components/AuthenticationFlow/GitHubButton.tsx +++ b/src/components/AuthenticationFlow/GitHubButton.tsx @@ -4,6 +4,7 @@ import Cookies from 'js-cookie'; import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt'; import { httpGet } from '../../lib/http'; import { Spinner } from '../ReactIcons/Spinner.tsx'; +import { triggerUtmRegistration } from '../../lib/browser.ts'; type GitHubButtonProps = { isDisabled?: boolean; @@ -46,6 +47,8 @@ export function GitHubButton(props: GitHubButtonProps) { return; } + triggerUtmRegistration(); + let redirectUrl = '/'; const gitHubRedirectAt = localStorage.getItem(GITHUB_REDIRECT_AT); const lastPageBeforeGithub = localStorage.getItem(GITHUB_LAST_PAGE); diff --git a/src/components/AuthenticationFlow/GoogleButton.tsx b/src/components/AuthenticationFlow/GoogleButton.tsx index 60b0dcf84..950c2237d 100644 --- a/src/components/AuthenticationFlow/GoogleButton.tsx +++ b/src/components/AuthenticationFlow/GoogleButton.tsx @@ -4,6 +4,10 @@ import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt'; import { httpGet } from '../../lib/http'; import { Spinner } from '../ReactIcons/Spinner.tsx'; import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx'; +import { + getStoredUtmParams, + triggerUtmRegistration, +} from '../../lib/browser.ts'; type GoogleButtonProps = { isDisabled?: boolean; @@ -37,6 +41,8 @@ export function GoogleButton(props: GoogleButtonProps) { }`, ) .then(({ response, error }) => { + const utmParams = getStoredUtmParams(); + if (!response?.token) { setError(error?.message || 'Something went wrong.'); setIsLoading(false); @@ -45,6 +51,8 @@ export function GoogleButton(props: GoogleButtonProps) { return; } + triggerUtmRegistration(); + let redirectUrl = '/'; const googleRedirectAt = localStorage.getItem(GOOGLE_REDIRECT_AT); const lastPageBeforeGoogle = localStorage.getItem(GOOGLE_LAST_PAGE); @@ -97,9 +105,12 @@ export function GoogleButton(props: GoogleButtonProps) { // For non authentication pages, we want to redirect back to the page // the user was on before they clicked the social login button if (!['/login', '/signup'].includes(window.location.pathname)) { - const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes( - window.location.pathname, - ) + const pagePath = [ + '/respond-invite', + '/befriend', + '/r', + '/ai', + ].includes(window.location.pathname) ? window.location.pathname + window.location.search : window.location.pathname; diff --git a/src/components/AuthenticationFlow/LinkedInButton.tsx b/src/components/AuthenticationFlow/LinkedInButton.tsx index 6f36c319b..81924a068 100644 --- a/src/components/AuthenticationFlow/LinkedInButton.tsx +++ b/src/components/AuthenticationFlow/LinkedInButton.tsx @@ -4,6 +4,7 @@ import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt'; import { httpGet } from '../../lib/http'; import { Spinner } from '../ReactIcons/Spinner.tsx'; import { LinkedInIcon } from '../ReactIcons/LinkedInIcon.tsx'; +import { triggerUtmRegistration } from '../../lib/browser.ts'; type LinkedInButtonProps = { isDisabled?: boolean; @@ -45,6 +46,8 @@ export function LinkedInButton(props: LinkedInButtonProps) { return; } + triggerUtmRegistration(); + let redirectUrl = '/'; const linkedInRedirectAt = localStorage.getItem(LINKEDIN_REDIRECT_AT); const lastPageBeforeLinkedIn = localStorage.getItem(LINKEDIN_LAST_PAGE); @@ -97,9 +100,12 @@ export function LinkedInButton(props: LinkedInButtonProps) { // For non authentication pages, we want to redirect back to the page // the user was on before they clicked the social login button if (!['/login', '/signup'].includes(window.location.pathname)) { - const pagePath = ['/respond-invite', '/befriend', '/r', '/ai'].includes( - window.location.pathname, - ) + const pagePath = [ + '/respond-invite', + '/befriend', + '/r', + '/ai', + ].includes(window.location.pathname) ? window.location.pathname + window.location.search : window.location.pathname; diff --git a/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx b/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx index c9442ab52..0978e38d1 100644 --- a/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx +++ b/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx @@ -4,6 +4,7 @@ import { httpPost } from '../../lib/http'; import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt'; import { Spinner } from '../ReactIcons/Spinner'; import { ErrorIcon2 } from '../ReactIcons/ErrorIcon2'; +import { triggerUtmRegistration } from '../../lib/browser.ts'; export function TriggerVerifyAccount() { const [isLoading, setIsLoading] = useState(true); @@ -26,6 +27,8 @@ export function TriggerVerifyAccount() { return; } + triggerUtmRegistration(); + setAuthToken(response.token); window.location.href = '/'; }) diff --git a/src/components/PageSponsor.tsx b/src/components/PageSponsor.tsx index 392cf0c02..28591579b 100644 --- a/src/components/PageSponsor.tsx +++ b/src/components/PageSponsor.tsx @@ -6,6 +6,7 @@ import { X } from 'lucide-react'; import { setViewSponsorCookie } from '../lib/jwt'; import { isMobile } from '../lib/is-mobile'; import Cookies from 'js-cookie'; +import { getUrlUtmParams } from '../lib/browser.ts'; export type PageSponsorType = { company: string; @@ -50,6 +51,16 @@ export function PageSponsor(props: PageSponsorProps) { const [sponsorId, setSponsorId] = useState(null); const [sponsor, setSponsor] = useState(); + useEffect(() => { + const foundUtmParams = getUrlUtmParams(); + + if (!foundUtmParams.utmSource) { + return; + } + + localStorage.setItem('utm_params', JSON.stringify(foundUtmParams)); + }, []); + const loadSponsor = async () => { const currentPath = window.location.pathname; if ( diff --git a/src/lib/browser.ts b/src/lib/browser.ts index 425e27cc9..ffb39031c 100644 --- a/src/lib/browser.ts +++ b/src/lib/browser.ts @@ -1,3 +1,65 @@ +type UtmParams = Partial<{ + utmSource: string; + utmMedium: string; + utmCampaign: string; + utmContent: string; + utmTerm: string; +}>; + +export function getUrlUtmParams(): UtmParams { + if (typeof window === 'undefined') { + return {}; + } + + const utmParams = new URLSearchParams(window.location.search); + const utmSource = utmParams.get('utm_source') ?? undefined; + const utmMedium = utmParams.get('utm_medium') ?? undefined; + const utmCampaign = utmParams.get('utm_campaign') ?? undefined; + const utmContent = utmParams.get('utm_content') ?? undefined; + const utmTerm = utmParams.get('utm_term') ?? undefined; + + if (!utmSource || !utmCampaign) { + return {}; + } + + return { + utmSource: utmCampaign ? utmSource.toLowerCase() : undefined, + utmMedium: utmMedium ? utmMedium.toLowerCase() : undefined, + utmCampaign: utmCampaign ? utmCampaign.toLowerCase() : undefined, + utmContent: utmContent ? utmContent.toLowerCase() : undefined, + utmTerm: utmTerm ? utmTerm.toLowerCase() : undefined, + }; +} + +export function triggerUtmRegistration() { + const utmParams = getStoredUtmParams(); + console.log(utmParams); + if (!utmParams.utmSource) { + return; + } + + localStorage.removeItem('utm_params'); + + window.fireEvent({ + category: 'UserRegistration', + action: `Registration: ${utmParams.utmSource || 'unknown'}-${utmParams.utmCampaign || 'unknown'}`, + label: `Registration: ${utmParams.utmSource || 'unknown'}-${utmParams.utmCampaign || 'unknown'}`, + }); +} + +export function getStoredUtmParams(): UtmParams { + if (typeof window === 'undefined') { + return {}; + } + + const utmParams = localStorage.getItem('utm_params'); + if (!utmParams) { + return {}; + } + + return JSON.parse(utmParams); +} + export function getUrlParams() { if (typeof window === 'undefined') { return {};