Parcourir la source

网站底部footer部分

fogwind il y a 2 semaines
Parent
commit
044bb1d47d

BIN
public/image/footer-payment.webp


BIN
public/image/logo-white.webp


BIN
public/image/logo.webp


+ 16 - 2
src/app/(public)/(cms)/[slug]/page.tsx

@@ -1,8 +1,22 @@
 import { notFound } from "next/navigation";
-import { getPage } from "@utils/bagisto";
+import { serverGraphqlFetch } from "@/utils/bagisto";
+import { PAGE_BY_URL_KEY } from "@/graphql";
 import type { PageData } from "@/types/theme/theme-customization";
 import CmsContent from "./_components/CMSContent";
 
+interface PageByUrlKeyResponse {
+  pageByUrlKeypages?: PageData[];
+}
+export async function getPage(input: { urlKey: string }): Promise<PageData[]> {
+  const res = await serverGraphqlFetch<PageByUrlKeyResponse,{ pageByUrlKey: string }>({
+    query: PAGE_BY_URL_KEY,
+    cache: "no-store",
+    variables: { pageByUrlKey: input.urlKey },
+    takeAuthorization: false
+  });
+
+  return res.data?.pageByUrlKeypages || [];
+}
 
 export default async function Page({
     params,
@@ -25,4 +39,4 @@ export default async function Page({
         
         </div>
     );
-}
+}

+ 0 - 12
src/app/globals.css

@@ -141,15 +141,9 @@
 
   --color-selected-black: var(--selected-black);
 
-  --color-dark-grey: var(--dark-grey);
-
   --color-selected-color: var(--selected-color);
   --color-selected-color-dark: var(--selected-color-dark);
 
-  --color-selected-bottom-dark: var(--selected-bottom-dark);
-  --color-selected-bg-bottom-dark: var(--selected-bg-bottom-dark);
-
-  --color-input-color: var(--input-color);
 }
 
 
@@ -176,15 +170,9 @@
     --hero-background: #ffffff;
     --hero-foreground: #000000;
 
-    --dark-grey: #404040;
-
     --selected-color: #e3ecff;
     --selected-color-dark: #555b69;
 
-    --selected-bottom-dark: #95b6ff;
-    --selected-bg-bottom-dark: #314779;
-
-    --input-color: #b3b3b3;
   }
 
 

+ 20 - 5
src/assets/styles/cms-content.css

@@ -1,8 +1,28 @@
 .cms-content h2{
+    font-size: 1.25rem;
+    font-weight: 500;
+    margin: 1rem 0;
+}
+.cms-content h3{
     font-size: 1.125rem;
     font-weight: 500;
     margin: 1rem 0;
 }
+.cms-content h4{
+    font-size: 1rem;
+    font-weight: 500;
+    margin: 1rem 0;
+}
+.cms-content h5{
+    font-size: 0.875rem;
+    font-weight: 500;
+    margin: 1rem 0;
+}
+.cms-content h6{
+    font-size: 0.75rem;
+    font-weight: 500;
+    margin: 1rem 0;
+}
 .cms-content p{
     font-size: 0.875rem;
     line-height: 1.375rem;
@@ -15,8 +35,3 @@
     font-size: 0.875rem;
     line-height: 1.375rem;
 }
-.cms-content img{
-    display: block;
-    width: 100%;
-    height: auto;
-}

+ 0 - 37
src/components/common/icons/social-icon/FaceBookIcon.tsx

@@ -1,37 +0,0 @@
-const FaceBookIcon = () => {
-  return (
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      version="1.1"
-      id="svg9"
-      width="24"
-      height="24"
-      viewBox="0 0 666.66668 666.66717"
-    >
-      <defs id="defs13">
-        <clipPath clipPathUnits="userSpaceOnUse" id="clipPath25">
-          <path d="M 0,700 H 700 V 0 H 0 Z" id="path23" />
-        </clipPath>
-      </defs>
-      <g
-        id="g17"
-        transform="matrix(1.3333333,0,0,-1.3333333,-133.33333,799.99999)"
-      >
-        <g id="g19">
-          <g id="g21" clipPath="url(#clipPath25)">
-            <g id="g27" transform="translate(600,350)">
-              <path
-                d="m 0,0 c 0,138.071 -111.929,250 -250,250 -138.071,0 -250,-111.929 -250,-250 0,-117.245 80.715,-215.622 189.606,-242.638 v 166.242 h -51.552 V 0 h 51.552 v 32.919 c 0,85.092 38.508,124.532 122.048,124.532 15.838,0 43.167,-3.105 54.347,-6.211 V 81.986 c -5.901,0.621 -16.149,0.932 -28.882,0.932 -40.993,0 -56.832,-15.528 -56.832,-55.9 V 0 h 81.659 l -14.028,-76.396 h -67.631 V -248.169 C -95.927,-233.218 0,-127.818 0,0"
-                className="fill-black dark:fill-white"
-                id="path29"
-                fillOpacity="0.62"
-              />
-            </g>
-          </g>
-        </g>
-      </g>
-    </svg>
-  );
-};
-
-export default FaceBookIcon;

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 19
src/components/common/icons/social-icon/InstaGramIcon.tsx


