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();