From c365503382a8a0b8d50ee26ee48f6590f26cf131 Mon Sep 17 00:00:00 2001 From: Beilin-b Date: Sat, 11 Jul 2026 05:21:38 -0700 Subject: [PATCH] payment with haram --- app/payments/page.js | 314 +++++++++++++++++++++++++------------------ app/utils/api.js | 31 ++++- 2 files changed, 210 insertions(+), 135 deletions(-) diff --git a/app/payments/page.js b/app/payments/page.js index cdb1659..e2d3a09 100644 --- a/app/payments/page.js +++ b/app/payments/page.js @@ -61,6 +61,10 @@ function formatDate(date) { return d.toLocaleDateString('en-GB'); } +function normalizeText(value) { + return String(value ?? '').trim().toLowerCase(); +} + export default function PaymentsPage() { const { t, i18n } = useTranslation(); const [reservations, setReservations] = useState([]); @@ -69,7 +73,7 @@ export default function PaymentsPage() { const [isGuest, setIsGuest] = useState(null); const [paymentMethods, setPaymentMethods] = useState([]); const [loadingPaymentMethods, setLoadingPaymentMethods] = useState(true); - const [selectedPayment, setSelectedPayment] = useState('cash'); + const [selectedPayment, setSelectedPayment] = useState(null); const loadReservations = useCallback(async () => { try { @@ -117,9 +121,10 @@ export default function PaymentsPage() { const data = await getPaymentTypes(); const methods = Array.isArray(data) ? data : []; setPaymentMethods(methods); + const firstActive = methods.find((method) => method?.isActive === true || method?.active === true); if (firstActive) { - setSelectedPayment(firstActive.id ?? firstActive.name ?? selectedPayment); + setSelectedPayment(firstActive.name ?? firstActive.id ?? null); } } catch (err) { console.error('[Payments] failed to load payment methods', err); @@ -127,7 +132,7 @@ export default function PaymentsPage() { } finally { setLoadingPaymentMethods(false); } - }, [selectedPayment]); + }, []); useEffect(() => { if (AuthService.isGuest()) { @@ -150,13 +155,25 @@ export default function PaymentsPage() { return method?.id ?? paymentKey; }; - const handlePayDeposit = async (reservation, paymentKey) => { - setPayingId(reservation.id); + const handlePayDeposit = async (reservation, paymentKey, paymentImageFile) => { const paymentTypeId = resolvePaymentTypeId(paymentKey); + const selectedMethod = paymentMethods.find( + (m) => String(m.id) === String(paymentTypeId) || String(m.name) === String(paymentTypeId), + ); + + const isHaram = normalizeText(selectedMethod?.name).includes('haram'); + + if (isHaram && !paymentImageFile) { + toast.error(t('payments.uploadReceiptRequired', { defaultValue: 'يرجى رفع صورة الوصل' })); + return; + } + + setPayingId(reservation.id); try { await payDeposit({ reservationId: reservation.id, paymentTypeId, + paymentImage: paymentImageFile, }); toast.success(t('payments.depositPaidSuccess')); loadReservations(); @@ -282,36 +299,54 @@ export default function PaymentsPage() { )} -{reservations.length === 0 && ( - - -

{t('payments.noTransactions')}

-

{t('payments.noTransactionsDesc')}

-
- )} - + {reservations.length === 0 && ( + + +

{t('payments.noTransactions')}

+

{t('payments.noTransactionsDesc')}

+
+ )} - ); - } + + ); +} - // eslint-disable-next-line react/prop-types - function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMethods, selectedPayment, onSelectPayment, onPay }) { - const r = reservation; - const amount = r.depositAmount || r.totalPrice || 0; - const [showCashDialog, setShowCashDialog] = useState(false); - const methods = paymentMethods.length > 0 ? paymentMethods : getPaymentMethods(t); +// eslint-disable-next-line react/prop-types +function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMethods, selectedPayment, onSelectPayment, onPay }) { + const { t } = useTranslation(); + const r = reservation; + const amount = r.depositAmount || r.totalPrice || 0; + const [showCashDialog, setShowCashDialog] = useState(false); + const [localPaymentImage, setLocalPaymentImage] = useState(null); - return ( - <> - + const methods = paymentMethods.length > 0 ? paymentMethods : getPaymentMethods(t); + + const selectedMethod = methods.find((m) => { + const methodName = normalizeText(m?.name); + const methodLabel = normalizeText(m?.label); + const methodId = normalizeText(m?.id); + const paymentKey = normalizeText(selectedPayment); + + return methodName === paymentKey || methodLabel === paymentKey || methodId === paymentKey; + }); + + const isHaramPayment = + normalizeText(selectedPayment).includes('haram') || + normalizeText(selectedMethod?.name).includes('haram') || + normalizeText(selectedMethod?.label).includes('haram') || + normalizeText(selectedMethod?.id).includes('haram'); + + return ( + <> + {/* Property details */}
@@ -380,7 +415,7 @@ export default function PaymentsPage() {
) : ( methods.map((method, index) => { - const optionId = method.id ?? method.name ?? `payment-method-${index}`; + const optionId = method.name ?? method.id ?? `payment-method-${index}`; const isActive = method?.isActive === true || method?.active === true; const isSelected = String(selectedPayment) === String(optionId); const Icon = method.icon || CreditCard; @@ -399,9 +434,11 @@ export default function PaymentsPage() { : 'border-gray-200 bg-white hover:border-amber-300 cursor-pointer' }`} > -
+
@@ -413,9 +450,11 @@ export default function PaymentsPage() { {method.description}

)} - + {isActive ? t('active', { defaultValue: 'نشط' }) : t('inactive', { defaultValue: 'غير نشط' })}
@@ -431,103 +470,118 @@ export default function PaymentsPage() {
- {/* Pay button */} -
- onPay(r, selectedPayment)} - disabled={payingId === r.id} - whileHover={{ scale: 1.01 }} - whileTap={{ scale: 0.99 }} - className="w-full bg-amber-500 hover:bg-amber-600 disabled:bg-amber-300 text-white font-bold py-4 px-6 rounded-2xl text-lg transition-all shadow-lg hover:shadow-xl flex items-center justify-center gap-3" - > - {payingId === r.id ? ( - <> - - {t('payments.processingPayment')} - - ) : ( - <> - - {t('payments.payButton', { amount: formatCurrency(amount, r.currencySign) })} - - )} - -
+ {/* Upload receipt only for Haram */} + {isHaramPayment && ( +
+ + setLocalPaymentImage(e.target.files?.[0] || null)} + className="block w-full rounded-2xl border border-gray-300 bg-white px-4 py-3 text-sm text-gray-700 file:mr-4 file:rounded-xl file:border-0 file:bg-amber-500 file:px-4 file:py-2 file:text-white hover:file:bg-amber-600" + /> +
+ )} - {/* Cash pay button */} -
- -
- - {/* Cash payment dialog */} - {showCashDialog && ( -
- + onPay(r, selectedPayment, localPaymentImage)} + disabled={payingId === r.id} + whileHover={{ scale: 1.01 }} + whileTap={{ scale: 0.99 }} + className="w-full bg-amber-500 hover:bg-amber-600 disabled:bg-amber-300 text-white font-bold py-4 px-6 rounded-2xl text-lg transition-all shadow-lg hover:shadow-xl flex items-center justify-center gap-3" > -
- -
-

{t('payments.cashPendingDialogTitle')}

-

- {t('payments.cashPendingDialogDesc1')} -
- {t('payments.cashPendingDialogDesc2')} -

-
-

{t('payments.platformOfficeLabel')}

-

{t('payments.platformOfficeFullAddress')}

-

+ {payingId === r.id ? ( + <> + + {t('payments.processingPayment')} + + ) : ( + <> + + {t('payments.payButton', { amount: formatCurrency(amount, r.currencySign) })} + + )} + +

+ + {/* Cash pay button */} +
+ +
+ + {/* Cash payment dialog */} + {showCashDialog && ( +
+ +
+ +
+

{t('payments.cashPendingDialogTitle')}

+

+ {t('payments.cashPendingDialogDesc1')} +
+ {t('payments.cashPendingDialogDesc2')} +

+
+

{t('payments.platformOfficeLabel')}

+

{t('payments.platformOfficeFullAddress')}

+

+ + +963567823411 +

+
+
+ + + {t('payments.myReservationsLink')} + +
+
+
+ )} + + {/* Platform location */} +
+
+ +
+

{t('payments.cashPaymentLocationLabel')}

+

+ {t('payments.platformOfficeFullAddress')} +

+

- +963567823411 + +963567823411

-
- - - {t('payments.myReservationsLink')} - -
- -
- )} - - {/* Platform location */} -
-
- -
-

{t('payments.cashPaymentLocationLabel')}

-

- {t('payments.platformOfficeFullAddress')} -

-

- - +963567823411 -

-
- + ); -} +} \ No newline at end of file diff --git a/app/utils/api.js b/app/utils/api.js index f824624..32fbb1c 100644 --- a/app/utils/api.js +++ b/app/utils/api.js @@ -672,17 +672,38 @@ export async function ownerConfirmReservation(id) { // ─── Payments ─── + export async function payDeposit(data) { + const formData = new FormData(); + + formData.append( + "ReservationId", + String(data.reservationId ?? data.ReservationId ?? "") + ); + + formData.append( + "PaymentTypeId", + String(data.paymentTypeId ?? data.PaymentTypeId ?? "") + ); + + if (data.comment) { + formData.append("Comment", data.comment); + } else { + formData.append("Comment", ""); + } + + // صورة الوصل لطريقة الدفع Haram + if (data.paymentImage) { + formData.append("TransactionType", data.paymentImage); + formData.append("paymentImage", data.paymentImage); + } + return apiFetch("/Reservations/PayDeposit/pay-deposit", { method: "POST", - body: data, + body: formData, }); } -export async function getMyTransaction() { - return apiFetch("/Customer/GetMyTransaction"); -} - // ─── Owner Contact & Stats ─── export async function getOwnerContactInformation(propertyInformationId) {