Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webapp",
"version": "2.27.1",
"version": "2.28.0",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
6 changes: 5 additions & 1 deletion src/features/auth/components/login-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LoaderIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { JobstashLogo } from '@/components/jobstash-logo';
import { GA_EVENT, trackEvent } from '@/lib/analytics';

import { useLoginAuth } from './use-login-auth';
import { useLoginContent } from './use-login-content';
Expand Down Expand Up @@ -53,7 +54,10 @@ export const LoginContent = () => {
'bg-sidebar font-semibold text-white',
'hover:bg-sidebar/80',
)}
onClick={() => login()}
onClick={() => {
trackEvent(GA_EVENT.LOGIN_STARTED, { login_method: 'privy' });
login();
}}
>
Get Started
</Button>
Expand Down
22 changes: 18 additions & 4 deletions src/features/jobs/components/job-details/apply-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,17 @@ export const ApplyButton = ({
setNudgeOpen(true);
};

const handleApplyClick = () => {
const trackApply = (destination: string) => {
trackEvent(GA_EVENT.APPLY_BUTTON_CLICKED, {
job_id: jobId,
job_title: jobTitle,
organization: organization ?? '',
apply_destination: destination,
});
};

const handleApplyClick = () => {
trackApply('external');
apply().catch(() => {});
};

Expand All @@ -82,22 +87,31 @@ export const ApplyButton = ({

if (!isAuthLoading && !isAuthenticated) {
const href = `/login?redirect=${encodeURIComponent(pathname)}`;
return { icon: linkIcon, label: 'Apply Now', internalHref: href };
return {
icon: linkIcon,
label: 'Apply Now',
internalHref: href,
onClick: () => trackApply('login_redirect'),
};
}

if (status === APPLY_STATUS.ALREADY_APPLIED) {
return {
icon: linkIcon,
label: 'Revisit Application',
externalHref: applyUrl ?? undefined,
onClick: () => trackApply('revisit'),
};
}

if (isExpertJob && status === APPLY_STATUS.INELIGIBLE && missing) {
return {
icon: linkIcon,
label: 'Unlock Application',
onClick: () => openNudge(missing),
onClick: () => {
trackApply('unlock_nudge');
openNudge(missing);
},
};
}

Expand All @@ -121,7 +135,7 @@ export const ApplyButton = ({
if (internalHref) {
button = (
<Button asChild size='lg' className={GRADIENT_BUTTON}>
<Link href={internalHref} prefetch={false}>
<Link href={internalHref} prefetch={false} onClick={onClick}>
{icon}
{label}
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type KeyboardEvent, type MouseEvent, useRef, useState } from 'react';

import { useQueryClient } from '@tanstack/react-query';

import { GA_EVENT, trackEvent } from '@/lib/analytics';
import { SKILL_ERROR_THRESHOLD, getSkillStatus } from '@/lib/constants';
import { getTagColorIndex } from '@/lib/utils/get-tag-color-index';
import { useSkillsSearch } from '@/features/profile/hooks/use-skills-search';
Expand Down Expand Up @@ -97,6 +98,11 @@ export const useProfileSkillsEditor = (currentSkills: ProfileSkill[]) => {

if (!res.ok) throw new Error('Failed to save skills');

trackEvent(GA_EVENT.SKILLS_SAVED, {
skill_count: editedSkills.length,
source: 'editor',
});

await queryClient.invalidateQueries({ queryKey: ['profile-skills'] });
handleClose();
} finally {
Expand Down
3 changes: 3 additions & 0 deletions src/features/profile/components/use-delete-account-dialog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useState } from 'react';

import { GA_EVENT, trackEvent } from '@/lib/analytics';

interface UseDeleteAccountDialogParams {
logout: () => Promise<void>;
}
Expand Down Expand Up @@ -35,6 +37,7 @@ export const useDeleteAccountDialog = ({
return;
}

trackEvent(GA_EVENT.ACCOUNT_DELETED, {});
await logout();
} catch {
setError('Something went wrong. Please try again.');
Expand Down
14 changes: 13 additions & 1 deletion src/features/profile/components/use-profile-logout-button.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react';

import { GA_EVENT, trackEvent } from '@/lib/analytics';
import { useSession } from '@/features/auth/hooks/use-session';

export const useProfileLogoutButton = () => {
Expand All @@ -13,5 +14,16 @@ export const useProfileLogoutButton = () => {

const handleOpen = () => setIsOpen(true);

return { isLoggingOut, isOpen, onOpenChange, handleOpen, logout };
const handleLogout = () => {
trackEvent(GA_EVENT.LOGOUT, {});
logout();
};

return {
isLoggingOut,
isOpen,
onOpenChange,
handleOpen,
logout: handleLogout,
};
};
8 changes: 8 additions & 0 deletions src/features/profile/hooks/use-resume-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { type ChangeEvent, type DragEvent, useRef, useState } from 'react';

import { useQueryClient } from '@tanstack/react-query';

import { GA_EVENT, trackEvent } from '@/lib/analytics';
import { SKILL_ERROR_THRESHOLD, getSkillStatus } from '@/lib/constants';
import { clientEnv } from '@/lib/env/client';
import { getTagColorIndex } from '@/lib/utils/get-tag-color-index';
Expand Down Expand Up @@ -204,6 +205,9 @@ export const useResumeUpload = ({ onOpenChange }: UseResumeUploadParams) => {
if (!parseRes.ok) {
const body = await parseRes.json().catch(() => null);
const serverError = String(body?.error ?? '');
trackEvent(GA_EVENT.RESUME_PARSE_FAILED, {
resume_parse_error: serverError,
});
setError(getErrorMessage(serverError));
setFileName(null);
return;
Expand Down Expand Up @@ -329,6 +333,10 @@ export const useResumeUpload = ({ onOpenChange }: UseResumeUploadParams) => {
await queryClient.invalidateQueries({ queryKey: ['profile-skills'] });
}

trackEvent(GA_EVENT.RESUME_UPLOADED, {
skill_count: skillsToSync?.length ?? 0,
});

handleOpenChange(false);
} catch {
setError(
Expand Down
30 changes: 20 additions & 10 deletions src/lib/analytics/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ export const GA_EVENT = {
PILLAR_CLICKED: 'pillar_clicked',

// Browsing
JOB_LIST_VIEWED: 'job_list_viewed',
PAGINATION_CLICKED: 'pagination_clicked',
JOB_CARD_CLICKED: 'job_card_clicked',

// Interest
JOB_DETAILS_VIEWED: 'job_details_viewed',
SIMILAR_JOB_CLICKED: 'similar_job_clicked',

// Conversion
Expand All @@ -20,6 +18,14 @@ export const GA_EVENT = {
// Auth
LOGIN_STARTED: 'login_started',
LOGIN_COMPLETED: 'login_completed',
LOGOUT: 'logout',
ACCOUNT_DELETED: 'account_deleted',

// Profile
RESUME_UPLOADED: 'resume_uploaded',
RESUME_PARSE_FAILED: 'resume_parse_failed',
SKILLS_SAVED: 'skills_saved',

// Navigation
HERO_CTA_CLICKED: 'hero_cta_clicked',
SUGGESTED_FILTER_APPLIED: 'suggested_filter_applied',
Expand All @@ -45,9 +51,6 @@ export type GaEventParams = {
pillar_category: string;
source: string;
};
[GA_EVENT.JOB_LIST_VIEWED]: {
page_number: number;
};
[GA_EVENT.PAGINATION_CLICKED]: {
page_number: number;
};
Expand All @@ -56,11 +59,6 @@ export type GaEventParams = {
job_title: string;
organization: string;
};
[GA_EVENT.JOB_DETAILS_VIEWED]: {
job_id: string;
job_title: string;
organization: string;
};
[GA_EVENT.SIMILAR_JOB_CLICKED]: {
job_id: string;
source: string;
Expand All @@ -77,6 +75,18 @@ export type GaEventParams = {
[GA_EVENT.LOGIN_COMPLETED]: {
login_method: string;
};
[GA_EVENT.LOGOUT]: Record<string, never>;
[GA_EVENT.ACCOUNT_DELETED]: Record<string, never>;
[GA_EVENT.RESUME_UPLOADED]: {
skill_count: number;
};
[GA_EVENT.RESUME_PARSE_FAILED]: {
resume_parse_error: string;
};
[GA_EVENT.SKILLS_SAVED]: {
skill_count: number;
source: string;
};
[GA_EVENT.HERO_CTA_CLICKED]: {
source: string;
};
Expand Down
Loading