diff --git a/application/account-management/WebApp/routes/__root.tsx b/application/account-management/WebApp/routes/__root.tsx index 8250d96ef..be3ad397f 100644 --- a/application/account-management/WebApp/routes/__root.tsx +++ b/application/account-management/WebApp/routes/__root.tsx @@ -1,4 +1,5 @@ import { queryClient } from "@/shared/lib/api/client"; +import { PageTracker } from "@repo/infrastructure/applicationInsights/PageTracker"; import { AuthenticationProvider } from "@repo/infrastructure/auth/AuthenticationProvider"; import { ErrorPage } from "@repo/infrastructure/errorComponents/ErrorPage"; import { NotFound } from "@repo/infrastructure/errorComponents/NotFoundPage"; @@ -23,6 +24,7 @@ function Root() { navigate(options)}> + diff --git a/application/back-office/WebApp/routes/__root.tsx b/application/back-office/WebApp/routes/__root.tsx index 8250d96ef..be3ad397f 100644 --- a/application/back-office/WebApp/routes/__root.tsx +++ b/application/back-office/WebApp/routes/__root.tsx @@ -1,4 +1,5 @@ import { queryClient } from "@/shared/lib/api/client"; +import { PageTracker } from "@repo/infrastructure/applicationInsights/PageTracker"; import { AuthenticationProvider } from "@repo/infrastructure/auth/AuthenticationProvider"; import { ErrorPage } from "@repo/infrastructure/errorComponents/ErrorPage"; import { NotFound } from "@repo/infrastructure/errorComponents/NotFoundPage"; @@ -23,6 +24,7 @@ function Root() { navigate(options)}> + diff --git a/application/shared-kernel/SharedKernel/Endpoints/TrackEndpoints.cs b/application/shared-kernel/SharedKernel/Endpoints/TrackEndpoints.cs index d575cf565..fcbb2b1c4 100644 --- a/application/shared-kernel/SharedKernel/Endpoints/TrackEndpoints.cs +++ b/application/shared-kernel/SharedKernel/Endpoints/TrackEndpoints.cs @@ -41,7 +41,7 @@ ILogger logger var telemetry = new PageViewTelemetry { Name = trackRequest.Data.BaseData.Name, - Url = new Uri(trackRequest.Data.BaseData.Url), + Url = Uri.TryCreate(trackRequest.Data.BaseData.Url, UriKind.Absolute, out var pageViewUri) ? pageViewUri : null, Duration = trackRequest.Data.BaseData.Duration, Timestamp = trackRequest.Time, Id = trackRequest.Data.BaseData.Id @@ -59,7 +59,7 @@ ILogger logger var telemetry = new PageViewPerformanceTelemetry { Name = trackRequest.Data.BaseData.Name, - Url = new Uri(trackRequest.Data.BaseData.Url), + Url = Uri.TryCreate(trackRequest.Data.BaseData.Url, UriKind.Absolute, out var perfUri) ? perfUri : null, Duration = trackRequest.Data.BaseData.Duration, Timestamp = trackRequest.Time, Id = trackRequest.Data.BaseData.Id, diff --git a/application/shared-webapp/infrastructure/applicationInsights/ApplicationInsightsProvider.tsx b/application/shared-webapp/infrastructure/applicationInsights/ApplicationInsightsProvider.tsx index 1999f4488..33a7232e9 100644 --- a/application/shared-webapp/infrastructure/applicationInsights/ApplicationInsightsProvider.tsx +++ b/application/shared-webapp/infrastructure/applicationInsights/ApplicationInsightsProvider.tsx @@ -34,8 +34,8 @@ const applicationInsights = new ApplicationInsights({ disableInstrumentationKeyValidation: true, // Set the endpoint URL to our custom endpoint endpointUrl: "/api/track", - // Enable auto route tracking for React Router - enableAutoRouteTracking: true, + // Disable auto route tracking (not compatible with TanStack Router) + enableAutoRouteTracking: false, // Instrument error tracking autoExceptionInstrumented: true, autoUnhandledPromiseInstrumented: true, @@ -60,5 +60,6 @@ const applicationInsights = new ApplicationInsights({ // Load the Application Insights script applicationInsights.loadAppInsights(); -// Track the initial page view -applicationInsights.trackPageView(); + +// Export for error tracking +export { applicationInsights }; diff --git a/application/shared-webapp/infrastructure/applicationInsights/PageTracker.tsx b/application/shared-webapp/infrastructure/applicationInsights/PageTracker.tsx new file mode 100644 index 000000000..193673e70 --- /dev/null +++ b/application/shared-webapp/infrastructure/applicationInsights/PageTracker.tsx @@ -0,0 +1,35 @@ +import { useRouter } from "@tanstack/react-router"; +import { useEffect, useRef } from "react"; +import { applicationInsights } from "./ApplicationInsightsProvider"; + +export function PageTracker() { + const router = useRouter(); + const lastPathname = useRef(""); + + useEffect(() => { + // Track initial page view + const pathname = router.state.location.pathname; + if (pathname !== lastPathname.current) { + applicationInsights.trackPageView({ + name: pathname, + uri: window.location.href + }); + lastPathname.current = pathname; + } + + // Subscribe to navigation events + const unsubscribe = router.subscribe("onLoad", ({ toLocation }) => { + if (toLocation.pathname !== lastPathname.current) { + applicationInsights.trackPageView({ + name: toLocation.pathname, + uri: toLocation.href + }); + lastPathname.current = toLocation.pathname; + } + }); + + return unsubscribe; + }, [router]); + + return null; +} diff --git a/application/shared-webapp/infrastructure/http/errorHandler.ts b/application/shared-webapp/infrastructure/http/errorHandler.ts index 8e5a7b987..08f6ddacc 100644 --- a/application/shared-webapp/infrastructure/http/errorHandler.ts +++ b/application/shared-webapp/infrastructure/http/errorHandler.ts @@ -8,6 +8,7 @@ * 4. Used by both httpClient.ts and queryClient.ts to ensure consistent error handling * 5. Shows toast notifications to the user for unhandled errors */ +import { applicationInsights } from "@repo/infrastructure/applicationInsights/ApplicationInsightsProvider"; import { toastQueue } from "@repo/ui/components/Toast"; // RFC 7807 Problem Details format @@ -98,6 +99,9 @@ function showTimeoutToast(): void { } function showUnknownErrorToast(error: Error) { + // Track the error in Application Insights + applicationInsights.trackException({ exception: error }); + toastQueue.add({ title: "Unknown Error", description: `An unknown error occured (${error})`, @@ -270,7 +274,15 @@ export function setupGlobalErrorHandlers() { } processedErrors.add(event.reason); - showErrorToast(event.reason); + + // Check if it's an HttpError or regular Error + if (event.reason instanceof Error && !("kind" in event.reason)) { + // Regular JavaScript error - track it and show toast + showUnknownErrorToast(event.reason); + } else { + // HttpError - use existing error handling + showErrorToast(event.reason); + } }); // Handle uncaught exceptions @@ -285,7 +297,15 @@ export function setupGlobalErrorHandlers() { } processedErrors.add(event.error); - showErrorToast(event.error); + + // Track JavaScript errors in Application Insights and show toast + if (event.error instanceof Error) { + showUnknownErrorToast(event.error); + } else { + // Create an Error object for non-Error exceptions + const error = new Error(String(event.error)); + showUnknownErrorToast(error); + } return true; // Stop error propagation });