From 8ed623bbe240bcfa1b0b95e7b060508808520d02 Mon Sep 17 00:00:00 2001 From: Rahaf Date: Wed, 15 Jul 2026 19:25:39 +0300 Subject: [PATCH] Edit haram payment --- app/i18n/config.js | 13 ++++ app/payments/page.js | 173 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 160 insertions(+), 26 deletions(-) diff --git a/app/i18n/config.js b/app/i18n/config.js index da5e4ca..852e335 100644 --- a/app/i18n/config.js +++ b/app/i18n/config.js @@ -1498,6 +1498,12 @@ const resources = { "payments.closeButton": "Close", "payments.propertyLabel": "Property", "payments.uploadReceipt": "Upload Receipt Image", + "payments.receiptDialogTitle": "Upload payment receipt", + "payments.receiptDialogDescription": "Please upload a clear image of the transfer receipt before completing the payment.", + "payments.receiptUploadRequiredHint": "A receipt image is required for this payment method.", + "payments.receiptRequired": "Please upload the payment receipt image first.", + "payments.receiptSelected": "Selected file:", + "payments.continueToPay": "Continue to pay", "payments.loadingPaymentMethods": "Loading payment methods...", "payments.noPaymentMethodsAvailable": "No payment methods available.", "payments.paymentMethodName": "Payment Method", @@ -2549,6 +2555,13 @@ const resources = { "payment.transferDesc": "حول من حساب داخلي ثم أرسل المرجع", "payment.electronic": "دفع إلكتروني", "payment.electronicDesc": "سيتم تفعيل الدفع الإلكتروني والتحويل لاحقاً", + "payments.uploadReceipt": "رفع صورة الوصل", + "payments.receiptDialogTitle": "رفع وصل الدفع", + "payments.receiptDialogDescription": "يرجى رفع صورة واضحة لوصل التحويل قبل إكمال الدفع.", + "payments.receiptUploadRequiredHint": "مطلوب رفع صورة الوصل لهذه الطريقة.", + "payments.receiptRequired": "يرجى رفع صورة وصل الدفع أولاً.", + "payments.receiptSelected": "الملف المختار:", + "payments.continueToPay": "متابعة الدفع", /* ─── Owner Properties Page ─── */ "deleteConfirmTitle": "تأكيد الحذف", diff --git a/app/payments/page.js b/app/payments/page.js index a8563de..9db8d7e 100644 --- a/app/payments/page.js +++ b/app/payments/page.js @@ -70,6 +70,32 @@ function isHaramText(value) { return text.includes('haram') || text.includes('هرم') || text.includes('هرام'); } +function isReceiptRequiredPayment(value, method = null) { + const haystack = [ + value, + method?.name, + method?.label, + method?.id, + method?.description, + ] + .filter(Boolean) + .join(' '); + + const text = normalizeText(haystack); + return ( + text.includes('transfer') || + text.includes('تحويل') || + text.includes('حوالة') || + text.includes('bank') || + text.includes('بنك') || + isHaramText(value) || + isHaramText(method?.name) || + isHaramText(method?.label) || + isHaramText(method?.id) || + isHaramText(method?.description) + ); +} + export default function PaymentsPage() { const { t, i18n } = useTranslation(); const [reservations, setReservations] = useState([]); @@ -165,13 +191,12 @@ export default function PaymentsPage() { const selectedMethod = paymentMethods.find( (m) => String(m.id) === String(paymentTypeId) || String(m.name) === String(paymentTypeId), ); + const requiresReceiptUpload = isReceiptRequiredPayment(paymentKey, selectedMethod); - const isHaram = - isHaramText(selectedMethod?.name) || - isHaramText(selectedMethod?.label) || - isHaramText(selectedMethod?.id) || - isHaramText(selectedMethod?.description) || - isHaramText(paymentKey); + if (requiresReceiptUpload && !paymentImageFile) { + toast.error(t('payments.receiptRequired')); + return; + } setPayingId(reservation.id); try { @@ -201,7 +226,7 @@ export default function PaymentsPage() { if (isGuest) { return ( -
+
0 ? paymentMethods : getPaymentMethods(t); @@ -339,12 +367,48 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth return methodName === paymentKey || methodLabel === paymentKey || methodId === paymentKey; }); - const isHaramPayment = - isHaramText(selectedPayment) || - isHaramText(selectedMethod?.name) || - isHaramText(selectedMethod?.label) || - isHaramText(selectedMethod?.id) || - isHaramText(selectedMethod?.description); + const requiresReceiptUpload = isReceiptRequiredPayment(selectedPayment, selectedMethod); + + useEffect(() => { + if (!localPaymentImage) { + setReceiptPreviewUrl(''); + return undefined; + } + + const previewUrl = URL.createObjectURL(localPaymentImage); + setReceiptPreviewUrl(previewUrl); + + return () => URL.revokeObjectURL(previewUrl); + }, [localPaymentImage]); + + const handleSelectMethod = (optionId, method) => { + onSelectPayment(optionId); + + if (isReceiptRequiredPayment(optionId, method)) { + setShowReceiptModal(true); + return; + } + + setShowReceiptModal(false); + setLocalPaymentImage(null); + setReceiptPreviewUrl(''); + setReceiptFileName(''); + }; + + const handleReceiptSelection = (event) => { + const file = event.target.files?.[0] || null; + setLocalPaymentImage(file); + setReceiptFileName(file?.name || ''); + }; + + const handleReceiptConfirm = () => { + if (!localPaymentImage) { + toast.error(t('payments.receiptRequired')); + return; + } + + setShowReceiptModal(false); + }; return ( <> @@ -431,7 +495,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth key={optionId} type="button" disabled={!isActive} - onClick={() => isActive && onSelectPayment(optionId)} + onClick={() => isActive && handleSelectMethod(optionId, method)} className={`relative flex items-start gap-3 p-4 rounded-2xl border-2 text-right transition-all ${ !isActive ? 'border-gray-100 bg-gray-50 opacity-50 cursor-not-allowed' @@ -476,18 +540,12 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
- {/* 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" - /> + {requiresReceiptUpload && ( +
+
+ +

{t('payments.receiptUploadRequiredHint')}

+
)} @@ -515,6 +573,69 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
+ {showReceiptModal && ( +
+ +
+
+

{t('payments.receiptDialogTitle')}

+

{t('payments.receiptDialogDescription')}

+
+
+ +
+
+ + + + + {receiptFileName && ( +
+ {t('payments.receiptSelected')} {receiptFileName} +
+ )} + + {localPaymentImage && receiptPreviewUrl && ( +
+ {t('payments.uploadReceipt')} +
+ )} + +
+ + +
+
+
+ )} + {/* Cash pay button */}