80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
const EXCLUDED_PREFIXES = ["/admin", "/api", "/_next"];
|
|
|
|
export function PageTracker() {
|
|
const pathname = usePathname();
|
|
const viewIdRef = useRef<string | null>(null);
|
|
const startTimeRef = useRef<number>(Date.now());
|
|
|
|
function getSessionId(): string {
|
|
let sid = sessionStorage.getItem("_mpv_sid");
|
|
if (!sid) {
|
|
sid = crypto.randomUUID();
|
|
sessionStorage.setItem("_mpv_sid", sid);
|
|
}
|
|
return sid;
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (EXCLUDED_PREFIXES.some((p) => pathname.startsWith(p))) return;
|
|
|
|
startTimeRef.current = Date.now();
|
|
viewIdRef.current = null;
|
|
|
|
fetch("/api/analytics/track", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
path: pathname,
|
|
session_id: getSessionId(),
|
|
referrer: typeof document !== "undefined" ? document.referrer || undefined : undefined,
|
|
}),
|
|
})
|
|
.then((r) => r.json())
|
|
.then((d) => { viewIdRef.current = d.view_id ?? null; })
|
|
.catch(() => {});
|
|
|
|
function sendDuration() {
|
|
if (!viewIdRef.current) return;
|
|
const ms = Date.now() - startTimeRef.current;
|
|
const blob = new Blob(
|
|
[JSON.stringify({ path: pathname, session_id: getSessionId(), view_id: viewIdRef.current, duration_ms: ms })],
|
|
{ type: "application/json" }
|
|
);
|
|
navigator.sendBeacon("/api/analytics/track", blob);
|
|
}
|
|
|
|
function trackPhoneClick(event: Event) {
|
|
const target = event.target as HTMLElement;
|
|
const link = target.closest('a[href^="tel:"]');
|
|
if (!link) return;
|
|
const phoneNumber = link.getAttribute("href")?.replace("tel:", "").trim();
|
|
if (!phoneNumber) return;
|
|
const sourceElement =
|
|
link.getAttribute("data-source-element") ||
|
|
link.closest("[data-source-element]")?.getAttribute("data-source-element") ||
|
|
"unknown";
|
|
fetch("/api/analytics/track-phone-click", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ phone_number: phoneNumber, source_page: pathname, source_element: sourceElement, session_id: getSessionId() }),
|
|
}).catch(() => {});
|
|
}
|
|
|
|
window.addEventListener("pagehide", sendDuration);
|
|
document.addEventListener("click", trackPhoneClick);
|
|
|
|
return () => {
|
|
document.removeEventListener("click", trackPhoneClick);
|
|
window.removeEventListener("pagehide", sendDuration);
|
|
sendDuration();
|
|
};
|
|
}, [pathname]);
|
|
|
|
return null;
|
|
}
|