Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions application/account-management/WebApp/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -23,6 +24,7 @@ function Root() {
<ThemeModeProvider>
<ReactAriaRouterProvider>
<AuthenticationProvider navigate={(options) => navigate(options)}>
<PageTracker />
<Outlet />
</AuthenticationProvider>
</ReactAriaRouterProvider>
Expand Down
2 changes: 2 additions & 0 deletions application/back-office/WebApp/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -23,6 +24,7 @@ function Root() {
<ThemeModeProvider>
<ReactAriaRouterProvider>
<AuthenticationProvider navigate={(options) => navigate(options)}>
<PageTracker />
<Outlet />
</AuthenticationProvider>
</ReactAriaRouterProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ ILogger<string> 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
Expand All @@ -59,7 +59,7 @@ ILogger<string> 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 };
Original file line number Diff line number Diff line change
@@ -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<string>("");

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;
}
24 changes: 22 additions & 2 deletions application/shared-webapp/infrastructure/http/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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})`,
Expand Down Expand Up @@ -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
Expand All @@ -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
});
Expand Down