From 167873fb86a66f3a79685a0c8c0e3b735a67193e Mon Sep 17 00:00:00 2001 From: Ashutosh Dash Date: Tue, 23 Jun 2026 12:13:05 +0530 Subject: [PATCH 1/3] feat: integrate posthog analytics and event tracking across components --- app/providers.tsx | 12 +++--------- components/BookEvent.tsx | 11 +++++------ components/CreateNewEvent.tsx | 13 +++++++++++++ components/EventCard.tsx | 8 +++++++- components/SearchFilters.tsx | 6 ++++++ instrumentation-client.ts | 5 +++++ lib/posthog.js | 9 --------- lib/posthog/events.ts | 21 +++++++++++++++++++++ lib/posthog/helpers.ts | 22 ++++++++++++++++++++++ 9 files changed, 82 insertions(+), 25 deletions(-) create mode 100644 instrumentation-client.ts delete mode 100644 lib/posthog.js create mode 100644 lib/posthog/events.ts create mode 100644 lib/posthog/helpers.ts diff --git a/app/providers.tsx b/app/providers.tsx index 59e86e2..9e9810f 100644 --- a/app/providers.tsx +++ b/app/providers.tsx @@ -4,14 +4,8 @@ import { useEffect, Suspense } from "react"; import { usePathname, useSearchParams } from "next/navigation"; import posthog from "posthog-js"; import { PostHogProvider as PHProvider } from "posthog-js/react"; - -if (typeof window !== "undefined") { - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { - api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, - person_profiles: "identified_only", - capture_pageview: false, - }); -} +import { captureEvent } from "@/lib/posthog/helpers"; +import { POSTHOG_EVENTS } from "@/lib/posthog/events"; function PostHogPageTracker() { const pathname = usePathname(); @@ -23,7 +17,7 @@ function PostHogPageTracker() { if (searchParams.toString()) { url += `?${searchParams.toString()}`; } - posthog.capture("$pageview", { $current_url: url }); + captureEvent(POSTHOG_EVENTS.PAGE_VIEW, { $current_url: url }); } }, [pathname, searchParams]); diff --git a/components/BookEvent.tsx b/components/BookEvent.tsx index ee5f8e2..7313482 100644 --- a/components/BookEvent.tsx +++ b/components/BookEvent.tsx @@ -2,7 +2,8 @@ import { useState } from "react"; import { createBooking } from "@/lib/actions/booking.actions"; -import posthog from "posthog-js"; +import { captureEvent } from "@/lib/posthog/helpers"; +import { POSTHOG_EVENTS } from "@/lib/posthog/events"; const BookEvent = ({ eventId, slug }: { eventId: string; slug: string }) => { const [email, setEmail] = useState(''); @@ -27,10 +28,10 @@ const BookEvent = ({ eventId, slug }: { eventId: string; slug: string }) => { if (response.success) { setSubmitted(true); // Do not send raw email (PII) to analytics. - posthog.capture('event_booked', { eventId, slug }); + captureEvent(POSTHOG_EVENTS.EVENT_BOOKED, { eventId, slug }); } else { setError(response.error || "An unexpected error occurred. Please try again."); - posthog.captureException('Booking creation failed'); + captureEvent(POSTHOG_EVENTS.BOOKING_FAILED, { eventId, slug, email }); } } catch { setError("A network error occurred. Please try again."); @@ -54,13 +55,11 @@ const BookEvent = ({ eventId, slug }: { eventId: string; slug: string }) => { id="email" placeholder="Enter your email address" required - disabled={isSubmitting} // 1. Freeze input when submitting + disabled={isSubmitting} /> - {/* 2. Show the red error message under the input if an error occurs */} {error &&

{error}

} - {/* 3. Disable the button and change text dynamically */} diff --git a/components/CreateNewEvent.tsx b/components/CreateNewEvent.tsx index b5395ab..28cfc88 100644 --- a/components/CreateNewEvent.tsx +++ b/components/CreateNewEvent.tsx @@ -5,6 +5,8 @@ import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { toast } from "sonner"; import { createEvent } from "@/lib/actions/create-event.actions"; +import { captureEvent, captureException } from "@/lib/posthog/helpers"; +import { POSTHOG_EVENTS } from "@/lib/posthog/events"; const eventSchema = z.object({ title: z.string().min(3, "Title must be at least 3 characters"), @@ -63,12 +65,22 @@ const CreateEventForm = () => { if (result.success) { toast.success("Event created successfully!"); + captureEvent(POSTHOG_EVENTS.EVENT_CREATED, { + type: data.type, + mode: data.mode, + location: data.location, + }); reset(); } else { toast.error(result.error || "Failed to create event"); + captureEvent(POSTHOG_EVENTS.EVENT_CREATION_FAILED, { + reason: result.error || "unknown", + type: data.type, + mode: data.mode, + }); } @@ -77,6 +89,7 @@ const CreateEventForm = () => { console.error(error); toast.error("Something went wrong"); + captureException(error instanceof Error ? error : new Error("event_creation_exception")); } }; diff --git a/components/EventCard.tsx b/components/EventCard.tsx index 46573ec..96aef17 100644 --- a/components/EventCard.tsx +++ b/components/EventCard.tsx @@ -3,6 +3,8 @@ import Image from "next/image"; import Link from "next/link"; import { useState } from "react"; +import { captureEvent } from "@/lib/posthog/helpers"; +import { POSTHOG_EVENTS } from "@/lib/posthog/events"; interface Props { title: string; @@ -48,9 +50,11 @@ const EventCard = ({ (item: string) => item !== slug ); setBookmarked(false); + captureEvent(POSTHOG_EVENTS.EVENT_UNBOOKMARKED, { slug, title }); } else { updated = [...saved, slug]; setBookmarked(true); + captureEvent(POSTHOG_EVENTS.EVENT_BOOKMARKED, { slug, title }); } localStorage.setItem( @@ -58,9 +62,11 @@ const EventCard = ({ JSON.stringify(updated) ); }; + return ( captureEvent(POSTHOG_EVENTS.EVENT_VIEWED, { slug, title })} className=" event-card group @@ -139,4 +145,4 @@ const EventCard = ({ ); }; -export default EventCard; \ No newline at end of file +export default EventCard; diff --git a/components/SearchFilters.tsx b/components/SearchFilters.tsx index 2d52c2a..525d849 100644 --- a/components/SearchFilters.tsx +++ b/components/SearchFilters.tsx @@ -2,6 +2,8 @@ import { useRouter, useSearchParams, usePathname } from 'next/navigation'; import { useState, useEffect } from 'react'; +import { captureEvent } from '@/lib/posthog/helpers'; +import { POSTHOG_EVENTS } from '@/lib/posthog/events'; const MODES = ['All', 'Online', 'Offline', 'Hybrid']; const POPULAR_TAGS = ['All', 'Hackathon', 'Meetup', 'Web3', 'React', 'DevOps', 'AI']; @@ -21,11 +23,15 @@ export default function SearchFilters() { params.delete(key); } router.push(`${pathname}?${params.toString()}`, { scroll: false }); + captureEvent(POSTHOG_EVENTS.EVENT_FILTER_CHANGED, { filter: key, value }); }; useEffect(() => { const delayDebounceFn = setTimeout(() => { handleFilterChange('query', search); + if (search.trim()) { + captureEvent(POSTHOG_EVENTS.EVENT_SEARCHED, { query: search }); + } }, 400); return () => clearTimeout(delayDebounceFn); }, [search]); diff --git a/instrumentation-client.ts b/instrumentation-client.ts new file mode 100644 index 0000000..715093a --- /dev/null +++ b/instrumentation-client.ts @@ -0,0 +1,5 @@ +import posthog from "posthog-js"; + +posthog.init(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN!, { + api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, +}); diff --git a/lib/posthog.js b/lib/posthog.js deleted file mode 100644 index 1f15c69..0000000 --- a/lib/posthog.js +++ /dev/null @@ -1,9 +0,0 @@ -import posthog from "posthog-js"; - -if (typeof window !== "undefined") { - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, { - api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, - }); -} - -export default posthog; diff --git a/lib/posthog/events.ts b/lib/posthog/events.ts new file mode 100644 index 0000000..6e84682 --- /dev/null +++ b/lib/posthog/events.ts @@ -0,0 +1,21 @@ +export const POSTHOG_EVENTS = { + // Navigation + PAGE_VIEW: "$pageview", + + // Event discovery + EVENT_VIEWED: "event_viewed", + EVENT_SEARCHED: "event_searched", + EVENT_FILTER_CHANGED: "event_filter_changed", + + // Bookmarks + EVENT_BOOKMARKED: "event_bookmarked", + EVENT_UNBOOKMARKED: "event_unbookmarked", + + // Booking + EVENT_BOOKED: "event_booked", + BOOKING_FAILED: "booking_failed", + + // Creation + EVENT_CREATED: "event_created", + EVENT_CREATION_FAILED: "event_creation_failed", +} as const; diff --git a/lib/posthog/helpers.ts b/lib/posthog/helpers.ts new file mode 100644 index 0000000..9120195 --- /dev/null +++ b/lib/posthog/helpers.ts @@ -0,0 +1,22 @@ +import posthog from "posthog-js"; + +export function identifyUser( + userId: string, + traits?: Record +) { + posthog.identify(userId, traits); +} + +export function captureEvent( + event: string, + properties?: Record +) { + posthog.capture(event, properties); +} + +export function captureException( + error: unknown, + properties?: Record +) { + posthog.captureException(error, properties); +} From b9677e0df22a4b7df0915b9c304d7658a7f8b52d Mon Sep 17 00:00:00 2001 From: Ashutosh Dash Date: Tue, 23 Jun 2026 12:22:37 +0530 Subject: [PATCH 2/3] feat: add error tracking with posthog in errorpage component --- app/error.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/error.tsx b/app/error.tsx index bce1803..d408138 100644 --- a/app/error.tsx +++ b/app/error.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; import Link from "next/link"; +import { captureException } from "@/lib/posthog/helpers"; export default function ErrorPage({ error, @@ -12,6 +13,7 @@ export default function ErrorPage({ }) { useEffect(() => { console.error(error); + captureException(error, { digest: error.digest }); }, [error]); return ( From ad51d58aa53fbf4a76c70153add5f2448f7b5910 Mon Sep 17 00:00:00 2001 From: Ashutosh Dash Date: Tue, 23 Jun 2026 13:33:53 +0530 Subject: [PATCH 3/3] fix: update posthog initialization to use correct env variable --- instrumentation-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation-client.ts b/instrumentation-client.ts index 715093a..099abf8 100644 --- a/instrumentation-client.ts +++ b/instrumentation-client.ts @@ -1,5 +1,5 @@ import posthog from "posthog-js"; -posthog.init(process.env.NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN!, { +posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, });