156 lines
5.0 KiB
TypeScript
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);
|