This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { Card, Icon, Wallet2 } from 'iconsax-react';
|
import { useGetCartItems } from "@/app/[name]/(Main)/cart/hooks/useCartData";
|
||||||
import { useCallback, useMemo, useRef } from 'react';
|
import { useGetUserWallet } from "@/app/[name]/(Main)/transactions/hooks/useTransactionData";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useGetProfile, useSingleUpload } from "@/app/[name]/(Profile)/profile/hooks/userProfileData";
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
import { toast } from "@/components/Toast";
|
||||||
import { useGetPaymentMethod } from '../../hooks/useOrderData';
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { useCheckoutStore } from '../../store/Store';
|
import { extractErrorMessage } from "@/lib/func";
|
||||||
import { useGetUserWallet } from '@/app/[name]/(Main)/transactions/hooks/useTransactionData';
|
import { Card, Icon, Wallet2 } from "iconsax-react";
|
||||||
import { useGetCartItems } from '@/app/[name]/(Main)/cart/hooks/useCartData';
|
import Image from "next/image";
|
||||||
import { useGetProfile, useSingleUpload } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
|
import { useCallback, useMemo, useRef } from "react";
|
||||||
import { PaymentMethodEnum } from '../../enum/Enum';
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from '@/components/Toast';
|
import { PaymentMethodEnum } from "../../enum/Enum";
|
||||||
import { extractErrorMessage } from '@/lib/func';
|
import { useGetPaymentMethod } from "../../hooks/useOrderData";
|
||||||
import Image from 'next/image';
|
import { useCheckoutStore } from "../../store/Store";
|
||||||
|
|
||||||
type PaymentSectionProps = {
|
type PaymentSectionProps = {
|
||||||
paymentType: string;
|
paymentType: string;
|
||||||
@@ -30,17 +30,10 @@ const getIconByMethod = (method: PaymentMethodEnum): Icon => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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 {
|
const { setIsSelectedPayment, paymentDesc, setPaymentDesc, paymentAttachments, setPaymentAttachments } = useCheckoutStore();
|
||||||
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);
|
||||||
@@ -51,24 +44,24 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
const totalAmount = cartData?.data?.total ?? 0;
|
const totalAmount = cartData?.data?.total ?? 0;
|
||||||
const isWalletInsufficient = walletAmount < totalAmount;
|
const isWalletInsufficient = walletAmount < totalAmount;
|
||||||
|
|
||||||
const formatPrice = useCallback(
|
const formatPrice = useCallback((value: number) => value.toLocaleString("fa-IR"), []);
|
||||||
(value: number) => value.toLocaleString('fa-IR'),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getPaymentMethodTranslation = useCallback((method: string, fallbackTitle: string) => {
|
const getPaymentMethodTranslation = useCallback(
|
||||||
|
(method: string, fallbackTitle: string) => {
|
||||||
const translationKey = `paymentMethod.${method}`;
|
const translationKey = `paymentMethod.${method}`;
|
||||||
const translation = tOrders(translationKey);
|
const translation = tOrders(translationKey);
|
||||||
return translation !== translationKey ? translation : fallbackTitle;
|
return translation !== translationKey ? translation : fallbackTitle;
|
||||||
}, [tOrders]);
|
},
|
||||||
|
[tOrders],
|
||||||
|
);
|
||||||
|
|
||||||
const paymentOptions = useMemo(() => {
|
const paymentOptions = useMemo(() => {
|
||||||
if (!paymentMethod?.data) return [];
|
if (!paymentMethod?.data) return [];
|
||||||
|
|
||||||
return paymentMethod.data
|
return paymentMethod.data
|
||||||
.filter(item => item.enabled)
|
.filter((item) => item.enabled)
|
||||||
.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.description),
|
label: getPaymentMethodTranslation(item.method, item.description),
|
||||||
icon: getIconByMethod(item.method),
|
icon: getIconByMethod(item.method),
|
||||||
@@ -81,21 +74,33 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
}, [paymentMethod, getPaymentMethodTranslation]);
|
}, [paymentMethod, getPaymentMethodTranslation]);
|
||||||
|
|
||||||
const selectedCreditCard = useMemo(() => {
|
const selectedCreditCard = useMemo(() => {
|
||||||
const selected = paymentOptions.find(opt => opt.id === paymentType);
|
const selected = paymentOptions.find((opt) => opt.id === paymentType);
|
||||||
if (selected?.method === PaymentMethodEnum.CreditCard) return selected;
|
if (selected?.method === PaymentMethodEnum.CreditCard) return selected;
|
||||||
return null;
|
return null;
|
||||||
}, [paymentOptions, paymentType]);
|
}, [paymentOptions, paymentType]);
|
||||||
|
|
||||||
const handlePaymentTypeChange = (value: string) => {
|
const handlePaymentTypeChange = (value: string) => {
|
||||||
const selected = paymentOptions.find(opt => opt.id === value);
|
const selected = paymentOptions.find((opt) => opt.id === value);
|
||||||
if (selected?.method !== PaymentMethodEnum.CreditCard) {
|
if (selected?.method !== PaymentMethodEnum.CreditCard) {
|
||||||
setPaymentDesc('');
|
setPaymentDesc("");
|
||||||
setPaymentAttachments([]);
|
setPaymentAttachments([]);
|
||||||
}
|
}
|
||||||
onPaymentTypeChange(value);
|
onPaymentTypeChange(value);
|
||||||
setIsSelectedPayment(true);
|
setIsSelectedPayment(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopyCardNumber = useCallback(
|
||||||
|
async (cardNumber: string) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(cardNumber);
|
||||||
|
toast(tOrderDetail("SectionPayment.CreditCard.CopySuccess"), "success");
|
||||||
|
} catch {
|
||||||
|
toast(tOrderDetail("SectionPayment.CreditCard.CopyError"), "error");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[tOrderDetail],
|
||||||
|
);
|
||||||
|
|
||||||
const handleReceiptUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleReceiptUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
@@ -104,13 +109,13 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
const response = await singleUpload(file);
|
const response = await singleUpload(file);
|
||||||
if (response?.data?.url) {
|
if (response?.data?.url) {
|
||||||
setPaymentAttachments([response.data.url]);
|
setPaymentAttachments([response.data.url]);
|
||||||
toast(tOrderDetail('SectionPayment.CreditCard.UploadSuccess'), 'success');
|
toast(tOrderDetail("SectionPayment.CreditCard.UploadSuccess"), "success");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast(extractErrorMessage(error), 'error');
|
toast(extractErrorMessage(error), "error");
|
||||||
} finally {
|
} finally {
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.value = '';
|
fileInputRef.current.value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -120,38 +125,19 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
<h3 className="text-sm2 font-medium leading-5">{tOrderDetail("SectionPayment.Title")}</h3>
|
<h3 className="text-sm2 font-medium leading-5">{tOrderDetail("SectionPayment.Title")}</h3>
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup value={paymentType} onValueChange={handlePaymentTypeChange} className="flex flex-col gap-6 mt-6">
|
||||||
value={paymentType}
|
|
||||||
onValueChange={handlePaymentTypeChange}
|
|
||||||
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;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={id} className='flex items-center justify-between'>
|
<div key={id} className="flex items-center justify-between">
|
||||||
<RadioGroupItem
|
<RadioGroupItem value={id} id={`paymentMethod${id}`} disabled={isDisabled} />
|
||||||
value={id}
|
<label className={`flex items-center gap-2 ${isDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"}`} htmlFor={`paymentMethod${id}`}>
|
||||||
id={`paymentMethod${id}`}
|
<div className="flex gap-1">
|
||||||
disabled={isDisabled}
|
{isWallet && <span className="text-xs text-gray-500 dark:text-gray-400 mt-1">({formatPrice(walletAmount)} T)</span>}
|
||||||
/>
|
<span className="text-xs mt-0.5">{label}</span>
|
||||||
<label
|
|
||||||
className={`flex items-center gap-2 ${isDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`}
|
|
||||||
htmlFor={`paymentMethod${id}`}
|
|
||||||
>
|
|
||||||
<div className='flex gap-1'>
|
|
||||||
{isWallet && (
|
|
||||||
<span className='text-xs text-gray-500 dark:text-gray-400 mt-1'>
|
|
||||||
({formatPrice(walletAmount)} T)
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className='text-xs mt-0.5'>{label}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<Icon
|
<Icon size={16} color="currentColor" className="mr-2" />
|
||||||
size={16}
|
|
||||||
color='currentColor'
|
|
||||||
className='mr-2'
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -159,85 +145,71 @@ export const PaymentSection = ({ paymentType, onPaymentTypeChange }: PaymentSect
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
{selectedCreditCard && (
|
{selectedCreditCard && (
|
||||||
<div className='mt-6 pt-4 border-t border-border flex flex-col gap-4'>
|
<div className="mt-6 pt-4 border-t border-border flex flex-col gap-4">
|
||||||
|
{(selectedCreditCard.cardOwner || selectedCreditCard.cardNumber) && (
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
{selectedCreditCard.cardOwner && (
|
{selectedCreditCard.cardOwner && (
|
||||||
<div>
|
<div>
|
||||||
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
<span className="text-xs text-gray-500 dark:text-gray-400">{tOrderDetail("SectionPayment.CreditCard.CardOwnerLabel")}</span>
|
||||||
{tOrderDetail('SectionPayment.CreditCard.CardOwnerLabel')}
|
<p className="text-sm font-medium mt-1">{selectedCreditCard.cardOwner}</p>
|
||||||
</span>
|
|
||||||
<p className='text-sm font-medium mt-1'>
|
|
||||||
{selectedCreditCard.cardOwner}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{selectedCreditCard.cardNumber && (
|
{selectedCreditCard.cardNumber && (
|
||||||
<div>
|
<div className="text-left">
|
||||||
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
<span className="text-xs text-gray-500 dark:text-gray-400">{tOrderDetail("SectionPayment.CreditCard.CardNumberLabel")}</span>
|
||||||
{tOrderDetail('SectionPayment.CreditCard.CardNumberLabel')}
|
<button
|
||||||
</span>
|
type="button"
|
||||||
<p className='text-sm font-medium mt-1' dir='ltr'>
|
className="block text-sm font-medium mt-1 cursor-pointer hover:text-primary transition-colors"
|
||||||
|
dir="ltr"
|
||||||
|
onClick={() => handleCopyCardNumber(selectedCreditCard.cardNumber!)}
|
||||||
|
>
|
||||||
{selectedCreditCard.cardNumber}
|
{selectedCreditCard.cardNumber}
|
||||||
</p>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{selectedCreditCard.description && (
|
</div>
|
||||||
|
)}
|
||||||
|
{/* {selectedCreditCard.description && (
|
||||||
<p className='text-xs text-gray-500 dark:text-gray-400 leading-5'>
|
<p className='text-xs text-gray-500 dark:text-gray-400 leading-5'>
|
||||||
{selectedCreditCard.description}
|
{selectedCreditCard.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)} */}
|
||||||
<div>
|
<div>
|
||||||
<label className='text-xs text-gray-500 dark:text-gray-400' htmlFor='paymentDesc'>
|
<label className="text-xs text-gray-500 dark:text-gray-400" htmlFor="paymentDesc">
|
||||||
{tOrderDetail('SectionPayment.CreditCard.PaymentDescLabel')}
|
{tOrderDetail("SectionPayment.CreditCard.PaymentDescLabel")}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id='paymentDesc'
|
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'
|
className="w-full px-2 py-2.5 mt-2 text-xs leading-6 outline-0 border border-border rounded-normal resize-none focus:ring-0"
|
||||||
placeholder={tOrderDetail('SectionPayment.CreditCard.PaymentDescPlaceholder')}
|
placeholder={tOrderDetail("SectionPayment.CreditCard.PaymentDescPlaceholder")}
|
||||||
value={paymentDesc}
|
value={paymentDesc}
|
||||||
onChange={(e) => setPaymentDesc(e.target.value)}
|
onChange={(e) => setPaymentDesc(e.target.value)}
|
||||||
rows={2}
|
rows={2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className='text-xs text-gray-500 dark:text-gray-400'>
|
<span className="text-xs text-gray-500 dark:text-gray-400">{tOrderDetail("SectionPayment.CreditCard.ReceiptLabel")}</span>
|
||||||
{tOrderDetail('SectionPayment.CreditCard.ReceiptLabel')}
|
<input ref={fileInputRef} type="file" accept="image/*" className="hidden" onChange={handleReceiptUpload} />
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
ref={fileInputRef}
|
|
||||||
type='file'
|
|
||||||
accept='image/*'
|
|
||||||
className='hidden'
|
|
||||||
onChange={handleReceiptUpload}
|
|
||||||
/>
|
|
||||||
{paymentAttachments[0] ? (
|
{paymentAttachments[0] ? (
|
||||||
<div className='mt-2 relative w-full h-32 rounded-normal overflow-hidden border border-border'>
|
<div className="mt-2 relative w-full h-32 rounded-normal overflow-hidden border border-border">
|
||||||
<Image
|
<Image src={paymentAttachments[0]} alt="receipt" fill className="object-contain" />
|
||||||
src={paymentAttachments[0]}
|
|
||||||
alt='receipt'
|
|
||||||
fill
|
|
||||||
className='object-contain'
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
type='button'
|
type="button"
|
||||||
className='absolute top-2 left-2 text-xs bg-white/90 dark:bg-background/90 px-2 py-1 rounded-normal'
|
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()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={isUploading}
|
disabled={isUploading}
|
||||||
>
|
>
|
||||||
{isUploading
|
{isUploading ? tOrderDetail("SectionPayment.CreditCard.Uploading") : tOrderDetail("SectionPayment.CreditCard.UploadReceipt")}
|
||||||
? tOrderDetail('SectionPayment.CreditCard.Uploading')
|
|
||||||
: tOrderDetail('SectionPayment.CreditCard.UploadReceipt')}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type='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'
|
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()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={isUploading}
|
disabled={isUploading}
|
||||||
>
|
>
|
||||||
{isUploading
|
{isUploading ? tOrderDetail("SectionPayment.CreditCard.Uploading") : tOrderDetail("SectionPayment.CreditCard.UploadReceipt")}
|
||||||
? tOrderDetail('SectionPayment.CreditCard.Uploading')
|
|
||||||
: tOrderDetail('SectionPayment.CreditCard.UploadReceipt')}
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
'use client';
|
"use client";
|
||||||
|
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import Button from "@/components/button/PrimaryButton";
|
||||||
import Image from 'next/image';
|
import { toast } from "@/components/Toast";
|
||||||
import React from 'react';
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import Button from '@/components/button/PrimaryButton';
|
import Prompt from "@/components/utils/Prompt";
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import useToggle from "@/hooks/helpers/useToggle";
|
||||||
import { Clock } from 'iconsax-react';
|
import { extractErrorMessage } from "@/lib/func";
|
||||||
import clsx from 'clsx';
|
import ordersTranslations from "@/locales/fa/orders.json";
|
||||||
import useToggle from '@/hooks/helpers/useToggle';
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import Prompt from '@/components/utils/Prompt';
|
import clsx from "clsx";
|
||||||
import { useCancelOrder, useGetOrderDetail } from '../../checkout/hooks/useOrderData';
|
import { Clock } from "iconsax-react";
|
||||||
import ordersTranslations from '@/locales/fa/orders.json';
|
import Image from "next/image";
|
||||||
import { toast } from '@/components/Toast';
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import { extractErrorMessage } from '@/lib/func';
|
import React from "react";
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useCancelOrder, useGetOrderDetail } from "../../checkout/hooks/useOrderData";
|
||||||
|
|
||||||
const getDeliveryMethodTitle = (method: string) => {
|
const getDeliveryMethodTitle = (method: string) => {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case 'deliveryCourier':
|
case "deliveryCourier":
|
||||||
return 'ارسال توسط پیک';
|
return "ارسال توسط پیک";
|
||||||
case 'deliveryCar':
|
case "deliveryCar":
|
||||||
return 'تحویل به خودرو';
|
return "تحویل به خودرو";
|
||||||
case 'pickup':
|
case "pickup":
|
||||||
return 'تحویل حضوری';
|
return "تحویل حضوری";
|
||||||
case 'dineIn':
|
case "dineIn":
|
||||||
return 'سرو در رستوران';
|
return "سرو در رستوران";
|
||||||
default:
|
default:
|
||||||
return 'روش ارسال';
|
return "روش ارسال";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPaymentMethodTitle = (method: string, fallback?: string) => {
|
const getPaymentMethodTitle = (method: string, fallback?: string) => {
|
||||||
const methodKey = method as keyof typeof ordersTranslations.paymentMethod;
|
const methodKey = method as keyof typeof ordersTranslations.paymentMethod;
|
||||||
return ordersTranslations.paymentMethod[methodKey] || fallback || 'نامشخص';
|
return ordersTranslations.paymentMethod[methodKey] || fallback || "نامشخص";
|
||||||
};
|
};
|
||||||
|
|
||||||
// const getStatusPercentage = (status: string) => {
|
// const getStatusPercentage = (status: string) => {
|
||||||
@@ -60,15 +60,15 @@ const getPaymentMethodTitle = (method: string, fallback?: string) => {
|
|||||||
|
|
||||||
const getStatusText = (status: string): string => {
|
const getStatusText = (status: string): string => {
|
||||||
const statusKey = status as keyof typeof ordersTranslations.status;
|
const statusKey = status as keyof typeof ordersTranslations.status;
|
||||||
return ordersTranslations.status[statusKey] || 'نامشخص';
|
return ordersTranslations.status[statusKey] || "نامشخص";
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (dateString: string): string => {
|
const formatDate = (dateString: string): string => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleDateString('fa-IR', {
|
return date.toLocaleDateString("fa-IR", {
|
||||||
year: 'numeric',
|
year: "numeric",
|
||||||
month: 'long',
|
month: "long",
|
||||||
day: 'numeric'
|
day: "numeric",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,175 +89,145 @@ function OrderTrackingPage() {
|
|||||||
const { mutate: cancelOrder } = useCancelOrder();
|
const { mutate: cancelOrder } = useCancelOrder();
|
||||||
|
|
||||||
const onCancelOrder = (e: React.MouseEvent | null) => {
|
const onCancelOrder = (e: React.MouseEvent | null) => {
|
||||||
|
|
||||||
if (!e || !id) return;
|
if (!e || !id) return;
|
||||||
|
|
||||||
cancelOrder(id as string, {
|
cancelOrder(id as string, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toggleCancelModal();
|
toggleCancelModal();
|
||||||
queryClient.invalidateQueries({ queryKey: ['order-detail', id] });
|
queryClient.invalidateQueries({ queryKey: ["order-detail", id] });
|
||||||
toast('سفارش با موفقیت لغو شد', 'success');
|
toast("سفارش با موفقیت لغو شد", "success");
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast(extractErrorMessage(error), 'error');
|
toast(extractErrorMessage(error), "error");
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-container flex flex-col">
|
<div className="fixed inset-0 bg-container flex flex-col">
|
||||||
<div className='flex-1 overflow-y-auto px-4 py-6'>
|
<div className="flex-1 overflow-y-auto px-4 py-6">
|
||||||
<div className='max-w-2xl mx-auto flex flex-col gap-6'>
|
<div className="max-w-2xl mx-auto flex flex-col gap-6">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Skeleton className='h-6 w-48 mx-auto' />
|
<Skeleton className="h-6 w-48 mx-auto" />
|
||||||
<div className='flex flex-col gap-4'>
|
<div className="flex flex-col gap-4">
|
||||||
<Skeleton className='h-5 w-full' />
|
<Skeleton className="h-5 w-full" />
|
||||||
<Skeleton className='h-5 w-full' />
|
<Skeleton className="h-5 w-full" />
|
||||||
<Skeleton className='h-20 w-full' />
|
<Skeleton className="h-20 w-full" />
|
||||||
<Skeleton className='h-5 w-full' />
|
<Skeleton className="h-5 w-full" />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : orderDetail?.data ? (
|
) : orderDetail?.data ? (
|
||||||
<>
|
<>
|
||||||
<h1 className='text-lg font-semibold text-center'>
|
<h1 className="text-lg font-semibold text-center">{getDeliveryMethodTitle(orderDetail.data.deliveryMethod.method)}</h1>
|
||||||
{getDeliveryMethodTitle(orderDetail.data.deliveryMethod.method)}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div className='flex flex-col'>
|
<div className="flex flex-col">
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<div className="flex justify-between items-center h-16 border-b border-border">
|
||||||
<span className='text-sm text-muted-foreground flex items-center gap-2'>
|
<span className="text-sm text-muted-foreground flex items-center gap-2">
|
||||||
<Clock className='stroke-current' size={16} />
|
<Clock className="stroke-current" size={16} />
|
||||||
زمان سفارش
|
زمان سفارش
|
||||||
</span>
|
</span>
|
||||||
<div className='flex items-end gap-0.5'>
|
<div className="flex items-end gap-0.5">
|
||||||
<span className='text-sm font-semibold'>{formatDate(orderDetail.data.createdAt)}</span>
|
<span className="text-sm font-semibold">{formatDate(orderDetail.data.createdAt)}</span>
|
||||||
{/* <span className='text-xs text-muted-foreground'>{formatTime(orderDetail.data.createdAt)}</span> */}
|
{/* <span className='text-xs text-muted-foreground'>{formatTime(orderDetail.data.createdAt)}</span> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<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 text-muted-foreground">نوع ارسال</span>
|
||||||
<span className='text-sm font-semibold'>{orderDetail.data?.deliveryMethod?.description}</span>
|
<span className="text-sm font-semibold">{orderDetail.data?.deliveryMethod?.description}</span>
|
||||||
</div>
|
</div>
|
||||||
{orderDetail.data.carAddress && (
|
{orderDetail.data.carAddress && (
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<div className="flex justify-between items-center h-16 border-b border-border">
|
||||||
<div className='text-sm text-muted-foreground mb-1'>اطلاعات خودرو</div>
|
<div className="text-sm text-muted-foreground mb-1">اطلاعات خودرو</div>
|
||||||
<div className='text-sm font-semibold'>
|
<div className="text-sm font-semibold">
|
||||||
{orderDetail.data.carAddress.carModel} {orderDetail.data.carAddress.carColor} - پلاک: {orderDetail.data.carAddress.plateNumber}
|
{orderDetail.data.carAddress.carModel} {orderDetail.data.carAddress.carColor} - پلاک: {orderDetail.data.carAddress.plateNumber}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<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 text-muted-foreground">شماره سفارش</span>
|
||||||
<span className='text-sm font-semibold'>#{orderDetail.data.orderNumber}</span>
|
<span className="text-sm font-semibold">#{orderDetail.data.orderNumber}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<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 text-muted-foreground">وضعیت سفارش</span>
|
||||||
<span className='text-sm font-semibold'>{getStatusText(orderDetail.data.status)}</span>
|
<span className="text-sm font-semibold">{getStatusText(orderDetail.data.status)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{orderDetail.data.tableNumber && (
|
{orderDetail.data.tableNumber && (
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<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 text-muted-foreground">شماره میز</span>
|
||||||
<span className='text-sm font-semibold'>#{orderDetail.data.tableNumber}</span>
|
<span className="text-sm font-semibold">#{orderDetail.data.tableNumber}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{orderDetail.data.userAddress && (
|
{orderDetail.data.userAddress && (
|
||||||
<div className='py-3 border-b border-border'>
|
<div className="py-3 border-b border-border">
|
||||||
<div className='text-sm text-muted-foreground mb-1'>آدرس تحویل</div>
|
<div className="text-sm text-muted-foreground mb-1">آدرس تحویل</div>
|
||||||
<div className='text-sm'>{orderDetail.data.userAddress.address}</div>
|
<div className="text-sm">{orderDetail.data.userAddress.address}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{orderDetail.data.paymentMethod && (
|
{orderDetail.data.paymentMethod && (
|
||||||
<div className='flex justify-between items-center h-16 border-b border-border'>
|
<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 text-muted-foreground">نوع پرداخت</span>
|
||||||
<span className='text-sm font-semibold'>
|
<span className="text-sm font-semibold">{getPaymentMethodTitle(orderDetail.data.paymentMethod.method, orderDetail.data.paymentMethod.description)}</span>
|
||||||
{getPaymentMethodTitle(
|
|
||||||
orderDetail.data.paymentMethod.method,
|
|
||||||
orderDetail.data.paymentMethod.description
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{orderDetail.data.payments?.[0]?.description?.trim() && (
|
{orderDetail.data.payments?.[0]?.description?.trim() && (
|
||||||
<div className='py-3 border-b border-border'>
|
<div className="py-3 border-b border-border">
|
||||||
<div className='text-sm text-muted-foreground mb-1'>توضیحات پرداخت</div>
|
<div className="text-sm text-muted-foreground mb-1">توضیحات پرداخت</div>
|
||||||
<div className='text-sm'>{orderDetail.data.payments[0].description}</div>
|
<div className="text-sm">{orderDetail.data.payments[0].description}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{orderDetail.data.payments?.[0]?.attachments?.length ? (
|
{orderDetail.data.payments?.[0]?.attachments?.length ? (
|
||||||
<div className='py-3 border-b border-border'>
|
<div className="py-3 border-b border-border">
|
||||||
<div className='text-sm text-muted-foreground mb-2'>پیوست پرداخت</div>
|
<div className="text-sm text-muted-foreground mb-2">پیوست پرداخت</div>
|
||||||
<div className='flex flex-col gap-2'>
|
<div className="flex flex-col gap-2">
|
||||||
{orderDetail.data.payments[0].attachments.map((attachment, index) => (
|
{orderDetail.data.payments[0].attachments.map((attachment, index) => (
|
||||||
<div
|
<div key={attachment} className="relative w-full h-40 rounded-normal overflow-hidden border border-border">
|
||||||
key={attachment}
|
<Image src={attachment} alt={`پیوست پرداخت ${index + 1}`} fill className="object-contain" />
|
||||||
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : 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 ">{orderDetail.data.total.toLocaleString("fa-IR")} تومان</span>
|
||||||
{orderDetail.data.total.toLocaleString('fa-IR')} تومان
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className='text-center text-muted-foreground py-12'>
|
<div className="text-center text-muted-foreground py-12">اطلاعات سفارش یافت نشد</div>
|
||||||
اطلاعات سفارش یافت نشد
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='p-4 border-t border-border bg-container'>
|
<div className="p-4 border-t border-border bg-container">
|
||||||
<div className='max-w-2xl mx-auto grid grid-cols-2 gap-4'>
|
<div className="max-w-2xl mx-auto grid grid-cols-2 gap-4">
|
||||||
<Button
|
<Button className="dark:bg-white dark:text-black! dark:hover:bg-gray-100" onClick={() => router.push(`/${name}`)} type="button">
|
||||||
className='dark:bg-white dark:text-black! dark:hover:bg-gray-100'
|
|
||||||
onClick={() => router.push(`/${name}`)}
|
|
||||||
type='button'>
|
|
||||||
بازگشت به منو
|
بازگشت به منو
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type='submit'
|
type="submit"
|
||||||
onClick={toggleCancelModal}
|
onClick={toggleCancelModal}
|
||||||
className='bg-disabled! text-foreground!'
|
className="bg-disabled! text-foreground!"
|
||||||
disabled={
|
disabled={orderDetail?.data?.status === "cancelled" || orderDetail?.data?.status === "delivered" || orderDetail?.data?.status === "delivering"}
|
||||||
orderDetail?.data?.status === 'cancelled' ||
|
|
||||||
orderDetail?.data?.status === 'delivered' ||
|
|
||||||
orderDetail?.data?.status === 'delivering'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
لغو سفارش
|
لغو سفارش
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={clsx(
|
<div className={clsx("fixed inset-0 z-1001", !cancelModal && "pointer-events-none")}>
|
||||||
'fixed inset-0 z-1001',
|
|
||||||
!cancelModal && 'pointer-events-none'
|
|
||||||
)}>
|
|
||||||
<Prompt
|
<Prompt
|
||||||
title={'لغو سفارش'}
|
title={"لغو سفارش"}
|
||||||
description={'آیا از درخواست خود اطمینان دارید؟'}
|
description={"آیا از درخواست خود اطمینان دارید؟"}
|
||||||
textConfirm={'بله'}
|
textConfirm={"بله"}
|
||||||
textCancel={'منصرف شدم'}
|
textCancel={"منصرف شدم"}
|
||||||
onConfirm={onCancelOrder}
|
onConfirm={onCancelOrder}
|
||||||
visible={cancelModal}
|
visible={cancelModal}
|
||||||
onClick={toggleCancelModal}
|
onClick={toggleCancelModal}
|
||||||
|
|||||||
@@ -75,7 +75,9 @@
|
|||||||
"ReceiptLabel": "تصویر رسید پرداخت",
|
"ReceiptLabel": "تصویر رسید پرداخت",
|
||||||
"UploadReceipt": "آپلود رسید",
|
"UploadReceipt": "آپلود رسید",
|
||||||
"Uploading": "در حال آپلود...",
|
"Uploading": "در حال آپلود...",
|
||||||
"UploadSuccess": "رسید با موفقیت آپلود شد"
|
"UploadSuccess": "رسید با موفقیت آپلود شد",
|
||||||
|
"CopySuccess": "شماره کارت کپی شد",
|
||||||
|
"CopyError": "خطا در کپی شماره کارت"
|
||||||
},
|
},
|
||||||
"InputPaymentType": {
|
"InputPaymentType": {
|
||||||
"Label": "",
|
"Label": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user