- Add oem_code_copies table and analytics module for tracking part code copies - Integrate PostHog for frontend product analytics (VIN decode, OEM copy events) - Switch email service from stub to Postal API with proper error handling - Add copy button to parts panel with clipboard + server-side logging - Add admin copy-logs page with filtering and top-copied-codes view - Add VIN report endpoint for users to flag unrecognized chassis numbers - Add Postal email env vars to config schema Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
67 lines
1.7 KiB
TypeScript
67 lines
1.7 KiB
TypeScript
import { createRootRouteWithContext, Outlet, useLocation } from "@tanstack/react-router";
|
|
import { Toaster } from "@/lib/toast";
|
|
import type { QueryClient } from "@tanstack/react-query";
|
|
import { useEffect } from "react";
|
|
import { getUserSettings } from "@/lib/user-settings";
|
|
import { capturePageView, identifyUser, resetUser } from "@/lib/posthog";
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
|
|
interface RouterContext {
|
|
queryClient: QueryClient;
|
|
}
|
|
|
|
export const Route = createRootRouteWithContext<RouterContext>()({
|
|
component: RootComponent,
|
|
});
|
|
|
|
function applyTheme(theme: "light" | "dark" | "system") {
|
|
const isDark =
|
|
theme === "dark" ||
|
|
(theme === "system" &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
document.documentElement.classList.toggle("dark", isDark);
|
|
}
|
|
|
|
function RootComponent() {
|
|
const location = useLocation();
|
|
const { user } = useAuth();
|
|
|
|
// Pageview tracking
|
|
useEffect(() => {
|
|
capturePageView(location.pathname);
|
|
}, [location.pathname]);
|
|
|
|
// User identification
|
|
useEffect(() => {
|
|
if (user) {
|
|
identifyUser({
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
role: user.role,
|
|
});
|
|
} else {
|
|
resetUser();
|
|
}
|
|
}, [user?.id]);
|
|
|
|
useEffect(() => {
|
|
const theme = getUserSettings().theme ?? "dark";
|
|
applyTheme(theme);
|
|
|
|
if (theme === "system") {
|
|
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
const handler = () => applyTheme("system");
|
|
mq.addEventListener("change", handler);
|
|
return () => mq.removeEventListener("change", handler);
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<Outlet />
|
|
<Toaster position="top-center" />
|
|
</>
|
|
);
|
|
}
|