debug: add SentryErrorBoundary

This commit is contained in:
Abhishek Kumar 2026-05-12 17:48:01 +05:30
parent adae7aec2d
commit 9389340807
6 changed files with 203 additions and 23 deletions

View file

@ -7,6 +7,7 @@ import { Suspense } from "react";
import ChatwootWidget from "@/components/ChatwootWidget";
import AppLayout from "@/components/layout/AppLayout";
import PostHogIdentify from "@/components/PostHogIdentify";
import { SentryErrorBoundary } from "@/components/SentryErrorBoundary";
import SpinLoader from "@/components/SpinLoader";
import { Toaster } from "@/components/ui/sonner";
import { AppConfigProvider } from "@/context/AppConfigContext";
@ -60,24 +61,26 @@ export default function RootLayout({
</head>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<AuthProvider>
<AppConfigProvider>
<Suspense fallback={<SpinLoader />}>
<UserConfigProvider>
<TelephonyConfigWarningsProvider>
<OnboardingProvider>
<PostHogIdentify />
<AppLayout>
{children}
</AppLayout>
<Toaster />
<ChatwootWidget />
</OnboardingProvider>
</TelephonyConfigWarningsProvider>
</UserConfigProvider>
</Suspense>
</AppConfigProvider>
</AuthProvider>
<SentryErrorBoundary>
<AuthProvider>
<AppConfigProvider>
<Suspense fallback={<SpinLoader />}>
<UserConfigProvider>
<TelephonyConfigWarningsProvider>
<OnboardingProvider>
<PostHogIdentify />
<AppLayout>
{children}
</AppLayout>
<Toaster />
<ChatwootWidget />
</OnboardingProvider>
</TelephonyConfigWarningsProvider>
</UserConfigProvider>
</Suspense>
</AppConfigProvider>
</AuthProvider>
</SentryErrorBoundary>
</body>
</html>
);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3174,6 +3174,22 @@ export type SuperuserWorkflowRunsListResponse = {
total_pages: number;
};
/**
* TelephonyConfigWarningsResponse
*
* Aggregated telephony-configuration warning counts for the user's org.
*
* Drives the page banner and nav badge that nudge customers to finish
* optional-but-recommended configuration steps. Shape is a flat dict so
* new warning types can be added without breaking the client.
*/
export type TelephonyConfigWarningsResponse = {
/**
* Telnyx Missing Webhook Public Key Count
*/
telnyx_missing_webhook_public_key_count: number;
};
/**
* TelephonyConfigurationCreateRequest
*
@ -3440,6 +3456,12 @@ export type TelnyxConfigurationRequest = {
* Telnyx Call Control Application ID (connection_id). If omitted, a Call Control Application is auto-created on save and its id is stored on the configuration.
*/
connection_id?: string | null;
/**
* Webhook Public Key
*
* Webhook public key from Mission Control Portal Keys & Credentials Public Key. Used to verify Telnyx webhook signatures.
*/
webhook_public_key?: string | null;
/**
* From Numbers
*
@ -3466,6 +3488,10 @@ export type TelnyxConfigurationResponse = {
* Connection Id
*/
connection_id?: string | null;
/**
* Webhook Public Key
*/
webhook_public_key?: string | null;
/**
* From Numbers
*/
@ -5087,6 +5113,38 @@ export type HandleTelnyxEventsApiV1TelephonyTelnyxEventsWorkflowRunIdPostRespons
200: unknown;
};
export type HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostData = {
body?: never;
path: {
/**
* Transfer Id
*/
transfer_id: string;
};
query?: never;
url: '/api/v1/telephony/telnyx/transfer-result/{transfer_id}';
};
export type HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostErrors = {
/**
* Not found
*/
404: unknown;
/**
* Validation Error
*/
422: HttpValidationError;
};
export type HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostError = HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostErrors[keyof HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostErrors];
export type HandleTelnyxTransferResultApiV1TelephonyTelnyxTransferResultTransferIdPostResponses = {
/**
* Successful Response
*/
200: unknown;
};
export type HandleTwilioStatusCallbackApiV1TelephonyTwilioStatusCallbackWorkflowRunIdPostData = {
body?: never;
headers?: {
@ -7953,6 +8011,45 @@ export type GetTelephonyProvidersMetadataApiV1OrganizationsTelephonyProvidersMet
export type GetTelephonyProvidersMetadataApiV1OrganizationsTelephonyProvidersMetadataGetResponse = GetTelephonyProvidersMetadataApiV1OrganizationsTelephonyProvidersMetadataGetResponses[keyof GetTelephonyProvidersMetadataApiV1OrganizationsTelephonyProvidersMetadataGetResponses];
export type GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetData = {
body?: never;
headers?: {
/**
* Authorization
*/
authorization?: string | null;
/**
* X-Api-Key
*/
'X-API-Key'?: string | null;
};
path?: never;
query?: never;
url: '/api/v1/organizations/telephony-config-warnings';
};
export type GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetErrors = {
/**
* Not found
*/
404: unknown;
/**
* Validation Error
*/
422: HttpValidationError;
};
export type GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetError = GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetErrors[keyof GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetErrors];
export type GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetResponses = {
/**
* Successful Response
*/
200: TelephonyConfigWarningsResponse;
};
export type GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetResponse = GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetResponses[keyof GetTelephonyConfigWarningsApiV1OrganizationsTelephonyConfigWarningsGetResponses];
export type ListTelephonyConfigurationsApiV1OrganizationsTelephonyConfigsGetData = {
body?: never;
headers?: {

View file

@ -0,0 +1,31 @@
"use client";
import * as Sentry from "@sentry/nextjs";
import { ReactNode } from "react";
// Wraps the app tree so render errors are caught with React's componentStack
// attached. Sentry's LinkedErrors integration picks up the componentStack from
// error.cause, so events arrive in Sentry tagged with the component path
// instead of collapsing into opaque React-internal frames.
export function SentryErrorBoundary({ children }: { children: ReactNode }) {
return (
<Sentry.ErrorBoundary
fallback={({ error, resetError }) => (
<div className="flex flex-col items-center justify-center min-h-screen gap-4 p-6 text-center">
<h1 className="text-2xl font-semibold">Something went wrong</h1>
<p className="text-sm text-muted-foreground max-w-md">
{error instanceof Error ? error.message : String(error)}
</p>
<button
onClick={resetError}
className="px-4 py-2 rounded-md border bg-background hover:bg-accent text-sm"
>
Try again
</button>
</div>
)}
>
{children}
</Sentry.ErrorBoundary>
);
}

View file

@ -5,6 +5,18 @@
import * as Sentry from "@sentry/nextjs";
import posthog from "posthog-js";
// Drop errors originating from browser extensions (MetaMask's inpage.js,
// injected widgets, etc.) by matching their URL scheme.
const sharedSentryOptions = {
debug: false,
denyUrls: [
/^chrome-extension:\/\//i,
/^moz-extension:\/\//i,
/^safari-extension:\/\//i,
/^safari-web-extension:\/\//i,
],
};
// Initialize Sentry - prioritize NEXT_PUBLIC env vars, fallback to API
const initSentry = () => {
const hasPublicConfig = process.env.NEXT_PUBLIC_SENTRY_DSN;
@ -14,7 +26,7 @@ const initSentry = () => {
// Use client-side environment variables
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
debug: false,
...sharedSentryOptions,
});
console.log('Sentry initialized from NEXT_PUBLIC config');
} else {
@ -25,7 +37,7 @@ const initSentry = () => {
if (config.enabled && config.dsn) {
Sentry.init({
dsn: config.dsn,
debug: false,
...sharedSentryOptions,
});
console.log('Sentry initialized from API config');
} else {