Files
reui-blocks/blocks/ecommerce-footer20.tsx
T

501 lines
13 KiB
TypeScript

"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { ArrowRight, Plus } from "lucide-react";
import { Fragment, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import z from "zod";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Field, FieldError } from "@/components/ui/field";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/components/ui/input-group";
import { Separator } from "@/components/ui/separator";
import { useMediaQuery } from "@/hooks/useMediaQuery";
import { cn } from "@/lib/utils";
type NewsletterData = {
title?: string;
};
type NewsletterFormProps = NewsletterData;
type SocialIcon = {
title: string;
light: string;
dark: string;
};
type SocialLink = {
link: string;
icon: SocialIcon;
};
type FooterLink = {
text: string;
link?: string;
};
type FooterLinksSection = {
title: string;
id: string;
items: FooterLink[];
};
interface SocialMediaSectionProps {
links: SocialLink[];
}
interface FooterLinksSectionProps {
sections: FooterLinksSection[];
}
interface EcommerceFooter20Props {
newsletter: NewsletterData;
socialLinks: SocialLink[];
footerLinks: FooterLinksSection[];
paymentMethods: string[];
submenuLinks: {
text: string;
link: string;
}[];
footerData: {
image: {
src: string;
alt: string;
};
homeLink: {
logo: {
light: string;
dark: string;
};
link: string;
};
title: string;
description: string;
};
className?: string;
}
const NEWSLETTER_DATA = {
title: "از پیشنهادها و محصولات جدید باخبر شوید و ۲۰٪ تخفیف برای اولین سفارش دریافت کنید",
};
const SOCIAL_ICONS = {
facebook: {
title: "Facebook",
light:
"/block-media/shadcnblocks/block/logos/facebook-icon.svg",
dark: "/block-media/shadcnblocks/block/logos/facebook-icon.svg",
},
x: {
title: "X",
light:
"/block-media/shadcnblocks/block/logos/x.svg",
dark: "/block-media/shadcnblocks/block/logos/x.svg",
},
instagram: {
title: "Instagram",
light:
"/block-media/shadcnblocks/block/logos/instagram-icon.svg",
dark: "/block-media/shadcnblocks/block/logos/instagram-icon.svg",
},
};
const SOCIAL_MEDIA_LINKS = [
{
icon: SOCIAL_ICONS.facebook,
link: "#",
},
{
icon: SOCIAL_ICONS.x,
link: "#",
},
{
icon: SOCIAL_ICONS.instagram,
link: "#",
},
];
const FOOTER_LINKS: FooterLinksSection[] = [
{
title: "مجموعه‌ها",
id: "collections",
items: [
{
text: "تازه‌ها",
link: "#",
},
{
text: "پرفروش‌ترین‌ها",
link: "#",
},
{
text: "پیشنهادهای فصلی",
link: "#",
},
{
text: "محصولات ضروری",
link: "#",
},
],
},
{
title: "راهنما",
id: "help",
items: [
{
text: "تماس با ما",
link: "#",
},
{
text: "سؤالات متداول",
link: "#",
},
{
text: "ارسال و پیگیری",
link: "#",
},
{
text: "مرجوعی و تعویض",
link: "#",
},
],
},
{
title: "اطلاعات",
id: "information",
items: [
{
text: "شرایط و ضوابط",
link: "#",
},
{
text: "حریم خصوصی",
link: "#",
},
{
text: "سیاست ضمانت",
link: "#",
},
{
text: "شرایط استفاده",
link: "#",
},
],
},
];
const SUBMENU = [
{
text: "سیاست ارسال",
link: "#",
},
{
text: "سیاست مرجوعی",
link: "#",
},
{
text: "شرایط استفاده",
link: "#",
},
{
text: "حریم خصوصی",
link: "#",
},
{
text: "پایداری",
link: "#",
},
];
const FOOTER_DATA = {
image: {
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Modern-Portrait-of-a-Woman-and-Man-2.png",
alt: "پرتره مدرن زن و مرد",
},
homeLink: {
logo: {
light:
"/block-media/shadcnblocks/block/logos/ShadcnBlockscom-wordmark.svg",
dark: "/block-media/shadcnblocks/block/logos/ShadcnBlockscom-wordmark-white.svg",
},
link: "#",
},
title: "جایی که مد روز با راحتی پیوند می‌خورد",
description:
"لباس‌هایی طراحی می‌کنیم که به زنان کمک می‌کند با جزئیات دقیق، برش‌های مناسب و پایه‌های باکیفیت، شخصیت خود را بیان کنند.",
};
const PAYMENT_METHODS = [
"/block-media/shadcnblocks/block/ecommerce/payment-methods/amazonpay.svg",
"/block-media/shadcnblocks/block/ecommerce/payment-methods/applepay.svg",
"/block-media/shadcnblocks/block/ecommerce/payment-methods/mastercard.svg",
"/block-media/shadcnblocks/block/ecommerce/payment-methods/paypal.svg",
"/block-media/shadcnblocks/block/ecommerce/payment-methods/visa.svg",
"/block-media/shadcnblocks/block/ecommerce/payment-methods/discover.svg",
];
const EcommerceFooter20 = ({
newsletter = NEWSLETTER_DATA,
socialLinks = SOCIAL_MEDIA_LINKS,
footerLinks = FOOTER_LINKS,
paymentMethods = PAYMENT_METHODS,
submenuLinks = SUBMENU,
footerData = FOOTER_DATA,
className,
}: EcommerceFooter20Props) => {
return (
<footer dir="rtl" lang="fa" className={cn("bg-muted", className)}>
<div className="grid grid-cols-1 lg:grid-cols-2">
<div className="overflow-hidden max-lg:aspect-square">
<img
className="block size-full object-cover object-center"
src={footerData.image.src}
alt={footerData.image.alt}
/>
</div>
<div>
<div className="px-6 py-8 md:p-12 lg:px-20 lg:pt-8 lg:pb-20">
<div className="space-y-12">
<div className="mt-12 max-w-125">
<NewsletterSection {...newsletter} />
</div>
<div className="space-y-6">
<a href="#" className="block">
<img
className="h-12 w-auto dark:hidden"
src={footerData.homeLink.logo.light}
alt="لوگو"
/>
<img
className="hidden h-12 w-auto dark:inline-block"
src={footerData.homeLink.logo.dark}
alt="لوگو"
/>
</a>
<div className="space-y-1">
<h3 className="font-bold">{footerData.title}</h3>
<p className="leading-relaxed text-balance">
{footerData.description}
</p>
</div>
</div>
<SocialMediaSection links={socialLinks} />
<Separator className="max-lg:hidden" />
<FooterLinksSection sections={footerLinks} />
<Separator className="max-lg:hidden" />
<PaymentMethods cards={paymentMethods} />
<Separator />
<div className="space-y-8">
<FooterSubMenu links={submenuLinks} />
<p className="text-sm font-light">
© ۲۲۶ ری یو آی. تمامی حقوق محفوظ است.
</p>
</div>
</div>
</div>
</div>
</div>
<div className="px-6 py-10 lg:px-20">
<img
className="w-full dark:hidden"
src={footerData.homeLink.logo.light}
alt="لوگو"
/>
<img
className="hidden w-full dark:inline-block"
src={footerData.homeLink.logo.dark}
alt="لوگو"
/>
</div>
</footer>
);
};
const newsletterFormSchema = z.object({
email: z
.string()
.min(1, "ایمیل الزامی است")
.email("آدرس ایمیل نامعتبر است"),
});
type newsletterFormType = z.infer<typeof newsletterFormSchema>;
const NewsletterSection = ({ title }: NewsletterFormProps) => {
const form = useForm({
resolver: zodResolver(newsletterFormSchema),
defaultValues: {
email: "",
},
});
const onSubmit = (data: newsletterFormType) => {
console.log(data);
};
return (
<div className="space-y-6">
<h2 className="text-xl font-light">{title}</h2>
<form onSubmit={form.handleSubmit(onSubmit)}>
<Controller
name="email"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<InputGroup
className="rounded-none border-x-0 border-t-0 !border-b border-foreground shadow-none"
aria-invalid={fieldState.invalid}
>
<InputGroupInput
{...field}
aria-invalid={fieldState.invalid}
placeholder="آدرس ایمیل"
/>
<InputGroupAddon align="inline-end">
<InputGroupButton type="submit" size="icon-xs">
<ArrowRight className="rtl:rotate-180" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
</Field>
)}
/>
</form>
</div>
);
};
const SocialMediaSection = ({ links }: SocialMediaSectionProps) => {
return (
<ul className="flex flex-wrap gap-4">
{links.map(({ icon, link }) => (
<li key={crypto.randomUUID()}>
<Button size="icon" variant="ghost" asChild className="rounded-full">
<a href={link}>
<img
className="size-6 dark:invert"
alt={icon.title}
src={icon.light}
/>
</a>
</Button>
</li>
))}
</ul>
);
};
const FooterLinksSection = ({ sections }: FooterLinksSectionProps) => {
const isDesktop = useMediaQuery("(min-width: 1024px)");
const [value, setValue] = useState("");
if (!sections) return;
const allAccordionIds = sections.map(({ id }) => id);
const handleOnValueChange = (value: string) => {
setValue(value);
};
if (isDesktop) {
return (
<Accordion
value={allAccordionIds}
type="multiple"
className="grid grid-cols-3 gap-4"
>
<AccordionItems sections={sections} />
</Accordion>
);
} else {
return (
<Accordion
value={value}
type="single"
collapsible={true}
onValueChange={handleOnValueChange}
className="border-y"
>
<AccordionItems sections={sections} />
</Accordion>
);
}
};
const AccordionItems = ({ sections }: { sections: FooterLinksSection[] }) => {
return (
<Fragment>
{sections.map(({ id, title, items }) => (
<AccordionItem
key={id}
value={id}
className="border-b lg:border-transparent"
>
<AccordionTrigger className="cursor-auto rounded-none pt-0 pb-2 text-base leading-normal font-bold hover:no-underline max-lg:py-4 [&>svg]:hidden">
{title}
<div className="lg:hidden">
<Plus className="size-5" />
</div>
</AccordionTrigger>
<AccordionContent className="pb-1 max-lg:py-4">
<ul className="space-y-4 lg:space-y-2">
{items.map(({ link, text }) => (
<li
className="text-sm leading-tight font-light"
key={crypto.randomUUID()}
>
<a
href={link}
className="hover:underline hover:underline-offset-3"
>
{text}
</a>
</li>
))}
</ul>
</AccordionContent>
</AccordionItem>
))}
</Fragment>
);
};
const PaymentMethods = ({ cards }: { cards: string[] }) => {
return (
<ul className="flex flex-wrap items-center gap-3">
{cards.map((card) => (
<li key={crypto.randomUUID()}>
<img className="w-9.5" src={card} alt="روش پرداخت" />
</li>
))}
</ul>
);
};
const FooterSubMenu = ({ links }: { links: FooterLink[] }) => {
return (
<ul className="flex flex-wrap gap-x-6 gap-y-4">
{links.map(({ link, text }) => (
<li key={crypto.randomUUID()}>
<a href={link} className="text-sm font-light">
{text}
</a>
</li>
))}
</ul>
);
};
export { EcommerceFooter20 };