+ 0 - 19
src/components/common/icons/social-icon/TwitterIcon.tsx

@@ -1,19 +0,0 @@
-const TwitterIcon = () => {
-  return (
-    <svg
-      width="24"
-      height="24"
-      viewBox="0 0 24 24"
-      fill="none"
-      xmlns="http://www.w3.org/2000/svg"
-      className="fill-black/[62] dark:fill-white"
-    >
-      <path
-        d="M13.6753 10.6587L20.2284 3.19922H18.6761L12.9836 9.67485L8.4405 3.19922H3.19922L10.0708 12.9924L3.19922 20.8139H4.75154L10.759 13.9739L15.5579 20.8139H20.7992M5.31181 4.34557H7.69661L18.6749 19.7239H16.2895"
-        fillOpacity="0.62"
-      />
-    </svg>
-  );
-};
-
-export default TwitterIcon;

+ 0 - 14
src/components/customer/types.ts

@@ -1,14 +0,0 @@
-export type RecoverPasswordFormState = {
-  success?: {
-    status: boolean;
-    msg: string;
-  };
-  errors?: {
-    email?: string[];
-    apiRes?: {
-      status: boolean;
-      msg: string;
-    };
-  };
-};
-

+ 33 - 0
src/components/layout/footer/CompanyInfo.tsx

@@ -0,0 +1,33 @@
+"use client";
+
+import Link from "next/link";
+
+export function CompanyInfo() {
+
+    return (
+        <div className="w-full">
+            <div className="text-ly-16 text-[#FFDBA3] font-bold">
+                Company Info
+            </div>
+  
+            <div className="w-full">
+                <Link href="mailto:Service@asteriahair.com" className="block text-ly-14 text-white mt-6">
+                    service@asteriahair.com
+                </Link>
+                <Link href="tel:+8616696557019" className="block text-ly-14 text-white mt-4 ">
+                    Phone/WhatsApp: +86 166 9655 7019
+                </Link>
+                <p className="block text-ly-14 text-white mt-4">
+                    Wholesale WhatsApp: <Link href="tel:+8615238065852">+86 152 3806 5852</Link> / <Link href="tel:+8615136424039">+86 151 3642 4039</Link>
+                </p>
+                <p className="block text-ly-14 text-white mt-4">
+                    Company: LONGYIHK IMP EXP CO.,LIMITED FLAT 2502B FLOOR 25 148 ELECTRIC ROAD, NORTH POINT, HK
+                </p>
+                <p className="block text-ly-14 text-white mt-4">
+                    Customer Service Available: Sunday to Thursday, PDT 17:00pm-3:00am
+                </p>
+            </div>
+
+        </div>
+    )
+}

+ 45 - 73
src/components/layout/footer/FooterMenu.tsx

@@ -1,84 +1,56 @@
-import Link from "next/link";
-import type { ThemeOptions } from "@/types/types";
-import { isArray } from "@/utils/type-guards";
-import { safeParse } from "@/utils/helper";
-
-const getUrlparams = (url: string) => {
-  const splitUrl = url.split("/");
+"use client";
 
-  if (isArray(splitUrl)) {
-    const urlLength = splitUrl.length;
-
-    if (urlLength >= 1) {
-      return `/${splitUrl.at(urlLength - 1)}`;
-    }
-  }
+import { use } from "react";
+import Link from "next/link";
+import type { FetchGraphqlResult } from "@/types/graphqlFetch/type";
+import type { GetThemeCustomization } from "@/types/theme/theme-customization";
 
-  return "/";
-};
+interface FooterMenuLink {
+    url: string;
+    title: string;
+    sort_order: string;
+}
 
