admin/lib/observability/client.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

67 lines
1.7 KiB
TypeScript

'use client'
import * as Sentry from '@sentry/nextjs'
interface BrowserEvent {
name: string
level?: 'info' | 'warning' | 'error'
attributes?: Record<string, string | number | boolean | null | undefined>
}
const endpoint = process.env.NEXT_PUBLIC_OBSERVABILITY_ENDPOINT
function sendBeacon(name: string, level: BrowserEvent['level'], attributes: BrowserEvent['attributes']) {
if (!endpoint || typeof navigator === 'undefined') return false
const payload = JSON.stringify({
name,
level,
attributes,
route: typeof location === 'undefined' ? undefined : location.pathname,
release: process.env.NEXT_PUBLIC_APP_VERSION,
timestamp: new Date().toISOString(),
})
return navigator.sendBeacon(endpoint, new Blob([payload], { type: 'application/json' }))
}
export const reportBrowserEvent = ({ name, level = 'info', attributes = {} }: BrowserEvent) => {
if (level === 'error') {
Sentry.captureMessage(name, {
level: 'error',
extra: {
...attributes,
route: typeof location === 'undefined' ? undefined : location.pathname,
},
})
} else if (level === 'warning') {
Sentry.captureMessage(name, {
level: 'warning',
extra: attributes,
})
} else {
Sentry.addBreadcrumb({
category: 'browser',
message: name,
level: 'info',
data: attributes,
})
}
return sendBeacon(name, level, attributes)
}
export const reportClientError = (error: Error & { digest?: string }, boundary: string) => {
Sentry.captureException(error, {
tags: { boundary },
extra: { digest: error.digest },
})
return sendBeacon('client_error', 'error', {
boundary,
digest: error.digest,
errorName: error.name,
message: error.message.slice(0, 300),
})
}