Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e27bf39e8 | |||
| 20309be061 | |||
| 22e443cf7b | |||
| 1dd639c0b1 | |||
| da738a8adc |
+1
-1
@@ -770,7 +770,7 @@
|
|||||||
"payment": {
|
"payment": {
|
||||||
"Online": "آنلاین",
|
"Online": "آنلاین",
|
||||||
"Cash": "نقدی",
|
"Cash": "نقدی",
|
||||||
"CardOnDelivery": "کارت هنگام تحویل",
|
"CreditCard": "کارت به کارت",
|
||||||
"Wallet": "کیف پول",
|
"Wallet": "کیف پول",
|
||||||
"zarinpal": "زرینپال",
|
"zarinpal": "زرینپال",
|
||||||
"payments": "پرداخت ها",
|
"payments": "پرداخت ها",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type FC, useState } from 'react'
|
import { type FC, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
useGetOrderById,
|
useGetOrderById,
|
||||||
useVerifyCashPayment,
|
useVerifyPayment,
|
||||||
useChangeOrderStatus,
|
useChangeOrderStatus,
|
||||||
useRefundPayment
|
useRefundPayment
|
||||||
} from './hooks/useOrderData'
|
} from './hooks/useOrderData'
|
||||||
@@ -14,6 +14,7 @@ import ModalConfrim from '@/components/ModalConfrim'
|
|||||||
import CustomerInfo from './components/CustomerInfo'
|
import CustomerInfo from './components/CustomerInfo'
|
||||||
import OrderItemsDetails from './components/OrderItemsDetails'
|
import OrderItemsDetails from './components/OrderItemsDetails'
|
||||||
import PaymentInfo from './components/PaymentInfo'
|
import PaymentInfo from './components/PaymentInfo'
|
||||||
|
import PaymentVerificationBox from './components/PaymentVerificationBox'
|
||||||
import OrderActions from './components/OrderActions'
|
import OrderActions from './components/OrderActions'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { extractErrorMessage } from '@/config/func'
|
import { extractErrorMessage } from '@/config/func'
|
||||||
@@ -22,7 +23,7 @@ import { printOrderReceipt } from './print/orderReceiptPrint'
|
|||||||
const OrderDetails: FC = () => {
|
const OrderDetails: FC = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const { data: orderData, isLoading } = useGetOrderById(id!)
|
const { data: orderData, isLoading } = useGetOrderById(id!)
|
||||||
const { mutate: verifyCashPayment, isPending: isVerifyingPayment } = useVerifyCashPayment()
|
const { mutate: verifyPayment, isPending: isVerifyingPayment } = useVerifyPayment()
|
||||||
const { mutate: changeOrderStatus, isPending: isChangingStatus } = useChangeOrderStatus()
|
const { mutate: changeOrderStatus, isPending: isChangingStatus } = useChangeOrderStatus()
|
||||||
const { mutate: refundPayment, isPending: isRefunding } = useRefundPayment()
|
const { mutate: refundPayment, isPending: isRefunding } = useRefundPayment()
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ const OrderDetails: FC = () => {
|
|||||||
|
|
||||||
// منطق نمایش دکمهها
|
// منطق نمایش دکمهها
|
||||||
const isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
|
const isCashPayment = order.paymentMethod?.method === PaymentMethodEnum.Cash
|
||||||
|
const isCreditCardPayment = order.paymentMethod?.method === PaymentMethodEnum.CreditCard
|
||||||
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
|
const isPendingPayment = order.status === OrderStatus.PENDING_PAYMENT
|
||||||
const isOrderPaid = order.status === OrderStatus.PAID
|
const isOrderPaid = order.status === OrderStatus.PAID
|
||||||
const isPreparing = order.status === OrderStatus.PREPARING
|
const isPreparing = order.status === OrderStatus.PREPARING
|
||||||
@@ -102,9 +104,7 @@ const OrderDetails: FC = () => {
|
|||||||
const isDineInOrCarDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DineIn || order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCar
|
const isDineInOrCarDelivery = order.deliveryMethod?.method === DeliveryMethodEnum.DineIn || order.deliveryMethod?.method === DeliveryMethodEnum.DeliveryCar
|
||||||
const isCustomerPickup = order.deliveryMethod?.method === DeliveryMethodEnum.CustomerPickup
|
const isCustomerPickup = order.deliveryMethod?.method === DeliveryMethodEnum.CustomerPickup
|
||||||
|
|
||||||
// نمایش دکمه تایید پرداخت نقدی
|
const showVerifyPaymentBox = (isCashPayment || isCreditCardPayment) && isPaymentPending && !isCanceled
|
||||||
// وقتی روش پرداخت نقدی است و وضعیت پرداخت Pending باشد
|
|
||||||
const showVerifyCashPaymentButton = isCashPayment && isPaymentPending
|
|
||||||
|
|
||||||
// نمایش دکمه ارسال به آشپزخانه
|
// نمایش دکمه ارسال به آشپزخانه
|
||||||
// برای pendingPayment با پرداخت نقدی یا برای paid
|
// برای pendingPayment با پرداخت نقدی یا برای paid
|
||||||
@@ -114,10 +114,10 @@ const OrderDetails: FC = () => {
|
|||||||
// همیشه نمایش داده میشود مگر اینکه سفارش قبلاً کنسل شده باشد
|
// همیشه نمایش داده میشود مگر اینکه سفارش قبلاً کنسل شده باشد
|
||||||
const showCancelButton = !isCanceled
|
const showCancelButton = !isCanceled
|
||||||
|
|
||||||
const handleVerifyCashPayment = () => {
|
const handleVerifyPayment = () => {
|
||||||
const paymentId = order.paymentId || order.payments?.[0]?.id
|
const paymentId = order.paymentId || order.payments?.[0]?.id
|
||||||
if (paymentId) {
|
if (paymentId) {
|
||||||
verifyCashPayment(paymentId, {
|
verifyPayment(paymentId, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
// Success handled by query invalidation
|
// Success handled by query invalidation
|
||||||
},
|
},
|
||||||
@@ -232,9 +232,15 @@ const OrderDetails: FC = () => {
|
|||||||
getPaymentStatusTextColor={getPaymentStatusTextColor}
|
getPaymentStatusTextColor={getPaymentStatusTextColor}
|
||||||
getPaymentStatusText={getPaymentStatusText}
|
getPaymentStatusText={getPaymentStatusText}
|
||||||
/>
|
/>
|
||||||
|
{showVerifyPaymentBox && (
|
||||||
|
<PaymentVerificationBox
|
||||||
|
order={order}
|
||||||
|
isVerifyingPayment={isVerifyingPayment}
|
||||||
|
onVerifyPayment={handleVerifyPayment}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<OrderActions
|
<OrderActions
|
||||||
onPrint={handlePrintOrder}
|
onPrint={handlePrintOrder}
|
||||||
showVerifyCashPaymentButton={showVerifyCashPaymentButton}
|
|
||||||
showSendToKitchenButton={showSendToKitchenButton}
|
showSendToKitchenButton={showSendToKitchenButton}
|
||||||
showCancelButton={showCancelButton}
|
showCancelButton={showCancelButton}
|
||||||
isPreparing={isPreparing}
|
isPreparing={isPreparing}
|
||||||
@@ -242,9 +248,7 @@ const OrderDetails: FC = () => {
|
|||||||
isDineInOrCarDelivery={isDineInOrCarDelivery}
|
isDineInOrCarDelivery={isDineInOrCarDelivery}
|
||||||
isCustomerPickup={isCustomerPickup}
|
isCustomerPickup={isCustomerPickup}
|
||||||
isCanceled={isCanceled}
|
isCanceled={isCanceled}
|
||||||
isVerifyingPayment={isVerifyingPayment}
|
|
||||||
isChangingStatus={isChangingStatus}
|
isChangingStatus={isChangingStatus}
|
||||||
onVerifyCashPayment={handleVerifyCashPayment}
|
|
||||||
onSendToKitchen={handleSendToKitchen}
|
onSendToKitchen={handleSendToKitchen}
|
||||||
onDeliverToCourier={handleDeliverToCourier}
|
onDeliverToCourier={handleDeliverToCourier}
|
||||||
onDeliverToWaiter={handleDeliverToWaiter}
|
onDeliverToWaiter={handleDeliverToWaiter}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { type FC, useState, useEffect } from 'react'
|
|||||||
|
|
||||||
interface OrderActionsProps {
|
interface OrderActionsProps {
|
||||||
onPrint: () => void
|
onPrint: () => void
|
||||||
showVerifyCashPaymentButton: boolean
|
|
||||||
showSendToKitchenButton: boolean
|
showSendToKitchenButton: boolean
|
||||||
showCancelButton: boolean
|
showCancelButton: boolean
|
||||||
isPreparing: boolean
|
isPreparing: boolean
|
||||||
@@ -11,9 +10,7 @@ interface OrderActionsProps {
|
|||||||
isDineInOrCarDelivery: boolean
|
isDineInOrCarDelivery: boolean
|
||||||
isCustomerPickup: boolean
|
isCustomerPickup: boolean
|
||||||
isCanceled: boolean
|
isCanceled: boolean
|
||||||
isVerifyingPayment: boolean
|
|
||||||
isChangingStatus: boolean
|
isChangingStatus: boolean
|
||||||
onVerifyCashPayment: () => void
|
|
||||||
onSendToKitchen: () => void
|
onSendToKitchen: () => void
|
||||||
onDeliverToCourier: () => void
|
onDeliverToCourier: () => void
|
||||||
onDeliverToWaiter: () => void
|
onDeliverToWaiter: () => void
|
||||||
@@ -23,7 +20,6 @@ interface OrderActionsProps {
|
|||||||
|
|
||||||
const OrderActions: FC<OrderActionsProps> = ({
|
const OrderActions: FC<OrderActionsProps> = ({
|
||||||
onPrint,
|
onPrint,
|
||||||
showVerifyCashPaymentButton,
|
|
||||||
showSendToKitchenButton,
|
showSendToKitchenButton,
|
||||||
showCancelButton,
|
showCancelButton,
|
||||||
isPreparing,
|
isPreparing,
|
||||||
@@ -31,9 +27,7 @@ const OrderActions: FC<OrderActionsProps> = ({
|
|||||||
isDineInOrCarDelivery,
|
isDineInOrCarDelivery,
|
||||||
isCustomerPickup,
|
isCustomerPickup,
|
||||||
isCanceled,
|
isCanceled,
|
||||||
isVerifyingPayment,
|
|
||||||
isChangingStatus,
|
isChangingStatus,
|
||||||
onVerifyCashPayment,
|
|
||||||
onSendToKitchen,
|
onSendToKitchen,
|
||||||
onDeliverToCourier,
|
onDeliverToCourier,
|
||||||
onDeliverToWaiter,
|
onDeliverToWaiter,
|
||||||
@@ -43,10 +37,10 @@ const OrderActions: FC<OrderActionsProps> = ({
|
|||||||
const [loadingAction, setLoadingAction] = useState<string | null>(null)
|
const [loadingAction, setLoadingAction] = useState<string | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isChangingStatus && !isVerifyingPayment) {
|
if (!isChangingStatus) {
|
||||||
setLoadingAction(null)
|
setLoadingAction(null)
|
||||||
}
|
}
|
||||||
}, [isChangingStatus, isVerifyingPayment])
|
}, [isChangingStatus])
|
||||||
|
|
||||||
const handleSendToKitchen = () => {
|
const handleSendToKitchen = () => {
|
||||||
setLoadingAction('sendToKitchen')
|
setLoadingAction('sendToKitchen')
|
||||||
@@ -81,15 +75,6 @@ const OrderActions: FC<OrderActionsProps> = ({
|
|||||||
className='bg-slate-700 hover:bg-slate-800'
|
className='bg-slate-700 hover:bg-slate-800'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isCanceled && showVerifyCashPaymentButton && (
|
|
||||||
<Button
|
|
||||||
label='تایید پرداخت'
|
|
||||||
onClick={onVerifyCashPayment}
|
|
||||||
isloading={isVerifyingPayment}
|
|
||||||
className='bg-green-600 hover:bg-green-700'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isCanceled && showSendToKitchenButton && (
|
{!isCanceled && showSendToKitchenButton && (
|
||||||
<Button
|
<Button
|
||||||
label='ارسال به آشپزخانه'
|
label='ارسال به آشپزخانه'
|
||||||
|
|||||||
@@ -1,178 +1,139 @@
|
|||||||
import type { FC } from 'react'
|
import { formatFaNumber, formatPrice } from "@/helpers/func";
|
||||||
import type { Order } from '../types/Types'
|
import { PaymentMethodEnum } from "@/pages/paymentMethods/enum/Enum";
|
||||||
import { DeliveryMethodEnum } from '@/pages/shipmentMethod/enum/Enum'
|
import { DeliveryMethodEnum } from "@/pages/shipmentMethod/enum/Enum";
|
||||||
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
|
import type { FC } from "react";
|
||||||
import { formatPrice, formatFaNumber } from '@/helpers/func'
|
import type { Order } from "../types/Types";
|
||||||
|
|
||||||
interface PaymentInfoProps {
|
interface PaymentInfoProps {
|
||||||
order: Order
|
order: Order;
|
||||||
getPaymentStatusColor: (status: string) => string
|
getPaymentStatusColor: (status: string) => string;
|
||||||
getPaymentStatusTextColor: (status: string) => string
|
getPaymentStatusTextColor: (status: string) => string;
|
||||||
getPaymentStatusText: (status: string) => string
|
getPaymentStatusText: (status: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaymentInfo: FC<PaymentInfoProps> = ({
|
const PaymentInfo: FC<PaymentInfoProps> = ({ order, getPaymentStatusColor, getPaymentStatusTextColor, getPaymentStatusText }) => {
|
||||||
order,
|
const paymentStatus = order.payments?.[0]?.status;
|
||||||
getPaymentStatusColor,
|
|
||||||
getPaymentStatusTextColor,
|
|
||||||
getPaymentStatusText
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
const paymentStatus = order.payments?.[0]?.status
|
|
||||||
|
|
||||||
const getDeliveryMethodText = (method: string): string => {
|
const getDeliveryMethodText = (method: string): string => {
|
||||||
const methodMap: Record<string, string> = {
|
const methodMap: Record<string, string> = {
|
||||||
[DeliveryMethodEnum.DineIn]: 'سرو در رستوران',
|
[DeliveryMethodEnum.DineIn]: "سرو در رستوران",
|
||||||
[DeliveryMethodEnum.CustomerPickup]: 'دریافت از محل',
|
[DeliveryMethodEnum.CustomerPickup]: "دریافت از محل",
|
||||||
[DeliveryMethodEnum.DeliveryCar]: 'تحویل به خودرو',
|
[DeliveryMethodEnum.DeliveryCar]: "تحویل به خودرو",
|
||||||
[DeliveryMethodEnum.DeliveryCourier]: 'بیرون بر',
|
[DeliveryMethodEnum.DeliveryCourier]: "بیرون بر",
|
||||||
}
|
};
|
||||||
return methodMap[method] || method
|
return methodMap[method] || method;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getPaymentMethodText = (method: string): string => {
|
const getPaymentMethodText = (method: string): string => {
|
||||||
const methodMap: Record<string, string> = {
|
const methodMap: Record<string, string> = {
|
||||||
[PaymentMethodEnum.Cash]: 'پرداخت نقدی',
|
[PaymentMethodEnum.Cash]: "پرداخت نقدی",
|
||||||
[PaymentMethodEnum.Online]: 'پرداخت آنلاین',
|
[PaymentMethodEnum.Online]: "پرداخت آنلاین",
|
||||||
[PaymentMethodEnum.CardOnDelivery]: 'پرداخت در محل',
|
[PaymentMethodEnum.CreditCard]: "پرداخت کارت به کارت",
|
||||||
[PaymentMethodEnum.Wallet]: 'کیف پول',
|
[PaymentMethodEnum.Wallet]: "کیف پول",
|
||||||
}
|
};
|
||||||
return methodMap[method] || method
|
return methodMap[method] || method;
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-[330px] h-fit bg-white rounded-4xl p-8'>
|
<div className="w-[330px] h-fit bg-white rounded-4xl p-8">
|
||||||
<div className='flex justify-between items-center'>
|
<div className="flex justify-between items-center">
|
||||||
<div className='font-light'>
|
<div className="font-light">اطلاعات پرداخت</div>
|
||||||
اطلاعات پرداخت
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex gap-1.5 items-center'>
|
<div className="flex gap-1.5 items-center">
|
||||||
<div className={`size-2 rounded-full ${getPaymentStatusColor(paymentStatus)}`}></div>
|
<div className={`size-2 rounded-full ${getPaymentStatusColor(paymentStatus)}`}></div>
|
||||||
<div className={`text-[13px] ${getPaymentStatusTextColor(paymentStatus)}`}>
|
<div className={`text-[13px] ${getPaymentStatusTextColor(paymentStatus)}`}>{getPaymentStatusText(paymentStatus)}</div>
|
||||||
{getPaymentStatusText(paymentStatus)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-10'>
|
<div className="mt-10">
|
||||||
<div className='flex text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">روش ارسال</div>
|
||||||
روش ارسال
|
<div>{order.deliveryMethod ? getDeliveryMethodText(order.deliveryMethod.method) : "-"}</div>
|
||||||
</div>
|
|
||||||
<div>{order.deliveryMethod ? getDeliveryMethodText(order.deliveryMethod.method) : '-'}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{order.deliveryMethod?.description && (
|
{order.deliveryMethod?.description && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">توضیحات روش ارسال</div>
|
||||||
توضیحات روش ارسال
|
|
||||||
</div>
|
|
||||||
<div>{order.deliveryMethod.description}</div>
|
<div>{order.deliveryMethod.description}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">روش پرداخت</div>
|
||||||
روش پرداخت
|
<div>{order.paymentMethod ? getPaymentMethodText(order.paymentMethod.method) : "-"}</div>
|
||||||
</div>
|
|
||||||
<div>{order.paymentMethod ? getPaymentMethodText(order.paymentMethod.method) : '-'}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{order.paymentMethod?.description && (
|
{order.paymentMethod?.description && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">توضیحات روش پرداخت</div>
|
||||||
توضیحات روش پرداخت
|
|
||||||
</div>
|
|
||||||
<div>{order.paymentMethod.description}</div>
|
<div>{order.paymentMethod.description}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{order.paymentMethod?.gateway && (
|
{order.paymentMethod?.gateway && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">درگاه پرداخت</div>
|
||||||
درگاه پرداخت
|
|
||||||
</div>
|
|
||||||
<div>{order.paymentMethod.gateway}</div>
|
<div>{order.paymentMethod.gateway}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{order.paymentId && (
|
{order.paymentId && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">شناسه پرداخت</div>
|
||||||
شناسه پرداخت
|
<div className="font-mono text-xs">{order.paymentId}</div>
|
||||||
</div>
|
|
||||||
<div className='font-mono text-xs'>{order.paymentId}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">جمع آیتمها</div>
|
||||||
جمع آیتمها
|
|
||||||
</div>
|
|
||||||
<div>{formatPrice(order.subTotal)}</div>
|
<div>{formatPrice(order.subTotal)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{order.itemsDiscount > 0 && (
|
{order.itemsDiscount > 0 && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">تخفیف آیتمها</div>
|
||||||
تخفیف آیتمها
|
<div className="text-green-600">-{formatPrice(order.itemsDiscount)}</div>
|
||||||
</div>
|
|
||||||
<div className='text-green-600'>-{formatPrice(order.itemsDiscount)}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{order.couponDiscount > 0 && (
|
{order.couponDiscount > 0 && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">تخفیف کوپن</div>
|
||||||
تخفیف کوپن
|
<div className="text-green-600">-{formatPrice(order.couponDiscount)}</div>
|
||||||
</div>
|
|
||||||
<div className='text-green-600'>-{formatPrice(order.couponDiscount)}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{order.totalDiscount > 0 && (
|
{order.totalDiscount > 0 && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description font-medium'>
|
<div className="text-description font-medium">جمع تخفیفها</div>
|
||||||
جمع تخفیفها
|
<div className="text-green-600 font-medium">-{formatPrice(order.totalDiscount)}</div>
|
||||||
</div>
|
|
||||||
<div className='text-green-600 font-medium'>-{formatPrice(order.totalDiscount)}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">هزینه ارسال</div>
|
||||||
هزینه ارسال
|
|
||||||
</div>
|
|
||||||
<div>{formatPrice(order.deliveryFee)}</div>
|
<div>{formatPrice(order.deliveryFee)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{order.tax > 0 && (
|
{order.tax > 0 && (
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">مالیات</div>
|
||||||
مالیات
|
|
||||||
</div>
|
|
||||||
<div>{formatPrice(order.tax)}</div>
|
<div>{formatPrice(order.tax)}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4'>
|
<div className="flex mt-4 text-[13px] font-light justify-between items-center border-b border-border border-dashed pb-4">
|
||||||
<div className='text-description'>
|
<div className="text-description">تعداد آیتمها</div>
|
||||||
تعداد آیتمها
|
|
||||||
</div>
|
|
||||||
<div>{formatFaNumber(order.totalItems)}</div>
|
<div>{formatFaNumber(order.totalItems)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex mt-4 text-[13px] font-bold justify-between items-center'>
|
<div className="flex mt-4 text-[13px] font-bold justify-between items-center">
|
||||||
<div>
|
<div>مبلغ کل</div>
|
||||||
مبلغ کل
|
|
||||||
</div>
|
|
||||||
<div>{formatPrice(order.total)}</div>
|
<div>{formatPrice(order.total)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default PaymentInfo
|
|
||||||
|
|
||||||
|
export default PaymentInfo;
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import Button from '@/components/Button'
|
||||||
|
import { PaymentMethodEnum } from '@/pages/paymentMethods/enum/Enum'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import type { Order } from '../types/Types'
|
||||||
|
|
||||||
|
interface PaymentVerificationBoxProps {
|
||||||
|
order: Order
|
||||||
|
isVerifyingPayment: boolean
|
||||||
|
onVerifyPayment: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PaymentVerificationBox: FC<PaymentVerificationBoxProps> = ({
|
||||||
|
order,
|
||||||
|
isVerifyingPayment,
|
||||||
|
onVerifyPayment,
|
||||||
|
}) => {
|
||||||
|
const payment = order.payments?.[0]
|
||||||
|
const isCreditCard = order.paymentMethod?.method === PaymentMethodEnum.CreditCard
|
||||||
|
const hasDescription = Boolean(payment?.description)
|
||||||
|
const hasAttachments = Boolean(payment?.attachments && payment.attachments.length > 0)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-[330px] h-fit bg-white rounded-4xl p-8 mt-6'>
|
||||||
|
<div className='font-light'>اطلاعات تایید پرداخت</div>
|
||||||
|
|
||||||
|
<div className='mt-6 space-y-4 text-[13px] font-light'>
|
||||||
|
{isCreditCard && hasDescription && (
|
||||||
|
<div>
|
||||||
|
<div className='text-description mb-2'>توضیحات پرداخت</div>
|
||||||
|
<div className='text-gray-700 bg-gray-50 p-3 rounded-lg'>{payment?.description}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCreditCard && hasAttachments && (
|
||||||
|
<div>
|
||||||
|
<div className='text-description mb-2'>پیوست پرداخت</div>
|
||||||
|
<div className='flex gap-3 flex-wrap'>
|
||||||
|
{payment?.attachments?.map((attachment, index) => (
|
||||||
|
<a
|
||||||
|
key={index}
|
||||||
|
href={attachment}
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={attachment}
|
||||||
|
alt={`پیوست پرداخت ${index + 1}`}
|
||||||
|
className='w-24 h-24 object-cover rounded-xl border border-border'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label='تایید پرداخت'
|
||||||
|
onClick={onVerifyPayment}
|
||||||
|
isloading={isVerifyingPayment}
|
||||||
|
className='bg-green-600 hover:bg-green-700 w-full'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentVerificationBox
|
||||||
@@ -28,10 +28,10 @@ export const useConfirmOrder = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useVerifyCashPayment = () => {
|
export const useVerifyPayment = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (paymentId: string) => api.verifyCashPayment(paymentId),
|
mutationFn: (paymentId: string) => api.verifyPayment(paymentId),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["order"] });
|
queryClient.invalidateQueries({ queryKey: ["order"] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["orders"] });
|
queryClient.invalidateQueries({ queryKey: ["orders"] });
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ export const confirmOrder = async (orderId: string): Promise<void> => {
|
|||||||
await axios.patch(`/admin/orders/${orderId}/confirm`);
|
await axios.patch(`/admin/orders/${orderId}/confirm`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifyCashPayment = async (paymentId: string): Promise<void> => {
|
export const verifyPayment = async (paymentId: string): Promise<void> => {
|
||||||
await axios.post(`/admin/payments/verify-cash-method/${paymentId}`);
|
await axios.post(`/admin/payments/verify/${paymentId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const changeOrderStatus = async (
|
export const changeOrderStatus = async (
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export interface OrderRestaurant {
|
|||||||
menuColor: string | null;
|
menuColor: string | null;
|
||||||
latitude: number | null;
|
latitude: number | null;
|
||||||
longitude: number | null;
|
longitude: number | null;
|
||||||
serviceArea: ServiceArea;
|
serviceArea: ServiceArea | null;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
establishedYear: number | null;
|
establishedYear: number | null;
|
||||||
phone: string;
|
phone: string;
|
||||||
@@ -62,6 +62,8 @@ export interface OrderRestaurant {
|
|||||||
score: RestaurantScore;
|
score: RestaurantScore;
|
||||||
plan: string;
|
plan: string;
|
||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
|
subscriptionStartDate: string;
|
||||||
|
subscriptionEndDate: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OrderDeliveryMethod {
|
export interface OrderDeliveryMethod {
|
||||||
@@ -108,6 +110,7 @@ export interface OrderPaymentMethod {
|
|||||||
method: string;
|
method: string;
|
||||||
gateway: string | null;
|
gateway: string | null;
|
||||||
description: string;
|
description: string;
|
||||||
|
cardNumber: string | null;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
merchantId: string | null;
|
merchantId: string | null;
|
||||||
@@ -122,7 +125,7 @@ export interface OrderFood {
|
|||||||
category: string;
|
category: string;
|
||||||
title: string;
|
title: string;
|
||||||
desc: string;
|
desc: string;
|
||||||
content: string[];
|
content: string[] | null;
|
||||||
price: number;
|
price: number;
|
||||||
order: number | null;
|
order: number | null;
|
||||||
prepareTime: number | null;
|
prepareTime: number | null;
|
||||||
@@ -172,6 +175,7 @@ export interface OrderPayment {
|
|||||||
paidAt: string | null;
|
paidAt: string | null;
|
||||||
failedAt: string | null;
|
failedAt: string | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
attachments: string[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CouponDetail {
|
export interface CouponDetail {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const CreatePaymentMethod: FC = () => {
|
|||||||
const paymentMethods = [
|
const paymentMethods = [
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Online}`), value: PaymentMethodEnum.Online },
|
{ label: t(`payment.${PaymentMethodEnum.Online}`), value: PaymentMethodEnum.Online },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Cash}`), value: PaymentMethodEnum.Cash },
|
{ label: t(`payment.${PaymentMethodEnum.Cash}`), value: PaymentMethodEnum.Cash },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.CardOnDelivery}`), value: PaymentMethodEnum.CardOnDelivery },
|
{ label: t(`payment.${PaymentMethodEnum.CreditCard}`), value: PaymentMethodEnum.CreditCard },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Wallet}`), value: PaymentMethodEnum.Wallet },
|
{ label: t(`payment.${PaymentMethodEnum.Wallet}`), value: PaymentMethodEnum.Wallet },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -41,6 +41,8 @@ const CreatePaymentMethod: FC = () => {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
order: 0,
|
order: 0,
|
||||||
merchantId: '',
|
merchantId: '',
|
||||||
|
cardNumber: '',
|
||||||
|
cardOwner: '',
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object().shape({
|
validationSchema: Yup.object().shape({
|
||||||
method: Yup.string().required('روش پرداخت الزامی است'),
|
method: Yup.string().required('روش پرداخت الزامی است'),
|
||||||
@@ -57,6 +59,16 @@ const CreatePaymentMethod: FC = () => {
|
|||||||
then: (schema) => schema.required('شناسه مرچنت الزامی است'),
|
then: (schema) => schema.required('شناسه مرچنت الزامی است'),
|
||||||
otherwise: (schema) => schema.notRequired(),
|
otherwise: (schema) => schema.notRequired(),
|
||||||
}),
|
}),
|
||||||
|
cardNumber: Yup.string().when('method', {
|
||||||
|
is: PaymentMethodEnum.CreditCard,
|
||||||
|
then: (schema) => schema.required('شماره کارت الزامی است'),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
|
cardOwner: Yup.string().when('method', {
|
||||||
|
is: PaymentMethodEnum.CreditCard,
|
||||||
|
then: (schema) => schema.required('نام صاحب کارت الزامی است'),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
const submitValues: CreateRestaurantPaymentMethodType = {
|
const submitValues: CreateRestaurantPaymentMethodType = {
|
||||||
@@ -70,6 +82,12 @@ const CreatePaymentMethod: FC = () => {
|
|||||||
merchantId: values.merchantId,
|
merchantId: values.merchantId,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
...(values.method === PaymentMethodEnum.CreditCard
|
||||||
|
? {
|
||||||
|
cardNumber: values.cardNumber,
|
||||||
|
cardOwner: values.cardOwner,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
createPaymentMethod(submitValues, {
|
createPaymentMethod(submitValues, {
|
||||||
@@ -141,6 +159,28 @@ const CreatePaymentMethod: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{formik.values.method === PaymentMethodEnum.CreditCard && (
|
||||||
|
<>
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label='شماره کارت'
|
||||||
|
name='cardNumber'
|
||||||
|
value={formik.values.cardNumber}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.cardNumber && formik.errors.cardNumber ? formik.errors.cardNumber : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label='نام صاحب کارت'
|
||||||
|
name='cardOwner'
|
||||||
|
value={formik.values.cardOwner}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.cardOwner && formik.errors.cardOwner ? formik.errors.cardOwner : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<Textarea
|
<Textarea
|
||||||
label='توضیحات'
|
label='توضیحات'
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
const paymentMethods = [
|
const paymentMethods = [
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Online}`), value: PaymentMethodEnum.Online },
|
{ label: t(`payment.${PaymentMethodEnum.Online}`), value: PaymentMethodEnum.Online },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Cash}`), value: PaymentMethodEnum.Cash },
|
{ label: t(`payment.${PaymentMethodEnum.Cash}`), value: PaymentMethodEnum.Cash },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.CardOnDelivery}`), value: PaymentMethodEnum.CardOnDelivery },
|
{ label: t(`payment.${PaymentMethodEnum.CreditCard}`), value: PaymentMethodEnum.CreditCard },
|
||||||
{ label: t(`payment.${PaymentMethodEnum.Wallet}`), value: PaymentMethodEnum.Wallet },
|
{ label: t(`payment.${PaymentMethodEnum.Wallet}`), value: PaymentMethodEnum.Wallet },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -45,6 +45,8 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
enabled: data.enabled ?? true,
|
enabled: data.enabled ?? true,
|
||||||
order: data.order || 0,
|
order: data.order || 0,
|
||||||
merchantId: data.merchantId || '',
|
merchantId: data.merchantId || '',
|
||||||
|
cardNumber: data.cardNumber || '',
|
||||||
|
cardOwner: data.cardOwner || '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -54,6 +56,8 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
order: 0,
|
order: 0,
|
||||||
merchantId: '',
|
merchantId: '',
|
||||||
|
cardNumber: '',
|
||||||
|
cardOwner: '',
|
||||||
}
|
}
|
||||||
}, [paymentMethodData])
|
}, [paymentMethodData])
|
||||||
|
|
||||||
@@ -75,6 +79,16 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
then: (schema) => schema.required('شناسه مرچنت الزامی است'),
|
then: (schema) => schema.required('شناسه مرچنت الزامی است'),
|
||||||
otherwise: (schema) => schema.notRequired(),
|
otherwise: (schema) => schema.notRequired(),
|
||||||
}),
|
}),
|
||||||
|
cardNumber: Yup.string().when('method', {
|
||||||
|
is: PaymentMethodEnum.CreditCard,
|
||||||
|
then: (schema) => schema.required('شماره کارت الزامی است'),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
|
cardOwner: Yup.string().when('method', {
|
||||||
|
is: PaymentMethodEnum.CreditCard,
|
||||||
|
then: (schema) => schema.required('نام صاحب کارت الزامی است'),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
@@ -90,6 +104,12 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
merchantId: values.merchantId,
|
merchantId: values.merchantId,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
...(values.method === PaymentMethodEnum.CreditCard
|
||||||
|
? {
|
||||||
|
cardNumber: values.cardNumber,
|
||||||
|
cardOwner: values.cardOwner,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRestaurantPaymentMethod(
|
updateRestaurantPaymentMethod(
|
||||||
@@ -180,6 +200,28 @@ const UpdatePaymentMethod: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{formik.values.method === PaymentMethodEnum.CreditCard && (
|
||||||
|
<>
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label='شماره کارت'
|
||||||
|
name='cardNumber'
|
||||||
|
value={formik.values.cardNumber}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.cardNumber && formik.errors.cardNumber ? formik.errors.cardNumber : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label='نام صاحب کارت'
|
||||||
|
name='cardOwner'
|
||||||
|
value={formik.values.cardOwner}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.cardOwner && formik.errors.cardOwner ? formik.errors.cardOwner : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<Textarea
|
<Textarea
|
||||||
label='توضیحات'
|
label='توضیحات'
|
||||||
|
|||||||
@@ -60,6 +60,20 @@ export const getPaymentMethodTableColumns = ({ onDelete, isDeleting }: GetPaymen
|
|||||||
return item.merchantId || '-'
|
return item.merchantId || '-'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'cardNumber',
|
||||||
|
title: 'شماره کارت',
|
||||||
|
render: (item: RestaurantPaymentMethod) => {
|
||||||
|
return item.cardNumber || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cardOwner',
|
||||||
|
title: 'نام صاحب کارت',
|
||||||
|
render: (item: RestaurantPaymentMethod) => {
|
||||||
|
return item.cardOwner || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
title: '',
|
title: '',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export const enum PaymentMethodEnum {
|
export enum PaymentMethodEnum {
|
||||||
Online = "Online",
|
Online = "Online",
|
||||||
Cash = "Cash",
|
Cash = "Cash",
|
||||||
CardOnDelivery = "CardOnDelivery",
|
|
||||||
Wallet = "Wallet",
|
Wallet = "Wallet",
|
||||||
|
CreditCard = "CreditCard",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum PaymentGatewayEnum {
|
export const enum PaymentGatewayEnum {
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ export type RestaurantPaymentMethod = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
merchantId: string;
|
merchantId: string;
|
||||||
|
cardNumber?: string;
|
||||||
|
cardOwner?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RestaurantPaymentMethodResponse = IResponse<
|
export type RestaurantPaymentMethodResponse = IResponse<
|
||||||
@@ -80,6 +82,8 @@ export type CreateRestaurantPaymentMethodType = {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
order: number;
|
order: number;
|
||||||
merchantId?: string;
|
merchantId?: string;
|
||||||
|
cardNumber?: string;
|
||||||
|
cardOwner?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetRestaurantPaymentMethodsParams = {
|
export type GetRestaurantPaymentMethodsParams = {
|
||||||
|
|||||||
Reference in New Issue
Block a user