-const FooterMenuItem = ({ item }: { item: ThemeOptions }) => {
-  return (
-    <li className="text-selected-black dark:text-neutral-300">
-      <Link
-        aria-label={`${item?.title}`}
-        title={`${item?.title}`}
-        className="block px-0 py-1 md:p-2 text-nowrap text-sm underline-offset-4 text-selected-black dark:text-neutral-300 hover:text-black hover:underline md:inline-block dark:hover:text-neutral-300"
-        href={getUrlparams(item.url)}
-      >
-        {item.title}
-      </Link>
-    </li>
-  );
-};
 
 export default function FooterMenu({
-  menu,
+    footerLinksPromise,
 }: {
-  menu: any;
+    footerLinksPromise: Promise<FetchGraphqlResult<GetThemeCustomization>>;
 }) {
-  if (!menu || menu.length === 0) return null;
-
-  const firstMenu = menu[0]?.node;
-  const firstTranslation = firstMenu?.translations?.edges?.[0]?.node;
-  const channels = typeof firstTranslation?.options === 'string'
-    ? safeParse(firstTranslation.options)
-    : firstTranslation?.options;
+    const res = use(footerLinksPromise);
+
+    const linkList = [];
+    if(!res.error) {
+        const edgeItem = res.data?.themeCustomizations.edges[0];
+        if(edgeItem) {
+            const options = JSON.parse(edgeItem.node.translation.options);
+            const columnLabels: Record<string,string> = options['column_labels'];
+            for (const [key, value] of Object.entries(columnLabels)) {
+                linkList.push( {
+                    title: value,
+                    id: key,
+                    list: options[key] || []
+                });
+            }
+        }
+    }
 
-  return (
-    <div className="flex justify-between gap-x-8 lg:gap-x-[50px]">
-      {/* Render columns 1 */}
-      {isArray(channels?.column_1) ? (
-        <nav className="w-full lg:min-w-[160px] xl:min-w-[200px]">
-          <ul>
-            {channels.column_1.map((item: ThemeOptions, index: number) => {
-              return <FooterMenuItem key={index} item={item} />;
-            })}
-          </ul>
-        </nav>
-      ) : null}
 
-      {/* Render columns 2 */}
-      {isArray(channels?.column_2) ? (
-        <nav className="w-full lg:min-w-[160px] xl:min-w-[200px]">
-          <ul>
-            {channels.column_2.map((item: ThemeOptions, index: number) => {
-              return <FooterMenuItem key={index} item={item} />;
-            })}
-          </ul>
-        </nav>
-      ) : null}
 
-      {/* Render columns 3 */}
-      {isArray(channels?.column_3) ? (
-        <nav className="w-full lg:min-w-[160px] xl:min-w-[200px]">
-          <ul>
-            {channels.column_3.map((item: ThemeOptions, index: number) => {
-              return <FooterMenuItem key={index} item={item} />;
-            })}
-          </ul>
-        </nav>
-      ) : null}
-    </div>
-  );
+    return (<>
+        {res.error && <p className="text-center text-ly-errorcolor text-ly-12 pt-4">{res.error.message}</p>}
+        {linkList.map((listItem) => {
+            return  <div className="w-full" key={listItem.id}>
+                        <div className="text-ly-16 text-[#FFDBA3] font-bold">
+                            {listItem.title}
+                        </div>
+                
+                        <div className="w-full">
+                            {listItem.list.map((item: FooterMenuLink) => {
+                                return <Link key={item.title} className="block text-ly-14 text-white mt-4 first:mt-6" href={item.url}>{item.title}</Link>
+                            })}
+                        </div>
+                    </div>
+        })}
+    </>);
 }

+ 0 - 116
src/components/layout/footer/Subscribe.tsx

