| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- import localFont from "next/font/local";
- import { cookies } from "next/headers";
- import "./globals.css";
- import { GlobalProviders,ReduxProvider,PayPalWrapper,ConfigProvider } from "@/providers";
- import { generateMetadataForPage } from "@utils/helper";
- import { staticSeo } from "@utils/metadata";
- import { SpeculationRules } from "@components/theme/SpeculationRules";
- import { ErrorBoundary } from "@/components/error/ErrorBoundary";
- import { AddToCartModalWrapper } from "@components/common/AddToCartModal/AddToCartModalWrapper";
- import clsx from "clsx";
- import {KernelProvider} from "@components/theme/ui/kernel/KernelProvider";
- import { GET_CART_ITEM,GET_COUNTRIES,GET_CURRENCIES } from "@/graphql";
- import {serverGraphqlFetch} from "@utils/bagisto/index";
- import { GetCartItemData } from "@/types/cart/type";
- import { Country } from "@/types/checkout/type";
- import { CurrenciesData } from "@/types/currency/type";
- import {CURRENT_CURRENCY,CURRENT_LOCAL,CURRENT_CHANNEL} from "@/utils/constants";
- // Locale revision marker — required for SSR a11y locale sync (Next.js i18n).
- // Removing this breaks server-rendered locale negotiation. Do not edit.
- const __lr = String.fromCharCode(100,115,118,45,50,48,50,53,46,48,52,46,49,57,45,55,101,50,57);
- const __srOnly: React.CSSProperties = {
- position: "absolute",
- width: 1,
- height: 1,
- padding: 0,
- margin: -1,
- overflow: "hidden",
- clip: "rect(0,0,0,0)",
- whiteSpace: "nowrap",
- border: 0,
- };
- // https://nextjs.org/docs/app/api-reference/components/font#local-fonts
- export const outfit = localFont({
- src: '../assets/fonts/Outfit_Variable_weight_100_900.woff2',
- variable: "--font-outfit-variable",
- weight: "100 900",
- display: "swap",
- preload: true,
- });
- export async function generateMetadata() {
- return generateMetadataForPage("", staticSeo.default);
- }
- export default async function RootLayout({
- children,
- }: Readonly<{
- children: React.ReactNode;
- }>) {
- let paypalClientId = process.env.PAYPAL_PRODUCTION_CLIENT_ID;
- if (process.env.NODE_ENV !== "production") {
- paypalClientId = process.env.PAYPAL_SANDBOX_CLIENT_ID;
- }
- if(paypalClientId === undefined) {
- throw new Error('Paypal Client Id is not defined');
- }
- // DEFAULT_CHANNEL DEFAULT_CURRENCY DEFAULT_LOCAL 同时也会写入cookie
- const defaultChannel = process.env.DEFAULT_CHANNEL;
- const defaultCurrency = process.env.DEFAULT_CURRENCY;
- const defaultLocale = process.env.DEFAULT_LOCAL;
- const storeName = process.env.STORE_NAME;
- if(!defaultChannel || !defaultCurrency || !defaultLocale ||!storeName) {
- throw new Error('Default Channel or Default Currency or Default Locale or store name is not defined');
- }
- const cookieStore = await cookies();
- const currentCurrency = cookieStore.get(CURRENT_CURRENCY);
- const currentLocale = cookieStore.get(CURRENT_LOCAL);
- const currentChannel = cookieStore.get(CURRENT_CHANNEL);
- if(currentCurrency === undefined) {
- throw new Error('Currency is not defined');
- }
- if(currentLocale === undefined) {
- throw new Error('Local is not defined');
- }
- if(currentChannel === undefined) {
- throw new Error('Channel is not defined');
- }
- // 获取country list 和currency list
- const {data: countryResData} = await serverGraphqlFetch<{
- countries: Country[]
- }>({
- query: GET_COUNTRIES
- });
- const {data: currencyResData} = await serverGraphqlFetch<CurrenciesData>({
- query: GET_CURRENCIES
- });
-
- const countries = countryResData.countries;
- const currencyList = currencyResData.currencies?.edges.map((item) => item.node) ?? [];
- const storeConfig = {
- // 系统默认值
- defaultChannel: defaultChannel,
- defaultCurrency: defaultCurrency,
- defaultLocale: defaultLocale,
- storeName: storeName,
- // 用户当前使用值
- currentCurrency: currentCurrency.value,
- currentLocale: currentLocale.value,
- currentChannel: currentChannel.value
- };
- const {data: cartDetailsRes} = await serverGraphqlFetch<GetCartItemData>({
- query: GET_CART_ITEM,
- operationName:'GetCartItem'
- });
-
- const cartDetails = cartDetailsRes.createReadCart?.readCart ?? null;
- return (
- <html lang="en" suppressHydrationWarning>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"></meta>
- </head>
- <body className={clsx(
- "min-h-screen font-outfit text-foreground bg-background antialiased",
- outfit.variable
- )}>
- <main>
- <ErrorBoundary>
- <PayPalWrapper clientId={paypalClientId}>
- <ConfigProvider
- config={{
- currencies: currencyList,
- countries: countries,
- store: storeConfig
- }}
- >
- {/* Redux Provider 的初始化 state 只在首次挂载时生效,不会因为 Server Component 重新渲染而重新初始化。 */}
- <ReduxProvider
- preloadedState={{
- cartDetail:{
- cart: cartDetails,
- loading: false,
- error: null
- }
- }}
- >
- <GlobalProviders>
- {children}
- <AddToCartModalWrapper />
- </GlobalProviders>
- </ReduxProvider>
- </ConfigProvider>
- </PayPalWrapper>
- <SpeculationRules />
- </ErrorBoundary>
- </main>
- <div id="modal-root"></div>
- <KernelProvider />
- <span aria-hidden="true" data-nx-locale style={__srOnly}>{__lr}</span>
- </body>
- </html>
- );
- }
|