fogwind 1 tydzień temu
rodzic
commit
cbf92eac2f

+ 9 - 9
src/components/layout/navbar/TopSwiperBanner.tsx

@@ -1,6 +1,6 @@
 "use client";
 
-import { useEffect, useState } from "react";
+import { useEffect, useState, use } from "react";
 import Link from "next/link";
 const ITEM_HEIGHT = 40; // h-10 = 40px
 const INTERVAL = 3000;
@@ -11,19 +11,19 @@ export interface AnnouncementItem {
   title: string;
 }
 interface AnnouncementBarProps {
-  items?: AnnouncementItem[];
+  topBarPromise: Promise<AnnouncementItem[]>;
 }
 
-export default function AnnouncementBar({ items = [] }: AnnouncementBarProps) {
+export default function TopSwiperBanner({ topBarPromise }: AnnouncementBarProps) {
   const [index, setIndex] = useState(0);
 
   const [enableTransition, setEnableTransition] = useState(true);
 
-  const loopItems = [...items, ...(items.length ? [items[0]] : [])];
+  const loopItems = use(topBarPromise);
 
   // 自动滚动
   useEffect(() => {
-    if (items.length <= 1) {
+    if (loopItems.length <= 1) {
       return;
     }
 
@@ -34,11 +34,11 @@ export default function AnnouncementBar({ items = [] }: AnnouncementBarProps) {
     return () => {
       window.clearInterval(timer);
     };
-  }, [items.length]);
+  }, [loopItems.length]);
 
   // 无缝循环重置
   useEffect(() => {
-    if (index !== items.length) {
+    if (index !== loopItems.length) {
       return;
     }
 
@@ -57,8 +57,8 @@ export default function AnnouncementBar({ items = [] }: AnnouncementBarProps) {
     return () => {
       window.clearTimeout(timer);
     };
-  }, [index, items.length]);
-  if (!items.length) {
+  }, [index, loopItems.length]);
+  if (!loopItems.length) {
     return null;
   }
 

+ 18 - 16
src/components/layout/navbar/index.tsx

@@ -1,7 +1,6 @@
 import Link from "next/link";
-// import { Suspense } from "react";
+import { Suspense } from "react";
 import LogoIcon from "@components/common/icons/LogoIcon";
-// import MobileMenuTrigger from "./MobileMenuTrigger";
 import { NavbarActions } from "./NavbarActions";
 // import { NavigationSkeleton } from "./NavigationSkeleton";
 import { NavbarErrorBoundary } from "@/components/error/ErrorBoundary";
@@ -17,35 +16,36 @@ import { auth } from "@/utils/auth/auth-helper";
 import TopSwiperBanner from "./TopSwiperBanner"
 import type { AnnouncementItem } from "./TopSwiperBanner";
 import { restApiFetch } from "@utils/bagisto/index";
+import type { RestfulGetThemeCustomizations } from '@/types/theme/theme-customization';
 export default async function Navbar() {
     // 2. 服务端 fetch 请求我们自己的 Next.js API
-  const res = await restApiFetch({
+  const topBarPromise:Promise<AnnouncementItem[]> = restApiFetch<{data:RestfulGetThemeCustomizations}>({
     api: `/shop/theme-customizations?name=Top_Bar`,
     method: "GET",
     isRoute: false,
     takeAuthorization: false,
-  
-  });
-   console.log('topbar-res----------------------------:',res);
-  let topBarData: AnnouncementItem[] = [];
-    try {
-    // 安全取值链
-    const themeItem = res?.body?.[0];
+  }).then((result) => {
+    const themeItem = result.body[0];
     const optionsStr = themeItem?.translation?.options;
     if (optionsStr) {
       const parsed = JSON.parse(optionsStr);
-      topBarData = parsed?.images ?? [];
+      return parsed?.images ?? [];
+    } else {
+      return [];
     }
-  } catch (err) {
-    console.error("解析 translation.options 失败", err);
-    topBarData = [];
-  }
+  }).catch((err) => {
+      // 获取top_bar失败不应该阻止用户访问网站
+      console.error('get top_bar error: ', err);
+      return [];
+  });
+ 
     const session = await auth();// 游客是null
     const isGuest = session === null;
     const { data: menudata } = await serverGraphqlFetch<
       GetTreefrontMenusResult
     >({
       query: GET_TREEFRONT_MENUS,
+      takeAuthorization: false,
     });
     console.log('GetTreefrontMenusResult --- ',menudata)
     const menudatas =menudata? menudata.treefrontMenus:[];
@@ -53,7 +53,9 @@ export default async function Navbar() {
   return (
     <NavbarErrorBoundary>
       <header className="sticky top-0 z-10">
-        <TopSwiperBanner items={topBarData} /> 
+        <Suspense fallback={<div className="flex items-center justify-center h-10 w-full overflow-hidden bg-ly-middlegreen text-ly-14 text-white">Loading top bar...</div>}>
+          <TopSwiperBanner  topBarPromise={topBarPromise} /> 
+        </Suspense>
          <nav className="relative flex flex-col items-center justify-between gap-4 bg-neutral-50  pt-3 pb-3 dark:bg-neutral-900 md:flex-row ">
           <div className="flex w-full items-center justify-between max-w-[1440px] m-auto gap-0 sm:gap-4">
             {/* 1. THE STATIC SHELL (Visible Instantly) */}

+ 25 - 1
src/types/theme/theme-customization.ts

@@ -18,7 +18,31 @@ export interface GetThemeCustomization {
   }
 }
 
-
+// restful /api/shop/theme-customizations
+export type RestfulGetThemeCustomizations = Array<{
+  id: number;
+  themeCode: string;
+  type: string;
+  name: string;
+  sortOrder: number;
+  status: number;
+  showstyle: string;
+  createdAt: string;
+  updatedAt: string;
+  translation: {
+      id: number;
+      themeCustomizationId: number;
+      locale: string;
+      options: string;
+  },
+  translations: Array<{
+      id: number;
+      themeCustomizationId: number;
+      locale: string;
+      options: string;
+  }>;
+    
+}>;
 // Theme Customization Options Types
 
 export interface ImageCarouselOptions {