Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b211b5b1a | |||
| 2229c13784 |
@@ -1,44 +1,51 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Card, Icon, Wallet2 } from 'iconsax-react';
|
import { Card, Icon, Wallet2 } from 'iconsax-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
import { useGetPaymentMethod } from '../../hooks/useOrderData';
|
import { useGetPaymentMethod } from '../../hooks/useOrderData';
|
||||||
import { useCheckoutStore } from '../../store/Store';
|
import { useCheckoutStore } from '../../store/Store';
|
||||||
import { useGetUserWallet } from '@/app/[name]/(Main)/transactions/hooks/useTransactionData';
|
import { useGetUserWallet } from '@/app/[name]/(Main)/transactions/hooks/useTransactionData';
|
||||||
import { useGetCartItems } from '@/app/[name]/(Main)/cart/hooks/useCartData';
|
import { useGetCartItems } from '@/app/[name]/(Main)/cart/hooks/useCartData';
|
||||||
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
import { useGetProfile, useSingleUpload } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
||||||
|
import { PaymentMethodEnum } from '../../enum/Enum';
|
||||||
|
import { toast } from '@/components/Toast';
|
||||||
|
import { extractErrorMessage } from '@/lib/func';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
type PaymentSectionProps = {
|
type PaymentSectionProps = {
|
||||||
paymentType: string;
|
paymentType: string;
|
||||||
onPaymentTypeChange: (id: string) => void;
|
onPaymentTypeChange: (id: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const getIconByMethod = (method: PaymentMethodEnum): Icon => {
|
||||||
const getIconByMethod = (method: "CardOnDelivery" | "Cash" | "Online", _gateway: string | null): Icon => {
|
if (method === PaymentMethodEnum.Online || method === PaymentMethodEnum.CreditCard) {
|
||||||
if (method === "Online" || method === "CardOnDelivery") {
|
|
||||||
return Card;
|
return Card;
|
||||||
}
|
}
|
||||||
if (method === "Cash") {
|
if (method === PaymentMethodEnum.Cash || method === PaymentMethodEnum.Wallet) {
|
||||||
return Wallet2;
|
return Wallet2;
|
||||||
}
|
}
|
||||||
return Card;
|
return Card;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isWalletPayment = (method: string, gateway: string | null): boolean => {
|
|
||||||
return gateway?.toLowerCase() === 'wallet' || method.toLowerCase() === 'wallet';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSectionProps) => {
|
export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSectionProps) => {
|
||||||
|
|
||||||
const { t: tOrderDetail } = useTranslation('parallels', { keyPrefix: 'OrderDetail' });
|
const { t: tOrderDetail } = useTranslation('parallels', { keyPrefix: 'OrderDetail' });
|
||||||
const { t: tOrders } = useTranslation('orders');
|
const { t: tOrders } = useTranslation('orders');
|
||||||
const { data: paymentMethod } = useGetPaymentMethod();
|
const { data: paymentMethod } = useGetPaymentMethod();
|
||||||
const { setIsSelectedPayment } = useCheckoutStore();
|
const {
|
||||||
|
setIsSelectedPayment,
|
||||||
|
paymentDesc,
|
||||||
|
setPaymentDesc,
|
||||||
|
paymentAttachments,
|
||||||
|
setPaymentAttachments,
|
||||||
|
} = useCheckoutStore();
|
||||||
const { data: userWallet } = useGetUserWallet();
|
const { data: userWallet } = useGetUserWallet();
|
||||||
const { isSuccess } = useGetProfile();
|
const { isSuccess } = useGetProfile();
|
||||||
const { data: cartData } = useGetCartItems(isSuccess);
|
const { data: cartData } = useGetCartItems(isSuccess);
|
||||||
|
const { mutateAsync: singleUpload, isPending: isUploading } = useSingleUpload();
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const walletAmount = userWallet?.data?.balance ?? 0;
|
const walletAmount = userWallet?.data?.balance ?? 0;
|
||||||
const totalAmount = cartData?.data?.total ?? 0;
|
const totalAmount = cartData?.data?.total ?? 0;
|
||||||
@@ -63,14 +70,51 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
.sort((a, b) => a.order - b.order)
|
.sort((a, b) => a.order - b.order)
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
label: getPaymentMethodTranslation(item.method, item.title),
|
label: getPaymentMethodTranslation(item.method, item.description),
|
||||||
icon: getIconByMethod(item.method, item.gateway),
|
icon: getIconByMethod(item.method),
|
||||||
method: item.method,
|
method: item.method,
|
||||||
gateway: item.gateway,
|
cardNumber: item.cardNumber,
|
||||||
isWallet: isWalletPayment(item.method, item.gateway),
|
cardOwner: item.cardOwner,
|
||||||
|
description: item.description,
|
||||||
|
isWallet: item.method === PaymentMethodEnum.Wallet,
|
||||||
}));
|
}));
|
||||||
}, [paymentMethod, getPaymentMethodTranslation]);
|
}, [paymentMethod, getPaymentMethodTranslation]);
|
||||||
|
|
||||||
|
const selectedCreditCard = useMemo(() => {
|
||||||
|
const selected = paymentOptions.find(opt => opt.id === paymentType);
|
||||||
|
if (selected?.method === PaymentMethodEnum.CreditCard) return selected;
|
||||||
|
return null;
|
||||||
|
}, [paymentOptions, paymentType]);
|
||||||
|
|
||||||
|
const handlePaymentTypeChange = (value: string) => {
|
||||||
|
const selected = paymentOptions.find(opt => opt.id === value);
|
||||||
|
if (selected?.method !== PaymentMethodEnum.CreditCard) {
|
||||||
|
setPaymentDesc('');
|
||||||
|
setPaymentAttachments([]);
|
||||||
|
}
|
||||||
|
onPaymentTypeChange(value);
|
||||||
|
setIsSelectedPayment(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReceiptUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await singleUpload(file);
|
||||||
|
if (response?.data?.url) {
|
||||||
|
setPaymentAttachments([response.data.url]);
|
||||||
|
toast(tOrderDetail('SectionPayment.CreditCard.UploadSuccess'), 'success');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast(extractErrorMessage(error), 'error');
|
||||||
|
} finally {
|
||||||
|
if (fileInputRef.current) {
|
||||||
|
fileInputRef.current.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-container rounded-container box-shadow-normal p-4">
|
<section className="bg-container rounded-container box-shadow-normal p-4">
|
||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
@@ -78,15 +122,11 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={paymentType}
|
value={paymentType}
|
||||||
onValueChange={(value) => {
|
onValueChange={handlePaymentTypeChange}
|
||||||
onPaymentTypeChange(value);
|
|
||||||
setIsSelectedPayment(true);
|
|
||||||
}}
|
|
||||||
className='flex flex-col gap-6 mt-6'
|
className='flex flex-col gap-6 mt-6'
|
||||||
>
|
>
|
||||||
{paymentOptions.map(({ id, label, icon: Icon, isWallet }) => {
|
{paymentOptions.map(({ id, label, icon: Icon, isWallet }) => {
|
||||||
const isDisabled = isWallet && isWalletInsufficient;
|
const isDisabled = isWallet && isWalletInsufficient;
|
||||||
const walletOption = paymentOptions.find(opt => opt.id === id && opt.isWallet);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={id} className='flex items-center justify-between'>
|
<div key={id} className='flex items-center justify-between'>
|
||||||
@@ -100,12 +140,11 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
htmlFor={`paymentMethod${id}`}
|
htmlFor={`paymentMethod${id}`}
|
||||||
>
|
>
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
{walletOption && (
|
{isWallet && (
|
||||||
<span className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
<span className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
||||||
({formatPrice(walletAmount)} T)
|
({formatPrice(walletAmount)} T)
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<span className='text-xs mt-0.5'>{label}</span>
|
<span className='text-xs mt-0.5'>{label}</span>
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
<Icon
|
||||||
@@ -118,8 +157,93 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
|
{selectedCreditCard && (
|
||||||
|
<div className='mt-6 pt-4 border-t border-border flex flex-col gap-4'>
|
||||||
|
{selectedCreditCard.cardOwner && (
|
||||||
|
<div>
|
||||||
|
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
{tOrderDetail('SectionPayment.CreditCard.CardOwnerLabel')}
|
||||||
|
</span>
|
||||||
|
<p className='text-sm font-medium mt-1'>
|
||||||
|
{selectedCreditCard.cardOwner}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedCreditCard.cardNumber && (
|
||||||
|
<div>
|
||||||
|
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
{tOrderDetail('SectionPayment.CreditCard.CardNumberLabel')}
|
||||||
|
</span>
|
||||||
|
<p className='text-sm font-medium mt-1' dir='ltr'>
|
||||||
|
{selectedCreditCard.cardNumber}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedCreditCard.description && (
|
||||||
|
<p className='text-xs text-gray-500 dark:text-gray-400 leading-5'>
|
||||||
|
{selectedCreditCard.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<label className='text-xs text-gray-500 dark:text-gray-400' htmlFor='paymentDesc'>
|
||||||
|
{tOrderDetail('SectionPayment.CreditCard.PaymentDescLabel')}
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id='paymentDesc'
|
||||||
|
className='w-full px-4 py-2.5 mt-2 text-sm2 leading-6 outline-0 border border-border rounded-normal resize-none focus:ring-0'
|
||||||
|
placeholder={tOrderDetail('SectionPayment.CreditCard.PaymentDescPlaceholder')}
|
||||||
|
value={paymentDesc}
|
||||||
|
onChange={(e) => setPaymentDesc(e.target.value)}
|
||||||
|
rows={2}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
||||||
|
{tOrderDetail('SectionPayment.CreditCard.ReceiptLabel')}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type='file'
|
||||||
|
accept='image/*'
|
||||||
|
className='hidden'
|
||||||
|
onChange={handleReceiptUpload}
|
||||||
|
/>
|
||||||
|
{paymentAttachments[0] ? (
|
||||||
|
<div className='mt-2 relative w-full h-32 rounded-normal overflow-hidden border border-border'>
|
||||||
|
<Image
|
||||||
|
src={paymentAttachments[0]}
|
||||||
|
alt='receipt'
|
||||||
|
fill
|
||||||
|
className='object-contain'
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='absolute top-2 left-2 text-xs bg-white/90 dark:bg-background/90 px-2 py-1 rounded-normal'
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
disabled={isUploading}
|
||||||
|
>
|
||||||
|
{isUploading
|
||||||
|
? tOrderDetail('SectionPayment.CreditCard.Uploading')
|
||||||
|
: tOrderDetail('SectionPayment.CreditCard.UploadReceipt')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='w-full mt-2 py-3 text-sm2 border border-dashed border-border rounded-normal text-gray-500 dark:text-gray-400 hover:border-primary transition-colors'
|
||||||
|
onClick={() => fileInputRef.current?.click()}
|
||||||
|
disabled={isUploading}
|
||||||
|
>
|
||||||
|
{isUploading
|
||||||
|
? tOrderDetail('SectionPayment.CreditCard.Uploading')
|
||||||
|
: tOrderDetail('SectionPayment.CreditCard.UploadReceipt')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import { useParams } from 'next/navigation';
|
|||||||
import { useGetCartItems, useSaveAllMethod } from '@/app/[name]/(Main)/cart/hooks/useCartData';
|
import { useGetCartItems, useSaveAllMethod } from '@/app/[name]/(Main)/cart/hooks/useCartData';
|
||||||
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useGetShipmentMethod } from '../../hooks/useOrderData';
|
import { useGetShipmentMethod, useGetPaymentMethod } from '../../hooks/useOrderData';
|
||||||
import CartItemsList from '@/app/[name]/(Main)/cart/components/CartItemsList';
|
import CartItemsList from '@/app/[name]/(Main)/cart/components/CartItemsList';
|
||||||
import { useCartStore } from '@/app/[name]/(Main)/cart/store/Store';
|
import { useCartStore } from '@/app/[name]/(Main)/cart/store/Store';
|
||||||
|
import { PaymentMethodEnum } from '../../enum/Enum';
|
||||||
|
|
||||||
type SummarySectionProps = {
|
type SummarySectionProps = {
|
||||||
cartModal: boolean;
|
cartModal: boolean;
|
||||||
@@ -25,7 +26,7 @@ type SummarySectionProps = {
|
|||||||
export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: SummarySectionProps) => {
|
export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: SummarySectionProps) => {
|
||||||
|
|
||||||
const { t } = useTranslation('parallels', { keyPrefix: 'OrderDetail' });
|
const { t } = useTranslation('parallels', { keyPrefix: 'OrderDetail' });
|
||||||
const { isSelectedShipment, isSelectedPayment, deliveryType: storeDeliveryType, paymentType, selectedAddressId, tableNumber, carAddress, clear } = useCheckoutStore();
|
const { isSelectedShipment, isSelectedPayment, deliveryType: storeDeliveryType, paymentType, selectedAddressId, tableNumber, carAddress, paymentDesc, paymentAttachments, clear } = useCheckoutStore();
|
||||||
const { description, reset: resetCart } = useCartStore();
|
const { description, reset: resetCart } = useCartStore();
|
||||||
const { mutate: createOrder, isPending: isCreateOrderPending } = useCreateOrder();
|
const { mutate: createOrder, isPending: isCreateOrderPending } = useCreateOrder();
|
||||||
const { mutate: saveAllMethod, isPending: isSaveAllMethodPending } = useSaveAllMethod()
|
const { mutate: saveAllMethod, isPending: isSaveAllMethodPending } = useSaveAllMethod()
|
||||||
@@ -34,6 +35,7 @@ export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: S
|
|||||||
const { isSuccess } = useGetProfile();
|
const { isSuccess } = useGetProfile();
|
||||||
const { data: cartData } = useGetCartItems(isSuccess);
|
const { data: cartData } = useGetCartItems(isSuccess);
|
||||||
const { data: shipmentMethod } = useGetShipmentMethod();
|
const { data: shipmentMethod } = useGetShipmentMethod();
|
||||||
|
const { data: paymentMethod } = useGetPaymentMethod();
|
||||||
|
|
||||||
const selectedDeliveryMethod = useMemo(() => {
|
const selectedDeliveryMethod = useMemo(() => {
|
||||||
if (!shipmentMethod?.data || !deliveryType) return null;
|
if (!shipmentMethod?.data || !deliveryType) return null;
|
||||||
@@ -52,6 +54,17 @@ export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: S
|
|||||||
return selectedDeliveryMethod?.method === 'dineIn';
|
return selectedDeliveryMethod?.method === 'dineIn';
|
||||||
}, [selectedDeliveryMethod]);
|
}, [selectedDeliveryMethod]);
|
||||||
|
|
||||||
|
const selectedPaymentMethod = useMemo(() => {
|
||||||
|
if (!paymentMethod?.data || !paymentType) return null;
|
||||||
|
return paymentMethod.data.find((item) => item.id === paymentType);
|
||||||
|
}, [paymentMethod?.data, paymentType]);
|
||||||
|
|
||||||
|
const isCreditCardPayment = selectedPaymentMethod?.method === PaymentMethodEnum.CreditCard;
|
||||||
|
|
||||||
|
const isCreditCardIncomplete = isCreditCardPayment && (
|
||||||
|
!paymentDesc.trim() || paymentAttachments.length === 0
|
||||||
|
);
|
||||||
|
|
||||||
const formatPrice = useCallback(
|
const formatPrice = useCallback(
|
||||||
(value: number) => value.toLocaleString('fa-IR'),
|
(value: number) => value.toLocaleString('fa-IR'),
|
||||||
[]
|
[]
|
||||||
@@ -91,6 +104,17 @@ export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: S
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCreditCardPayment) {
|
||||||
|
if (!paymentDesc.trim()) {
|
||||||
|
toast('لطفا توضیحات پرداخت را وارد کنید', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (paymentAttachments.length === 0) {
|
||||||
|
toast('لطفا تصویر رسید پرداخت را آپلود کنید', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (!isDeliveryCar && !isDineIn && !selectedAddressId) {
|
// if (!isDeliveryCar && !isDineIn && !selectedAddressId) {
|
||||||
// toast('لطفا آدرس را انتخاب کنید', 'error');
|
// toast('لطفا آدرس را انتخاب کنید', 'error');
|
||||||
// return;
|
// return;
|
||||||
@@ -103,6 +127,10 @@ export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: S
|
|||||||
tableNumber: isDineIn ? tableNumber.toString() : undefined,
|
tableNumber: isDineIn ? tableNumber.toString() : undefined,
|
||||||
...(isCourierDelivery && selectedAddressId && { addressId: selectedAddressId }),
|
...(isCourierDelivery && selectedAddressId && { addressId: selectedAddressId }),
|
||||||
...(isDeliveryCar && carAddress && { carAddress }),
|
...(isDeliveryCar && carAddress && { carAddress }),
|
||||||
|
...(isCreditCardPayment && {
|
||||||
|
attachments: paymentAttachments,
|
||||||
|
paymentDesc: paymentDesc.trim(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
saveAllMethod(saveAllMethodData, {
|
saveAllMethod(saveAllMethodData, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -192,7 +220,12 @@ export const SummarySection = ({ cartModal, onToggleCartModal, deliveryType }: S
|
|||||||
<div className='text-xs text-gray-400 dark:text-gray-500'>{t("PayableAmountLabel")}</div>
|
<div className='text-xs text-gray-400 dark:text-gray-500'>{t("PayableAmountLabel")}</div>
|
||||||
<div className='text-sm mt-2 font-semibold'>{formatPrice(total)} تومان</div>
|
<div className='text-sm mt-2 font-semibold'>{formatPrice(total)} تومان</div>
|
||||||
</div>
|
</div>
|
||||||
<Button pending={isCreateOrderPending || isSaveAllMethodPending} onClick={handleSubmit} className='px-10 w-fit dark:bg-white dark:text-black! dark:hover:bg-gray-100'>
|
<Button
|
||||||
|
pending={isCreateOrderPending || isSaveAllMethodPending}
|
||||||
|
disabled={isCreditCardIncomplete}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className='px-10 w-fit dark:bg-white dark:text-black! dark:hover:bg-gray-100'
|
||||||
|
>
|
||||||
{t("ButtonSubmit")}
|
{t("ButtonSubmit")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
export enum PaymentMethodEnum {
|
||||||
|
Online = "Online",
|
||||||
|
Cash = "Cash",
|
||||||
|
Wallet = "Wallet",
|
||||||
|
CreditCard = "CreditCard",
|
||||||
|
}
|
||||||
|
|
||||||
export const enum OrderStatus {
|
export const enum OrderStatus {
|
||||||
NEW = "new",
|
NEW = "new",
|
||||||
PENDING_PAYMENT = "pendingPayment",
|
PENDING_PAYMENT = "pendingPayment",
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const initialState = {
|
|||||||
carColor: string;
|
carColor: string;
|
||||||
plateNumber: string;
|
plateNumber: string;
|
||||||
} | null,
|
} | null,
|
||||||
|
paymentDesc: "",
|
||||||
|
paymentAttachments: [] as string[],
|
||||||
lastUpdated: null as number | null,
|
lastUpdated: null as number | null,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,6 +40,10 @@ export const useCheckoutStore = create<CheckoutStoreType>()(
|
|||||||
set({ tableNumber: value, lastUpdated: Date.now() }),
|
set({ tableNumber: value, lastUpdated: Date.now() }),
|
||||||
setCarAddress: (value) =>
|
setCarAddress: (value) =>
|
||||||
set({ carAddress: value, lastUpdated: Date.now() }),
|
set({ carAddress: value, lastUpdated: Date.now() }),
|
||||||
|
setPaymentDesc: (value) =>
|
||||||
|
set({ paymentDesc: value, lastUpdated: Date.now() }),
|
||||||
|
setPaymentAttachments: (value) =>
|
||||||
|
set({ paymentAttachments: value, lastUpdated: Date.now() }),
|
||||||
setLastUpdated: (value) => set({ lastUpdated: value }),
|
setLastUpdated: (value) => set({ lastUpdated: value }),
|
||||||
clear: () => set({ ...initialState }),
|
clear: () => set({ ...initialState }),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BaseResponse } from "@/app/[name]/(Main)/types/Types";
|
import { BaseResponse } from "@/app/[name]/(Main)/types/Types";
|
||||||
|
import { PaymentMethodEnum } from "../enum/Enum";
|
||||||
|
|
||||||
export interface ShipmentMethod {
|
export interface ShipmentMethod {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -107,10 +108,11 @@ export interface PaymentMethodResponse {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
restaurant: Restaurant;
|
restaurant: Restaurant;
|
||||||
title: string;
|
method: PaymentMethodEnum;
|
||||||
method: "CardOnDelivery" | "Cash" | "Online";
|
|
||||||
gateway: string | null;
|
gateway: string | null;
|
||||||
description: string;
|
description: string;
|
||||||
|
cardNumber: string | null;
|
||||||
|
cardOwner: string | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
merchantId: string | null;
|
merchantId: string | null;
|
||||||
@@ -145,6 +147,10 @@ export type CheckoutStoreType = {
|
|||||||
plateNumber: string;
|
plateNumber: string;
|
||||||
} | null
|
} | null
|
||||||
) => void;
|
) => void;
|
||||||
|
paymentDesc: string;
|
||||||
|
setPaymentDesc: (value: string) => void;
|
||||||
|
paymentAttachments: string[];
|
||||||
|
setPaymentAttachments: (value: string[]) => void;
|
||||||
lastUpdated: number | null;
|
lastUpdated: number | null;
|
||||||
setLastUpdated: (value: number | null) => void;
|
setLastUpdated: (value: number | null) => void;
|
||||||
clear: () => void;
|
clear: () => void;
|
||||||
@@ -353,9 +359,11 @@ export interface OrderDetailPaymentMethod {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
deletedAt: string | null;
|
deletedAt: string | null;
|
||||||
restaurant: string;
|
restaurant: string;
|
||||||
method: "CardOnDelivery" | "Cash" | "Online";
|
method: PaymentMethodEnum;
|
||||||
gateway: string | null;
|
gateway: string | null;
|
||||||
description: string;
|
description: string;
|
||||||
|
cardNumber: string | null;
|
||||||
|
cardOwner: string | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
merchantId: string | null;
|
merchantId: string | null;
|
||||||
@@ -420,6 +428,7 @@ export interface OrderDetailPayment {
|
|||||||
paidAt: string | null;
|
paidAt: string | null;
|
||||||
failedAt: string | null;
|
failedAt: string | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
attachments: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDetailCarAddress {
|
export interface OrderDetailCarAddress {
|
||||||
@@ -429,6 +438,17 @@ export interface OrderDetailCarAddress {
|
|||||||
plateNumber: string;
|
plateNumber: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OrderDetailUserAddress {
|
||||||
|
city: string;
|
||||||
|
phone: string;
|
||||||
|
address: string;
|
||||||
|
fullName: string;
|
||||||
|
latitude: number;
|
||||||
|
province: string;
|
||||||
|
longitude: number;
|
||||||
|
postalCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface OrderDetail {
|
export interface OrderDetail {
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -437,7 +457,7 @@ export interface OrderDetail {
|
|||||||
user: OrderDetailUser;
|
user: OrderDetailUser;
|
||||||
restaurant: OrderDetailRestaurant;
|
restaurant: OrderDetailRestaurant;
|
||||||
deliveryMethod: OrderDetailDeliveryMethod;
|
deliveryMethod: OrderDetailDeliveryMethod;
|
||||||
userAddress: OrderAddress | null;
|
userAddress: OrderDetailUserAddress | null;
|
||||||
carAddress: OrderDetailCarAddress | null;
|
carAddress: OrderDetailCarAddress | null;
|
||||||
paymentMethod: OrderDetailPaymentMethod;
|
paymentMethod: OrderDetailPaymentMethod;
|
||||||
orderNumber: number;
|
orderNumber: number;
|
||||||
@@ -455,9 +475,12 @@ export interface OrderDetail {
|
|||||||
status:
|
status:
|
||||||
| "pendingPayment"
|
| "pendingPayment"
|
||||||
| "new"
|
| "new"
|
||||||
|
| "paid"
|
||||||
|
| "confirmed"
|
||||||
| "preparing"
|
| "preparing"
|
||||||
| "ready"
|
| "ready"
|
||||||
| "delivering"
|
| "delivering"
|
||||||
|
| "completed"
|
||||||
| "delivered"
|
| "delivered"
|
||||||
| "cancelled";
|
| "cancelled";
|
||||||
history: OrderDetailHistory[];
|
history: OrderDetailHistory[];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import Image from 'next/image';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from '@/components/button/PrimaryButton';
|
import Button from '@/components/button/PrimaryButton';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
@@ -29,6 +30,11 @@ const getDeliveryMethodTitle = (method: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPaymentMethodTitle = (method: string, fallback?: string) => {
|
||||||
|
const methodKey = method as keyof typeof ordersTranslations.paymentMethod;
|
||||||
|
return ordersTranslations.paymentMethod[methodKey] || fallback || 'نامشخص';
|
||||||
|
};
|
||||||
|
|
||||||
// const getStatusPercentage = (status: string) => {
|
// const getStatusPercentage = (status: string) => {
|
||||||
// switch (status) {
|
// switch (status) {
|
||||||
// case 'new':
|
// case 'new':
|
||||||
@@ -164,6 +170,46 @@ function OrderTrackingPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{orderDetail.data.paymentMethod && (
|
||||||
|
<div className='flex justify-between items-center h-16 border-b border-border'>
|
||||||
|
<span className='text-sm text-muted-foreground'>نوع پرداخت</span>
|
||||||
|
<span className='text-sm font-semibold'>
|
||||||
|
{getPaymentMethodTitle(
|
||||||
|
orderDetail.data.paymentMethod.method,
|
||||||
|
orderDetail.data.paymentMethod.description
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{orderDetail.data.payments?.[0]?.description?.trim() && (
|
||||||
|
<div className='py-3 border-b border-border'>
|
||||||
|
<div className='text-sm text-muted-foreground mb-1'>توضیحات پرداخت</div>
|
||||||
|
<div className='text-sm'>{orderDetail.data.payments[0].description}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{orderDetail.data.payments?.[0]?.attachments?.length ? (
|
||||||
|
<div className='py-3 border-b border-border'>
|
||||||
|
<div className='text-sm text-muted-foreground mb-2'>پیوست پرداخت</div>
|
||||||
|
<div className='flex flex-col gap-2'>
|
||||||
|
{orderDetail.data.payments[0].attachments.map((attachment, index) => (
|
||||||
|
<div
|
||||||
|
key={attachment}
|
||||||
|
className='relative w-full h-40 rounded-normal overflow-hidden border border-border'
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={attachment}
|
||||||
|
alt={`پیوست پرداخت ${index + 1}`}
|
||||||
|
fill
|
||||||
|
className='object-contain'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className='flex justify-between items-center py-4 border-t border-border'>
|
<div className='flex justify-between items-center py-4 border-t border-border'>
|
||||||
<span className='text-sm font-medium'>مجموع سفارش</span>
|
<span className='text-sm font-medium'>مجموع سفارش</span>
|
||||||
<span className='text-sm font-bold '>
|
<span className='text-sm font-bold '>
|
||||||
|
|||||||
@@ -91,11 +91,14 @@ export type SaveAllMethod = {
|
|||||||
deliveryMethodId: string;
|
deliveryMethodId: string;
|
||||||
addressId?: string;
|
addressId?: string;
|
||||||
paymentMethodId: string;
|
paymentMethodId: string;
|
||||||
|
tableNumber?: string;
|
||||||
carAddress?: {
|
carAddress?: {
|
||||||
carModel: string;
|
carModel: string;
|
||||||
carColor: string;
|
carColor: string;
|
||||||
plateNumber: string;
|
plateNumber: string;
|
||||||
};
|
};
|
||||||
|
attachments?: string[];
|
||||||
|
paymentDesc?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CartStoreType = {
|
export type CartStoreType = {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
"paymentMethod": {
|
"paymentMethod": {
|
||||||
"Cash": "پرداخت نقدی",
|
"Cash": "پرداخت نقدی",
|
||||||
"Online": "پرداخت آنلاین",
|
"Online": "پرداخت آنلاین",
|
||||||
"Wallet": "پرداخت با کیف پول"
|
"Wallet": "پرداخت با کیف پول",
|
||||||
|
"CreditCard": "کارت به کارت"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"new": "سفارش جدید",
|
"new": "سفارش جدید",
|
||||||
|
|||||||
@@ -67,6 +67,16 @@
|
|||||||
},
|
},
|
||||||
"SectionPayment": {
|
"SectionPayment": {
|
||||||
"Title": "انتخاب شیوه پرداخت",
|
"Title": "انتخاب شیوه پرداخت",
|
||||||
|
"CreditCard": {
|
||||||
|
"CardOwnerLabel": "نام صاحب کارت",
|
||||||
|
"CardNumberLabel": "شماره کارت",
|
||||||
|
"PaymentDescLabel": "توضیحات پرداخت",
|
||||||
|
"PaymentDescPlaceholder": "مثال: واریز از حساب با شماره ۱۲۳۴",
|
||||||
|
"ReceiptLabel": "تصویر رسید پرداخت",
|
||||||
|
"UploadReceipt": "آپلود رسید",
|
||||||
|
"Uploading": "در حال آپلود...",
|
||||||
|
"UploadSuccess": "رسید با موفقیت آپلود شد"
|
||||||
|
},
|
||||||
"InputPaymentType": {
|
"InputPaymentType": {
|
||||||
"Label": "",
|
"Label": "",
|
||||||
"Options": {
|
"Options": {
|
||||||
|
|||||||
Reference in New Issue
Block a user