diff --git a/app/i18n/config.js b/app/i18n/config.js index 574607e..da5e4ca 100644 --- a/app/i18n/config.js +++ b/app/i18n/config.js @@ -1497,6 +1497,12 @@ const resources = { "payments.platformOfficeFullAddress": "Platform Office: Abu Rumaneh, Al-Malki Street, Damascus", "payments.closeButton": "Close", "payments.propertyLabel": "Property", + "payments.uploadReceipt": "Upload Receipt Image", + "payments.loadingPaymentMethods": "Loading payment methods...", + "payments.noPaymentMethodsAvailable": "No payment methods available.", + "payments.paymentMethodName": "Payment Method", + "payments.methodActive": "Active", + "payments.methodInactive": "Inactive", "payments.method.cash": "Cash Payment", "payments.method.cashDesc": "Pay now with automatic booking confirmation", @@ -2951,6 +2957,12 @@ const resources = { "payments.platformOfficeFullAddress": "مكتب المنصة: أبو رمانة، شارع المالكي، دمشق", "payments.closeButton": "إغلاق", "payments.propertyLabel": "عقار", + "payments.uploadReceipt": "صورة الوصل", + "payments.loadingPaymentMethods": "جاري تحميل طرق الدفع...", + "payments.noPaymentMethodsAvailable": "لا توجد طرق دفع متاحة حالياً.", + "payments.paymentMethodName": "طريقة الدفع", + "payments.methodActive": "نشط", + "payments.methodInactive": "غير نشط", "payments.method.cash": "الدفع النقدي", "payments.method.cashDesc": "ادفع الآن عبر شام كاش مع تأكيد تلقائي للحجز", diff --git a/app/payments/page.js b/app/payments/page.js index e2d3a09..59e31f5 100644 --- a/app/payments/page.js +++ b/app/payments/page.js @@ -65,6 +65,11 @@ function normalizeText(value) { return String(value ?? '').trim().toLowerCase(); } +function isHaramText(value) { + const text = normalizeText(value); + return text.includes('haram') || text.includes('هرم'); +} + export default function PaymentsPage() { const { t, i18n } = useTranslation(); const [reservations, setReservations] = useState([]); @@ -161,12 +166,11 @@ export default function PaymentsPage() { (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; - } + const isHaram = + isHaramText(selectedMethod?.name) || + isHaramText(selectedMethod?.label) || + isHaramText(selectedMethod?.id) || + isHaramText(paymentKey); setPayingId(reservation.id); try { @@ -335,10 +339,10 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth }); const isHaramPayment = - normalizeText(selectedPayment).includes('haram') || - normalizeText(selectedMethod?.name).includes('haram') || - normalizeText(selectedMethod?.label).includes('haram') || - normalizeText(selectedMethod?.id).includes('haram'); + isHaramText(selectedPayment) || + isHaramText(selectedMethod?.name) || + isHaramText(selectedMethod?.label) || + isHaramText(selectedMethod?.id); return ( <> @@ -407,11 +411,11 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
{loadingPaymentMethods ? (
- {t('payments.loadingPaymentMethods', { defaultValue: 'جاري تحميل طرق الدفع...' })} + {t('payments.loadingPaymentMethods')}
) : methods.length === 0 ? (
- {t('payments.noPaymentMethodsAvailable', { defaultValue: 'لا توجد طرق دفع متاحة حالياً.' })} + {t('payments.noPaymentMethodsAvailable')}
) : ( methods.map((method, index) => { @@ -443,7 +447,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth

- {method.name || method.label || t('paymentMethod', { defaultValue: 'طريقة الدفع' })} + {method.name || method.label || t('payments.paymentMethodName')}

{method.description && (

@@ -455,7 +459,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth isActive ? 'bg-amber-100 text-amber-700' : 'bg-gray-200 text-gray-500' }`} > - {isActive ? t('active', { defaultValue: 'نشط' }) : t('inactive', { defaultValue: 'غير نشط' })} + {isActive ? t('payments.methodActive') : t('payments.methodInactive')}

{isSelected && ( @@ -474,7 +478,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth {isHaramPayment && (
""); - throw new Error(`API ${res.status}: ${text || res.statusText}`); - } - - const text = await res.text(); - if (!text) return null; - try { - const json = JSON.parse(text); - if (json && typeof json === "object" && "data" in json) { - return json.data; + const res = await fetch(url, { + ...options, + headers, + body: + hasBody && !bodyIsFormData && typeof options.body !== "string" + ? JSON.stringify(options.body) + : options.body, + }); + + assertNotBlocked(res); + + if (!res.ok && res.status !== 206) { + const text = await res.text().catch(() => ""); + throw new Error(`API ${res.status}: ${text || res.statusText}`); } - return json; - } catch { - return text; + + const text = await res.text(); + if (!text) return null; + + try { + const json = JSON.parse(text); + if (json && typeof json === "object" && "data" in json) { + return json.data; + } + return json; + } catch { + return text; + } + } catch (error) { + if (error instanceof TypeError && error.message === 'Failed to fetch') { + throw new Error( + `تعذر الاتصال بالخادم. تحقق من اتصالك بالإنترنت أو حاول مرة أخرى لاحقاً. (${API_BASE}${endpoint})`, + ); + } + throw error; } } @@ -110,11 +118,16 @@ async function authFetch(endpoint, body, token = null) { headers["Authorization"] = `Bearer ${token}`; } - const res = await fetch(`${API_BASE}${endpoint}`, { - method: "POST", - headers, - body: bodyIsFormData ? body : JSON.stringify(body), - }); + let res; + try { + res = await fetch(`${API_BASE}${endpoint}`, { + method: "POST", + headers, + body: bodyIsFormData ? body : JSON.stringify(body), + }); + } catch (err) { + return { status: 0, data: null, ok: false, message: err.message }; + } assertNotBlocked(res); @@ -142,13 +155,18 @@ async function authFetch(endpoint, body, token = null) { } async function reportFetch(endpoint, body) { - const res = await fetch(buildApiUrl(REPORT_API_BASE, endpoint), { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); + let res; + try { + res = await fetch(buildApiUrl(REPORT_API_BASE, endpoint), { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + } catch (err) { + return { status: 0, data: null, ok: false, message: err.message }; + } assertNotBlocked(res); @@ -392,13 +410,18 @@ export async function uploadPicture(file) { const token = AuthService.getToken(); - const res = await fetch(`${API_BASE}/Files/UploadPicture`, { - method: "POST", - headers: { - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: formData, - }); + let res; + try { + res = await fetch(`${API_BASE}/Files/UploadPicture`, { + method: "POST", + headers: { + ...(token && { Authorization: `Bearer ${token}` }), + }, + body: formData, + }); + } catch (err) { + throw new Error(`تعذر الاتصال بالخادم: ${err.message}`); + } assertNotBlocked(res); @@ -604,7 +627,6 @@ export async function adminConfirmDeposit( comment = null, ) { const token = AuthService.getToken(); - const endpoint = `${API_BASE}/Reservations/AdminConfirmDeposit/admin-confirm-deposit`; const normalizedComment = typeof comment === "string" && comment.trim() ? comment.trim() : null; const payload = { @@ -613,7 +635,7 @@ export async function adminConfirmDeposit( comment: normalizedComment, }; - const res = await fetch(endpoint, { + const res = await fetch(buildApiUrl(API_BASE, "/Reservations/AdminConfirmDeposit/admin-confirm-deposit"), { method: "PUT", headers: { "Content-Type": "application/json", @@ -672,6 +694,9 @@ export async function ownerConfirmReservation(id) { // ─── Payments ─── +export async function getMyTransaction() { + return apiFetch("/Customer/GetMyTransaction"); +} export async function payDeposit(data) { const formData = new FormData();