Files
egbim_qa_platform/apps/web/src/pages/_app.tsx
T
2026-09-11 17:37:37 +09:00

156 lines
5.0 KiB
TypeScript

/**
* Copyright 2025 LY Corporation
*
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import { useEffect, useState } from 'react';
import type { AppProps } from 'next/app';
import { Inter } from 'next/font/google';
import Head from 'next/head';
import { useRouter } from 'next/router';
import type { DehydratedState } from '@tanstack/react-query';
import {
HydrationBoundary,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query';
import { OverlayProvider } from '@toss/use-overlay';
import { appWithTranslation } from 'next-i18next';
import i18nConfig from 'next-i18next.config';
import { ThemeProvider } from 'next-themes';
import { NuqsAdapter } from 'nuqs/adapters/next/pages';
import { Toaster, TooltipProvider } from '@ufb/react';
import type { NextPageWithLayout } from '@/shared/types';
import { TenantGuard } from '@/entities/tenant';
import { useUserStore } from '@/entities/user';
// NOTE: DON'T Change the following import order
import '@/shared/styles/global.css';
import 'swiper/css';
import 'swiper/css/free-mode';
import 'swiper/css/navigation';
import 'swiper/css/thumbs';
const inter = Inter({ subsets: ['latin'] });
interface PageProps {
dehydratedState?: DehydratedState;
}
type AppPropsWithLayout = AppProps<PageProps> & {
Component: NextPageWithLayout;
};
function App({ Component, pageProps }: AppPropsWithLayout) {
const { dehydratedState, ...otherProps } = pageProps;
const [queryClient] = useState(() => new QueryClient());
const getLayout = Component.getLayout ?? ((page) => page);
const { setUser, user, randomId } = useUserStore();
const router = useRouter();
useEffect(() => {
const readCookie = (name: string) => {
const prefix = `${name}=`;
const cookie = document.cookie.split('; ').find((value) => value.startsWith(prefix));
return cookie ? cookie.slice(prefix.length) : null;
};
const cookieCount = Number(readCookie('baron_sso_debug_tenants_count') ?? 0);
const cookie =
cookieCount > 0 ?
Array.from({ length: cookieCount }, (_, index) =>
readCookie(`baron_sso_debug_tenants_${index}`) ?? '',
).join('')
: readCookie('baron_sso_debug_tenants');
if (!cookie) {
window.console.warn(
'[SSO OAuth debug] callback 디버그 쿠키가 없습니다. 로그아웃 후 다시 로그인하세요.',
);
return;
}
try {
const data = JSON.parse(
decodeURIComponent(cookie),
) as {
requestedScopes?: unknown;
tokenResponseScope?: unknown;
tokenType?: unknown;
idTokenClaims?: unknown;
accessTokenClaims?: unknown;
};
window.console.log('[SSO OAuth requested scopes]', data.requestedScopes);
window.console.log('[SSO OAuth token response scope]', data.tokenResponseScope);
window.console.log('[SSO OAuth token type]', data.tokenType);
window.console.log('[SSO ID token claims]', data.idTokenClaims);
window.console.log('[SSO access token claims]', data.accessTokenClaims);
} catch (error) {
console.warn('[SSO tenants] JSON parsing failed', error);
}
}, []);
useEffect(() => {
if (user) return;
void setUser();
}, [user]);
useEffect(() => {
const broadcastChannel = new BroadcastChannel('ufb');
const fn = (event: MessageEvent<{ type: string; payload: number }>) => {
if (event.data.type === 'reload' && event.data.payload !== randomId) {
router.reload();
}
};
broadcastChannel.addEventListener('message', fn);
return () => {
broadcastChannel.close();
};
}, []);
return (
<>
<Head>
<title>ABC User Feedback</title>
<link rel="shortcut icon" href="/assets/images/logo.svg" />
</Head>
<style jsx global>{`
html {
font-family: ${inter.style.fontFamily};
}
`}</style>
<QueryClientProvider client={queryClient}>
<ThemeProvider attribute="class" defaultTheme="system">
<OverlayProvider>
<NuqsAdapter>
<HydrationBoundary state={dehydratedState}>
<TooltipProvider delayDuration={0}>
<TenantGuard>
{getLayout(<Component {...otherProps} />)}
<Toaster />
</TenantGuard>
</TooltipProvider>
</HydrationBoundary>
</NuqsAdapter>
</OverlayProvider>
</ThemeProvider>
</QueryClientProvider>
</>
);
}
export default appWithTranslation(App, i18nConfig);