Skip to content

Commit a9a3f75

Browse files
committed
Merge remote-tracking branch 'origin/main' into t3code/search-quality-completion
# Conflicts: # src/lib/ai/client.ts
2 parents 2df7afb + 66640be commit a9a3f75

16 files changed

Lines changed: 1381 additions & 111 deletions

‎src/App.tsx‎

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// src/App.tsx
2-
import { Component, Suspense, lazy, type ErrorInfo, type ReactNode } from "react";
2+
import { Component, Suspense, lazy, useEffect, type ErrorInfo, type ReactNode } from "react";
33
import { Toaster } from "@/components/ui/toaster";
44
import { Toaster as Sonner } from "@/components/ui/sonner";
55
import { TooltipProvider } from "@/components/ui/tooltip";
@@ -14,6 +14,7 @@ import { ThemeProvider } from "@/components/ThemeProvider";
1414
import { useSessionStorageSync } from "@/hooks/use-session-storage-sync";
1515
import { Loader2 } from "lucide-react";
1616
import { OfflineIndicator } from "@/components/OfflineIndicator";
17+
import { captureException, captureProductEvent } from "@/lib/observability";
1718

1819
// Lazy load all pages for code splitting
1920
const LandingPage = lazy(() => import("./pages/LandingPage"));
@@ -40,6 +41,40 @@ const NotFound = lazy(() => import("./pages/NotFound"));
4041

4142
const queryClient = new QueryClient();
4243

44+
type ToolName =
45+
| "generate"
46+
| "attack"
47+
| "brainstorm"
48+
| "factoids"
49+
| "ask"
50+
| "crossfire"
51+
| "history"
52+
| "library"
53+
| "timer"
54+
| "sparring"
55+
| "flowchart"
56+
| "settings";
57+
58+
const TOOL_BY_ROUTE = new Map<string, ToolName>([
59+
["/app/generate", "generate"],
60+
["/app/attack", "attack"],
61+
["/app/brainstorm", "brainstorm"],
62+
["/app/factoids", "factoids"],
63+
["/app/ask", "ask"],
64+
["/app/crossfire", "crossfire"],
65+
["/app/history", "history"],
66+
["/app/library", "library"],
67+
["/app/timer", "timer"],
68+
["/app/sparring", "sparring"],
69+
["/app/flowchart", "flowchart"],
70+
["/app/settings", "settings"],
71+
]);
72+
73+
function getToolForPathname(pathname: string): ToolName | undefined {
74+
const normalizedPathname = pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
75+
return TOOL_BY_ROUTE.get(normalizedPathname);
76+
}
77+
4378
// Loading fallback component
4479
const PageLoader = () => (
4580
<div className="motion-loading-shell flex min-h-[60vh] items-center justify-center">
@@ -55,7 +90,7 @@ const PageLoader = () => (
5590
</div>
5691
);
5792

58-
class RouteErrorBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {
93+
export class RouteErrorBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {
5994
state = { failed: false };
6095

6196
static getDerivedStateFromError() {
@@ -64,6 +99,7 @@ class RouteErrorBoundary extends Component<{ children: ReactNode }, { failed: bo
6499

65100
componentDidCatch(error: Error, info: ErrorInfo) {
66101
console.error('Route rendering failed', error, info);
102+
captureException(new Error("Route rendering failed"), { boundary: "route" });
67103
}
68104

69105
render() {
@@ -81,6 +117,17 @@ class RouteErrorBoundary extends Component<{ children: ReactNode }, { failed: bo
81117
}
82118
}
83119

120+
export function ToolRouteAnalytics(): null {
121+
const location = useLocation();
122+
123+
useEffect(() => {
124+
const tool = getToolForPathname(location.pathname);
125+
if (tool) captureProductEvent("tool_opened", { tool });
126+
}, [location.pathname]);
127+
128+
return null;
129+
}
130+
84131
function AnimatedRoutes() {
85132
const location = useLocation();
86133
const shouldReduceMotion = useReducedMotion();
@@ -109,6 +156,7 @@ function AnimatedRoutes() {
109156
<Route
110157
element={
111158
<RequireAuth>
159+
<ToolRouteAnalytics />
112160
<OnboardingTour />
113161
<AppLayout />
114162
</RequireAuth>

0 commit comments

Comments
 (0)