Merge remote-tracking branch 'refs/remotes/origin/master'
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-06-14 10:26:04 +03:30
8 changed files with 67 additions and 51 deletions
+5 -16
View File
@@ -3,7 +3,6 @@ import React from 'react'
import CategoryItem from './CategoryItem' import CategoryItem from './CategoryItem'
import { useGetCategories } from '@/share/hooks/useShareData' import { useGetCategories } from '@/share/hooks/useShareData'
import { Category } from '@/share/types/SharedTypes' import { Category } from '@/share/types/SharedTypes'
import { Swiper, SwiperSlide } from 'swiper/react'
const Categories = () => { const Categories = () => {
@@ -11,24 +10,14 @@ const Categories = () => {
const parentCategories = data?.results?.data?.filter(cat => !cat.parent) || [] const parentCategories = data?.results?.data?.filter(cat => !cat.parent) || []
return ( return (
<div className='w-full flex justify-center'> <div className='w-full overflow-x-auto overflow-y-hidden'>
<Swiper <div className='flex justify-center gap-4 sm:gap-6 md:gap-6 w-full min-w-max px-4 sm:px-8 md:px-20'>
spaceBetween={16}
slidesPerView={'auto'}
centeredSlides={true}
className='h-full'
breakpoints={{
640: {
spaceBetween: 24,
},
}}
>
{parentCategories?.map((item: Category) => ( {parentCategories?.map((item: Category) => (
<SwiperSlide key={item._id} className='!w-auto'> <div key={item._id} className='flex-shrink-0'>
<CategoryItem image={item.imageUrl} title={item.title_fa} categoryUrl={item.title_en} /> <CategoryItem image={item.imageUrl} title={item.title_fa} categoryUrl={item.title_en} />
</SwiperSlide> </div>
))} ))}
</Swiper> </div>
</div> </div>
) )
} }
+4 -4
View File
@@ -18,17 +18,17 @@ export const dynamic = 'force-dynamic'
const HomePage: NextPage = () => { const HomePage: NextPage = () => {
return ( return (
<div> <div className='w-full'>
<Carousel /> <Carousel />
<div className='mt-12 sm:mt-16 md:mt-24 px-4 sm:px-8 md:px-20'> <div className='mt-12 sm:mt-16 md:mt-24 px-4 sm:px-8 md:px-20'>
<HotOffer /> <HotOffer />
<div className='mt-12 sm:mt-16 md:mt-24'> </div>
<div className='mt-12 sm:mt-16 md:mt-24 w-full'>
<Categories /> <Categories />
</div> </div>
<div className='mt-12 sm:mt-16 md:mt-24'> <div className='mt-12 sm:mt-16 md:mt-24 px-4 sm:px-8 md:px-20'>
<Banners3 /> <Banners3 />
</div> </div>
</div>
<div className='mt-12 sm:mt-16 md:mt-24 bg-[#FAF8F6] p-4 sm:p-8 md:p-20'> <div className='mt-12 sm:mt-16 md:mt-24 bg-[#FAF8F6] p-4 sm:p-8 md:p-20'>
<NewestProducts /> <NewestProducts />
+7 -3
View File
@@ -4,16 +4,20 @@ import { useSharedStore } from '@/share/store/sharedStore'
import OnlineCart from './OnlineCart' import OnlineCart from './OnlineCart'
import OfflineCart from './OfflineCart' import OfflineCart from './OfflineCart'
const Cart = () => { interface CartProps {
stock: number
}
const Cart = ({ stock }: CartProps) => {
const { variantId } = useProductStore() const { variantId } = useProductStore()
const { isLogin } = useSharedStore() const { isLogin } = useSharedStore()
return ( return (
<div> <div>
{isLogin ? ( {isLogin ? (
<OnlineCart variantId={variantId} /> <OnlineCart variantId={variantId} stock={stock} />
) : ( ) : (
<OfflineCart variantId={variantId} /> <OfflineCart variantId={variantId} stock={stock} />
)} )}
</div> </div>
) )
+21 -6
View File
@@ -8,9 +8,10 @@ import { useLocalCart } from '../hooks/useLocalCart'
interface OfflineCartProps { interface OfflineCartProps {
variantId: string variantId: string
stock: number
} }
const OfflineCart: React.FC<OfflineCartProps> = ({ variantId }) => { const OfflineCart: React.FC<OfflineCartProps> = ({ variantId, stock }) => {
const { id } = useParams() const { id } = useParams()
// Local cart hooks for non-logged in users // Local cart hooks for non-logged in users
@@ -24,7 +25,11 @@ const OfflineCart: React.FC<OfflineCartProps> = ({ variantId }) => {
const [isInCart, setIsInCart] = useState<boolean>(false) const [isInCart, setIsInCart] = useState<boolean>(false)
const [quantity, setQuantity] = useState<number>(1) const [quantity, setQuantity] = useState<number>(1)
const isOutOfStock = stock <= 0
const handleAddToCart = () => { const handleAddToCart = () => {
if (isOutOfStock) return
addToLocalCart(Number(id), variantId, 1) addToLocalCart(Number(id), variantId, 1)
setIsInCart(true) setIsInCart(true)
setQuantity(1) setQuantity(1)
@@ -48,8 +53,9 @@ const OfflineCart: React.FC<OfflineCartProps> = ({ variantId }) => {
return return
} }
setQuantity(number) const clampedQuantity = Math.min(number, stock)
updateLocalCartItem(Number(id), variantId, number) setQuantity(clampedQuantity)
updateLocalCartItem(Number(id), variantId, clampedQuantity)
} }
const handleEmptyCart = () => { const handleEmptyCart = () => {
@@ -59,21 +65,30 @@ const OfflineCart: React.FC<OfflineCartProps> = ({ variantId }) => {
setQuantity(0) setQuantity(0)
} }
if (isOutOfStock) {
return (
<Button className="w-full mt-8" disabled>
ناموجود
</Button>
)
}
return ( return (
<div> <div>
{ {
!isInCart ? !isInCart ?
<Button className='w-full mt-8' onClick={handleAddToCart}> <Button className="w-full mt-8" onClick={handleAddToCart}>
افزودن به سبد خرید افزودن به سبد خرید
</Button> </Button>
: :
<div className={"flex items-center gap-2 sm:gap-4 mt-8"}> <div className="flex items-center gap-2 sm:gap-4 mt-8">
<div className="h-10 sm:h-12 rounded-lg sm:rounded-xl border border-border bg-white px-2 sm:px-4 flex-1 flex items-center justify-between"> <div className="h-10 sm:h-12 rounded-lg sm:rounded-xl border border-border bg-white px-2 sm:px-4 flex-1 flex items-center justify-between">
<button <button
type="button" type="button"
onClick={() => handleQuantityChange(quantity + 1)} onClick={() => handleQuantityChange(quantity + 1)}
className="p-1" className="disabled:opacity-40 disabled:cursor-not-allowed p-1"
aria-label="increase quantity" aria-label="increase quantity"
disabled={quantity >= stock}
> >
<Add size={18} color={PRIMARY_COLOR} className="sm:w-5 sm:h-5" /> <Add size={18} color={PRIMARY_COLOR} className="sm:w-5 sm:h-5" />
</button> </button>
+20 -12
View File
@@ -9,9 +9,10 @@ import { extractErrorMessage } from '@/helpers/errorUtils'
interface OnlineCartProps { interface OnlineCartProps {
variantId: string variantId: string
stock: number
} }
const OnlineCart: React.FC<OnlineCartProps> = ({ variantId }) => { const OnlineCart: React.FC<OnlineCartProps> = ({ variantId, stock }) => {
const { id } = useParams() const { id } = useParams()
// API hooks for logged in users // API hooks for logged in users
@@ -23,7 +24,11 @@ const OnlineCart: React.FC<OnlineCartProps> = ({ variantId }) => {
const [isInCart, setIsInCart] = useState<boolean>(false) const [isInCart, setIsInCart] = useState<boolean>(false)
const [quantity, setQuantity] = useState<number>(1) const [quantity, setQuantity] = useState<number>(1)
const isOutOfStock = stock <= 0
const handleAddToCart = () => { const handleAddToCart = () => {
if (isOutOfStock) return
addToCart({ addToCart({
productId: Number(id), productId: Number(id),
variantId: variantId, variantId: variantId,
@@ -40,14 +45,8 @@ const OnlineCart: React.FC<OnlineCartProps> = ({ variantId }) => {
} }
useEffect(() => { useEffect(() => {
// Check API cart for logged in users
console.log('cart', cart);
console.log('variantId', variantId);
const item = cart?.results?.cart?.items?.find(item => item.variant?._id === variantId) const item = cart?.results?.cart?.items?.find(item => item.variant?._id === variantId)
console.log('item', item);
if (item) { if (item) {
setIsInCart(true) setIsInCart(true)
setQuantity(item.quantity) setQuantity(item.quantity)
@@ -62,13 +61,14 @@ const OnlineCart: React.FC<OnlineCartProps> = ({ variantId }) => {
return return
} }
const clampedQuantity = Math.min(number, stock)
const oldQuantity = quantity const oldQuantity = quantity
setQuantity(number) setQuantity(clampedQuantity)
updateCart({ updateCart({
productId: Number(id), productId: Number(id),
variantId: variantId, variantId: variantId,
quantity: number, quantity: clampedQuantity,
}, { }, {
onError: (error: Error) => { onError: (error: Error) => {
toast(extractErrorMessage(error), 'error') toast(extractErrorMessage(error), 'error')
@@ -93,22 +93,30 @@ const OnlineCart: React.FC<OnlineCartProps> = ({ variantId }) => {
}) })
} }
if (isOutOfStock) {
return (
<Button className="w-full mt-8" disabled>
ناموجود
</Button>
)
}
return ( return (
<div> <div>
{ {
!isInCart && !isPending ? !isInCart && !isPending ?
<Button isLoading={isAddingToCart} className='w-full mt-8' onClick={handleAddToCart}> <Button isLoading={isAddingToCart} className="w-full mt-8" onClick={handleAddToCart}>
افزودن به سبد خرید افزودن به سبد خرید
</Button> </Button>
: :
<div className={"flex items-center gap-2 sm:gap-4 mt-8"}> <div className="flex items-center gap-2 sm:gap-4 mt-8">
<div className="h-10 sm:h-12 rounded-lg sm:rounded-xl border border-border bg-white px-2 sm:px-4 flex-1 flex items-center justify-between"> <div className="h-10 sm:h-12 rounded-lg sm:rounded-xl border border-border bg-white px-2 sm:px-4 flex-1 flex items-center justify-between">
<button <button
type="button" type="button"
onClick={() => handleQuantityChange(quantity + 1)} onClick={() => handleQuantityChange(quantity + 1)}
className="disabled:opacity-40 disabled:cursor-not-allowed p-1" className="disabled:opacity-40 disabled:cursor-not-allowed p-1"
aria-label="increase quantity" aria-label="increase quantity"
disabled={isAddingToCart || isUpdatingCart} disabled={isAddingToCart || isUpdatingCart || quantity >= stock}
> >
<Add size={18} color={PRIMARY_COLOR} className="sm:w-5 sm:h-5" /> <Add size={18} color={PRIMARY_COLOR} className="sm:w-5 sm:h-5" />
</button> </button>
@@ -1,6 +1,6 @@
'use client' 'use client'
import { Separator } from '@/components/ui/separator' import { Separator } from '@/components/ui/separator'
import { BoxTick, I3DRotate } from 'iconsax-react' import { I3DRotate } from 'iconsax-react'
import React from 'react' import React from 'react'
import { Product } from '@/types/product.types' import { Product } from '@/types/product.types'
import Image from 'next/image' import Image from 'next/image'
@@ -20,10 +20,10 @@ const ShopInformation = ({ product }: ShopInformationProps) => {
? product.variants.find((variant) => variant._id === variantId) || product.default_variant ? product.variants.find((variant) => variant._id === variantId) || product.default_variant
: product.default_variant : product.default_variant
const shop = selectedVariant.shop // const shop = selectedVariant.shop
const price = selectedVariant.price const price = selectedVariant.price
const warranty = selectedVariant.warranty const warranty = selectedVariant.warranty
const postingTime = selectedVariant.postingTime // const postingTime = selectedVariant.postingTime
const formatPrice = (price: number) => { const formatPrice = (price: number) => {
return price.toLocaleString('fa-IR') return price.toLocaleString('fa-IR')
@@ -107,7 +107,7 @@ const ShopInformation = ({ product }: ShopInformationProps) => {
</div> </div>
</div> </div>
<Cart /> <Cart stock={selectedVariant.stock} />
{/* <Button {/* <Button
onClick={handleChatWithSeller} onClick={handleChatWithSeller}
className='w-full mt-3 bg-[#323232] hover:bg-[#424242] flex items-center justify-center gap-2' className='w-full mt-3 bg-[#323232] hover:bg-[#424242] flex items-center justify-center gap-2'
+1 -1
View File
@@ -35,7 +35,7 @@ const SingleProduct = ({ id }: SingleProductProps) => {
notFound() notFound()
} }
const { product, categoryPath, reviews, stats } = productData.results const { product, categoryPath, stats } = productData.results
return ( return (
<div className='mt-14 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20'> <div className='mt-14 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20'>
+2 -2
View File
@@ -1,7 +1,7 @@
export const TOKEN_NAME = "sh_token"; export const TOKEN_NAME = "sh_token";
export const REFRESH_TOKEN_NAME = "sh_refresh_token"; export const REFRESH_TOKEN_NAME = "sh_refresh_token";
// export const BASE_URL = "https://shop-api.dev.danakcorp.com"; export const BASE_URL = "https://api.fajrtabloshop.com";
export const BASE_URL = "http://localhost:4000"; // export const BASE_URL = "http://192.168.99.235:4000";
export const PRIMARY_COLOR = "#DA2129"; export const PRIMARY_COLOR = "#DA2129";