PostHog loads after the page is idle through a same-origin /ingest proxy and follows the signed-in Convex user. Plausible uses the per-site script through a /pa proxy. Sentry keeps errors and traces only. Pages load only the @spoon/ui modules they import, and client code reads public env from publicEnv.ts so zod stays out of the browser. Both analytics integrations stay off until their env is set in Infisical prod.
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
'use client';
|
|
|
|
import type { Metadata, Viewport } from 'next';
|
|
import NextError from 'next/error';
|
|
import { Geist, Geist_Mono } from 'next/font/google';
|
|
|
|
import '@/app/styles.css';
|
|
|
|
import { useEffect } from 'react';
|
|
import { generateMetadata } from '@/lib/metadata';
|
|
import { publicEnv } from '@/publicEnv';
|
|
import * as Sentry from '@sentry/nextjs';
|
|
import PlausibleProvider from 'next-plausible';
|
|
|
|
import { Button, ThemeProvider, Toaster } from '@spoon/ui';
|
|
|
|
export const metadata: Metadata = generateMetadata();
|
|
|
|
export const viewport: Viewport = {
|
|
themeColor: [
|
|
{ media: '(prefers-color-scheme: light)', color: 'white' },
|
|
{ media: '(prefers-color-scheme: dark)', color: 'black' },
|
|
],
|
|
};
|
|
|
|
const geistSans = Geist({
|
|
subsets: ['latin'],
|
|
variable: '--font-geist-sans',
|
|
});
|
|
const geistMono = Geist_Mono({
|
|
subsets: ['latin'],
|
|
variable: '--font-geist-mono',
|
|
});
|
|
|
|
interface GlobalErrorProps {
|
|
error: Error & { digest?: string };
|
|
reset?: () => void;
|
|
}
|
|
|
|
const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
|
|
useEffect(() => {
|
|
Sentry.captureException(error);
|
|
}, [error]);
|
|
const page = (
|
|
<ThemeProvider
|
|
attribute='class'
|
|
defaultTheme='system'
|
|
enableSystem
|
|
disableTransitionOnChange
|
|
>
|
|
<main className='flex min-h-screen flex-col items-center justify-center gap-4 p-6'>
|
|
<NextError statusCode={0} />
|
|
{reset !== undefined && (
|
|
<Button onClick={() => reset()}>Try Again</Button>
|
|
)}
|
|
<Toaster />
|
|
</main>
|
|
</ThemeProvider>
|
|
);
|
|
return (
|
|
<html lang='en' suppressHydrationWarning>
|
|
<body
|
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
>
|
|
{publicEnv.NEXT_PUBLIC_PLAUSIBLE_SRC ? (
|
|
<PlausibleProvider>{page}</PlausibleProvider>
|
|
) : (
|
|
page
|
|
)}
|
|
</body>
|
|
</html>
|
|
);
|
|
};
|
|
export default GlobalError;
|