responsive home page and layout
This commit is contained in:
@@ -35,11 +35,37 @@ function useCarousel() {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResponsiveNumber = number | { base: number; md?: number; lg?: number };
|
||||||
|
|
||||||
|
function useResponsiveNumber(value: ResponsiveNumber): number {
|
||||||
|
const base = typeof value === "number" ? value : value.base;
|
||||||
|
const md = typeof value === "number" ? undefined : value.md;
|
||||||
|
const lg = typeof value === "number" ? undefined : value.lg;
|
||||||
|
|
||||||
|
const resolve = () => {
|
||||||
|
if (typeof window === "undefined") return base;
|
||||||
|
if (window.matchMedia("(min-width: 1024px)").matches) return lg ?? md ?? base;
|
||||||
|
if (window.matchMedia("(min-width: 768px)").matches) return md ?? base;
|
||||||
|
return base;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [resolved, setResolved] = useState(base);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const update = () => setResolved(resolve());
|
||||||
|
update();
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
return () => window.removeEventListener("resize", update);
|
||||||
|
}, [base, md, lg]);
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
type CarouselProps = {
|
type CarouselProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
slidesPerView?: number;
|
slidesPerView?: ResponsiveNumber;
|
||||||
slidesToScroll?: number;
|
slidesToScroll?: number;
|
||||||
gap?: number;
|
gap?: ResponsiveNumber;
|
||||||
direction?: "rtl" | "ltr";
|
direction?: "rtl" | "ltr";
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
@@ -52,10 +78,14 @@ const Carousel: FC<CarouselProps> = ({
|
|||||||
direction = "rtl",
|
direction = "rtl",
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
|
const resolvedSlidesPerView = useResponsiveNumber(slidesPerView);
|
||||||
|
const resolvedGap = useResponsiveNumber(gap);
|
||||||
|
const scrollBy = slidesToScroll ?? Math.max(1, Math.floor(resolvedSlidesPerView));
|
||||||
|
|
||||||
const [emblaRef, emblaApi] = useEmblaCarousel({
|
const [emblaRef, emblaApi] = useEmblaCarousel({
|
||||||
align: "start",
|
align: "start",
|
||||||
direction,
|
direction,
|
||||||
slidesToScroll: slidesToScroll ?? slidesPerView,
|
slidesToScroll: scrollBy,
|
||||||
containScroll: "trimSnaps",
|
containScroll: "trimSnaps",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -80,6 +110,10 @@ const Carousel: FC<CarouselProps> = ({
|
|||||||
};
|
};
|
||||||
}, [emblaApi]);
|
}, [emblaApi]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
emblaApi?.reInit({ slidesToScroll: scrollBy });
|
||||||
|
}, [emblaApi, scrollBy, resolvedSlidesPerView, resolvedGap]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CarouselContext.Provider
|
<CarouselContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -87,8 +121,8 @@ const Carousel: FC<CarouselProps> = ({
|
|||||||
scrollNext: () => emblaApi?.scrollNext(),
|
scrollNext: () => emblaApi?.scrollNext(),
|
||||||
canScrollPrev,
|
canScrollPrev,
|
||||||
canScrollNext,
|
canScrollNext,
|
||||||
slidesPerView,
|
slidesPerView: resolvedSlidesPerView,
|
||||||
gap,
|
gap: resolvedGap,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CarouselViewportContext.Provider value={emblaRef}>
|
<CarouselViewportContext.Provider value={emblaRef}>
|
||||||
|
|||||||
+4
-4
@@ -20,10 +20,10 @@ const Home = () => {
|
|||||||
<OrderRegistrationSteps />
|
<OrderRegistrationSteps />
|
||||||
<BlogsSection />
|
<BlogsSection />
|
||||||
|
|
||||||
<div className="mt-[120px] px-[120px]">
|
<div className="mt-12 px-4 sm:mt-16 sm:px-8 lg:mt-[120px] lg:px-[120px]">
|
||||||
<div className="bg-[#004A9005] p-16 w-full rounded-4xl border border-[#E9EEF2]">
|
<div className="bg-[#004A9005] p-6 sm:p-10 lg:p-16 w-full rounded-3xl lg:rounded-4xl border border-[#E9EEF2]">
|
||||||
<div className="font-bold text-2xl">چاپ جعبه، بستهبندی و محصولات تبلیغاتی با طراحی اختصاصی</div>
|
<div className="font-bold text-lg sm:text-xl lg:text-2xl">چاپ جعبه، بستهبندی و محصولات تبلیغاتی با طراحی اختصاصی</div>
|
||||||
<div className="mt-6 leading-6 font-bold text-[#194873] text-sm">
|
<div className="mt-4 sm:mt-6 leading-6 font-bold text-[#194873] text-sm">
|
||||||
جعبه، اولین چیزی است که مشتری از برند شما میبیند. ما با ارائه خدمات چاپ جعبه، لیبل، استیکر، کارت ویزیت و سایر محصولات تبلیغاتی، به شما کمک میکنیم تا بستهبندی حرفهای و ماندگاری برای
|
جعبه، اولین چیزی است که مشتری از برند شما میبیند. ما با ارائه خدمات چاپ جعبه، لیبل، استیکر، کارت ویزیت و سایر محصولات تبلیغاتی، به شما کمک میکنیم تا بستهبندی حرفهای و ماندگاری برای
|
||||||
محصولات خود داشته باشید. با استفاده از متریال باکیفیت، طراحی اختصاصی و چاپ دقیق، سفارش شما در کوتاهترین زمان آماده و ارسال میشود.
|
محصولات خود داشته باشید. با استفاده از متریال باکیفیت، طراحی اختصاصی و چاپ دقیق، سفارش شما در کوتاهترین زمان آماده و ارسال میشود.
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,20 +8,26 @@ const BannerSection: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Image src={banner} alt="banner" width={2000} height={574} className="w-full max-h-[574px] object-cover" />
|
<Image
|
||||||
<div className="absolute h-full right-[120px] top-0 bottom-0 m-auto flex flex-col justify-center">
|
src={banner}
|
||||||
<div className="text-base">چاپ و بسته بندی به روش هوشمندانه</div>
|
alt="banner"
|
||||||
<div className="text-2xl font-black mt-4">مجــتمع تخصصــــــی</div>
|
width={2000}
|
||||||
<div className="text-2xl font-black mt-4">
|
height={574}
|
||||||
|
className="w-full h-[420px] sm:h-[480px] lg:h-auto lg:max-h-[574px] object-cover"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-y-0 right-4 sm:right-8 lg:right-[120px] flex flex-col justify-center max-w-[min(100%,420px)] lg:max-w-none">
|
||||||
|
<div className="text-sm sm:text-base">چاپ و بسته بندی به روش هوشمندانه</div>
|
||||||
|
<div className="text-xl sm:text-2xl font-black mt-3 sm:mt-4">مجــتمع تخصصــــــی</div>
|
||||||
|
<div className="text-xl sm:text-2xl font-black mt-3 sm:mt-4">
|
||||||
چــــــاپ و بستــــه بندی <span className="text-primary">دستی</span> و <span className="text-primary">هوشمند</span>{" "}
|
چــــــاپ و بستــــه بندی <span className="text-primary">دستی</span> و <span className="text-primary">هوشمند</span>{" "}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 text-base flex flex-col gap-3">
|
<div className="mt-3 sm:mt-4 text-sm sm:text-base flex flex-col gap-2 sm:gap-3">
|
||||||
<div>قیمتهای عالی</div>
|
<div>قیمتهای عالی</div>
|
||||||
<div>خدمات مشتریان بینظیر</div>
|
<div>خدمات مشتریان بینظیر</div>
|
||||||
<div>سیستم سفارشگیری ساده</div>
|
<div>سیستم سفارشگیری ساده</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 flex gap-6 items-center">
|
<div className="mt-4 flex flex-wrap gap-3 sm:gap-6 items-center">
|
||||||
<Button>
|
<Button>
|
||||||
<div className="flex gap-2.5">
|
<div className="flex gap-2.5">
|
||||||
<div>محصولات</div>
|
<div>محصولات</div>
|
||||||
|
|||||||
@@ -32,20 +32,20 @@ const CategoryTile: FC<CategoryTileProps> = ({ title, image, featured }) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative overflow-hidden rounded-[40px]",
|
"relative overflow-hidden rounded-[24px] sm:rounded-[40px]",
|
||||||
featured ? "col-span-2 row-span-2" : "aspect-square",
|
featured ? "col-span-2 row-span-2 min-h-[220px] sm:min-h-0" : "aspect-square",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Image src={image} alt={title} fill className="object-cover" sizes={featured ? "50vw" : "25vw"} />
|
<Image src={image} alt={title} fill className="object-cover" sizes={featured ? "(max-width: 768px) 100vw, 50vw" : "(max-width: 768px) 50vw, 25vw"} />
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute inset-x-4 bottom-4 flex items-center justify-between rounded-3xl bg-[#0A1B2C]/60 backdrop-blur-[2px]",
|
"absolute inset-x-3 bottom-3 sm:inset-x-4 sm:bottom-4 flex items-center justify-between rounded-2xl sm:rounded-3xl bg-[#0A1B2C]/60 backdrop-blur-[2px]",
|
||||||
featured ? "px-6 py-3.5" : "px-5 py-3",
|
featured ? "px-4 py-3 sm:px-6 sm:py-3.5" : "px-3 py-2.5 sm:px-5 sm:py-3",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cn("text-white font-bold", featured ? "text-lg" : "text-base")}>{title}</div>
|
<div className={cn("text-white font-bold", featured ? "text-base sm:text-lg" : "text-sm sm:text-base")}>{title}</div>
|
||||||
<div className="flex items-center gap-1.5 text-white">
|
<div className="flex items-center gap-1.5 text-white">
|
||||||
<span className={cn("font-bold", featured ? "text-sm" : "text-xs")}>مشاهده</span>
|
<span className={cn("font-bold", featured ? "text-xs sm:text-sm" : "text-xs")}>مشاهده</span>
|
||||||
<ArrowLeft size={featured ? 20 : 18} color="currentColor" />
|
<ArrowLeft size={featured ? 20 : 18} color="currentColor" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,10 +55,10 @@ const CategoryTile: FC<CategoryTileProps> = ({ title, image, featured }) => {
|
|||||||
|
|
||||||
const BestSellerCategories: FC = () => {
|
const BestSellerCategories: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-[120px] px-[120px]">
|
<div className="mt-12 px-4 sm:mt-16 sm:px-8 lg:mt-[120px] lg:px-[120px]">
|
||||||
<div className="font-bold text-2xl">دسته بندی های پرفروش</div>
|
<div className="font-bold text-lg sm:text-2xl">دسته بندی های پرفروش</div>
|
||||||
|
|
||||||
<div className="mt-12 grid grid-cols-4 gap-10">
|
<div className="mt-8 sm:mt-12 grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 lg:gap-10">
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<CategoryTile key={category.title} title={category.title} image={category.image} featured={category.featured} />
|
<CategoryTile key={category.title} title={category.title} image={category.image} featured={category.featured} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import GridWrapper from "@/app/components/GridWrapper";
|
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import BlogCard from "./BlogCard";
|
import BlogCard from "./BlogCard";
|
||||||
|
|
||||||
const BlogsSection: FC = () => {
|
const BlogsSection: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-[120px] px-[120px]">
|
<div className="mt-12 px-4 sm:mt-16 sm:px-8 lg:mt-[120px] lg:px-[120px]">
|
||||||
<div className="text-2xl font-bold">مقالات ما</div>
|
<div className="text-lg sm:text-2xl font-bold">مقالات ما</div>
|
||||||
<GridWrapper desktop={4} mobile={1} className="mt-12">
|
<div className="mt-8 sm:mt-12 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-10">
|
||||||
<BlogCard />
|
<BlogCard />
|
||||||
<BlogCard />
|
<BlogCard />
|
||||||
<BlogCard />
|
<BlogCard />
|
||||||
<BlogCard />
|
<BlogCard />
|
||||||
</GridWrapper>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ type CategoryCardProps = {
|
|||||||
|
|
||||||
const CategoryCard: FC<CategoryCardProps> = ({ title }) => {
|
const CategoryCard: FC<CategoryCardProps> = ({ title }) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full bg-[#D4E3F154]/33 rounded-[40px] p-6">
|
<div className="w-full bg-[#D4E3F154]/33 rounded-[24px] sm:rounded-[40px] p-4 sm:p-6">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center gap-2">
|
||||||
<div className="text-[#21588C] font-bold">{title}</div>
|
<div className="text-[#21588C] font-bold text-sm sm:text-base">{title}</div>
|
||||||
<div className="bg-white/40 flex justify-center items-center">
|
<div className="bg-white/40 flex justify-center items-center">
|
||||||
<ArrowLeft size={20} color="currentColor" className="text-primary rotate-45" />
|
<ArrowLeft size={20} color="currentColor" className="text-primary rotate-45" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ const categories = [
|
|||||||
|
|
||||||
const CategorySection = () => {
|
const CategorySection = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-[120px] px-[120px]">
|
<div className="mt-12 px-4 sm:mt-16 sm:px-8 lg:mt-[120px] lg:px-[120px]">
|
||||||
<Carousel slidesPerView={5} gap={40}>
|
<Carousel slidesPerView={{ base: 1.4, md: 3, lg: 5 }} gap={{ base: 16, md: 24, lg: 40 }}>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center gap-4">
|
||||||
<div className="font-bold text-2xl">دسته بندی های محصولات</div>
|
<div className="font-bold text-lg sm:text-2xl">دسته بندی های محصولات</div>
|
||||||
<CarouselControls prevLabel="دستهبندیهای قبلی" nextLabel="دستهبندیهای بعدی" />
|
<CarouselControls prevLabel="دستهبندیهای قبلی" nextLabel="دستهبندیهای بعدی" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CarouselTrack className="mt-12">
|
<CarouselTrack className="mt-8 sm:mt-12">
|
||||||
{categories.map((title) => (
|
{categories.map((title) => (
|
||||||
<CarouselSlide key={title}>
|
<CarouselSlide key={title}>
|
||||||
<CategoryCard title={title} />
|
<CategoryCard title={title} />
|
||||||
|
|||||||
@@ -6,13 +6,15 @@ import { type FC } from "react";
|
|||||||
|
|
||||||
const ContactCtaSection: FC = () => {
|
const ContactCtaSection: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-[120px] flex items-center justify-center gap-6 bg-linear-to-l from-primary/10 to-transparent py-10">
|
<div className="mt-12 sm:mt-16 lg:mt-[120px] flex flex-col sm:flex-row items-center justify-center gap-4 sm:gap-6 bg-linear-to-l from-primary/10 to-transparent px-4 py-8 sm:py-10">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3 text-center sm:text-right">
|
||||||
<Image src={deliveryTruckImage} alt="" width={40} height={40} className="size-10 object-contain" />
|
<Image src={deliveryTruckImage} alt="" width={40} height={40} className="size-10 object-contain shrink-0" />
|
||||||
<p className="text-xl font-bold text-[#0A1B2C] whitespace-nowrap">ارسال سریع به سراسر ایران | مشاوره رایگان قبل از سفارش</p>
|
<p className="text-base sm:text-xl font-bold text-[#0A1B2C] sm:whitespace-nowrap">
|
||||||
|
ارسال سریع به سراسر ایران | مشاوره رایگان قبل از سفارش
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button className="gap-2 ps-5 pe-6 font-bold">
|
<Button className="gap-2 ps-5 pe-6 font-bold shrink-0">
|
||||||
<CallCalling size={20} color="currentColor" variant="Linear" />
|
<CallCalling size={20} color="currentColor" variant="Linear" />
|
||||||
تماس با ما
|
تماس با ما
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import ProductCard from "./ProductCard";
|
|||||||
|
|
||||||
const OrderByApplication: FC = () => {
|
const OrderByApplication: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="p-[120px] bg-[#F1F6FA] mt-[120px]">
|
<div className="px-4 py-12 sm:px-8 sm:py-16 lg:p-[120px] bg-[#F1F6FA] mt-12 sm:mt-16 lg:mt-[120px]">
|
||||||
<div className="text-2xl font-bold">سفارش بر اساس کاربرد</div>
|
<div className="text-lg sm:text-2xl font-bold">سفارش بر اساس کاربرد</div>
|
||||||
<Carousel slidesPerView={5} gap={40}>
|
<Carousel slidesPerView={{ base: 1.4, md: 3, lg: 5 }} gap={{ base: 16, md: 24, lg: 40 }}>
|
||||||
<div className="flex mt-12 justify-between items-center">
|
<div className="flex mt-8 sm:mt-12 justify-between items-center gap-4">
|
||||||
<div>
|
<div className="min-w-0 flex-1 max-w-[240px] sm:max-w-none sm:flex-none">
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ const OrderByApplication: FC = () => {
|
|||||||
<CarouselControls prevLabel="محصولات قبلی" nextLabel="محصولات بعدی" />
|
<CarouselControls prevLabel="محصولات قبلی" nextLabel="محصولات بعدی" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CarouselTrack className="mt-12 px-3 py-4 -mx-3">
|
<CarouselTrack className="mt-8 sm:mt-12 px-2 sm:px-3 py-4 -mx-2 sm:-mx-3">
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
<CarouselSlide key={index}>
|
<CarouselSlide key={index}>
|
||||||
<ProductCard />
|
<ProductCard />
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ import { FC } from "react";
|
|||||||
|
|
||||||
const OrderRegistrationSteps: FC = () => {
|
const OrderRegistrationSteps: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="p-[120px] bg-[#F1F6FA] mt-[120px]">
|
<div className="px-4 py-12 sm:px-8 sm:py-16 lg:p-[120px] bg-[#F1F6FA] mt-12 sm:mt-16 lg:mt-[120px]">
|
||||||
<div className="text-2xl font-bold">سفارش بر اساس کاربرد</div>
|
<div className="text-lg sm:text-2xl font-bold">سفارش بر اساس کاربرد</div>
|
||||||
<Image src={ImageSrc} alt="Order Registration Steps" width={1000} height={1000} className="w-full mt-12 h-auto object-contain" />
|
<Image
|
||||||
|
src={ImageSrc}
|
||||||
|
alt="Order Registration Steps"
|
||||||
|
width={1000}
|
||||||
|
height={1000}
|
||||||
|
className="w-full mt-8 sm:mt-12 h-auto object-contain"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import ProductCard from "./ProductCard";
|
|||||||
|
|
||||||
const ProductSection: FC = () => {
|
const ProductSection: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="mt-[120px] px-[120px]">
|
<div className="mt-12 px-4 sm:mt-16 sm:px-8 lg:mt-[120px] lg:px-[120px]">
|
||||||
<Carousel slidesPerView={5} gap={40}>
|
<Carousel slidesPerView={{ base: 1.4, md: 3, lg: 5 }} gap={{ base: 16, md: 24, lg: 40 }}>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center gap-4">
|
||||||
<div className="font-bold text-2xl">محبوبترین محصولات</div>
|
<div className="font-bold text-lg sm:text-2xl">محبوبترین محصولات</div>
|
||||||
<CarouselControls prevLabel="محصولات قبلی" nextLabel="محصولات بعدی" />
|
<CarouselControls prevLabel="محصولات قبلی" nextLabel="محصولات بعدی" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CarouselTrack className="mt-12 px-3 py-4 -mx-3">
|
<CarouselTrack className="mt-8 sm:mt-12 px-2 sm:px-3 py-4 -mx-2 sm:-mx-3">
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
<CarouselSlide key={index}>
|
<CarouselSlide key={index}>
|
||||||
<ProductCard />
|
<ProductCard />
|
||||||
|
|||||||
@@ -1,32 +1,25 @@
|
|||||||
import { ColorSwatch, Element4, LampCharge, Magicpen, Setting2, TruckFast } from "iconsax-reactjs";
|
import { ColorSwatch, Element4, LampCharge, Magicpen, Setting2, TruckFast } from "iconsax-reactjs";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
{ label: "تحلیل نیاز برند", Icon: LampCharge },
|
||||||
|
{ label: "طراحی و آمادهسازی فایل", Icon: Magicpen },
|
||||||
|
{ label: "انتخاب فناوری و متریال", Icon: Element4 },
|
||||||
|
{ label: "کنترل رنگ و نمونهگیری", Icon: ColorSwatch },
|
||||||
|
{ label: "عملیات تکمیلی", Icon: Setting2 },
|
||||||
|
{ label: "تحویل به موقع", Icon: TruckFast },
|
||||||
|
];
|
||||||
|
|
||||||
const StepSection: FC = () => {
|
const StepSection: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full px-[120px] bg-secondary flex justify-between items-center h-[80px]">
|
<div className="w-full px-4 sm:px-8 lg:px-[120px] bg-secondary">
|
||||||
<div className="flex gap-4 3tems-center">
|
<div className="flex gap-6 lg:gap-4 lg:justify-between items-center h-auto py-4 lg:py-0 lg:h-[80px] overflow-x-auto scrollbar-none [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden">
|
||||||
<LampCharge variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
{steps.map(({ label, Icon }) => (
|
||||||
<div className="font-bold text-sm">تحلیل نیاز برند</div>
|
<div key={label} className="flex gap-3 items-center shrink-0">
|
||||||
|
<Icon variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
||||||
|
<div className="font-bold text-sm whitespace-nowrap">{label}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-3 items-center">
|
))}
|
||||||
<Magicpen variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
|
||||||
<div className="font-bold text-sm">طراحی و آمادهسازی فایل</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Element4 variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
|
||||||
<div className="font-bold text-sm">انتخاب فناوری و متریال</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-4 i3ems-center">
|
|
||||||
<ColorSwatch variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
|
||||||
<div className="font-bold text-sm">کنترل رنگ و نمونهگیری</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-3 items-center">
|
|
||||||
<Setting2 variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
|
||||||
<div className="font-bold text-sm">عملیات تکمیلی</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-43items-center">
|
|
||||||
<TruckFast variant="Bulk" size={24} color="currentColor" className="text-primary" />
|
|
||||||
<div className="font-bold text-sm">تحویل به موقع</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ const categoryLinks = ["چاپ جعبه و بستهبندی", "جعبه ها
|
|||||||
const Footer: FC = () => {
|
const Footer: FC = () => {
|
||||||
return (
|
return (
|
||||||
<footer className="mt-auto border-t border-[#E9EEF2] bg-[#F7F9FB]">
|
<footer className="mt-auto border-t border-[#E9EEF2] bg-[#F7F9FB]">
|
||||||
<div className="px-[120px] pt-12 pb-6">
|
<div className="px-4 sm:px-8 lg:px-[120px] pt-8 sm:pt-12 pb-6">
|
||||||
<div className="flex justify-between gap-8">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:flex xl:justify-between gap-8 xl:gap-6">
|
||||||
<FooterContact />
|
<FooterContact />
|
||||||
<FooterLinkColumn title="راهنمای خرید" links={guideLinks} />
|
<FooterLinkColumn title="راهنمای خرید" links={guideLinks} />
|
||||||
<FooterLinkColumn title="خدمات مشتریان" links={serviceLinks} />
|
<FooterLinkColumn title="خدمات مشتریان" links={serviceLinks} />
|
||||||
@@ -25,19 +25,19 @@ const Footer: FC = () => {
|
|||||||
<FooterTrustValues />
|
<FooterTrustValues />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Seprator className="my-8 h-px w-full" />
|
<Seprator className="my-6 sm:my-8 h-px w-full" />
|
||||||
|
|
||||||
<div className="flex items-center justify-between gap-8">
|
<div className="flex flex-col lg:flex-row items-center lg:items-start justify-between gap-8">
|
||||||
<FooterSocial />
|
<FooterSocial />
|
||||||
<Seprator className="h-16 w-px shrink-0" />
|
<Seprator className="hidden lg:block h-16 w-px shrink-0" />
|
||||||
<FooterTrustBadges />
|
<FooterTrustBadges />
|
||||||
<Seprator className="h-16 w-px shrink-0" />
|
<Seprator className="hidden lg:block h-16 w-px shrink-0" />
|
||||||
<FooterNewsletter />
|
<FooterNewsletter />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Seprator className="my-8 h-px w-full" />
|
<Seprator className="my-6 sm:my-8 h-px w-full" />
|
||||||
|
|
||||||
<p className="text-center text-xs text-[#6C7680]">تمامی حقوق برای سایت نیکوپکجینگ بوده و با ذکر نام لینک به منبع مجاز میباشد.</p>
|
<p className="text-center text-xs text-[#6C7680] leading-5 px-2">تمامی حقوق برای سایت نیکوپکجینگ بوده و با ذکر نام لینک به منبع مجاز میباشد.</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
|
|||||||
+34
-15
@@ -8,33 +8,51 @@ import HeaderMenu from "./components/HeaderMenu";
|
|||||||
const Header = () => {
|
const Header = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="h-12 bg-secondary flex items-center justify-between px-[120px]">
|
<div className="min-h-12 bg-secondary flex items-center justify-between gap-3 px-4 sm:px-8 lg:px-[120px] py-2">
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center min-w-0">
|
||||||
<span className="text-primary flex items-center gap-2">
|
<span className="text-primary flex items-center gap-2 min-w-0">
|
||||||
<Truck size={24} color="currentColor" />
|
<Truck size={20} color="currentColor" className="shrink-0 sm:size-6" />
|
||||||
<div className="text-[#3A4147] text-sm font-bold">ارسال رایگان برای خرید بالای ۱۰۰ میلیون تومان</div>
|
<div className="text-[#3A4147] text-xs sm:text-sm font-bold truncate">ارسال رایگان برای خرید بالای ۱۰۰ میلیون تومان</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex text-sm font-bold gap-2 items-center">
|
<div className="hidden md:flex text-sm font-bold gap-2 items-center shrink-0">
|
||||||
<span className="text-primary">20%</span>
|
<span className="text-primary">20%</span>
|
||||||
<span>تخفیف برای اولین سفارش خود دریافت کنید |</span>
|
<span className="hidden lg:inline">تخفیف برای اولین سفارش خود دریافت کنید |</span>
|
||||||
<span>
|
<span>
|
||||||
کد تخفیف : <span className="text-primary">FIRSTORDER</span>
|
کد تخفیف : <span className="text-primary">FIRSTORDER</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center shrink-0">
|
||||||
<Profile className="text-primary" size={24} color="currentColor" />
|
<Profile className="text-primary" size={20} color="currentColor" />
|
||||||
<div>سلام حمید</div>
|
<div className="hidden sm:block text-sm">سلام حمید</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white h-[120px] flex gap-16 items-center border border-secondary px-[120px]">
|
|
||||||
<Image src={logo} alt="logo" width={116} height={72} />
|
<div className="bg-white flex flex-col gap-4 lg:flex-row lg:gap-16 lg:items-center lg:h-[120px] border border-secondary px-4 sm:px-8 lg:px-[120px] py-4 lg:py-0">
|
||||||
<div className="flex-1">
|
<div className="flex items-center justify-between gap-4 lg:contents">
|
||||||
|
<Image src={logo} alt="logo" width={116} height={72} className="h-12 w-auto sm:h-[72px] shrink-0" />
|
||||||
|
|
||||||
|
<div className="flex gap-4 items-center lg:hidden">
|
||||||
|
<button type="button" aria-label="پیگیری سفارش" className="text-[#6C7680]">
|
||||||
|
<TruckFast variant="Bold" color="currentColor" size={22} />
|
||||||
|
</button>
|
||||||
|
<button type="button" aria-label="تماس" className="text-[#6C7680]">
|
||||||
|
<Call variant="Bold" color="currentColor" size={22} />
|
||||||
|
</button>
|
||||||
|
<div className="relative">
|
||||||
|
<ShoppingCart variant="Bold" color="black" size={22} />
|
||||||
|
<div className="absolute -top-1.5 -right-1.5 bg-primary text-white text-xs font-medium rounded-full size-4 flex items-center justify-center">0</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 w-full">
|
||||||
<Input variant="search" placeholder="جستجوی محصول" />
|
<Input variant="search" placeholder="جستجوی محصول" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-8 items-center">
|
|
||||||
|
<div className="hidden lg:flex gap-8 items-center shrink-0">
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<TruckFast variant="Bold" color="black" />
|
<TruckFast variant="Bold" color="black" />
|
||||||
<div className="text-[#6C7680] text-sm">پیگیری سفارش</div>
|
<div className="text-[#6C7680] text-sm">پیگیری سفارش</div>
|
||||||
@@ -44,7 +62,7 @@ const Header = () => {
|
|||||||
|
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Call variant="Bold" color="black" />
|
<Call variant="Bold" color="black" />
|
||||||
<div className="text-[#6C7680] text-sm">(30 خط) 02134782000</div>
|
<div className="text-[#6C7680] text-sm whitespace-nowrap">(30 خط) 02134782000</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Seprator className="h-5 w-px" />
|
<Seprator className="h-5 w-px" />
|
||||||
@@ -55,6 +73,7 @@ const Header = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HeaderMenu />
|
<HeaderMenu />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,18 +42,19 @@ const columns: CategoryGroup[][] = [
|
|||||||
|
|
||||||
const AllProductsMenu: FC = () => {
|
const AllProductsMenu: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="absolute top-full right-[120px] z-50 mt-2 w-[1099px] rounded-[32px] bg-[#F9FAFB]/90 px-8 pt-8 pb-10">
|
<div className="absolute top-full z-50 mt-2 inset-x-4 sm:inset-x-8 lg:inset-x-auto lg:right-[120px] w-auto lg:w-[min(1099px,calc(100vw-240px))] max-h-[70vh] overflow-y-auto rounded-2xl sm:rounded-[32px] bg-[#F9FAFB]/95 backdrop-blur-sm px-4 sm:px-8 pt-6 sm:pt-8 pb-6 sm:pb-10 shadow-lg border border-[#E9EEF2]/80">
|
||||||
<div className="flex w-full items-start justify-between">
|
<div className="flex w-full flex-col lg:flex-row items-start justify-between gap-8">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-0 lg:contents w-full lg:w-auto">
|
||||||
{columns.map((groups) => (
|
{columns.map((groups) => (
|
||||||
<div key={groups[0].title} className="flex w-[228px] shrink-0 flex-col gap-6">
|
<div key={groups[0].title} className="flex w-full lg:w-[228px] shrink-0 flex-col gap-6">
|
||||||
{groups.map((group) => (
|
{groups.map((group) => (
|
||||||
<div key={group.title} className="flex flex-col gap-2">
|
<div key={group.title} className="flex flex-col gap-2">
|
||||||
<div className="flex h-10 items-center px-4 text-base font-bold text-black">{group.title}</div>
|
<div className="flex h-10 items-center px-2 sm:px-4 text-sm sm:text-base font-bold text-black">{group.title}</div>
|
||||||
{group.items.map((item) => (
|
{group.items.map((item) => (
|
||||||
<button
|
<button
|
||||||
key={item}
|
key={item}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex h-6 w-full items-center px-4 text-right text-base font-medium text-[#011E3A] transition-colors hover:text-primary"
|
className="flex h-6 w-full items-center px-2 sm:px-4 text-right text-sm sm:text-base font-medium text-[#011E3A] transition-colors hover:text-primary"
|
||||||
>
|
>
|
||||||
{item}
|
{item}
|
||||||
</button>
|
</button>
|
||||||
@@ -62,8 +63,9 @@ const AllProductsMenu: FC = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="relative h-[157px] w-[163px] shrink-0 overflow-hidden rounded-2xl">
|
<div className="relative mx-auto lg:mx-0 h-[140px] w-[145px] sm:h-[157px] sm:w-[163px] shrink-0 overflow-hidden rounded-2xl">
|
||||||
<Image src={businessCardMenu} alt="کارت ویزیت" fill className="object-cover" sizes="163px" />
|
<Image src={businessCardMenu} alt="کارت ویزیت" fill className="object-cover" sizes="163px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const ContactItem: FC<ContactItemProps> = ({ icon, children }) => {
|
|||||||
|
|
||||||
const FooterContact: FC = () => {
|
const FooterContact: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex max-w-[280px] flex-col gap-5">
|
<div className="flex max-w-none sm:max-w-[280px] flex-col gap-5">
|
||||||
<ContactItem icon={<Location size={18} color="currentColor" variant="Linear" />}>
|
<ContactItem icon={<Location size={18} color="currentColor" variant="Linear" />}>
|
||||||
جاده خاوران، شریف آباد، شهرک صنعتی شنزار، بعد از میدان چهارم، خیابان بوستان پنجم، خیابان گلبهار، پلاک ۱۲۲
|
جاده خاوران، شریف آباد، شهرک صنعتی شنزار، بعد از میدان چهارم، خیابان بوستان پنجم، خیابان گلبهار، پلاک ۱۲۲
|
||||||
</ContactItem>
|
</ContactItem>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { type FC } from "react";
|
|||||||
|
|
||||||
const FooterNewsletter: FC = () => {
|
const FooterNewsletter: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex max-w-[360px] flex-col gap-4">
|
<div className="flex w-full max-w-[360px] flex-col gap-4 lg:ms-auto">
|
||||||
<p className="text-sm leading-6 font-bold text-[#0A1B2C]">برای دریافت آخرین پیشنهادها، اخبار و مطالب الهامبخش، در خبرنامه ما عضو شوید.</p>
|
<p className="text-sm leading-6 font-bold text-[#0A1B2C]">برای دریافت آخرین پیشنهادها، اخبار و مطالب الهامبخش، در خبرنامه ما عضو شوید.</p>
|
||||||
<form className="relative" action="#">
|
<form className="relative" action="#">
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ const socialLinks: SocialLink[] = [
|
|||||||
|
|
||||||
const FooterSocial: FC = () => {
|
const FooterSocial: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4 items-center lg:items-start text-center lg:text-right">
|
||||||
<h3 className="text-sm font-bold text-[#0A1B2C]">ما را در شبکههای اجتماعی دنبال کنید</h3>
|
<h3 className="text-sm font-bold text-[#0A1B2C]">ما را در شبکههای اجتماعی دنبال کنید</h3>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3 flex-wrap justify-center lg:justify-start">
|
||||||
{socialLinks.map((item) => (
|
{socialLinks.map((item) => (
|
||||||
<a key={item.label} href={item.href} aria-label={item.label} className={`flex size-9 items-center justify-center rounded-full ${item.bg}`}>
|
<a key={item.label} href={item.href} aria-label={item.label} className={`flex size-9 items-center justify-center rounded-full ${item.bg}`}>
|
||||||
{item.icon}
|
{item.icon}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ const FooterTrustBadges: FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
<h3 className="text-sm font-bold text-[#0A1B2C]">نماد های اعتماد</h3>
|
<h3 className="text-sm font-bold text-[#0A1B2C]">نماد های اعتماد</h3>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 sm:gap-4 flex-wrap justify-center">
|
||||||
{badges.map((badge) => (
|
{badges.map((badge) => (
|
||||||
<div key={badge} className="flex size-16 items-center justify-center rounded-full border border-[#E9EEF2] bg-white text-center text-[10px] font-bold text-[#6C7680]">
|
<div key={badge} className="flex size-14 sm:size-16 items-center justify-center rounded-full border border-[#E9EEF2] bg-white text-center text-[10px] font-bold text-[#6C7680]">
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -36,21 +36,21 @@ const HeaderMenu: FC = () => {
|
|||||||
}, [isAllProductsOpen]);
|
}, [isAllProductsOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="relative h-[72px] w-full flex-1 px-[120px]">
|
<div ref={containerRef} className="relative h-14 sm:h-[72px] w-full flex-1 px-4 sm:px-8 lg:px-[120px]">
|
||||||
<div className="flex h-full items-center justify-center gap-8">
|
<div className="flex h-full items-center gap-4 sm:gap-6 lg:gap-8 overflow-x-auto scrollbar-none [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden lg:justify-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn("flex items-center gap-2 font-bold transition-colors", isAllProductsOpen && "text-primary")}
|
className={cn("flex items-center gap-2 font-bold transition-colors shrink-0 text-sm sm:text-base", isAllProductsOpen && "text-primary")}
|
||||||
onClick={() => setIsAllProductsOpen((open) => !open)}
|
onClick={() => setIsAllProductsOpen((open) => !open)}
|
||||||
aria-expanded={isAllProductsOpen}
|
aria-expanded={isAllProductsOpen}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
>
|
>
|
||||||
<HamburgerMenu size={24} color="currentColor" />
|
<HamburgerMenu size={22} color="currentColor" />
|
||||||
<span>همه محصولات</span>
|
<span>همه محصولات</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{menuItems.map((item) => (
|
{menuItems.map((item) => (
|
||||||
<div key={item} className="font-bold">
|
<div key={item} className="font-bold shrink-0 whitespace-nowrap text-sm sm:text-base">
|
||||||
{item}
|
{item}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user