From 3b66bb53f142909c221475add10a4583d5e6cf03 Mon Sep 17 00:00:00 2001 From: Rahaf Date: Sat, 4 Jul 2026 01:49:04 +0300 Subject: [PATCH] Added payment types --- app/property/[id]/PropertyDetail.js | 2 +- app/reservations/page.js | 162 ++++++++++++++-------------- app/utils/api.js | 4 + 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/app/property/[id]/PropertyDetail.js b/app/property/[id]/PropertyDetail.js index e1c5027..5ead963 100644 --- a/app/property/[id]/PropertyDetail.js +++ b/app/property/[id]/PropertyDetail.js @@ -187,7 +187,7 @@ function mapApiDetail(item) { const rawImages = Array.isArray(info.images) ? info.images : []; const photosFallback = Array.isArray(details.photos) ? details.photos : []; const allRaw = rawImages.length > 0 ? rawImages : photosFallback; - const images = allRaw.length > 0 ? allRaw.map(buildImageUrl) : []; + const images = allRaw.length > 0 ? allRaw.map(buildImageUrl).filter(Boolean) : []; // Normalize services: Flutter sends list of strings or object with boolean values const rawServices = details.services || {}; diff --git a/app/reservations/page.js b/app/reservations/page.js index 6887749..9265475 100644 --- a/app/reservations/page.js +++ b/app/reservations/page.js @@ -425,7 +425,7 @@ import { } from 'lucide-react'; import toast, { Toaster } from 'react-hot-toast'; import AuthService from '../services/AuthService'; -import { getRentProperties, getUserReservations, payDeposit } from '../utils/api'; +import { getPaymentTypes, getRentProperties, getUserReservations, payDeposit } from '../utils/api'; import { addPropertyRating } from '../utils/ratings'; const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api'; @@ -441,37 +441,6 @@ const STATUS_UI = { cancelled: { color: 'bg-gray-100 text-gray-800', icon: XCircle }, }; -const PAYMENT_METHODS = [ - { - id: 'cash', - labelKey: 'payment.cash', - descKey: 'payment.cashDesc', - icon: Banknote, - active: true, - }, - { - id: 'office', - labelKey: 'payment.office', - descKey: 'payment.officeDesc', - icon: Building, - active: true, - }, - { - id: 'transfer', - labelKey: 'payment.transfer', - descKey: 'payment.transferDesc', - icon: ArrowLeftRight, - active: false, - }, - { - id: 'electronic', - labelKey: 'payment.electronic', - descKey: 'payment.electronicDesc', - icon: CreditCard, - active: false, - }, -]; - function statusColor(code) { return STATUS_UI[STATUS_MAP[code]]?.color ?? 'bg-gray-100 text-gray-700'; } function statusIcon(code) { return STATUS_UI[STATUS_MAP[code]]?.icon ?? Clock; } @@ -667,6 +636,8 @@ function PaymentDialog({ onSelectPayment, onConfirmPay, payingId, + paymentMethods, + loadingPaymentMethods, }) { const { t } = useTranslation(); const [showCashDialog, setShowCashDialog] = useState(false); @@ -745,52 +716,66 @@ function PaymentDialog({

{t('paymentMethod')}

-
- {PAYMENT_METHODS.map((method) => { - const isSelected = selectedPayment === method.id; - const Icon = method.icon; + {loadingPaymentMethods ? ( +
+ {t('loadingPaymentMethods', { defaultValue: 'جاري تحميل طرق الدفع...' })} +
+ ) : (!Array.isArray(paymentMethods) || paymentMethods.length === 0) ? ( +
+ {t('noPaymentMethodsAvailable', { defaultValue: 'لا توجد طرق دفع متاحة حالياً.' })} +
+ ) : ( +
+ {paymentMethods.map((method, index) => { + const isActive = method?.isActive === true || method?.active === true; + const optionId = method?.id ?? method?.name ?? `payment-method-${index}`; + const label = method?.name || t('paymentMethod', { defaultValue: 'طريقة الدفع' }); + const isSelected = selectedPayment === optionId; - return ( - - ); - })} -
+
+

+ {label} +

+

+ {isActive + ? t('paymentMethodAvailable', { defaultValue: 'متاحة للاستعمال' }) + : t('paymentMethodUnavailable', { defaultValue: 'غير متاحة حالياً' })} +

+ + {isActive ? t('active', { defaultValue: 'نشطة' }) : t('inactive', { defaultValue: 'غير نشطة' })} + +
+ {isSelected && ( +
+ +
+ )} + + ); + })} +
+ )}
@@ -1176,8 +1161,8 @@ export default function UserReservationsPage() { const [reportingId, setReportingId] = useState(null); const [selectedPayment, setSelectedPayment] = useState('cash'); const [paymentDialog, setPaymentDialog] = useState({ open: false, reservation: null }); - - useEffect(() => { if (!AuthService.getUser()) { router.push('/login'); return; } loadReservations(); }, [router]); + const [paymentMethods, setPaymentMethods] = useState([]); + const [loadingPaymentMethods, setLoadingPaymentMethods] = useState(false); const loadReservations = useCallback(async () => { try { @@ -1207,8 +1192,27 @@ export default function UserReservationsPage() { setFiltered([]); } setLoading(false); + }, [t]); + + useEffect(() => { if (!AuthService.getUser()) { router.push('/login'); return; } loadReservations(); }, [router, loadReservations]); + + const loadPaymentMethods = useCallback(async () => { + setLoadingPaymentMethods(true); + try { + const data = await getPaymentTypes(); + setPaymentMethods(Array.isArray(data) ? data : []); + } catch (err) { + console.error(err); + setPaymentMethods([]); + } finally { + setLoadingPaymentMethods(false); + } }, []); + useEffect(() => { + loadPaymentMethods(); + }, [loadPaymentMethods]); + useEffect(() => { let r = reservations; if (filterStatus !== 'all') r = r.filter(x => STATUS_MAP[x.status] === filterStatus); @@ -1301,6 +1305,8 @@ export default function UserReservationsPage() { onSelectPayment={setSelectedPayment} onConfirmPay={handleConfirmPay} payingId={payingId} + paymentMethods={paymentMethods} + loadingPaymentMethods={loadingPaymentMethods} />
diff --git a/app/utils/api.js b/app/utils/api.js index 53c0327..1f0ee27 100644 --- a/app/utils/api.js +++ b/app/utils/api.js @@ -355,6 +355,10 @@ export async function getCurrencies() { return apiFetch("/Currency/GetAll"); } +export async function getPaymentTypes() { + return apiFetch("/PaymentType/GetAll"); +} + // ─── Files ─── export async function uploadPicture(file) {