@@ -1,116 +0,0 @@
-"use client";
-
-import { useEffect, useState } from "react";
-import clsx from "clsx";
-import { useForm } from "react-hook-form";
-import type { RecoverPasswordFormState } from "@components/customer/types";
-import { Button } from "@components/common/button/LoadingButton";
-import { userSubscribe } from "@utils/actions";
-import { useCustomToast } from "@utils/hooks/useToast";
-import { EMAIL_REGEX } from "@utils/constants";
-
-type FormValues = {
-  email: string;
-};
-
-const Subscribe = () => {
-  const [loading, setLoading] = useState<boolean>(false);
-  const {
-    register,
-    handleSubmit,
-    reset,
-    formState: { errors, isSubmitting },
-  } = useForm<FormValues>({ mode: "onSubmit" });
-  const { showToast } = useCustomToast();
-
-  const [status, setStatus] = useState<
-    RecoverPasswordFormState["errors"] | null
-  >(null);
-
-  const onSubmit = async (data: FormValues) => {
-    setStatus(null);
-    const formData = new FormData();
-
-    formData.append("email", data.email);
-    setLoading(true);
-    const result = await userSubscribe(undefined as any, formData);
-
-    setStatus(result?.errors || null);
-    if (result?.errors?.apiRes?.status) {
-      reset();
-    }
-    setLoading(false);
-  };
-
-  useEffect(() => {
-    if (status) {
-      setTimeout(() => {
-        setStatus(null);
-      }, 3500);
-    }
-    if (status?.email) {
-      showToast(status?.email[0], "warning");
-    }
-    if (status?.apiRes?.status === false) {
-      showToast(status?.apiRes?.msg, "warning");
-    }
-    if (status?.apiRes?.status === true) {
-      showToast("Successfully Subscribed", "success");
-    }
-  }, [status]);
-
-  return (
-    <form
-      noValidate
-      className="mt-4 md:mt-0 md:px-0 relative"
-      onSubmit={handleSubmit(onSubmit)}
-    >
-      <p className="mb-1 text-base font-semibold">Newsletter</p>
-      <p className="font-sm font-normal">
-        Subscribe to our newsletter for exclusive offers!
-      </p>
-
-      <div className="mt-4 flex gap-x-3">
-        <input
-          type="email"
-          aria-label="Email Address"
-          {...register("email", {
-            required: "Email is required",
-            pattern: {
-              value: EMAIL_REGEX,
-              message: "Enter a valid email",
-            },
-          })}
-          className={clsx(
-            "block rounded-2xl w-full xl:min-w-[283px] p-2.5 text-sm focus:border-blue-200 border border-neutral-200 bg-white  text-black outline-none placeholder:text-neutral-500 focus:ring-1 focus:ring-neutral-300 dark:border-neutral-800 dark:bg-transparent dark:text-white dark:placeholder:text-neutral-400 ",
-            errors.email || status?.email
-              ? "border-red-500 dark:border-red-500"
-              : "border-gray-300 dark:border-neutral-200"
-          )}
-          placeholder="Email Address"
-        />
-        <Button
-          className={clsx(
-            "relative flex font-medium w-full min-w-32 max-w-32 items-center justify-center !rounded-2xl border-1 border-solid border-neutral-600 bg-transparent px-5 py-2.5 font-outfit text-[15px]  tracking-wide dark:!text-white !text-neutral-600",
-            {
-              "hover:opacity-90": !isSubmitting,
-              "cursor-not-allowed opacity-50": isSubmitting,
-            }
-          )}
-          disabled={loading || isSubmitting}
-          loading={loading || isSubmitting}
-          title="Subscribe"
-          type="submit"
-        />
-      </div>
-
-      {errors.email && (
-        <p className="mt-1 absolute left-0 -bottom-2.5 text-sm text-red-600 dark:text-red-400">
-          {errors.email.message}
-        </p>
-      )}
-    </form>
-  );
-};
-
-export default Subscribe;

Fichier diff supprimé car celui-ci est trop grand
+ 124 - 0
src/components/layout/footer/SubscribeNewsletter.tsx


+ 50 - 120
src/components/layout/footer/index.tsx

@@ -1,134 +1,64 @@
-import Link from "next/link";
+import Image from "next/image";
 import { Suspense } from "react";
 import { serverGraphqlFetch } from "@/utils/bagisto";
-import type {
-  GetFooterResponse,
-  ThemeCustomizationResult
-} from "@/types/theme/theme-customization";
-import {
-  GET_FOOTER
-} from "@/graphql";
+import type { GetThemeCustomization } from "@/types/theme/theme-customization";
+import { GET_THEME_CUSTOMIZATION } from "@/graphql";
 import { env } from "@/env";
