base logig pages cart shipping and payment
This commit is contained in:
@@ -5,14 +5,49 @@ import { clx } from '@/helpers/utils';
|
|||||||
import { ArchiveTick, Shop, TruckFast } from 'iconsax-react'
|
import { ArchiveTick, Shop, TruckFast } from 'iconsax-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { CartItemType } from '../types/Types'
|
||||||
|
import { useUpdateCart, useRemoveFromCart } from '../hooks/useCartData'
|
||||||
|
import { toast } from '@/components/Toast'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
|
item: CartItemType
|
||||||
}
|
}
|
||||||
|
|
||||||
const CartItem: FC<Props> = (props) => {
|
const CartItem: FC<Props> = (props) => {
|
||||||
|
|
||||||
const { noBorder } = props
|
const { noBorder, item } = props
|
||||||
|
const updateCartMutation = useUpdateCart()
|
||||||
|
const removeFromCartMutation = useRemoveFromCart()
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleQuantityChange = async (newQuantity: number) => {
|
||||||
|
try {
|
||||||
|
await updateCartMutation.mutateAsync({
|
||||||
|
productId: item.product._id,
|
||||||
|
variantId: item.variant._id,
|
||||||
|
quantity: newQuantity
|
||||||
|
})
|
||||||
|
toast('تعداد بهروزرسانی شد', 'success')
|
||||||
|
} catch {
|
||||||
|
toast('خطا در بهروزرسانی تعداد', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRemove = async () => {
|
||||||
|
try {
|
||||||
|
await removeFromCartMutation.mutateAsync({
|
||||||
|
productId: item.product._id,
|
||||||
|
variantId: item.variant._id
|
||||||
|
})
|
||||||
|
toast('محصول از سبد خرید حذف شد', 'success')
|
||||||
|
} catch {
|
||||||
|
toast('خطا در حذف محصول', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clx(
|
<div className={clx(
|
||||||
@@ -21,8 +56,8 @@ const CartItem: FC<Props> = (props) => {
|
|||||||
)}>
|
)}>
|
||||||
<div className="flex-shrink-0 flex justify-center sm:justify-start">
|
<div className="flex-shrink-0 flex justify-center sm:justify-start">
|
||||||
<Image
|
<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'}
|
src={item.product.imagesUrl.cover}
|
||||||
alt=''
|
alt={item.product.title_fa}
|
||||||
width={200}
|
width={200}
|
||||||
height={200}
|
height={200}
|
||||||
className='w-24 h-24 sm:w-32 sm:h-32 lg:max-w-[150px] lg:max-h-[150px] object-contain'
|
className='w-24 h-24 sm:w-32 sm:h-32 lg:max-w-[150px] lg:max-h-[150px] object-contain'
|
||||||
@@ -32,33 +67,35 @@ const CartItem: FC<Props> = (props) => {
|
|||||||
<div className='flex-1 min-w-0'>
|
<div className='flex-1 min-w-0'>
|
||||||
<div className='flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2 sm:gap-4'>
|
<div className='flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2 sm:gap-4'>
|
||||||
<div className='text-sm sm:text-base font-medium leading-relaxed sm:max-w-[250px] sm:truncate'>
|
<div className='text-sm sm:text-base font-medium leading-relaxed sm:max-w-[250px] sm:truncate'>
|
||||||
گوشی موبایل سامسونگ مدل Galaxy S24 Ultra دو سیم کارت ظرفیت 1 ترابایت و رم 12 گیگابایت - ویتنام
|
{item.product.title_fa}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl font-semibold text-primary">
|
<div className="text-lg sm:text-xl font-semibold text-primary">
|
||||||
70,400,000 <span className='text-xs sm:text-sm'>تومان</span>
|
{item.variant.price.selling_price.toLocaleString()} <span className='text-xs sm:text-sm'>تومان</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='font-light text-[#333333] mt-4 sm:mt-6 lg:mt-14'>
|
<div className='font-light text-[#333333] mt-4 sm:mt-6 lg:mt-14'>
|
||||||
<div className='flex items-center gap-2.5 mb-3 sm:mb-4'>
|
{item.variant.size && (
|
||||||
<div className='w-5 sm:w-6 flex-shrink-0'>
|
<div className='flex items-center gap-2.5 mb-3 sm:mb-4'>
|
||||||
<div className='size-3 sm:size-[14px] rounded-full bg-black'></div>
|
<div className='w-5 sm:w-6 flex-shrink-0'>
|
||||||
|
<div className='size-3 sm:size-[14px] rounded-full bg-black'></div>
|
||||||
|
</div>
|
||||||
|
<div className='text-xs sm:text-sm'>{item.variant.size.value}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs sm:text-sm'>مشکی</div>
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex items-center gap-2.5 mb-3 sm:mb-4'>
|
<div className='flex items-center gap-2.5 mb-3 sm:mb-4'>
|
||||||
<div className='w-5 sm:w-6 flex-shrink-0'>
|
<div className='w-5 sm:w-6 flex-shrink-0'>
|
||||||
<Shop size={16} color='#333333' className="sm:w-5 sm:h-5" />
|
<Shop size={16} color='#333333' className="sm:w-5 sm:h-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs sm:text-sm'>فروشنده فروشگاه سندس</div>
|
<div className='text-xs sm:text-sm'>فروشنده {item.variant.shop.shopName}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center gap-2.5 mb-4 sm:mb-6'>
|
<div className='flex items-center gap-2.5 mb-4 sm:mb-6'>
|
||||||
<div className='w-5 sm:w-6 flex-shrink-0'>
|
<div className='w-5 sm:w-6 flex-shrink-0'>
|
||||||
<ArchiveTick size={16} color='#333333' className="sm:w-5 sm:h-5" />
|
<ArchiveTick size={16} color='#333333' className="sm:w-5 sm:h-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs sm:text-sm'>گارانتی اصالت و سلامت فیزیکی کالا</div>
|
<div className='text-xs sm:text-sm'>گارانتی {item.variant.warranty.name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4'>
|
<div className='flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4'>
|
||||||
@@ -66,14 +103,18 @@ const CartItem: FC<Props> = (props) => {
|
|||||||
<div className='w-5 sm:w-6 flex-shrink-0'>
|
<div className='w-5 sm:w-6 flex-shrink-0'>
|
||||||
<TruckFast size={16} color='blue' className="sm:w-5 sm:h-5" />
|
<TruckFast size={16} color='blue' className="sm:w-5 sm:h-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs sm:text-sm'>ارسال از سندس حداکثر پس از 3 روز کاری</div>
|
<div className='text-xs sm:text-sm'>
|
||||||
|
ارسال از {item.variant.shop.shopName} حداکثر پس از {item.variant.shipmentMethod[0]?.deliveryTime || 3} روز کاری
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center sm:justify-end">
|
<div className="flex justify-center sm:justify-end">
|
||||||
<CartControls
|
<CartControls
|
||||||
onChange={() => null}
|
onChange={handleQuantityChange}
|
||||||
onRemove={() => null}
|
onRemove={handleRemove}
|
||||||
quantity={1}
|
quantity={item.quantity}
|
||||||
|
max={item.variant.stock}
|
||||||
|
className={updateCartMutation.isPending || removeFromCartMutation.isPending ? 'opacity-50 pointer-events-none' : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,78 @@
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { FC } from 'react'
|
import Input from '@/components/Input'
|
||||||
|
import { FC, useState } from 'react'
|
||||||
|
|
||||||
const DiscountCard: FC = () => {
|
type DiscountCardProps = {
|
||||||
return (
|
discountCode: string
|
||||||
<div className='h-16 border border-border rounded-2xl flex justify-between mt-5 items-center px-6'>
|
setDiscountCode: (code: string) => void
|
||||||
<div>
|
onSubmit: () => void
|
||||||
کد تخفیف دارید؟
|
isLoading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const DiscountCard: FC<DiscountCardProps> = ({
|
||||||
|
discountCode,
|
||||||
|
setDiscountCode,
|
||||||
|
onSubmit,
|
||||||
|
isLoading = false
|
||||||
|
}) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false)
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
onSubmit()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isExpanded) {
|
||||||
|
return (
|
||||||
|
<div className='h-16 border border-border rounded-2xl flex justify-between mt-5 items-center px-6'>
|
||||||
|
<div>
|
||||||
|
کد تخفیف دارید؟
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className='text-left p-0 text-primary hover:bg-transparent cursor-pointer'
|
||||||
|
onClick={() => setIsExpanded(true)}
|
||||||
|
>
|
||||||
|
ثبت کد
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<Button variant={'ghost'} className='text-left p-0 text-primary hover:bg-transparent cursor-pointer'>
|
return (
|
||||||
ثبت کد
|
<div className='border border-border rounded-2xl mt-5 p-4 sm:p-6'>
|
||||||
</Button>
|
<div className='text-sm font-medium mb-4'>کد تخفیف</div>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className='flex gap-3'>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="کد تخفیف خود را وارد کنید"
|
||||||
|
value={discountCode}
|
||||||
|
onChange={(e) => setDiscountCode(e.target.value)}
|
||||||
|
className="flex-1"
|
||||||
|
disabled={isLoading}
|
||||||
|
label=""
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={isLoading || !discountCode.trim()}
|
||||||
|
className="px-6"
|
||||||
|
>
|
||||||
|
{isLoading ? 'در حال اعمال...' : 'اعمال'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setIsExpanded(false)
|
||||||
|
setDiscountCode('')
|
||||||
|
}}
|
||||||
|
className="px-4"
|
||||||
|
>
|
||||||
|
لغو
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as api from "../service/Service";
|
import * as api from "../service/Service";
|
||||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
export const useGetCart = () => {
|
export const useGetCart = () => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -9,19 +9,48 @@ export const useGetCart = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useAddToCart = () => {
|
export const useAddToCart = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: api.addToCart,
|
mutationFn: api.addToCart,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["cart"] });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateCart = () => {
|
export const useUpdateCart = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: api.updateCart,
|
mutationFn: api.updateCart,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["cart"] });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useRemoveFromCart = () => {
|
export const useRemoveFromCart = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: api.removeFromCart,
|
mutationFn: api.removeFromCart,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["cart"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useShippingCost = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["shipping-cost"],
|
||||||
|
queryFn: api.shippingCost,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const usePaymentMethods = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["payment-methods"],
|
||||||
|
queryFn: api.paymentMethods,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+89
-12
@@ -5,11 +5,72 @@ import CartSummary from "@/components/CartSummary"
|
|||||||
import Layout from "@/hoc/Layout"
|
import Layout from "@/hoc/Layout"
|
||||||
import { Trash } from "iconsax-react"
|
import { Trash } from "iconsax-react"
|
||||||
import { NextPage } from "next"
|
import { NextPage } from "next"
|
||||||
import { useGetCart } from "./hooks/useCartData"
|
import { useGetCart, useRemoveFromCart } from "./hooks/useCartData"
|
||||||
|
import { toast } from "@/components/Toast"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
const Cart: NextPage = () => {
|
const Cart: NextPage = () => {
|
||||||
|
|
||||||
const { data } = useGetCart();
|
const { data, isLoading, error } = useGetCart();
|
||||||
|
const removeFromCartMutation = useRemoveFromCart()
|
||||||
|
const [isClearingAll, setIsClearingAll] = useState(false)
|
||||||
|
|
||||||
|
const handleClearAll = async () => {
|
||||||
|
if (!data?.results?.cart?.items?.length) return
|
||||||
|
|
||||||
|
setIsClearingAll(true)
|
||||||
|
try {
|
||||||
|
// حذف همه آیتمها یکی یکی
|
||||||
|
const removePromises = data.results.cart.items
|
||||||
|
.filter(item => item && item.product && item.variant)
|
||||||
|
.map(item =>
|
||||||
|
removeFromCartMutation.mutateAsync({
|
||||||
|
productId: item.product._id,
|
||||||
|
variantId: item.variant._id
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await Promise.all(removePromises)
|
||||||
|
toast('همه محصولات از سبد خرید حذف شدند', 'success')
|
||||||
|
} catch {
|
||||||
|
toast('خطا در حذف محصولات', 'error')
|
||||||
|
} finally {
|
||||||
|
setIsClearingAll(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="mt-12 sm:mt-14 px-3 sm:px-4 md:px-8 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">در حال بارگذاری...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="mt-12 sm:mt-14 px-3 sm:px-4 md:px-8 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg text-red-500">خطا در بارگذاری سبد خرید</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = data?.results?.cart
|
||||||
|
const items = cart?.items || []
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
return (
|
||||||
|
<div className="mt-12 sm:mt-14 px-3 sm:px-4 md:px-8 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">سبد خرید شما خالی است</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-12 sm:mt-14 px-3 sm:px-4 md:px-8 lg:px-20">
|
<div className="mt-12 sm:mt-14 px-3 sm:px-4 md:px-8 lg:px-20">
|
||||||
@@ -19,31 +80,47 @@ const Cart: NextPage = () => {
|
|||||||
سبد خرید
|
سبد خرید
|
||||||
</div>
|
</div>
|
||||||
<div className="size-6 sm:size-7 lg:size-8 rounded-md sm:rounded-lg bg-[#F2F2F2] flex items-center justify-center text-primary text-xs sm:text-sm font-medium">
|
<div className="size-6 sm:size-7 lg:size-8 rounded-md sm:rounded-lg bg-[#F2F2F2] flex items-center justify-center text-primary text-xs sm:text-sm font-medium">
|
||||||
10
|
{cart?.items_count || 0}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="bg-white shadow h-8 sm:h-9 lg:h-10 w-auto px-2 sm:px-3 lg:px-4 flex-shrink-0"
|
onClick={handleClearAll}
|
||||||
|
disabled={isClearingAll || !items.length}
|
||||||
|
className="bg-white shadow h-8 sm:h-9 lg:h-10 w-auto px-2 sm:px-3 lg:px-4 flex-shrink-0 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1 sm:gap-1.5 lg:gap-2">
|
<div className="flex items-center gap-1 sm:gap-1.5 lg:gap-2">
|
||||||
<Trash size={12} color="#AD3434" className="sm:w-4 sm:h-4 lg:w-5 lg:h-5" />
|
<Trash size={12} color="#AD3434" className="sm:w-4 sm:h-4 lg:w-5 lg:h-5" />
|
||||||
<div className="text-xs sm:text-sm text-[#AD3434] font-light hidden sm:block">حذف همه از سبد خرید</div>
|
<div className="text-xs sm:text-sm text-[#AD3434] font-light hidden sm:block">
|
||||||
<div className="text-xs text-[#AD3434] font-light sm:hidden">حذف همه</div>
|
{isClearingAll ? 'در حال حذف...' : 'حذف همه از سبد خرید'}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-[#AD3434] font-light sm:hidden">
|
||||||
|
{isClearingAll ? 'حذف...' : 'حذف همه'}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 sm:mt-5 lg:mt-6 flex flex-col lg:flex-row gap-3 sm:gap-4 lg:gap-6">
|
<div className="mt-4 sm:mt-5 lg:mt-6 flex flex-col lg:flex-row gap-3 sm:gap-4 lg:gap-6">
|
||||||
<div className="flex-1 border border-border rounded-lg sm:rounded-xl lg:rounded-2xl overflow-hidden">
|
<div className="flex-1 border border-border rounded-lg sm:rounded-xl lg:rounded-2xl overflow-hidden">
|
||||||
<CartItem noBorder />
|
{items.map((item, index) => {
|
||||||
<CartItem />
|
if (!item || !item.product || !item.variant) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CartItem
|
||||||
|
key={`${item.product._id}-${item.variant._id}`}
|
||||||
|
item={item}
|
||||||
|
noBorder={index === 0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<CartSummary
|
<CartSummary
|
||||||
itemsCount={3}
|
itemsCount={cart?.items_count || 0}
|
||||||
itemsPrice={580000}
|
itemsPrice={cart?.retail_price || 0}
|
||||||
discount={580000}
|
discount={cart?.total_discount || 0}
|
||||||
total={80580000}
|
total={cart?.payable_price || 0}
|
||||||
confirmHref="/cart/shipping"
|
confirmHref="/cart/shipping"
|
||||||
confirmLabel="ادامه به آدرس و ارسال"
|
confirmLabel="ادامه به آدرس و ارسال"
|
||||||
className="w-full lg:w-[360px] xl:w-[400px]"
|
className="w-full lg:w-[360px] xl:w-[400px]"
|
||||||
|
|||||||
+128
-15
@@ -1,43 +1,156 @@
|
|||||||
|
'use client'
|
||||||
import CartSummary from '@/components/CartSummary'
|
import CartSummary from '@/components/CartSummary'
|
||||||
import Layout from '@/hoc/Layout'
|
import Layout from '@/hoc/Layout'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
import TitleBack from '../components/TitleBack'
|
import TitleBack from '../components/TitleBack'
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||||
import DiscountCard from '../components/DiscountCard'
|
import DiscountCard from '../components/DiscountCard'
|
||||||
|
import { usePaymentMethods, useGetCart, useShippingCost } from '../hooks/useCartData'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { toast } from '@/components/Toast'
|
||||||
|
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||||
|
|
||||||
const CartPayment: NextPage = () => {
|
const CartPayment: NextPage = () => {
|
||||||
|
const { data: paymentMethodsData, isLoading: paymentMethodsLoading } = usePaymentMethods()
|
||||||
|
const { data: cartData, isLoading: cartLoading } = useGetCart()
|
||||||
|
const { data: shippingData, isLoading: shippingLoading } = useShippingCost()
|
||||||
|
const { data: profileData, isLoading: profileLoading } = useGetProfile()
|
||||||
|
|
||||||
|
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<string>('')
|
||||||
|
const [discountCode, setDiscountCode] = useState<string>('')
|
||||||
|
const [isApplyingDiscount, setIsApplyingDiscount] = useState(false)
|
||||||
|
|
||||||
|
const isLoading = paymentMethodsLoading || cartLoading || shippingLoading || profileLoading
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="mt-14 px-4 sm:px-8 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">در حال بارگذاری...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = cartData?.results?.cart
|
||||||
|
const paymentMethods = paymentMethodsData?.results?.paymentMethods || []
|
||||||
|
const profile = profileData?.results?.info
|
||||||
|
|
||||||
|
if (!cart?.items?.length) {
|
||||||
|
return (
|
||||||
|
<div className="mt-14 px-4 sm:px-8 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">سبد خرید شما خالی است</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// محاسبه هزینه ارسال
|
||||||
|
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||||
|
return total + (shop.shippers?.[0]?.totalShippingCost || 0)
|
||||||
|
}, 0) || 0
|
||||||
|
|
||||||
|
// محاسبه قیمت نهایی
|
||||||
|
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||||
|
|
||||||
|
const handlePaymentMethodChange = (value: string) => {
|
||||||
|
setSelectedPaymentMethod(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDiscountCodeSubmit = async () => {
|
||||||
|
if (!discountCode.trim()) {
|
||||||
|
toast('لطفاً کد تخفیف را وارد کنید', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsApplyingDiscount(true)
|
||||||
|
try {
|
||||||
|
// TODO: پیادهسازی API اعمال کد تخفیف
|
||||||
|
// await applyDiscountCode(discountCode)
|
||||||
|
toast('کد تخفیف با موفقیت اعمال شد', 'success')
|
||||||
|
} catch {
|
||||||
|
toast('کد تخفیف نامعتبر است', 'error')
|
||||||
|
} finally {
|
||||||
|
setIsApplyingDiscount(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePayment = async () => {
|
||||||
|
if (!selectedPaymentMethod) {
|
||||||
|
toast('لطفاً روش پرداخت را انتخاب کنید', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!profile?.userAddress) {
|
||||||
|
toast('لطفاً ابتدا آدرس خود را تکمیل کنید', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: پیادهسازی API پرداخت نهایی
|
||||||
|
// await processPayment({
|
||||||
|
// paymentMethodId: selectedPaymentMethod,
|
||||||
|
// discountCode: discountCode || null,
|
||||||
|
// shippingAddress: profile.userAddress
|
||||||
|
// })
|
||||||
|
toast('پرداخت با موفقیت انجام شد', 'success')
|
||||||
|
} catch {
|
||||||
|
toast('خطا در پردازش پرداخت', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-14 px-4 sm:px-8 lg:px-20">
|
<div className="mt-14 px-4 sm:px-8 lg:px-20">
|
||||||
|
|
||||||
<div className="mt-6 flex flex-col lg:flex-row gap-6">
|
<div className="mt-6 flex flex-col lg:flex-row gap-6">
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<TitleBack title='پرداخت' />
|
<TitleBack title='پرداخت' />
|
||||||
<DiscountCard
|
<DiscountCard
|
||||||
|
discountCode={discountCode}
|
||||||
|
setDiscountCode={setDiscountCode}
|
||||||
|
onSubmit={handleDiscountCodeSubmit}
|
||||||
|
isLoading={isApplyingDiscount}
|
||||||
/>
|
/>
|
||||||
<div className='mt-5 border border-border rounded-2xl p-4 sm:p-6 font-light'>
|
<div className='mt-5 border border-border rounded-2xl p-4 sm:p-6 font-light'>
|
||||||
<div>انتخاب روش پرداخت</div>
|
<div>انتخاب روش پرداخت</div>
|
||||||
|
|
||||||
<div className='mt-6 sm:mt-10 text-[#333333] font-light'>
|
<div className='mt-6 sm:mt-10 text-[#333333] font-light'>
|
||||||
<RadioGroup defaultValue="option-one">
|
<RadioGroup
|
||||||
<div className="flex items-center gap-2">
|
value={selectedPaymentMethod}
|
||||||
<RadioGroupItem value="option-one" id="option-one" />
|
onValueChange={handlePaymentMethodChange}
|
||||||
<div className='text-sm'>زین پال</div>
|
>
|
||||||
</div>
|
{paymentMethods.map((method) => (
|
||||||
<div className="flex items-center gap-2">
|
<div key={method._id} className="flex items-center gap-2 mb-3">
|
||||||
<RadioGroupItem value="option-one" id="option-one" />
|
<RadioGroupItem
|
||||||
<div className='text-sm'>زین پال</div>
|
value={method._id}
|
||||||
</div>
|
id={method._id}
|
||||||
|
disabled={!method.isActive}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor={method._id}
|
||||||
|
className={`text-sm ${method.isActive ? 'cursor-pointer' : 'cursor-not-allowed'} ${!method.isActive ? 'text-gray-400' : ''}`}
|
||||||
|
>
|
||||||
|
<span className="font-medium">{method.title_fa}</span>
|
||||||
|
{method.provider && (
|
||||||
|
<span className="text-xs text-gray-500 mr-2">
|
||||||
|
({method.provider})
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full lg:w-[360px] lg:flex-shrink-0">
|
<div className="w-full lg:w-[360px] lg:flex-shrink-0">
|
||||||
<CartSummary
|
<CartSummary
|
||||||
itemsCount={3}
|
itemsCount={cart?.items_count || 0}
|
||||||
itemsPrice={580000}
|
itemsPrice={cart?.retail_price || 0}
|
||||||
discount={580000}
|
discount={cart?.total_discount || 0}
|
||||||
total={80580000}
|
shippingCost={totalShippingCost}
|
||||||
confirmHref="/cart/payment"
|
total={finalTotal}
|
||||||
|
onConfirm={handlePayment}
|
||||||
confirmLabel="پرداخت"
|
confirmLabel="پرداخت"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
UpdateCartType,
|
UpdateCartType,
|
||||||
RemoveCartType,
|
RemoveCartType,
|
||||||
CartResponseType,
|
CartResponseType,
|
||||||
|
ShippingCostResponseType,
|
||||||
|
PaymentMethodsResponseType,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
|
|
||||||
export const getCart = async (): Promise<CartResponseType> => {
|
export const getCart = async (): Promise<CartResponseType> => {
|
||||||
@@ -25,3 +27,13 @@ export const removeFromCart = async (params: RemoveCartType) => {
|
|||||||
const { data } = await axios.post("/cart/remove", params);
|
const { data } = await axios.post("/cart/remove", params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const shippingCost = async (): Promise<ShippingCostResponseType> => {
|
||||||
|
const { data } = await axios.get("/shipment/shipping-cost");
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paymentMethods = async (): Promise<PaymentMethodsResponseType> => {
|
||||||
|
const { data } = await axios.get("/payment/methods");
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,63 @@
|
|||||||
|
'use client'
|
||||||
import CartSummary from '@/components/CartSummary'
|
import CartSummary from '@/components/CartSummary'
|
||||||
import Layout from '@/hoc/Layout'
|
import Layout from '@/hoc/Layout'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
import CartItem from '../components/CartItem'
|
import CartItem from '../components/CartItem'
|
||||||
import TitleBack from '../components/TitleBack'
|
import TitleBack from '../components/TitleBack'
|
||||||
import { Location } from 'iconsax-react'
|
import { Location } from 'iconsax-react'
|
||||||
|
import { useGetCart, useShippingCost } from '../hooks/useCartData'
|
||||||
|
import { CartItemType } from '../types/Types'
|
||||||
|
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
const CartShipping: NextPage = () => {
|
const CartShipping: NextPage = () => {
|
||||||
|
|
||||||
|
const { data: cartData, isLoading: cartLoading, error: cartError } = useGetCart()
|
||||||
|
const { data: shippingData, isLoading: shippingLoading, error: shippingError } = useShippingCost()
|
||||||
|
const { data: profileData, isLoading: profileLoading, error: profileError } = useGetProfile()
|
||||||
|
|
||||||
|
if (cartLoading || shippingLoading || profileLoading) {
|
||||||
|
return (
|
||||||
|
<div className="mt-14 px-4 sm:px-6 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">در حال بارگذاری...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cartError || shippingError || profileError) {
|
||||||
|
return (
|
||||||
|
<div className="mt-14 px-4 sm:px-6 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg text-red-500">خطا در بارگذاری اطلاعات</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = cartData?.results?.cart
|
||||||
|
const items = cart?.items || []
|
||||||
|
const profile = profileData?.results?.info
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
return (
|
||||||
|
<div className="mt-14 px-4 sm:px-6 lg:px-20">
|
||||||
|
<div className="flex items-center justify-center h-64">
|
||||||
|
<div className="text-lg">سبد خرید شما خالی است</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// محاسبه هزینه ارسال
|
||||||
|
const totalShippingCost = shippingData?.results?.shipping?.reduce((total, shop) => {
|
||||||
|
return total + (shop.shippers?.[0]?.totalShippingCost || 0)
|
||||||
|
}, 0) || 0
|
||||||
|
|
||||||
|
// محاسبه قیمت نهایی
|
||||||
|
const finalTotal = (cart?.payable_price || 0) + totalShippingCost
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-14 px-4 sm:px-6 lg:px-20">
|
<div className="mt-14 px-4 sm:px-6 lg:px-20">
|
||||||
|
|
||||||
@@ -19,25 +71,39 @@ const CartShipping: NextPage = () => {
|
|||||||
<div>
|
<div>
|
||||||
آدرس تحویل سفارش
|
آدرس تحویل سفارش
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-2 text-sm text-black'>
|
{profile?.userAddress && (
|
||||||
اراک خیابان امام حسن غریبی خیابان 123
|
<div className='mt-2 text-sm text-black'>
|
||||||
</div>
|
{profile.userAddress.address}
|
||||||
<div className='mt-2'>حمید ضرغامی</div>
|
{profile.userAddress.city && `، ${profile.userAddress.city.name}`}
|
||||||
|
{profile.userAddress.province && `، ${profile.userAddress.province.name}`}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='text-primary text-sm self-end sm:self-auto'>ویرایش</div>
|
<Link href={'/profile'} className='text-primary text-sm self-end sm:self-auto'>ویرایش</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 mt-5 border border-border p-4 sm:p-5 rounded-2xl">
|
<div className="flex-1 mt-5 border border-border p-4 sm:p-5 rounded-2xl">
|
||||||
<CartItem noBorder />
|
{items.map((item: CartItemType, index: number) => {
|
||||||
<CartItem />
|
if (!item || !item.product || !item.variant) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CartItem
|
||||||
|
key={`${item.product._id}-${item.variant._id}`}
|
||||||
|
item={item}
|
||||||
|
noBorder={index === 0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full lg:w-[360px] lg:flex-shrink-0">
|
<div className="w-full lg:w-[360px] lg:flex-shrink-0">
|
||||||
<CartSummary
|
<CartSummary
|
||||||
itemsCount={3}
|
itemsCount={cart?.items_count || 0}
|
||||||
itemsPrice={580000}
|
itemsPrice={cart?.retail_price || 0}
|
||||||
discount={580000}
|
discount={cart?.total_discount || 0}
|
||||||
total={80580000}
|
shippingCost={totalShippingCost}
|
||||||
|
total={finalTotal}
|
||||||
confirmHref="/cart/payment"
|
confirmHref="/cart/payment"
|
||||||
confirmLabel="پرداخت"
|
confirmLabel="پرداخت"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|||||||
@@ -166,3 +166,139 @@ export type RemoveCartType = {
|
|||||||
productId: number;
|
productId: number;
|
||||||
variantId: string;
|
variantId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Shipping Cost Types
|
||||||
|
export type ShippingItemType = {
|
||||||
|
product: number;
|
||||||
|
variant: string;
|
||||||
|
quantity: number;
|
||||||
|
selling_price: number;
|
||||||
|
retail_price: number;
|
||||||
|
discount_percent: number;
|
||||||
|
shippingCost: number;
|
||||||
|
isFreeShip: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShipperType = {
|
||||||
|
shipperId: number;
|
||||||
|
shipperName: string;
|
||||||
|
shippingDaysRange: string[];
|
||||||
|
totalShippingCost: number;
|
||||||
|
items: ShippingItemType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShopShippingType = {
|
||||||
|
shopId: string;
|
||||||
|
shopName: string;
|
||||||
|
items: Array<{
|
||||||
|
product: {
|
||||||
|
_id: number;
|
||||||
|
url: string;
|
||||||
|
title_fa: string;
|
||||||
|
title_en: string;
|
||||||
|
seoTitle: string | null;
|
||||||
|
seoDescription: string | null;
|
||||||
|
source: string;
|
||||||
|
description: string;
|
||||||
|
metaDescription: string;
|
||||||
|
tags: string[];
|
||||||
|
advantages: string[];
|
||||||
|
disAdvantages: string[];
|
||||||
|
imagesUrl: {
|
||||||
|
cover: string;
|
||||||
|
list: string[];
|
||||||
|
};
|
||||||
|
isFake: string;
|
||||||
|
voice: string | null;
|
||||||
|
specifications: Array<{
|
||||||
|
title: string;
|
||||||
|
values: string[];
|
||||||
|
}>;
|
||||||
|
brand: {
|
||||||
|
_id: string;
|
||||||
|
};
|
||||||
|
category: {
|
||||||
|
_id: string;
|
||||||
|
};
|
||||||
|
variants: Array<{
|
||||||
|
_id: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
variant: {
|
||||||
|
_id: string;
|
||||||
|
market_status: string;
|
||||||
|
price: {
|
||||||
|
order_limit: number;
|
||||||
|
retailPrice: number;
|
||||||
|
selling_price: number;
|
||||||
|
is_specialSale: boolean;
|
||||||
|
discount_percent: number;
|
||||||
|
specialSale_order_limit: number | null;
|
||||||
|
specialSale_quantity: number | null;
|
||||||
|
specialSale_endDate: string | null;
|
||||||
|
};
|
||||||
|
stock: number;
|
||||||
|
postingTime: number;
|
||||||
|
isFreeShip: boolean;
|
||||||
|
isWholeSale: boolean;
|
||||||
|
shop: {
|
||||||
|
_id: string;
|
||||||
|
shopName: string;
|
||||||
|
shopCode: number;
|
||||||
|
owner: string;
|
||||||
|
shopDescription: string;
|
||||||
|
telephoneNumber: string;
|
||||||
|
isChatActive: boolean;
|
||||||
|
shopPostalCode: string;
|
||||||
|
logo: string;
|
||||||
|
};
|
||||||
|
warranty: {
|
||||||
|
_id: number;
|
||||||
|
duration: string;
|
||||||
|
logoUrl: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
meterage?: {
|
||||||
|
_id: number;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
size?: {
|
||||||
|
_id: number;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
quantity: number;
|
||||||
|
}>;
|
||||||
|
shippers: ShipperType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shipping Cost API Response Type
|
||||||
|
export type ShippingCostResponseType = {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
shipping: ShopShippingType[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Payment Method Types
|
||||||
|
export type PaymentMethodType = {
|
||||||
|
_id: string;
|
||||||
|
title_fa: string;
|
||||||
|
title_en: string;
|
||||||
|
description: string;
|
||||||
|
provider: string;
|
||||||
|
isActive: boolean;
|
||||||
|
type: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Payment Methods API Response Type
|
||||||
|
export type PaymentMethodsResponseType = {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
paymentMethods: PaymentMethodType[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/Service";
|
||||||
|
import { useSharedStore } from "@/share/store/sharedStore";
|
||||||
|
|
||||||
|
export const useGetProfile = () => {
|
||||||
|
const { isLogin } = useSharedStore();
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["profile"],
|
||||||
|
queryFn: api.getProfile,
|
||||||
|
enabled: isLogin,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
import { UserMeResponseType } from "@/share/types/SharedTypes";
|
||||||
|
|
||||||
|
export const getProfile = async () => {
|
||||||
|
const { data } = await axios.get<UserMeResponseType>("/user/profile");
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ type CartSummaryProps = {
|
|||||||
itemsCount: number;
|
itemsCount: number;
|
||||||
itemsPrice: number;
|
itemsPrice: number;
|
||||||
discount: number;
|
discount: number;
|
||||||
|
shippingCost?: number;
|
||||||
total: number;
|
total: number;
|
||||||
onConfirm?: () => void;
|
onConfirm?: () => void;
|
||||||
confirmHref?: string;
|
confirmHref?: string;
|
||||||
@@ -31,7 +32,7 @@ const Row: FC<{ label: string; value: string | number; emphasize?: boolean }> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CartSummary: FC<CartSummaryProps> = (props) => {
|
const CartSummary: FC<CartSummaryProps> = (props) => {
|
||||||
const { itemsCount, itemsPrice, discount, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel } = props;
|
const { itemsCount, itemsPrice, discount, shippingCost, total, onConfirm, onDownloadInvoice, className, confirmHref, confirmLabel } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"rounded-xl sm:rounded-2xl border border-border p-4 sm:p-6 h-fit bg-[#FAFAFA] " + (className ?? "")}>
|
<div className={"rounded-xl sm:rounded-2xl border border-border p-4 sm:p-6 h-fit bg-[#FAFAFA] " + (className ?? "")}>
|
||||||
@@ -43,6 +44,9 @@ const CartSummary: FC<CartSummaryProps> = (props) => {
|
|||||||
<div className="mt-4 sm:mt-6 space-y-3 sm:space-y-5">
|
<div className="mt-4 sm:mt-6 space-y-3 sm:space-y-5">
|
||||||
<Row label="قیمت کالاها" value={itemsPrice} />
|
<Row label="قیمت کالاها" value={itemsPrice} />
|
||||||
<Row label="تخفیف" value={discount} />
|
<Row label="تخفیف" value={discount} />
|
||||||
|
{shippingCost !== undefined && shippingCost > 0 && (
|
||||||
|
<Row label="هزینه ارسال" value={shippingCost} />
|
||||||
|
)}
|
||||||
<Row label="جمع فاکتور" value={total} emphasize />
|
<Row label="جمع فاکتور" value={total} emphasize />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { FC, Fragment, useEffect, useState } from 'react'
|
|||||||
import Menu from './components/Menu'
|
import Menu from './components/Menu'
|
||||||
import { useSharedStore } from './store/sharedStore'
|
import { useSharedStore } from './store/sharedStore'
|
||||||
import { getToken } from '@/config/func'
|
import { getToken } from '@/config/func'
|
||||||
|
import { useGetProfile } from '@/app/profile/hooks/useProfileData'
|
||||||
|
|
||||||
const Header: FC = () => {
|
const Header: FC = () => {
|
||||||
|
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
||||||
const { setIsLogin } = useSharedStore()
|
const { setIsLogin } = useSharedStore()
|
||||||
|
const { data: profile } = useGetProfile()
|
||||||
|
|
||||||
const handleSetIsLogin = async () => {
|
const handleSetIsLogin = async () => {
|
||||||
const token = await getToken()
|
const token = await getToken()
|
||||||
@@ -49,7 +51,9 @@ const Header: FC = () => {
|
|||||||
<div className='flex items-center gap-1 sm:gap-2 md:gap-6'>
|
<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'>
|
<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' />
|
<Profile size={20} color='#333333' />
|
||||||
<div className='text-sm text-[#333333]'>حساب کاربری</div>
|
{
|
||||||
|
<div className='text-sm text-[#333333]'>{profile?.results?.info?.fullName ? profile?.results?.info?.fullName : 'حساب کاربری'}</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='md:hidden'>
|
<div className='md:hidden'>
|
||||||
|
|||||||
@@ -44,24 +44,42 @@ export type CategoryResponseType = ApiResponse<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type UserMeResponseType = ApiResponse<{
|
export type UserMeResponseType = ApiResponse<{
|
||||||
user: {
|
info: {
|
||||||
id: string;
|
_id: string;
|
||||||
|
fullName: string;
|
||||||
|
phoneNumber: string;
|
||||||
|
dateOfBirth: string | null;
|
||||||
|
address: string;
|
||||||
|
nationalCode: string | null;
|
||||||
|
homeNumber: string | null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
firstName: string;
|
userAddress: {
|
||||||
lastName: string;
|
_id: string;
|
||||||
email: string;
|
address: string;
|
||||||
emailVerified: boolean;
|
city: {
|
||||||
phone: string;
|
_id: number;
|
||||||
birthDate: string;
|
name: string;
|
||||||
nationalCode: string;
|
province: number;
|
||||||
financialType: string;
|
createdAt: string;
|
||||||
profilePic: string | null;
|
updatedAt: string;
|
||||||
userName: string | null;
|
__v: number;
|
||||||
roles: Array<{
|
};
|
||||||
id: string;
|
province: {
|
||||||
|
_id: number;
|
||||||
|
name: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
__v: number;
|
||||||
|
};
|
||||||
|
postalCode: string;
|
||||||
|
plaque: string;
|
||||||
|
lat: string;
|
||||||
|
lon: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
name: string;
|
updatedAt: string;
|
||||||
}>;
|
__v: number;
|
||||||
|
};
|
||||||
|
isSeller: boolean;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
Reference in New Issue
Block a user