feat(analytics): Add Firebase Analytics integration with page tracking

- Initialize Firebase Analytics with configuration and client-side setup
- Create GoogleAnalytics component to track page views on route changes
- Add analytics utility functions for tracking custom events (button clicks, form submissions, link clicks, searches)
- Integrate GoogleAnalytics component into home, not-found, and marketing layouts with Suspense boundary
- Add firebase dependency to package.json
- Enable automatic page view tracking with pathname and search parameters
This commit is contained in:
AmirReza Jamali
2025-11-22 10:56:40 +03:30
parent 9f16f89338
commit 807a77992e
8 changed files with 1135 additions and 2 deletions
+5
View File
@@ -5,6 +5,7 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { Suspense } from "react"; import { Suspense } from "react";
import { ToastContainer, Zoom } from "react-toastify"; import { ToastContainer, Zoom } from "react-toastify";
import GoogleAnalytics from "../_components/GoogleAnalytics";
import NavigationBar from "../_components/NavigationBar"; import NavigationBar from "../_components/NavigationBar";
import "../globals.css"; import "../globals.css";
const geistSans = Geist({ const geistSans = Geist({
@@ -136,6 +137,10 @@ export default async function RootLayout({
pauseOnHover pauseOnHover
/> />
<Suspense>
<GoogleAnalytics />
</Suspense>
<div className="container px-4 m-auto flex-grow">{children}</div> <div className="container px-4 m-auto flex-grow">{children}</div>
<footer> <footer>
<div className="relative z-10 container px-4 py-4 m-auto flex flex-col justify-center items-center "> <div className="relative z-10 container px-4 py-4 m-auto flex flex-col justify-center items-center ">
@@ -5,6 +5,8 @@ import Link from "next/link";
import { ToastContainer, Zoom } from "react-toastify"; import { ToastContainer, Zoom } from "react-toastify";
import "../../globals.css"; import "../../globals.css";
import { Suspense } from "react";
import GoogleAnalytics from "@/app/_components/GoogleAnalytics";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
subsets: ["latin"], subsets: ["latin"],
@@ -126,6 +128,10 @@ export default async function RootLayout({
pauseOnHover pauseOnHover
/> />
<Suspense>
<GoogleAnalytics />
</Suspense>
<main className="container px-4 py-4 m-auto flex-grow">{children}</main> <main className="container px-4 py-4 m-auto flex-grow">{children}</main>
<footer className="relative h-full bg-[url('/footer-bg.svg')] bg-cover bg-no-repeat mt-20"> <footer className="relative h-full bg-[url('/footer-bg.svg')] bg-cover bg-no-repeat mt-20">
<div className="relative z-10 container px-4 py-4 m-auto flex flex-col justify-center items-center"> <div className="relative z-10 container px-4 py-4 m-auto flex flex-col justify-center items-center">
+26
View File
@@ -0,0 +1,26 @@
"use client";
import { analytics } from "@/lib/firebase";
import { logEvent } from "firebase/analytics";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
export default function GoogleAnalytics() {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (analytics) {
const url =
pathname +
(searchParams?.toString() ? `?${searchParams.toString()}` : "");
logEvent(analytics, "page_view", {
page_path: url,
page_title: document.title,
});
}
}, [pathname, searchParams]);
return null;
}
+6
View File
@@ -2,6 +2,8 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import { ToastContainer, Zoom } from "react-toastify"; import { ToastContainer, Zoom } from "react-toastify";
import "../globals.css"; import "../globals.css";
import { Suspense } from "react";
import GoogleAnalytics from "../_components/GoogleAnalytics";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
subsets: ["latin"], subsets: ["latin"],
@@ -43,6 +45,10 @@ export default async function RootLayout({
pauseOnHover pauseOnHover
/> />
<Suspense>
<GoogleAnalytics />
</Suspense>
{children} {children}
</body> </body>
</html> </html>
+40
View File
@@ -0,0 +1,40 @@
import { logEvent } from "firebase/analytics";
import { analytics } from "./firebase";
// Custom event tracking functions
export const trackEvent = (
eventName: string,
eventParams?: Record<string, any>
) => {
if (analytics) {
logEvent(analytics, eventName, eventParams);
}
};
// Common event trackers
export const trackButtonClick = (buttonName: string, location?: string) => {
trackEvent("button_click", {
button_name: buttonName,
location: location || "unknown",
});
};
export const trackFormSubmit = (formName: string, success: boolean) => {
trackEvent("form_submit", {
form_name: formName,
success,
});
};
export const trackLinkClick = (linkUrl: string, linkText?: string) => {
trackEvent("link_click", {
link_url: linkUrl,
link_text: linkText || "unknown",
});
};
export const trackSearch = (searchTerm: string) => {
trackEvent("search", {
search_term: searchTerm,
});
};
+25
View File
@@ -0,0 +1,25 @@
import { Analytics, getAnalytics } from "firebase/analytics";
import { getApps, initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: "AIzaSyCTjdsk2jE34IfnvSKP1JMTIf_Abd7tbt0",
authDomain: "opplens-270d1.firebaseapp.com",
projectId: "opplens-270d1",
storageBucket: "opplens-270d1.firebasestorage.app",
messagingSenderId: "6923326255",
appId: "1:6923326255:web:cb4f630d92a286daed87da",
measurementId: "G-TMLH6BM963",
};
// Initialize Firebase
const app =
getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
let analytics: Analytics | null = null;
// Initialize Analytics only on client side
if (typeof window !== "undefined") {
analytics = getAnalytics(app);
}
export { analytics, app };
+1026 -2
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -11,6 +11,7 @@
"@tanstack/react-query": "^5.90.7", "@tanstack/react-query": "^5.90.7",
"apexcharts": "^5.3.5", "apexcharts": "^5.3.5",
"axios": "^1.12.2", "axios": "^1.12.2",
"firebase": "^12.6.0",
"next": "15.5.5", "next": "15.5.5",
"react": "19.1.0", "react": "19.1.0",
"react-apexcharts": "^1.8.0", "react-apexcharts": "^1.8.0",