-import LogoIcon from "@components/common/icons/LogoIcon";
-import FaceBookIcon from "@components/common/icons/social-icon/FaceBookIcon";
-import InstaGramIcon from "@components/common/icons/social-icon/InstaGramIcon";
-import TwitterIcon from "@components/common/icons/social-icon/TwitterIcon";
-import Subscribe from "./Subscribe";
+import { CompanyInfo } from "./CompanyInfo";
+import SubscribeNewsletter from "./SubscribeNewsletter";
 import FooterMenu from "./FooterMenu";
 
-async function getFooterData(): Promise<ThemeCustomizationResult> {
- 
-    const [{data: footerRes}, {data:servicesRes}] = await Promise.all([
-      serverGraphqlFetch<GetFooterResponse,{type: string;}>({
-        query: GET_FOOTER,
-        variables:{
-          type: "footer_links",
-        },
-        takeAuthorization: false
-      }),
-      serverGraphqlFetch<GetFooterResponse,{type: string;}>({
-        query: GET_FOOTER,
+
+export default async function Footer() {
+    const currentYear = new Date().getFullYear();
+    const copyrightDate = 2016 + (currentYear > 2010 ? `-${currentYear}` : "");
+    const copyrightName = env.STORE_NAME;
+
+    const footerLinksPromise = serverGraphqlFetch<GetThemeCustomization,{name: string;}>({
+        query: GET_THEME_CUSTOMIZATION,
         variables:{
-          type: "services_content",
+            name: "bottom_footer_links",
         },
         takeAuthorization: false
-      }),
-    ]);
-    return {
-      footer_links: footerRes,
-      services_content: servicesRes,
-    };
-}
+    });
 
-export default async function Footer() {
-  const currentYear = new Date().getFullYear();
-  const copyrightDate = 2010 + (currentYear > 2010 ? `-${currentYear}` : "");
-  const skeleton =
-    "w-full h-6 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700";
-  const menu = await getFooterData();
-  const copyrightName = env.STORE_NAME;
 
+    return (<footer className="box-border w-full bg-[#1B2623] pt-32 pb-16 mt-24">
+        <div className="box-border w-full max-w-360 mx-auto">
+            <div className="box-border w-full flex px-16 gap-10">
+                <div className="flex-auto flex justify-between pr-6 border-r-1 border-[#404D49]">
+                    <Suspense fallback={<div className="text-center text-white text-ly-12 py-6">Loading menu list...</div>}>
+                        <FooterMenu footerLinksPromise={footerLinksPromise} />
+                    </Suspense>
+                    <CompanyInfo />
+                </div>
+                
+
+                <div className="flex-none w-86">
+                    <SubscribeNewsletter />
+                </div>
+            </div>
 
-  return (
-    <>
-      <footer className="border-t border-neutral-200 text-sm text-neutral-500 dark:border-neutral-700 dark:text-neutral-400">
-        <div className="mx-auto flex w-full max-w-screen-2xl flex-col justify-between gap-6 gap-y-6 px-6 py-12 text-sm dark:border-neutral-700 min-[880px]:flex-row min-[880px]:gap-12 min-[880px]:gap-y-20 min-[880px]:px-4">
-          <div className="flex flex-col gap-[14px]">
-            <Link
-              className="flex items-center gap-2 md:pt-1 cursor-pointer"
-              href="/"
-              aria-label="Go to homepage"
-              title="Go to homepage"
-            >
-              <LogoIcon />
-              <span className="sr-only">Go to homepage</span>
-            </Link>
-            <div className="flex gap-[14px]">
-              <Link
-                href={"#"}
-                aria-label="Visit Bagisto Store on Facebook"
-                title="Facebook"
-                target="_blank"
-                className="cursor-pointer"
-              >
-                <FaceBookIcon />
-                <span className="sr-only">Facebook</span>
-              </Link>
-              <Link
-                href={"#"}
-                aria-label="Visit Bagisto Store on Instagram"
-                title="Instagram"
-                target="_blank"
-                className="cursor-pointer"
-              >
-                <InstaGramIcon />
-                <span className="sr-only">Instagram</span>
-              </Link>
-              <Link
-                href={"#"}
-                aria-label="Visit Bagisto Store on Twitter"
-                title="Twitter"
-                target="_blank"
-                className="cursor-pointer"
-              >
-                <TwitterIcon />
-                <span className="sr-only">Twitter</span>
-              </Link>
+
+            <div className="w-full mt-12 pt-8 border-t-1 border-[#404D49]">
+                <Image src="/image/footer-payment.webp"
+                    className="block max-w-178.25 h-auto mx-auto"
+                    alt="Support payment methods"
+                    width={713}
+                    height={32}
+                />
+            </div>
+
+            <div className="w-full mt-14">
+                <Image src="/image/logo-white.webp"
+                    className="block w-51.5 h-12.75 mx-auto"
+                    alt="Asteriahair logo"
+                    width={206}
+                    height={51}
+                />
+                <p className="text-center mt-3 text-ly-14 text-white">{copyrightDate} {copyrightName} Hair . All Rights Reserved</p>
             </div>
-          </div>
-          <div className="flex flex-col gap-x-8 md:flex-row lg:gap-x-[50px]">
-            <Suspense
-              fallback={
-                <div className="flex h-[188px] w-[200px] flex-col gap-2">
-                  {Array(6)
-                    .fill(0)
-                    .map((_, index) => (
-                      <div key={index} className={skeleton} />
-                    ))}
-                </div>
-              }
-            >
-              <FooterMenu
-                menu={menu?.footer_links?.themeCustomizations?.edges}
-              />
-            </Suspense>
-            <Subscribe />
-          </div>
-        </div>
-        <div className="border-t border-neutral-200 py-6 text-sm dark:border-neutral-700">
-          <div className="mx-auto flex w-full max-w-screen-2xl flex-col justify-center gap-1 px-4 md:flex-row">
-            <p className="text-center">
-              &copy; {copyrightDate} {copyrightName}
-              {copyrightName.length && !copyrightName.endsWith(".")
-                ? "."
-                : ""}{" "}
-              All rights reserved.
-            </p>
-            <hr className="mx-4 hidden h-4 w-[1px] border-l border-neutral-400 md:inline-block" />
-            <p className="text-center">Designed in Bagisto</p>
-          </div>
         </div>
-      </footer>
-    </>
-  );
+
+    </footer>);
 }

+ 7 - 6
src/graphql/theme/mutations/subscribeToNewsletter.ts

@@ -1,12 +1,13 @@
-import { gql } from "@apollo/client";
+import { gql, type TypedDocumentNode} from "@apollo/client";
+import type { CreateNewsletterResponse } from "@/types/customer/type";
 
-export const SUBSCRIBE_TO_NEWSLETTER = gql`
-  mutation SubscribeToNewsletter($input: createNewsletterSubscriptionInput!) {
-    createNewsletterSubscription(input: $input) {
-      newsletterSubscription {
+export const SUBSCRIBE_TO_NEWSLETTER: TypedDocumentNode<CreateNewsletterResponse,{input:{customerEmail: string}}> = gql`
+  mutation createNewsletter($input: createNewsletterInput!) {
+    createNewsletter(input: $input) {
+      newsletter {
         success
         message
       }
     }
   }
-`;
+`;

+ 1 - 1
src/graphql/theme/queries/ThemeCustomization.ts

@@ -16,7 +16,7 @@ import { gql } from "@apollo/client";
  * 如果定义变量时后边有!,表示这个变量是必须要有值的,没有!表示这个变量可以不传值(也就是接口参数是否是必传的)
  * edges 和 node 是graphql中的概念,edges相当于数组,node相当于数组中的项
  */
-export const GET_THEME_CUSTOMIZATION = gql`
+export const ALL_THEME_CUSTOMIZATIONS = gql`
   query themeCustomization($first: Int) {
     themeCustomizations(first: $first) {
       edges {

+ 0 - 26
src/graphql/theme/queries/Footer.ts

@@ -1,26 +0,0 @@
-import { gql } from "@apollo/client";
-
-export const GET_FOOTER = gql`
-  query footerQuery($type: String) {
-    themeCustomizations(type: $type) {
-      edges {
-        node {
-          id
-          type
-          name
-          status
-          translations {
-            edges {
-              node {
-                id
-                themeCustomizationId
-                locale
-                options
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-`;

+ 26 - 0
src/graphql/theme/queries/GetThemeCustomization.ts

@@ -0,0 +1,26 @@
+import { gql } from "@apollo/client";
+
+// type 查询的是一组type相同的数据
+// name查询的是一个name是给定值的数据
+export const GET_THEME_CUSTOMIZATION = gql`
+  query ThemeCustomizationQuery($type: String,$name:String) {
+    themeCustomizations(type: $type, name:$name) {
+      edges {
+        node {
+          id
+          type
+          name
+          status
+          translation {
+            id
+            _id
+            themeCustomizationId
+            locale
+            options
+            
+          }
+        }
+      }
+    }
+  }
+`;

+ 4 - 4
src/graphql/theme/queries/index.ts

@@ -1,10 +1,10 @@
 export {
-    GET_FOOTER
-} from "./Footer"
+    GET_THEME_CUSTOMIZATION
+} from "./GetThemeCustomization"
 
 export {
-GET_THEME_CUSTOMIZATION
-} from "./ThemeCustomization"
+ALL_THEME_CUSTOMIZATIONS
+} from "./AllThemeCustomization"
 
 export {
     PAGE_BY_URL_KEY

+ 9 - 0
src/types/customer/type.ts

@@ -110,4 +110,13 @@ export interface GetCustomerAddressResponse {
         endCursor: string;
     };
     totalCount: number;
+}
+
+export interface CreateNewsletterResponse{
+    createNewsletter: {
+        newsletter: {
+            success: boolean;
+            message: string;
+        }
+    }
 }

+ 18 - 69
src/types/theme/theme-customization.ts

@@ -1,74 +1,23 @@
-export interface ThemeTranslationNode {
-  id: string;
-  themeCustomizationId: string;
-  locale: string;
-  options: string;
-}
-
-export interface ThemeTranslationEdge {
-  node: ThemeTranslationNode;
-}
-
-
-
-// footer 
-export interface ThemeOptions {
-  title: string;
-  url: string;
-}
-export interface FooterColumns {
-  column_1?: ThemeOptions[];
-  column_2?: ThemeOptions[];
-  column_3?: ThemeOptions[];
-}
-export interface GetFooterResponse {
-  themeCustomizations: ThemeCustomizationConnection;
-}
-
-export interface GetFooterVariables {
-  type?: string;
-}
-export interface ThemeCustomizationConnection {
-  edges: ThemeCustomizationEdge[];
-}
-
-export interface ThemeCustomizationEdge {
-  node: ThemeCustomizationFooterNode;
+export interface ThemeCustomizationNodeTranslation {
+    id: string;
+    _id: number;
+    themeCustomizationId: string;
+    locale: string;
+    options: string;
+}
+export interface ThemeCustomizationNode {
+    id: string;
+    type: string;
+    name: string
+    status: string;
+    translation: ThemeCustomizationNodeTranslation;
+}
+export interface GetThemeCustomization {
+  themeCustomizations: {
+    edges: Array<{node:ThemeCustomizationNode}>
+  }
 }
 
-export interface ThemeCustomizationFooterNode {
-  id: string;
-  type: string;
-  name: string;
-  status: boolean;
-  themeCode?: string;
-  translations: ThemeCustomizationTranslationConnection;
-}
-export interface ThemeCustomizationTranslationConnection {
-  edges: ThemeCustomizationTranslationEdge[];
-}
-
-export interface ThemeCustomizationTranslationNode {
-  id: string;
-  themeCustomizationId: string;
-  locale: string;
-  options: string | FooterColumns;
-}
-
-
-export interface ThemeCustomizationTranslationEdge {
-  node: ThemeCustomizationTranslationNode;
-}
-export interface FooterMenuProps {
-  menu: ThemeCustomizationEdge[];
-}
-
-export interface ThemeCustomizationResult {
-  footer_links: GetFooterResponse | null;
-  services_content: GetFooterResponse | null;
-}
-
-
 
 // Theme Customization Options Types
 

+ 0 - 77
src/utils/actions.ts

@@ -1,77 +0,0 @@
-"use server";
-// 文件顶部的 "use server" 是所有导出的异步函数自动成为 Server Action 的标志。
-import { redirect } from "next/navigation";
-import {
-  subscribeUser,
-} from '@/utils/bagisto';
-
-import type { RecoverPasswordFormState } from "@components/customer/types";
-
-export type RegisterFormState = {
-  errors?: {
-    firstName?: string[];
-    lastName?: string[];
-    email?: string[];
-    password?: string[];
-    passwordConfirmation?: string[];
-    apiError?: string;
-    agreement?: string[];
-  };
-};
-
-export async function redirectToCheckout(formData: FormData) {
-  const url = formData.get("url") as string;
-  redirect(url);
-}
-
-
-
-export async function userSubscribe(
-  _prevState: RecoverPasswordFormState,
-  formData: FormData
-): Promise<RecoverPasswordFormState> {
-  const email = formData.get("email");
-
-  const data = {
-    email: typeof email === "string" ? email.trim() : "",
-  };
-
-  try {
-    const result = await subscribeUser(data) as Record<string, unknown>;
-
-    if (result?.error) {
-      const error = result.error as Record<string, unknown>;
-      return {
-        errors: {
-          apiRes: {
-            status: false,
-            msg: (error.message as string) || "Something went wrong",
-          },
-        },
-      };
-    }
-
-    const body = result?.body as Record<string, unknown>;
-    const bodyData = body?.data as Record<string, unknown>;
-
-    return {
-      errors: {
-        apiRes: {
-          status: true,
-          msg: (bodyData?.message as string) || "Subscription successful!",
-        },
-      },
-    };
-  } catch {
-    return {
-      errors: {
-        apiRes: {
-          status: false,
-          msg: "Unexpected error occurred. Please try again.",
-        },
-      },
-    };
-  }
-}
-
-

+ 1 - 48
src/utils/bagisto/index.ts

@@ -11,13 +11,6 @@ import {
   CURRENT_LOCAL,
   CURRENT_CHANNEL,
 } from "@/utils/constants";
-import {
-  PAGE_BY_URL_KEY,
-} from "@/graphql";
-import { SUBSCRIBE_TO_NEWSLETTER } from "@/graphql/theme/mutations";
-import type {
-  PageData,
-} from "@/types/theme/theme-customization";
 import type {FetchGraphqlResult} from "@/types/graphqlFetch/type";
 import { env } from "@/env";
 
@@ -28,9 +21,7 @@ type ExtractVariables<T> = T extends { variables: object }
 type ExtractRestFulData<T> = T extends { data: infer D } ? D : any;
 type ExtractGraphqlData<T> = T extends { data: infer D } ? { data: D } : any;
 
-interface PageByUrlKeyResponse {
-  pageByUrlKeypages?: PageData[];
-}
+
 const STOREFRONT_KEY = env.NEXT_PUBLIC_BAGISTO_STOREFRONT_KEY;
 async function getBaseHeader() {
     /** 
@@ -314,41 +305,3 @@ export async function bagistoFetch<T>({
     throw e;
   }
 }
-
-
-
-export async function subscribeUser(
-  input: Record<string, unknown>,
-): Promise<unknown> {
-  try {
-    return await bagistoFetch<{
-      data: unknown;
-      variables: Record<string, unknown>;
-    }>({
-      query: SUBSCRIBE_TO_NEWSLETTER,
-      variables: {
-        ...input,
-      },
-      cache: "no-store",
-    });
-  } catch (error) {
-    return error;
-  }
-}
-
-
-
-
-export async function getPage(input: { urlKey: string }): Promise<PageData[]> {
-  const res = await bagistoFetch<{
-    data: PageByUrlKeyResponse;
-    variables: { pageByUrlKey: string };
-  }>({
-    query: PAGE_BY_URL_KEY,
-    cache: "no-store",
-    // isCookies: false,
-    variables: { pageByUrlKey: input.urlKey },
-  });
-
-  return res.body.data?.pageByUrlKeypages || [];
-}

+ 1 - 1
src/utils/constants.ts

@@ -30,7 +30,7 @@ export const SORT = "sort";
  * Placeholder Images
  */
 export const NOT_IMAGE = "/image/placeholder.webp";
-export const LOGO_IMG = "/image/logo.png";
+export const LOGO_IMG = "/image/logo.webp";
 
 export const variants = {
   hidden: { opacity: 0, y: 50 },

+ 3 - 3
src/utils/metadata.ts

@@ -3,19 +3,19 @@ export const staticSeo = {
   default: {
     title: "Aateria Hair",
     description: "Aateria Hair",
-    image: "/logo.png",
+    image: "/image/logo.webp",
     canonical: "/",
   },
   login: {
     title: "Login or Register",
     description: "Login or Register to Aateria",
-    image: "/logo.png",
+    image: "/image/logo.webp",
     canonical: "/customer/login",
   },
   forget:{
     title: "Forget Password",
   description: "Recover your account by resetting your password.",
-  image: "/logo.png",
+  image: "/image/logo.webp",
   canonical: "/customer/forget-password",
   }
 };