Edit payment with haram
All checks were successful
Build frontend / build (push) Successful in 1m31s

This commit is contained in:
Rahaf
2026-07-13 20:20:17 +03:00
parent c365503382
commit c01ada480d
3 changed files with 106 additions and 65 deletions

View File

@ -1497,6 +1497,12 @@ const resources = {
"payments.platformOfficeFullAddress": "Platform Office: Abu Rumaneh, Al-Malki Street, Damascus", "payments.platformOfficeFullAddress": "Platform Office: Abu Rumaneh, Al-Malki Street, Damascus",
"payments.closeButton": "Close", "payments.closeButton": "Close",
"payments.propertyLabel": "Property", "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.cash": "Cash Payment",
"payments.method.cashDesc": "Pay now with automatic booking confirmation", "payments.method.cashDesc": "Pay now with automatic booking confirmation",
@ -2951,6 +2957,12 @@ const resources = {
"payments.platformOfficeFullAddress": "مكتب المنصة: أبو رمانة، شارع المالكي، دمشق", "payments.platformOfficeFullAddress": "مكتب المنصة: أبو رمانة، شارع المالكي، دمشق",
"payments.closeButton": "إغلاق", "payments.closeButton": "إغلاق",
"payments.propertyLabel": "عقار", "payments.propertyLabel": "عقار",
"payments.uploadReceipt": "صورة الوصل",
"payments.loadingPaymentMethods": "جاري تحميل طرق الدفع...",
"payments.noPaymentMethodsAvailable": "لا توجد طرق دفع متاحة حالياً.",
"payments.paymentMethodName": "طريقة الدفع",
"payments.methodActive": "نشط",
"payments.methodInactive": "غير نشط",
"payments.method.cash": "الدفع النقدي", "payments.method.cash": "الدفع النقدي",
"payments.method.cashDesc": "ادفع الآن عبر شام كاش مع تأكيد تلقائي للحجز", "payments.method.cashDesc": "ادفع الآن عبر شام كاش مع تأكيد تلقائي للحجز",

View File

@ -65,6 +65,11 @@ function normalizeText(value) {
return String(value ?? '').trim().toLowerCase(); return String(value ?? '').trim().toLowerCase();
} }
function isHaramText(value) {
const text = normalizeText(value);
return text.includes('haram') || text.includes('هرم');
}
export default function PaymentsPage() { export default function PaymentsPage() {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const [reservations, setReservations] = useState([]); const [reservations, setReservations] = useState([]);
@ -161,12 +166,11 @@ export default function PaymentsPage() {
(m) => String(m.id) === String(paymentTypeId) || String(m.name) === String(paymentTypeId), (m) => String(m.id) === String(paymentTypeId) || String(m.name) === String(paymentTypeId),
); );
const isHaram = normalizeText(selectedMethod?.name).includes('haram'); const isHaram =
isHaramText(selectedMethod?.name) ||
if (isHaram && !paymentImageFile) { isHaramText(selectedMethod?.label) ||
toast.error(t('payments.uploadReceiptRequired', { defaultValue: 'يرجى رفع صورة الوصل' })); isHaramText(selectedMethod?.id) ||
return; isHaramText(paymentKey);
}
setPayingId(reservation.id); setPayingId(reservation.id);
try { try {
@ -335,10 +339,10 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
}); });
const isHaramPayment = const isHaramPayment =
normalizeText(selectedPayment).includes('haram') || isHaramText(selectedPayment) ||
normalizeText(selectedMethod?.name).includes('haram') || isHaramText(selectedMethod?.name) ||
normalizeText(selectedMethod?.label).includes('haram') || isHaramText(selectedMethod?.label) ||
normalizeText(selectedMethod?.id).includes('haram'); isHaramText(selectedMethod?.id);
return ( return (
<> <>
@ -407,11 +411,11 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{loadingPaymentMethods ? ( {loadingPaymentMethods ? (
<div className="col-span-full rounded-2xl border border-dashed border-gray-200 bg-gray-50 p-4 text-sm text-gray-600"> <div className="col-span-full rounded-2xl border border-dashed border-gray-200 bg-gray-50 p-4 text-sm text-gray-600">
{t('payments.loadingPaymentMethods', { defaultValue: 'جاري تحميل طرق الدفع...' })} {t('payments.loadingPaymentMethods')}
</div> </div>
) : methods.length === 0 ? ( ) : methods.length === 0 ? (
<div className="col-span-full rounded-2xl border border-dashed border-gray-200 bg-gray-50 p-4 text-sm text-gray-600"> <div className="col-span-full rounded-2xl border border-dashed border-gray-200 bg-gray-50 p-4 text-sm text-gray-600">
{t('payments.noPaymentMethodsAvailable', { defaultValue: 'لا توجد طرق دفع متاحة حالياً.' })} {t('payments.noPaymentMethodsAvailable')}
</div> </div>
) : ( ) : (
methods.map((method, index) => { methods.map((method, index) => {
@ -443,7 +447,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className={`text-sm font-bold ${isSelected ? 'text-amber-900' : 'text-gray-800'}`}> <p className={`text-sm font-bold ${isSelected ? 'text-amber-900' : 'text-gray-800'}`}>
{method.name || method.label || t('paymentMethod', { defaultValue: 'طريقة الدفع' })} {method.name || method.label || t('payments.paymentMethodName')}
</p> </p>
{method.description && ( {method.description && (
<p className="text-xs text-gray-500 mt-0.5 leading-relaxed"> <p className="text-xs text-gray-500 mt-0.5 leading-relaxed">
@ -455,7 +459,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
isActive ? 'bg-amber-100 text-amber-700' : 'bg-gray-200 text-gray-500' 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')}
</span> </span>
</div> </div>
{isSelected && ( {isSelected && (
@ -474,7 +478,7 @@ function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMeth
{isHaramPayment && ( {isHaramPayment && (
<div className="px-6 py-5 border-b border-gray-100"> <div className="px-6 py-5 border-b border-gray-100">
<label className="block text-sm font-bold text-gray-700 mb-3"> <label className="block text-sm font-bold text-gray-700 mb-3">
{t('payments.uploadReceipt', { defaultValue: 'صورة الوصل' })} {t('payments.uploadReceipt')}
</label> </label>
<input <input
type="file" type="file"

View File

@ -1,8 +1,7 @@
import AuthService from "../services/AuthService"; import AuthService from "../services/AuthService";
const API_BASE =
process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api"; const API_BASE = process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api";
const REPORT_API_BASE = const REPORT_API_BASE = process.env.NEXT_PUBLIC_REPORT_API_URL || "http://45.93.137.91/api";
process.env.NEXT_PUBLIC_API_URL || "http://45.93.137.91/api";
function isFormData(value) { function isFormData(value) {
return typeof FormData !== "undefined" && value instanceof FormData; return typeof FormData !== "undefined" && value instanceof FormData;
@ -65,33 +64,42 @@ async function apiFetch(endpoint, options = {}) {
const url = `${API_BASE}${endpoint}`; const url = `${API_BASE}${endpoint}`;
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}`);
}
const text = await res.text();
if (!text) return null;
try { try {
const json = JSON.parse(text); const res = await fetch(url, {
if (json && typeof json === "object" && "data" in json) { ...options,
return json.data; 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 { const text = await res.text();
return 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}`; headers["Authorization"] = `Bearer ${token}`;
} }
const res = await fetch(`${API_BASE}${endpoint}`, { let res;
method: "POST", try {
headers, res = await fetch(`${API_BASE}${endpoint}`, {
body: bodyIsFormData ? body : JSON.stringify(body), method: "POST",
}); headers,
body: bodyIsFormData ? body : JSON.stringify(body),
});
} catch (err) {
return { status: 0, data: null, ok: false, message: err.message };
}
assertNotBlocked(res); assertNotBlocked(res);
@ -142,13 +155,18 @@ async function authFetch(endpoint, body, token = null) {
} }
async function reportFetch(endpoint, body) { async function reportFetch(endpoint, body) {
const res = await fetch(buildApiUrl(REPORT_API_BASE, endpoint), { let res;
method: "POST", try {
headers: { res = await fetch(buildApiUrl(REPORT_API_BASE, endpoint), {
"Content-Type": "application/json", method: "POST",
}, headers: {
body: JSON.stringify(body), "Content-Type": "application/json",
}); },
body: JSON.stringify(body),
});
} catch (err) {
return { status: 0, data: null, ok: false, message: err.message };
}
assertNotBlocked(res); assertNotBlocked(res);
@ -392,13 +410,18 @@ export async function uploadPicture(file) {
const token = AuthService.getToken(); const token = AuthService.getToken();
const res = await fetch(`${API_BASE}/Files/UploadPicture`, { let res;
method: "POST", try {
headers: { res = await fetch(`${API_BASE}/Files/UploadPicture`, {
...(token && { Authorization: `Bearer ${token}` }), method: "POST",
}, headers: {
body: formData, ...(token && { Authorization: `Bearer ${token}` }),
}); },
body: formData,
});
} catch (err) {
throw new Error(`تعذر الاتصال بالخادم: ${err.message}`);
}
assertNotBlocked(res); assertNotBlocked(res);
@ -604,7 +627,6 @@ export async function adminConfirmDeposit(
comment = null, comment = null,
) { ) {
const token = AuthService.getToken(); const token = AuthService.getToken();
const endpoint = `${API_BASE}/Reservations/AdminConfirmDeposit/admin-confirm-deposit`;
const normalizedComment = const normalizedComment =
typeof comment === "string" && comment.trim() ? comment.trim() : null; typeof comment === "string" && comment.trim() ? comment.trim() : null;
const payload = { const payload = {
@ -613,7 +635,7 @@ export async function adminConfirmDeposit(
comment: normalizedComment, comment: normalizedComment,
}; };
const res = await fetch(endpoint, { const res = await fetch(buildApiUrl(API_BASE, "/Reservations/AdminConfirmDeposit/admin-confirm-deposit"), {
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -672,6 +694,9 @@ export async function ownerConfirmReservation(id) {
// ─── Payments ─── // ─── Payments ───
export async function getMyTransaction() {
return apiFetch("/Customer/GetMyTransaction");
}
export async function payDeposit(data) { export async function payDeposit(data) {
const formData = new FormData(); const formData = new FormData();