payment with haram
Some checks failed
Build frontend / build (push) Failing after 1m39s

This commit is contained in:
Beilin-b
2026-07-11 05:21:38 -07:00
parent bb47b05858
commit c365503382
2 changed files with 210 additions and 135 deletions

View File

@ -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,7 +299,7 @@ export default function PaymentsPage() {
</div>
)}
{reservations.length === 0 && (
{reservations.length === 0 && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@ -296,15 +313,33 @@ export default function PaymentsPage() {
</div>
</div>
);
}
}
// eslint-disable-next-line react/prop-types
function PaymentCard({ reservation, payingId, paymentMethods, loadingPaymentMethods, selectedPayment, onSelectPayment, onPay }) {
// 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);
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 (
<>
<motion.div
@ -380,7 +415,7 @@ export default function PaymentsPage() {
</div>
) : (
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'
}`}
>
<div className={`w-10 h-10 rounded-xl flex items-center justify-center shrink-0 ${
<div
className={`w-10 h-10 rounded-xl flex items-center justify-center shrink-0 ${
isSelected ? 'bg-amber-500 text-white' : 'bg-gray-100 text-gray-500'
}`}>
}`}
>
<Icon className="w-5 h-5" />
</div>
<div className="min-w-0 flex-1">
@ -413,9 +450,11 @@ export default function PaymentsPage() {
{method.description}
</p>
)}
<span className={`inline-block mt-1 text-[10px] font-medium px-2 py-0.5 rounded-full ${
<span
className={`inline-block mt-1 text-[10px] font-medium px-2 py-0.5 rounded-full ${
isActive ? 'bg-amber-100 text-amber-700' : 'bg-gray-200 text-gray-500'
}`}>
}`}
>
{isActive ? t('active', { defaultValue: 'نشط' }) : t('inactive', { defaultValue: 'غير نشط' })}
</span>
</div>
@ -431,11 +470,26 @@ export default function PaymentsPage() {
</div>
</div>
{/* Upload receipt only for Haram */}
{isHaramPayment && (
<div className="px-6 py-5 border-b border-gray-100">
<label className="block text-sm font-bold text-gray-700 mb-3">
{t('payments.uploadReceipt', { defaultValue: 'صورة الوصل' })}
</label>
<input
type="file"
accept="image/*"
onChange={(e) => 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"
/>
</div>
)}
{/* Pay button */}
<div className="px-6 py-5 border-b border-gray-100">
<motion.button
type="button"
onClick={() => onPay(r, selectedPayment)}
onClick={() => onPay(r, selectedPayment, localPaymentImage)}
disabled={payingId === r.id}
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
@ -457,7 +511,7 @@ export default function PaymentsPage() {
{/* Cash pay button */}
<div className="px-6 py-4 border-b border-gray-100">
<button
<button
type="button"
onClick={() => setShowCashDialog(true)}
className="w-full bg-white border-2 border-amber-200 hover:border-amber-400 text-amber-700 font-bold py-3 px-6 rounded-2xl text-base transition-all flex items-center justify-center gap-2"

View File

@ -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) {