responsive base v1
This commit is contained in:
@@ -27,7 +27,7 @@ const AboutPage: NextPage = () => {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="mt-14 px-4 md:px-8 lg:px-20">
|
||||
<div className="mt-14 px-4 md:px-8 lg:px-20 max-w-full overflow-x-hidden">
|
||||
{/* عنوان اصلی */}
|
||||
<div className="text-center mb-16">
|
||||
<h1 className="text-3xl font-bold text-gray-800">
|
||||
|
||||
+14
-14
@@ -5,20 +5,20 @@ import BlogItem from './components/BlogItem'
|
||||
import LastPost from './components/LastPost'
|
||||
|
||||
const BlogsPage: NextPage = () => {
|
||||
return (
|
||||
<div className='flex gap-24 p-10'>
|
||||
<Categories />
|
||||
<div className='flex-1 flex flex-col gap-6'>
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
</div>
|
||||
<div className='max-w-[340px] w-full'>
|
||||
<LastPost />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className='flex gap-24 p-10'>
|
||||
<Categories />
|
||||
<div className='flex-1 flex flex-col gap-6'>
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
<BlogItem />
|
||||
</div>
|
||||
<div className='max-w-[340px] w-full'>
|
||||
<LastPost />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function BlogsPageWithLayout() {
|
||||
|
||||
+15
-15
@@ -7,10 +7,10 @@ import { NextPage } from "next"
|
||||
|
||||
const Cart: NextPage = () => {
|
||||
return (
|
||||
<div className="mt-14 px-20">
|
||||
<div className="border-b border-border flex justify-between items-center">
|
||||
<div className="flex items-center gap-3 pb-6 border-b-2 border-primary relative top-[1px]">
|
||||
<div>
|
||||
<div className="mt-14 px-4 sm:px-8 md:px-20">
|
||||
<div className="border-b border-border flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 sm:gap-0">
|
||||
<div className="flex items-center gap-2 sm:gap-3 pb-4 sm:pb-6 border-b-2 border-primary relative top-[1px]">
|
||||
<div className="text-sm sm:text-base">
|
||||
سبد خرید
|
||||
</div>
|
||||
<div className="size-8 rounded-lg bg-[#F2F2F2] flex items-center justify-center text-primary">
|
||||
@@ -19,17 +19,17 @@ const Cart: NextPage = () => {
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="bg-white shadow h-10"
|
||||
className="bg-white shadow h-8 sm:h-10 w-full sm:w-auto"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Trash size={20} color="#AD3434" />
|
||||
<div className="text-sm text-[#AD3434] font-light">حذف همه از سبد خرید</div>
|
||||
<Trash size={16} color="#AD3434" className="sm:w-5 sm:h-5" />
|
||||
<div className="text-xs sm:text-sm text-[#AD3434] font-light">حذف همه از سبد خرید</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex gap-6">
|
||||
<div className="flex-1 border border-border p-5 rounded-2xl">
|
||||
<div className="mt-4 sm:mt-6 flex flex-col lg:flex-row gap-4 sm:gap-6">
|
||||
<div className="flex-1 border border-border p-3 sm:p-5 rounded-xl sm:rounded-2xl">
|
||||
<CartItem noBorder />
|
||||
<CartItem />
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ const Cart: NextPage = () => {
|
||||
total={80580000}
|
||||
confirmHref="/cart/shipping"
|
||||
confirmLabel="ادامه به آدرس و ارسال"
|
||||
className="w-[360px]"
|
||||
className="w-full lg:w-[360px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,9 +48,9 @@ const Cart: NextPage = () => {
|
||||
}
|
||||
|
||||
export default function CartWithLayout() {
|
||||
return (
|
||||
<Layout>
|
||||
<Cart />
|
||||
</Layout>
|
||||
)
|
||||
return (
|
||||
<Layout>
|
||||
<Cart />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,23 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
/* بهبود responsive design برای موبایل */
|
||||
html {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
/* اطمینان از عدم overflow در container ها */
|
||||
.container,
|
||||
.max-w-full {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
@@ -131,6 +148,13 @@ body {
|
||||
font-feature-settings: "lnum"; /* اطمینان از نمایش اعداد انگلیسی */
|
||||
}
|
||||
|
||||
/* Add bottom padding for mobile bottom menu */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
|
||||
@@ -4,31 +4,31 @@ import { FC } from 'react'
|
||||
|
||||
const BlogCard: FC = () => {
|
||||
return (
|
||||
<div className='bg-white p-3 rounded-2xl shadow w-full '>
|
||||
<div className='bg-white p-3 rounded-2xl shadow w-full overflow-hidden'>
|
||||
<Image
|
||||
src='https://picsum.photos/200/300?grayscale'
|
||||
width={357}
|
||||
height={201}
|
||||
alt=''
|
||||
className='max-w-full max-h-[181px] object-cover rounded-2xl'
|
||||
className='w-full h-[120px] sm:h-[181px] object-cover rounded-xl sm:rounded-2xl'
|
||||
/>
|
||||
|
||||
<div className='mt-3'>
|
||||
<h6 className='text-primary text-sm'>تکنولوژی</h6>
|
||||
</div>
|
||||
<h4 className='mt-2'>
|
||||
<h4 className='mt-2 text-sm sm:text-base'>
|
||||
نسل جدید پردازنده اینتل
|
||||
</h4>
|
||||
|
||||
<p className='mt-2 text-sm text-[#7F7F7F] font-light leading-6'>
|
||||
<p className='mt-2 text-xs sm:text-sm text-[#7F7F7F] font-light leading-5 sm:leading-6'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است....
|
||||
</p>
|
||||
|
||||
<div className='mt-6 flex justify-between items-center'>
|
||||
<div className='text-sm font-light text-[#7F7F7F]'>1401/7/21</div>
|
||||
<div className='mt-4 sm:mt-6 flex justify-between items-center'>
|
||||
<div className='text-xs sm:text-sm font-light text-[#7F7F7F]'>1401/7/21</div>
|
||||
<div className='flex gap-1 items-center text-[#7F7F7F]'>
|
||||
<div className='text-sm font-light'>ادامه مطلب</div>
|
||||
<ArrowLeft size={16} color='#7F7F7F' />
|
||||
<div className='text-xs sm:text-sm font-light'>ادامه مطلب</div>
|
||||
<ArrowLeft size={14} color='#7F7F7F' className='sm:w-4 sm:h-4' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { FC } from 'react'
|
||||
import { FC, useState, useEffect } from 'react'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import { Pagination } from 'swiper/modules';
|
||||
import Image from 'next/image';
|
||||
@@ -7,10 +7,25 @@ import { useGetLanding } from '../hooks/useHomeData';
|
||||
|
||||
const Carousel: FC = () => {
|
||||
const { data } = useGetLanding()
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
|
||||
if (!data?.results?.sliders?.desktop || data.results.sliders.desktop.length === 0) {
|
||||
useEffect(() => {
|
||||
const checkIsMobile = () => {
|
||||
setIsMobile(window.innerWidth < 768) // md breakpoint
|
||||
}
|
||||
|
||||
checkIsMobile()
|
||||
window.addEventListener('resize', checkIsMobile)
|
||||
|
||||
return () => window.removeEventListener('resize', checkIsMobile)
|
||||
}, [])
|
||||
|
||||
// Choose appropriate sliders based on device type
|
||||
const sliders = isMobile ? data?.results?.sliders?.mobile : data?.results?.sliders?.desktop
|
||||
|
||||
if (!sliders || sliders.length === 0) {
|
||||
return (
|
||||
<div className='mt-5 h-[400px] bg-gray-200 rounded-lg flex items-center justify-center'>
|
||||
<div className='mt-5 h-[200px] sm:h-[300px] md:h-[400px] bg-gray-200 rounded-lg flex items-center justify-center'>
|
||||
<p className="text-gray-500">اسلایدری موجود نیست</p>
|
||||
</div>
|
||||
);
|
||||
@@ -19,9 +34,9 @@ const Carousel: FC = () => {
|
||||
return (
|
||||
<div className='mt-5 relative' style={{ zIndex: 0 }}>
|
||||
<Swiper pagination={true} modules={[Pagination]} className="mySwiper pb-5" style={{ zIndex: 0, position: 'relative' }}>
|
||||
{data.results.sliders.desktop.map((slider) => (
|
||||
{sliders.map((slider) => (
|
||||
<SwiperSlide key={slider._id}>
|
||||
<div className='h-[400px] relative'>
|
||||
<div className='h-[200px] sm:h-[300px] md:h-[400px] relative'>
|
||||
<Image
|
||||
src={slider.imageUrl}
|
||||
alt={slider.altText}
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react'
|
||||
import CategoryItem from './CategoryItem'
|
||||
import { useGetCategories } from '@/share/hooks/useShareData'
|
||||
import { Category } from '@/share/types/SharedTypes'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
|
||||
const Categories = () => {
|
||||
|
||||
@@ -10,17 +11,23 @@ const Categories = () => {
|
||||
const parentCategories = data?.results?.data?.filter(cat => !cat.parent) || []
|
||||
|
||||
return (
|
||||
<div className='flex gap-6 justify-center'>
|
||||
{
|
||||
parentCategories?.map((item: Category) => {
|
||||
return (
|
||||
<CategoryItem key={item._id} image={item.imageUrl} title={item.title_fa} />
|
||||
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
<div className='w-full'>
|
||||
<Swiper
|
||||
spaceBetween={16}
|
||||
slidesPerView={'auto'}
|
||||
className='h-full'
|
||||
breakpoints={{
|
||||
640: {
|
||||
spaceBetween: 24,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{parentCategories?.map((item: Category) => (
|
||||
<SwiperSlide key={item._id} className='!w-auto'>
|
||||
<CategoryItem image={item.imageUrl} title={item.title_fa} />
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ type Props = {
|
||||
|
||||
const CategoryItem: FC<Props> = (props) => {
|
||||
return (
|
||||
<div className='text-center' >
|
||||
<div className='size-[113px] rounded-full overflow-hidden'>
|
||||
<Image src={props.image} alt={props.title} width={113} height={113} />
|
||||
<div className='text-center'>
|
||||
<div className='size-[80px] sm:size-[100px] md:size-[113px] rounded-full overflow-hidden'>
|
||||
<Image src={props.image} alt={props.title} width={113} height={113} className='w-full h-full object-cover' />
|
||||
</div>
|
||||
<div className='mt-3 text-sm text-[#191919]'>
|
||||
<div className='mt-2 sm:mt-3 text-xs sm:text-sm text-[#191919]'>
|
||||
{props.title}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,9 @@ import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
|
||||
const HotOffer: FC = () => {
|
||||
return (
|
||||
<div className=' bg-[#F2F2F2] rounded-2xl flex py-6'>
|
||||
<div className='px-6 min-w-[140px] max-w-[180px] flex flex-col justify-center'>
|
||||
<div className='text-center text-primary font-extrabold text-sm'>
|
||||
<div className='bg-[#F2F2F2] rounded-xl sm:rounded-2xl flex flex-col sm:flex-row py-4 sm:py-6'>
|
||||
<div className='px-4 sm:px-6 w-full sm:min-w-[140px] sm:max-w-[180px] flex flex-col justify-center items-center sm:items-start'>
|
||||
<div className='text-center text-primary font-extrabold text-xs sm:text-sm'>
|
||||
پیشنهاد های
|
||||
<br />
|
||||
داغ
|
||||
@@ -17,22 +17,27 @@ const HotOffer: FC = () => {
|
||||
سندس
|
||||
کالا
|
||||
</div>
|
||||
<div className='mt-6 flex justify-center'>
|
||||
<div className='mt-4 sm:mt-6 flex justify-center'>
|
||||
<Image
|
||||
src={'/images/hot_offer.png'}
|
||||
alt=''
|
||||
width={120}
|
||||
height={150}
|
||||
className='max-w-full h-auto'
|
||||
className='max-w-[80px] sm:max-w-full h-auto'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex-1 overflow-hidden px-4'>
|
||||
<div className='flex-1 overflow-hidden px-2 sm:px-4 mt-4 sm:mt-0'>
|
||||
<Swiper
|
||||
spaceBetween={16}
|
||||
spaceBetween={8}
|
||||
slidesPerView={'auto'}
|
||||
className='h-full'
|
||||
breakpoints={{
|
||||
640: {
|
||||
spaceBetween: 16,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SwiperSlide className='!w-auto'>
|
||||
<ProductCard />
|
||||
|
||||
@@ -31,10 +31,10 @@ const NewestProducts: FC = () => {
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<CarouselContent className='gap-2 h-[380px]'>
|
||||
<CarouselContent className='gap-1 sm:gap-2 h-[380px]'>
|
||||
{
|
||||
landing?.results?.latestProducts?.map((product) => (
|
||||
<CarouselItem key={product._id} className="basis-[292px]">
|
||||
<CarouselItem key={product._id} className="basis-[240px] sm:basis-[292px]">
|
||||
<ProductCard item={product} />
|
||||
</CarouselItem>
|
||||
))
|
||||
|
||||
@@ -25,9 +25,9 @@ const PopularProducts: FC = () => {
|
||||
}}
|
||||
className="w-full"
|
||||
>
|
||||
<CarouselContent className='gap-2'>
|
||||
<CarouselContent className='gap-1 sm:gap-2'>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<CarouselItem key={index} className="basis-[292px]">
|
||||
<CarouselItem key={index} className="basis-[240px] sm:basis-[292px]">
|
||||
<ProductCard />
|
||||
</CarouselItem>
|
||||
))}
|
||||
|
||||
+5
-1
@@ -4,14 +4,17 @@ import "../../public/css/fontiran.css";
|
||||
import 'swiper/css';
|
||||
import QueryProvider from "@/config/QueryProvider";
|
||||
import ToastContainer from "@/components/Toast";
|
||||
import MobileBottomMenu from "@/components/MobileBottomMenu";
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
|
||||
import { viewport } from './viewport'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export { viewport };
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
@@ -23,6 +26,7 @@ export default function RootLayout({
|
||||
<QueryProvider>
|
||||
{children}
|
||||
<ToastContainer />
|
||||
<MobileBottomMenu />
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</QueryProvider>
|
||||
</body>
|
||||
|
||||
+7
-7
@@ -18,27 +18,27 @@ const HomePage: NextPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<Carousel />
|
||||
<div className='mt-24 px-20'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24 px-4 sm:px-8 md:px-20'>
|
||||
<HotOffer />
|
||||
<div className='mt-24'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24'>
|
||||
<Categories />
|
||||
</div>
|
||||
<div className='mt-24'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24'>
|
||||
<Banners3 />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-24 bg-[#FAF8F6] p-20'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24 bg-[#FAF8F6] p-4 sm:p-8 md:p-20'>
|
||||
<NewestProducts />
|
||||
<div className='mt-24'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24'>
|
||||
<Banners4 />
|
||||
</div>
|
||||
<div className='mt-20'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-20'>
|
||||
<PopularProducts />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-24 px-20'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24 px-4 sm:px-8 md:px-20'>
|
||||
<Blogs />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,8 +19,8 @@ import Questions from '@/app/product/components/Questions'
|
||||
|
||||
const SingleProduct: NextPage = () => {
|
||||
return (
|
||||
<div className='mt-14 px-20'>
|
||||
<div className='flex justify-between'>
|
||||
<div className='mt-14 px-4 sm:px-8 md:px-20'>
|
||||
<div className='flex flex-col sm:flex-row justify-between gap-4 sm:gap-0'>
|
||||
<Breadcrumb aria-label="breadcrumb">
|
||||
<BreadcrumbList className="text-sm text-muted-foreground">
|
||||
<BreadcrumbItem>
|
||||
@@ -37,13 +37,13 @@ const SingleProduct: NextPage = () => {
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
<div className='text-primary text-sm'>فروشنده شوید</div>
|
||||
<div className='text-primary text-xs sm:text-sm'>فروشنده شوید</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-6 mt-14 flex-row-reverse'>
|
||||
<div className='flex flex-col lg:flex-row gap-4 sm:gap-6 mt-8 sm:mt-12 md:mt-14'>
|
||||
<ShopInformation />
|
||||
<div className='flex-1 flex flex-col gap-6'>
|
||||
<div className='flex gap-6'>
|
||||
<div className='flex flex-col lg:flex-row gap-4 sm:gap-6'>
|
||||
<ProductImage />
|
||||
<BaseInformation />
|
||||
</div>
|
||||
@@ -61,9 +61,9 @@ const SingleProduct: NextPage = () => {
|
||||
}
|
||||
|
||||
export default function SingleProductWithLayout() {
|
||||
return (
|
||||
<Layout>
|
||||
<SingleProduct />
|
||||
</Layout>
|
||||
)
|
||||
return (
|
||||
<Layout>
|
||||
<SingleProduct />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import ProductCard from '@/components/ProductCard'
|
||||
|
||||
const Products: NextPage = () => {
|
||||
return (
|
||||
<div className="mt-14 px-20">
|
||||
<div className="mt-14 px-4 sm:px-8 md:px-20">
|
||||
<Breadcrumb aria-label="breadcrumb">
|
||||
<BreadcrumbList className="text-sm text-muted-foreground">
|
||||
<BreadcrumbItem>
|
||||
@@ -32,13 +32,13 @@ const Products: NextPage = () => {
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
|
||||
<div className='mt-14 flex gap-6'>
|
||||
<div className='mt-8 sm:mt-12 md:mt-14 flex flex-col lg:flex-row gap-4 sm:gap-6'>
|
||||
<Filters />
|
||||
<div className='flex-1'>
|
||||
<Sorts />
|
||||
|
||||
<div className='mt-10'>
|
||||
<GridWrapper desktop={4} mobile={1}>
|
||||
<div className='mt-6 sm:mt-8 md:mt-10'>
|
||||
<GridWrapper desktop={4} mobile={2} gapMobile={3} gapDesktop={6}>
|
||||
<ProductCard />
|
||||
<ProductCard />
|
||||
<ProductCard />
|
||||
|
||||
+17
-17
@@ -36,15 +36,15 @@ const ProfilePage = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="p-4 sm:p-6">
|
||||
<Menu pageActive='profile' />
|
||||
<div className='mt-10'>
|
||||
<div className="flex gap-6">
|
||||
<div className='mt-6 sm:mt-8 md:mt-10'>
|
||||
<div className="flex flex-col xl:flex-row gap-4 sm:gap-6">
|
||||
{/* Left Content - Form */}
|
||||
<div className="flex-1 ">
|
||||
<div className="bg-[#FAFAFA] rounded-lg p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h1 className="text-lg font-bold text-gray-900">اطلاعات کاربری</h1>
|
||||
<div className="bg-[#FAFAFA] rounded-lg p-4 sm:p-6">
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 sm:mb-6 gap-3 sm:gap-0">
|
||||
<h1 className="text-base sm:text-lg font-bold text-gray-900">اطلاعات کاربری</h1>
|
||||
<Button
|
||||
onClick={() => setIsEditing(!isEditing)}
|
||||
variant="outline"
|
||||
@@ -55,7 +55,7 @@ const ProfilePage = () => {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
|
||||
<Input
|
||||
label="نام و نام خانوادگی"
|
||||
value={userInfo.name}
|
||||
@@ -121,7 +121,7 @@ const ProfilePage = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="mt-3 sm:mt-4">
|
||||
<Input
|
||||
label="آدرس"
|
||||
value={userInfo.address}
|
||||
@@ -131,7 +131,7 @@ const ProfilePage = () => {
|
||||
</div>
|
||||
|
||||
{isEditing && (
|
||||
<div className="mt-6 flex gap-3">
|
||||
<div className="mt-4 sm:mt-6 flex flex-col sm:flex-row gap-3">
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
className="px-8 bg-blue-600 hover:bg-blue-700"
|
||||
@@ -149,18 +149,18 @@ const ProfilePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col gap-6'>
|
||||
<div className='size-[240px] bg-[#FAFAFA] rounded-[10px] flex flex-col gap-6 justify-center items-center h-full'>
|
||||
<div className='size-20 bg-[#F2F2F2] rounded-full flex justify-center items-center'>
|
||||
<ShoppingCart size={40} color='#000' />
|
||||
<div className='flex flex-row xl:flex-col gap-4 sm:gap-6'>
|
||||
<div className='w-full xl:w-[240px] xl:h-[240px] bg-[#FAFAFA] rounded-[10px] flex flex-col gap-4 sm:gap-6 justify-center items-center h-[120px] sm:h-[160px] xl:h-[240px]'>
|
||||
<div className='size-12 sm:size-16 xl:size-20 bg-[#F2F2F2] rounded-full flex justify-center items-center'>
|
||||
<ShoppingCart size={24} color='#000' className='sm:w-8 sm:h-8 xl:w-10 xl:h-10' />
|
||||
</div>
|
||||
<div>
|
||||
سفارشات
|
||||
</div>
|
||||
</div>
|
||||
<div className='size-[240px] bg-[#FAFAFA] rounded-[10px] flex flex-col gap-6 justify-center items-center h-full'>
|
||||
<div className='size-20 bg-[#F2F2F2] rounded-full flex justify-center items-center'>
|
||||
<Heart size={40} color='#000' />
|
||||
<div className='w-full xl:w-[240px] xl:h-[240px] bg-[#FAFAFA] rounded-[10px] flex flex-col gap-4 sm:gap-6 justify-center items-center h-[120px] sm:h-[160px] xl:h-[240px]'>
|
||||
<div className='size-12 sm:size-16 xl:size-20 bg-[#F2F2F2] rounded-full flex justify-center items-center'>
|
||||
<Heart size={24} color='#000' className='sm:w-8 sm:h-8 xl:w-10 xl:h-10' />
|
||||
</div>
|
||||
<div>
|
||||
علاقه مندی ها
|
||||
@@ -168,7 +168,7 @@ const ProfilePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-[300px] min-h-full bg-red-50 rounded-[10px]'>
|
||||
<div className='w-full xl:w-[300px] min-h-[200px] xl:min-h-full bg-red-50 rounded-[10px] hidden xl:block'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Viewport } from "next";
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
userScalable: false,
|
||||
};
|
||||
@@ -0,0 +1,216 @@
|
||||
'use client'
|
||||
import { FC, useState } from 'react'
|
||||
import { ArrowLeft2 } from 'iconsax-react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const CategoryModal: FC<Props> = ({ isOpen, onClose }) => {
|
||||
const router = useRouter()
|
||||
const [selectedMainCategory, setSelectedMainCategory] = useState<number>(0)
|
||||
const [expandedSubcategory, setExpandedSubcategory] = useState<string | null>(null)
|
||||
|
||||
// Main categories for the left column
|
||||
const mainCategories = [
|
||||
{ icon: '🏪', title: 'سوپر مارکت ارگانیک' },
|
||||
{ icon: '🍞', title: 'خانه نان و شیرینی سالم' },
|
||||
{ icon: '🍎', title: 'بوفه رژیمهای خاص' },
|
||||
{ icon: '💪', title: 'بوفه سبک زندگی' },
|
||||
{ icon: '🌿', title: 'عطاری سلامت' },
|
||||
{ icon: '🎁', title: 'بوفه هدایا و سوغات' }
|
||||
]
|
||||
|
||||
// Subcategories for each main category
|
||||
const categorySubcategories = {
|
||||
'سوپر مارکت ارگانیک': [
|
||||
{ title: 'ارگانیک گواهیدار', hasChildren: false },
|
||||
{ title: 'روغن های سالم', hasChildren: true },
|
||||
{ title: 'چاشنی سلامت', hasChildren: true },
|
||||
{ title: 'صبحانه سالم', hasChildren: false },
|
||||
{ title: 'نوشیدنی سالم', hasChildren: false },
|
||||
{ title: 'آرایشی', hasChildren: false },
|
||||
{ title: 'بهداشتی', hasChildren: false },
|
||||
{ title: 'تازه خوری', hasChildren: false },
|
||||
{ title: 'حبوبات و غلات', hasChildren: false },
|
||||
{ title: 'تنقلات', hasChildren: false },
|
||||
{ title: 'چای و دمنوش', hasChildren: false },
|
||||
{ title: 'قهوه', hasChildren: false },
|
||||
{ title: 'قند و شکر', hasChildren: false }
|
||||
],
|
||||
'خانه نان و شیرینی سالم': [
|
||||
{ title: 'نان سنتی', hasChildren: false },
|
||||
{ title: 'نان صنعتی', hasChildren: false },
|
||||
{ title: 'شیرینی', hasChildren: false },
|
||||
{ title: 'کیک و کلوچه', hasChildren: false }
|
||||
],
|
||||
'بوفه رژیمهای خاص': [
|
||||
{ title: 'رژیم کتو', hasChildren: false },
|
||||
{ title: 'رژیم وگان', hasChildren: false },
|
||||
{ title: 'بدون گلوتن', hasChildren: false }
|
||||
],
|
||||
'بوفه سبک زندگی': [
|
||||
{ title: 'ورزشی', hasChildren: false },
|
||||
{ title: 'لاغری', hasChildren: false },
|
||||
{ title: 'سالمندان', hasChildren: false }
|
||||
],
|
||||
'عطاری سلامت': [
|
||||
{ title: 'گیاهان دارویی', hasChildren: false },
|
||||
{ title: 'عسل طبیعی', hasChildren: false },
|
||||
{ title: 'دمنوش', hasChildren: false }
|
||||
],
|
||||
'بوفه هدایا و سوغات': [
|
||||
{ title: 'هدایا', hasChildren: false },
|
||||
{ title: 'سوغات', hasChildren: false },
|
||||
{ title: 'بسته بندی ویژه', hasChildren: false }
|
||||
]
|
||||
}
|
||||
|
||||
// Third level subcategories (for items that expand)
|
||||
const thirdLevelCategories = {
|
||||
'روغن های سالم': [
|
||||
{ icon: '🫒', title: 'روغن زیتون پیوست و مو' },
|
||||
{ icon: '🌻', title: 'روغن ماساژ' },
|
||||
{ icon: '🥥', title: 'روغن های خوراکی' }
|
||||
],
|
||||
'چاشنی سلامت': [
|
||||
{ icon: '🥒', title: 'ترشی' },
|
||||
{ icon: '🥫', title: 'رب ها' },
|
||||
{ icon: '🍯', title: 'سس ها' },
|
||||
{ icon: '🥬', title: 'سبزیجات خشک' },
|
||||
{ icon: '🌶️', title: 'ادویه' },
|
||||
{ icon: '🍎', title: 'سرکه' },
|
||||
{ icon: '🫒', title: 'زیتون' }
|
||||
]
|
||||
}
|
||||
|
||||
const handleMainCategoryClick = (index: number) => {
|
||||
setSelectedMainCategory(index)
|
||||
setExpandedSubcategory(null)
|
||||
}
|
||||
|
||||
const handleSubcategoryClick = (subcategory: { title: string; hasChildren: boolean }) => {
|
||||
if (subcategory.hasChildren && thirdLevelCategories[subcategory.title as keyof typeof thirdLevelCategories]) {
|
||||
setExpandedSubcategory(expandedSubcategory === subcategory.title ? null : subcategory.title)
|
||||
} else {
|
||||
router.push(`/products/${subcategory.title}`)
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
const currentMainCategory = mainCategories[selectedMainCategory]
|
||||
const currentSubcategories = categorySubcategories[currentMainCategory.title as keyof typeof categorySubcategories] || []
|
||||
|
||||
return (
|
||||
<div className="fixed top-[120px] left-0 right-0 bottom-0 bg-white z-40 md:hidden">
|
||||
<div className="flex h-full overflow-hidden">
|
||||
{/* Left Column - Main Categories */}
|
||||
<div className="w-28 bg-gray-100 overflow-y-auto">
|
||||
<div className="p-2 space-y-2">
|
||||
{mainCategories.map((category, index) => (
|
||||
<div key={index}>
|
||||
<div
|
||||
className={`flex flex-col items-center text-center p-2 cursor-pointer rounded-lg transition-colors ${index === selectedMainCategory ? 'bg-white shadow-sm' : 'hover:bg-gray-200'
|
||||
}`}
|
||||
onClick={() => handleMainCategoryClick(index)}
|
||||
>
|
||||
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center mb-1 shadow-sm">
|
||||
<span className="text-lg">{category.icon}</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-800 leading-tight text-center">
|
||||
{category.title}
|
||||
</span>
|
||||
</div>
|
||||
{index < mainCategories.length - 1 && (
|
||||
<div className="border-b border-gray-200 mx-1" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - Subcategories */}
|
||||
<div className="flex-1 bg-white overflow-y-auto">
|
||||
<div className="px-4 py-4">
|
||||
{/* Special Categories */}
|
||||
<div className="space-y-1 mb-4">
|
||||
<div
|
||||
className="py-3 cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
onClick={() => {
|
||||
router.push('/products')
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
<span className="text-primary font-medium text-sm">همه محصولات سوپر مارکت ارگانیک</span>
|
||||
</div>
|
||||
<div
|
||||
className="py-3 cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
onClick={() => {
|
||||
router.push('/products/organic')
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
<span className="text-gray-800 font-medium text-sm">ارگانیک گواهیدار</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Subcategories List */}
|
||||
<div>
|
||||
{currentSubcategories.map((subcategory, index) => (
|
||||
<div key={index}>
|
||||
<div
|
||||
className="flex items-center justify-between py-3 cursor-pointer hover:bg-gray-50 transition-colors"
|
||||
onClick={() => handleSubcategoryClick(subcategory)}
|
||||
>
|
||||
<span className="text-gray-800 text-sm">{subcategory.title}</span>
|
||||
{subcategory.hasChildren && (
|
||||
<ArrowLeft2 size={16} color="#666" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Expanded third level categories */}
|
||||
{expandedSubcategory === subcategory.title && thirdLevelCategories[subcategory.title as keyof typeof thirdLevelCategories] && (
|
||||
<div className="mt-2 mb-4 p-4 bg-gray-50 rounded-lg">
|
||||
<div className="text-sm text-primary mb-3 font-medium">
|
||||
همه محصولات {subcategory.title}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{thirdLevelCategories[subcategory.title as keyof typeof thirdLevelCategories].map((item, itemIndex) => (
|
||||
<div
|
||||
key={itemIndex}
|
||||
className="flex flex-col items-center p-4 bg-white rounded-lg cursor-pointer hover:bg-gray-100 transition-colors"
|
||||
onClick={() => {
|
||||
router.push(`/products/${item.title}`)
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
<div className="w-14 h-14 bg-white rounded-full flex items-center justify-center mb-3 border border-gray-200">
|
||||
<span className="text-xl">{item.icon}</span>
|
||||
</div>
|
||||
<span className="text-sm text-gray-800 text-center leading-tight font-medium">
|
||||
{item.title}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{index < currentSubcategories.length - 1 && (
|
||||
<div className="border-b border-gray-100" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CategoryModal
|
||||
@@ -33,7 +33,7 @@ const Input: FC<Props> = (props: Props) => {
|
||||
const [search, setSearch] = useState<string>('')
|
||||
|
||||
const inputClass = clx(
|
||||
'w-full h-10 text-black block px-4 text-xs rounded-xl border border-[#D0D0D0]',
|
||||
'w-full h-10 sm:h-12 text-black block px-3 sm:px-4 text-xs sm:text-sm rounded-xl border border-[#D0D0D0]',
|
||||
props.readOnly && 'bg-gray-100 border-0 text-description',
|
||||
props.variant === 'search' && 'bg-[#EEF0F7] border-0 pr-10',
|
||||
props.className
|
||||
@@ -80,7 +80,7 @@ const Input: FC<Props> = (props: Props) => {
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<label className='text-sm text-black'>
|
||||
<label className='text-xs sm:text-sm text-black'>
|
||||
{props.label}
|
||||
</label>
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
import { FC, useState } from 'react'
|
||||
import { Profile, MessageText, ShoppingCart, HambergerMenu, Home } from 'iconsax-react'
|
||||
import CategoryModal from './CategoryModal'
|
||||
|
||||
const MobileBottomMenu: FC = () => {
|
||||
const [isCategoryModalOpen, setIsCategoryModalOpen] = useState(false)
|
||||
|
||||
const menuItems = [
|
||||
{ icon: Profile, label: 'پروفایل', active: false },
|
||||
{ icon: MessageText, label: 'چت', active: false },
|
||||
{ icon: ShoppingCart, label: 'سبد خرید', active: false },
|
||||
{ icon: HambergerMenu, label: 'منو', active: false, onClick: () => setIsCategoryModalOpen(!isCategoryModalOpen) },
|
||||
{ icon: Home, label: 'خانه', active: true },
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile Bottom Menu */}
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 z-50 md:hidden">
|
||||
<div className="flex items-center justify-around py-3 px-2">
|
||||
{menuItems.map((item, index) => {
|
||||
const IconComponent = item.icon
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={item.onClick}
|
||||
className={`flex flex-col items-center gap-1 p-2 min-w-0 flex-1 ${item.active ? 'text-purple-600' : 'text-gray-600'
|
||||
}`}
|
||||
>
|
||||
<IconComponent
|
||||
size={24}
|
||||
color={item.active ? '#9333ea' : '#6b7280'}
|
||||
/>
|
||||
<span className="text-xs text-center leading-tight">{item.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category Modal */}
|
||||
<CategoryModal
|
||||
isOpen={isCategoryModalOpen}
|
||||
onClose={() => setIsCategoryModalOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MobileBottomMenu
|
||||
@@ -27,8 +27,13 @@ const Modal: FC<Props> = ({ open, close, children, title, isHeader = false, clas
|
||||
|
||||
if (!open) return null
|
||||
|
||||
const isFullScreen = className?.includes('max-w-full') && className?.includes('h-full')
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div className={cn(
|
||||
"fixed inset-0 z-50",
|
||||
isFullScreen ? "flex" : "flex items-center justify-center"
|
||||
)}>
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
|
||||
@@ -37,13 +42,16 @@ const Modal: FC<Props> = ({ open, close, children, title, isHeader = false, clas
|
||||
|
||||
{/* Modal */}
|
||||
<div className={cn(
|
||||
"relative bg-white rounded-2xl shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-y-auto",
|
||||
"relative bg-white shadow-xl overflow-hidden",
|
||||
isFullScreen
|
||||
? "w-full h-full"
|
||||
: "rounded-xl sm:rounded-2xl max-w-sm sm:max-w-md w-full mx-2 sm:mx-4 max-h-[90vh]",
|
||||
className
|
||||
)}>
|
||||
{/* Header */}
|
||||
{isHeader && (
|
||||
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
||||
<h2 className="text-lg font-semibold text-gray-900">{title}</h2>
|
||||
<div className="flex items-center justify-between p-4 sm:p-6 border-b border-gray-200">
|
||||
<h2 className="text-base sm:text-lg font-semibold text-gray-900">{title}</h2>
|
||||
<button
|
||||
onClick={close}
|
||||
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
@@ -54,7 +62,7 @@ const Modal: FC<Props> = ({ open, close, children, title, isHeader = false, clas
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
<div className="p-4 sm:p-6">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,18 +11,18 @@ type Props = {
|
||||
|
||||
const ProductCard: FC<Props> = ({ item }) => {
|
||||
if (item) return (
|
||||
<div className='bg-white rounded-[40px] p-6 w-full max-w-[280px] mx-auto shadow'>
|
||||
<div className='bg-white rounded-[20px] sm:rounded-[40px] p-4 sm:p-6 w-full max-w-[240px] sm:max-w-[280px] mx-auto shadow'>
|
||||
<div className='flex justify-center'>
|
||||
<Image
|
||||
src={item.imagesUrl.cover}
|
||||
width={170}
|
||||
height={170}
|
||||
alt=''
|
||||
className='max-w-[150px] max-h-[150px] object-contain'
|
||||
className='max-w-[120px] sm:max-w-[150px] max-h-[120px] sm:max-h-[150px] object-contain'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h4 className='line-clamp-2 text-[#383E43] mt-6 text-sm text-center leading-5 h-12'>
|
||||
<h4 className='line-clamp-2 text-[#383E43] mt-4 sm:mt-6 text-xs sm:text-sm text-center leading-4 sm:leading-5 h-10 sm:h-12'>
|
||||
{item.title_fa}
|
||||
</h4>
|
||||
|
||||
@@ -32,10 +32,10 @@ const ProductCard: FC<Props> = ({ item }) => {
|
||||
<div className='size-2 rounded-full bg-red-500'></div>
|
||||
</div>
|
||||
|
||||
<div className='mt-5 text-sm'>
|
||||
<div className='mt-4 sm:mt-5 text-xs sm:text-sm'>
|
||||
{NumberFormat(item.default_variant.price.selling_price)} تومان
|
||||
</div>
|
||||
<div className='mt-1 text-sm text-[#7F7F7F] line-through'>
|
||||
<div className='mt-1 text-xs sm:text-sm text-[#7F7F7F] line-through'>
|
||||
{NumberFormat(item.default_variant.price.retailPrice)}
|
||||
</div>
|
||||
|
||||
@@ -45,18 +45,18 @@ const ProductCard: FC<Props> = ({ item }) => {
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className='bg-white rounded-[40px] p-6 w-full max-w-[280px] mx-auto shadow'>
|
||||
<div className='bg-white rounded-[20px] sm:rounded-[40px] p-4 sm:p-6 w-full max-w-[240px] sm:max-w-[280px] mx-auto shadow'>
|
||||
<div className='flex justify-center'>
|
||||
<Image
|
||||
src={`https://dkstatics-public.digikala.com/digikala-products/40026b18c2b053ac4a68c3288556dc899a77aecd_1727277566.jpg?x-oss-process=image/resize,m_lfit,h_300,w_300/format,webp/quality,q_80`}
|
||||
width={170}
|
||||
height={170}
|
||||
alt=''
|
||||
className='max-w-[150px] max-h-[150px] object-contain'
|
||||
className='max-w-[120px] sm:max-w-[150px] max-h-[120px] sm:max-h-[150px] object-contain'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h4 className='line-clamp-2 text-[#383E43] mt-6 text-sm text-center leading-5'>
|
||||
<h4 className='line-clamp-2 text-[#383E43] mt-4 sm:mt-6 text-xs sm:text-sm text-center leading-4 sm:leading-5'>
|
||||
شارژر دیواری 65 وات سامسونگ سه پورت Type-C مدل EP-T6530- اورجینال
|
||||
</h4>
|
||||
|
||||
@@ -66,10 +66,10 @@ const ProductCard: FC<Props> = ({ item }) => {
|
||||
<div className='size-2 rounded-full bg-red-500'></div>
|
||||
</div>
|
||||
|
||||
<div className='mt-5 text-sm'>
|
||||
<div className='mt-4 sm:mt-5 text-xs sm:text-sm'>
|
||||
260,000 تومان
|
||||
</div>
|
||||
<div className='mt-1 text-sm text-[#7F7F7F] line-through'>
|
||||
<div className='mt-1 text-xs sm:text-sm text-[#7F7F7F] line-through'>
|
||||
70,400,000
|
||||
</div>
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ const Select: FC<Props> = ({
|
||||
<div className="w-full relative" ref={dropdownRef}>
|
||||
<div
|
||||
className={cn(
|
||||
"w-full h-10 px-4 py-2 text-sm border border-[#D0D0D0] rounded-xl bg-white cursor-pointer flex items-center justify-between",
|
||||
"w-full h-10 sm:h-12 px-3 sm:px-4 py-2 text-xs sm:text-sm border border-[#D0D0D0] rounded-xl bg-white cursor-pointer flex items-center justify-between",
|
||||
disabled && "bg-gray-100 cursor-not-allowed",
|
||||
error_text && "border-red-500",
|
||||
className
|
||||
@@ -89,11 +89,11 @@ const Select: FC<Props> = ({
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-[#D0D0D0] rounded-xl shadow-lg z-50 max-h-60 overflow-y-auto">
|
||||
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-[#D0D0D0] rounded-xl shadow-lg z-50 max-h-48 sm:max-h-60 overflow-y-auto">
|
||||
{items.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="px-4 py-2 hover:bg-gray-50 cursor-pointer text-sm"
|
||||
className="px-3 sm:px-4 py-2 hover:bg-gray-50 cursor-pointer text-xs sm:text-sm"
|
||||
onClick={() => handleSelect(item)}
|
||||
>
|
||||
{item.label}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ interface LayoutProps {
|
||||
|
||||
const Layout: FC<LayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<div className="min-h-screen flex flex-col max-w-full overflow-x-hidden">
|
||||
<Header />
|
||||
<main className="flex-1">
|
||||
{children}
|
||||
|
||||
+32
-30
@@ -6,14 +6,14 @@ import { FC } from 'react'
|
||||
|
||||
const Footer: FC = () => {
|
||||
return (
|
||||
<div className='mt-24 bg-[#F5F5F5] p-20 flex gap-20'>
|
||||
<div className='bg-[#F2F2F2] max-w-[450px] p-7 rounded-2xl border border-border'>
|
||||
<div className='mt-12 sm:mt-16 md:mt-24 bg-[#F5F5F5] p-4 sm:p-8 md:p-20 flex flex-col lg:flex-row gap-8 sm:gap-12 md:gap-20'>
|
||||
<div className='bg-[#F2F2F2] w-full lg:max-w-[450px] p-4 sm:p-6 md:p-7 rounded-2xl border border-border'>
|
||||
<div>LOGO</div>
|
||||
<div className='mt-6 text-sm text-[#383E43] '>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده
|
||||
</div>
|
||||
|
||||
<div className='mt-20 text-[#383E43] text-sm'>
|
||||
<div className='mt-8 sm:mt-12 md:mt-20 text-[#383E43] text-sm'>
|
||||
<div className='flex gap-3'>
|
||||
<Call color='#383E43' size={20} />
|
||||
<div>تلفن: 67341</div>
|
||||
@@ -33,49 +33,51 @@ const Footer: FC = () => {
|
||||
|
||||
</div>
|
||||
|
||||
<div className='mt-14 flex justify-end'>
|
||||
<div className='mt-8 sm:mt-10 md:mt-14 flex justify-end'>
|
||||
<div className='size-10 bg-amber-200'></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-1'>
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-[72px] flex items-center justify-between px-6'>
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-auto sm:h-[72px] flex flex-col sm:flex-row items-start sm:items-center justify-between p-4 sm:px-6 sm:py-0 gap-3 sm:gap-0'>
|
||||
<div className='text-[#383E43]'>
|
||||
دانلود اپلیکیشن
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='flex gap-2 flex-wrap'>
|
||||
<Image
|
||||
width={132}
|
||||
height={48}
|
||||
alt='download from app store'
|
||||
src='/images/appstore.png'
|
||||
className='w-24 sm:w-[132px] h-auto'
|
||||
/>
|
||||
<Image
|
||||
width={142}
|
||||
height={48}
|
||||
alt='download from google play'
|
||||
src='/images/googleplay.png'
|
||||
className='w-24 sm:w-[142px] h-auto'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-[72px] flex items-center justify-between px-6 mt-6'>
|
||||
<div className='text-[#383E43]'>
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-auto sm:h-[72px] flex flex-col sm:flex-row items-start sm:items-center justify-between p-4 sm:px-6 sm:py-0 gap-3 sm:gap-0 mt-4 sm:mt-6'>
|
||||
<div className='text-[#383E43] text-sm sm:text-base'>
|
||||
با ثبت ایمیل از آخرین تخفیف ها با خبر شوید
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='bg-[#E5E5E5] h-12 flex gap-3 items-center px-3 rounded-[8px]'>
|
||||
<div className='flex gap-2 w-full sm:w-auto'>
|
||||
<div className='bg-[#E5E5E5] h-12 flex gap-3 items-center px-3 rounded-[8px] flex-1 sm:flex-none'>
|
||||
<Sms size={20} color='#383E43' />
|
||||
<Separator className='bg-[#C9C9C9]' style={{ height: 15 }} orientation='vertical' />
|
||||
<input placeholder='آدرس ایمیل' className='bg-transparent outline-none text-sm' />
|
||||
<input placeholder='آدرس ایمیل' className='bg-transparent outline-none text-sm w-full' />
|
||||
</div>
|
||||
<Button className='h-12 w-[72px]'>
|
||||
<Button className='h-12 w-[72px] flex-shrink-0'>
|
||||
ثبت
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-[72px] flex items-center justify-between px-6 mt-6'>
|
||||
<div className='text-[#383E43]'>
|
||||
<div className='w-full rounded-xl bg-[#F2F2F2] border border-border h-auto sm:h-[72px] flex flex-col sm:flex-row items-start sm:items-center justify-between p-4 sm:px-6 sm:py-0 gap-3 sm:gap-0 mt-4 sm:mt-6'>
|
||||
<div className='text-[#383E43] text-sm sm:text-base'>
|
||||
ما را در شبکه های اجتماعی دنبال کنید
|
||||
</div>
|
||||
|
||||
@@ -92,29 +94,29 @@ const Footer: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-20 flex justify-between'>
|
||||
<div className='mt-8 sm:mt-12 md:mt-20 flex flex-col sm:flex-row justify-between gap-6 sm:gap-0'>
|
||||
<div>
|
||||
<div>همراه با سندس کالا</div>
|
||||
<ul className='flex mt-6 flex-col gap-4 font-light'>
|
||||
<li className='text-[#878787]'>تماس با ما</li>
|
||||
<li className='text-[#878787]'>درباره سندس کالا</li>
|
||||
<li className='text-[#878787]'>فروش در سندس کالا</li>
|
||||
<div className='text-sm sm:text-base font-medium'>همراه با سندس کالا</div>
|
||||
<ul className='flex mt-4 sm:mt-6 flex-col gap-3 sm:gap-4 font-light'>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>تماس با ما</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>درباره سندس کالا</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>فروش در سندس کالا</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<div>راهنمای خرید از سندس کالا</div>
|
||||
<ul className='flex mt-6 flex-col gap-4 font-light'>
|
||||
<li className='text-[#878787]'>نحوه ثبت سفارش</li>
|
||||
<li className='text-[#878787]'>قوانین مرجوعی کالا</li>
|
||||
<li className='text-[#878787]'>پاسخ به پرسش ها متداول</li>
|
||||
<div className='text-sm sm:text-base font-medium'>راهنمای خرید از سندس کالا</div>
|
||||
<ul className='flex mt-4 sm:mt-6 flex-col gap-3 sm:gap-4 font-light'>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>نحوه ثبت سفارش</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>قوانین مرجوعی کالا</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>پاسخ به پرسش ها متداول</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<div>خدمات مشتریان</div>
|
||||
<ul className='flex mt-6 flex-col gap-4 font-light'>
|
||||
<li className='text-[#878787]'>حساب کاربری من</li>
|
||||
<li className='text-[#878787]'>سبد خرید</li>
|
||||
<li className='text-[#878787]'>حریم حصوصی</li>
|
||||
<div className='text-sm sm:text-base font-medium'>خدمات مشتریان</div>
|
||||
<ul className='flex mt-4 sm:mt-6 flex-col gap-3 sm:gap-4 font-light'>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>حساب کاربری من</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>سبد خرید</li>
|
||||
<li className='text-[#878787] text-sm sm:text-base'>حریم حصوصی</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+76
-14
@@ -1,47 +1,109 @@
|
||||
'use client'
|
||||
import Input from '@/components/Input'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { DocumentText, Home, Profile, ShoppingCart } from 'iconsax-react'
|
||||
import { FC, Fragment } from 'react'
|
||||
import { DocumentText, Home, Profile, ShoppingCart, HambergerMenu } from 'iconsax-react'
|
||||
import { FC, Fragment, useState } from 'react'
|
||||
import Menu from './components/Menu'
|
||||
|
||||
const Header: FC = () => {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='fixed top-0 bg-white w-full z-[9998] pb-4'>
|
||||
<div className='flex items-center justify-between px-6 py-[18px]'>
|
||||
<div className='flex gap-[30px] items-center'>
|
||||
<div>LOGO</div>
|
||||
<Input className='w-[500px]' variant='search' placeholder='جستجو' />
|
||||
<div className='fixed top-0 bg-white w-full z-[9998] border-b border-gray-100 max-w-full overflow-hidden'>
|
||||
{/* بخش بالایی هدر */}
|
||||
<div className='flex items-center justify-between px-3 sm:px-4 md:px-6 py-3 sm:py-4'>
|
||||
{/* منوی همبرگری موبایل */}
|
||||
<div className='md:hidden'>
|
||||
<button
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
className='p-2 rounded-lg hover:bg-gray-50'
|
||||
>
|
||||
<HambergerMenu size={24} color='#333333' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-6'>
|
||||
<div className='h-10 border border-border py-3 px-4 rounded-[12px] flex items-center gap-2.5'>
|
||||
<div className='flex gap-2 sm:gap-4 md:gap-[30px] items-center flex-1 md:flex-none'>
|
||||
<div className='text-base sm:text-lg font-bold text-purple-600'>LOGO</div>
|
||||
<Input
|
||||
className='hidden md:block w-[300px] lg:w-[500px]'
|
||||
variant='search'
|
||||
placeholder='جستجو'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-1 sm:gap-2 md:gap-6'>
|
||||
<div className='hidden md:flex h-10 border border-border py-3 px-4 rounded-[12px] items-center gap-2.5'>
|
||||
<Profile size={20} color='#333333' />
|
||||
<div className='text-sm text-[#333333]'>حساب کاربری</div>
|
||||
</div>
|
||||
|
||||
<Separator style={{ height: 25 }} orientation='vertical' />
|
||||
<div className='md:hidden'>
|
||||
<Profile size={24} color='#333333' />
|
||||
</div>
|
||||
|
||||
<Separator className='hidden md:block' style={{ height: 25 }} orientation='vertical' />
|
||||
|
||||
<div className='h-10 p-3 rounded-[12px] border border-border flex items-center gap-2.5'>
|
||||
<ShoppingCart size={20} color='#333333' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-7 text-sm px-6 flex items-center gap-7'>
|
||||
|
||||
{/* نوار جستجو موبایل */}
|
||||
<div className='md:hidden px-3 sm:px-4 pb-3 sm:pb-4'>
|
||||
<Input
|
||||
className='w-full'
|
||||
variant='search'
|
||||
placeholder='جستجو'
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* بخش پایینی هدر - فقط دسکتاپ */}
|
||||
<div className='hidden md:flex mt-2 text-sm px-6 pb-4 items-center gap-7'>
|
||||
<Menu />
|
||||
<Separator style={{ height: 20 }} orientation='vertical' />
|
||||
|
||||
<div className='flex items-center gap-2.5'>
|
||||
<div className='flex items-center gap-2.5 cursor-pointer hover:text-purple-600 transition-colors'>
|
||||
<Home size={20} color='#333333' />
|
||||
<div className='text-[#333333]'>صفحه نخست</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-2.5'>
|
||||
<div className='flex items-center gap-2.5 cursor-pointer hover:text-purple-600 transition-colors'>
|
||||
<DocumentText size={20} color='#333333' />
|
||||
<div className='text-[#333333]'>مجله</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* منوی موبایل */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className='md:hidden bg-white border-t border-gray-100'>
|
||||
<div className='px-4 py-4 space-y-4'>
|
||||
<div className='flex items-center gap-3 py-3 border-b border-gray-100'>
|
||||
<Profile size={20} color='#333333' />
|
||||
<span className='text-sm text-gray-800'>حساب کاربری</span>
|
||||
</div>
|
||||
|
||||
<div className='space-y-3'>
|
||||
<div className='flex items-center gap-3 py-2 cursor-pointer hover:bg-gray-50 rounded-lg px-2'>
|
||||
<Home size={20} color='#333333' />
|
||||
<span className='text-sm text-gray-800'>صفحه نخست</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-3 py-2 cursor-pointer hover:bg-gray-50 rounded-lg px-2'>
|
||||
<DocumentText size={20} color='#333333' />
|
||||
<span className='text-sm text-gray-800'>مجله</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='pt-4 border-t border-gray-100'>
|
||||
<Menu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='h-[140px]'></div>
|
||||
|
||||
{/* فضای خالی برای جبران ارتفاع fixed header */}
|
||||
<div className={`${isMobileMenuOpen ? 'h-[280px] sm:h-[260px] md:h-[140px]' : 'h-[100px] sm:h-[120px] md:h-[140px]'}`}></div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ const Menu: FC = () => {
|
||||
|
||||
const renderCategoryLevel = (categories: Category[], level: number) => {
|
||||
return (
|
||||
<div className='w-64 border-l border-gray-200 last:border-l-0 flex flex-col min-h-0'>
|
||||
<div className='w-48 md:w-64 border-l border-gray-200 last:border-l-0 flex flex-col min-h-0'>
|
||||
<div className='p-2 overflow-y-auto max-h-[calc(100vh-200px)]'>
|
||||
{categories.map((category) => (
|
||||
<div
|
||||
@@ -80,12 +80,13 @@ const Menu: FC = () => {
|
||||
onMouseEnter={() => setIsOpen(true)}
|
||||
>
|
||||
<HambergerMenu size={24} color='#333333' />
|
||||
<div className='text-[#333333]'>دسته بندی کالاها</div>
|
||||
<div className='text-[#333333] hidden md:block'>دسته بندی کالاها</div>
|
||||
<div className='text-[#333333] md:hidden text-xs'>دستهها</div>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
className='absolute top-full right-0 mt-2 bg-white rounded-xl shadow-lg border border-gray-200 z-[9999] flex max-w-[90vw] max-h-screen overflow-hidden'
|
||||
className='absolute top-full right-0 mt-2 bg-white rounded-xl shadow-lg border border-gray-200 z-[9999] flex max-w-[90vw] md:max-w-none max-h-screen overflow-hidden'
|
||||
onMouseLeave={() => {
|
||||
setIsOpen(false)
|
||||
setHoveredPath([])
|
||||
@@ -93,7 +94,7 @@ const Menu: FC = () => {
|
||||
>
|
||||
{/* دستههای اصلی (سطح اول) */}
|
||||
{isLoading ? (
|
||||
<div className='w-64 border-l border-gray-200 flex items-center justify-center h-40'>
|
||||
<div className='w-48 md:w-64 border-l border-gray-200 flex items-center justify-center h-40'>
|
||||
<div className='text-gray-500'>در حال بارگذاری...</div>
|
||||
</div>
|
||||
) : parentCategories.length > 0 ? (
|
||||
@@ -113,7 +114,7 @@ const Menu: FC = () => {
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<div className='w-64 border-l border-gray-200 flex items-center justify-center h-40'>
|
||||
<div className='w-48 md:w-64 border-l border-gray-200 flex items-center justify-center h-40'>
|
||||
<div className='text-gray-500'>دستهبندیای یافت نشد</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user