From e21dc53227642209134dd6a883d6dc24d5e114ee Mon Sep 17 00:00:00 2001 From: mouazkh Date: Tue, 26 May 2026 20:54:25 +0300 Subject: [PATCH] added a swicher on the properties --- app/reservations/page.js | 95 ++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/app/reservations/page.js b/app/reservations/page.js index 5c8eb52..a7f4f39 100644 --- a/app/reservations/page.js +++ b/app/reservations/page.js @@ -5,11 +5,11 @@ import { motion } from 'framer-motion'; import { useRouter } from 'next/navigation'; import { Calendar, Clock, CheckCircle, XCircle, Eye, Loader2, Search, - MapPin, DollarSign, Home, ArrowLeft, + MapPin, DollarSign, Home, ArrowLeft, CreditCard, Timer, } from 'lucide-react'; import toast, { Toaster } from 'react-hot-toast'; import AuthService from '../services/AuthService'; -import { getRentProperty } from '../utils/api'; +import { getRentProperty, getUserReservations, payDeposit } from '../utils/api'; const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api'; @@ -51,13 +51,52 @@ const propImages = (p) => Array.isArray(p?.images) ? p.images : []; const propBeds = (p) => p?.numberOfBedRooms ?? 0; const propBaths = (p) => p?.numberOfBathRooms ?? 0; -function ReservationCard({ r, onViewDetails }) { +function parseTimeSpan(str) { + if (!str) return 0; + const clean = str.replace(/-/g, ''); + const dotIdx = clean.indexOf('.'); + let days = 0, timePart = clean; + if (dotIdx !== -1) { + days = parseInt(clean.substring(0, dotIdx), 10) || 0; + timePart = clean.substring(dotIdx + 1); + } + const parts = timePart.split(':'); + if (parts.length < 2) return days * 86400000; + const hh = parseInt(parts[0], 10) || 0; + const mm = parseInt(parts[1], 10) || 0; + const ss = parts.length > 2 ? (parseInt(parts[2], 10) || 0) : 0; + return ((days * 86400) + (hh * 3600) + (mm * 60) + ss) * 1000; +} + +function CountdownTimer({ deadline }) { + const [remaining, setRemaining] = useState(deadline ? Math.max(0, deadline - Date.now()) : 0); + useEffect(() => { + if (!deadline) return; + const tick = () => setRemaining(Math.max(0, deadline - Date.now())); + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [deadline]); + if (remaining <= 0) return انتهت المهلة; + const h = Math.floor(remaining / 3600000); + const m = Math.floor((remaining % 3600000) / 60000); + const s = Math.floor((remaining % 60000) / 1000); + const pad = (n) => String(n).padStart(2, '0'); + return {pad(h)}:{pad(m)}:{pad(s)}; +} + +function ReservationCard({ r, onViewDetails, onPay, payingId }) { const p = r._prop; const imgs = propImages(p); const img = imgs.length > 0 ? `${API_BASE}${imgs[0]}` : null; const addr = propAddr(p); const beds = propBeds(p); const baths = propBaths(p); + const isOwnerConfirmed = STATUS_MAP[r.status] === 'ownerConfirmed'; + const deadline = isOwnerConfirmed && r.ownerApprovalDate + ? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod) + : null; + const isPaying = payingId === r.id; return ( {new Date(r.endDate).toLocaleDateString('ar')} + {isOwnerConfirmed &&
+ متبقي للدفع: + +
}
+ {isOwnerConfirmed && }
); } -function DetailsModal({ r, isOpen, onClose }) { +function DetailsModal({ r, isOpen, onClose, onPay, payingId }) { if (!isOpen || !r) return null; const p = r._prop; + const isOwnerConfirmed = STATUS_MAP[r.status] === 'ownerConfirmed'; + const deadline = isOwnerConfirmed && r.ownerApprovalDate + ? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod) + : null; + const isPaying = payingId === r.id; return ( المعلومات المالية
الإجمالي{r.totalPrice?.toLocaleString()??'—'}
+ {isOwnerConfirmed &&
+ متبقي للدفع: + + +
}
@@ -148,18 +208,14 @@ export default function UserReservationsPage() { const [selected, setSelected] = useState(null); const [filterStatus, setFilterStatus] = useState('all'); const [searchTerm, setSearchTerm] = useState(''); + const [payingId, setPayingId] = useState(null); useEffect(() => { if (!AuthService.getUser()) { router.push('/login'); return; } loadReservations(); }, [router]); const loadReservations = useCallback(async () => { try { - const res = await fetch(`${API_BASE}/Reservations/GetUserResevations`, { - headers: { Authorization: `Bearer ${AuthService.getToken()}` }, - }); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const json = await res.json(); - let list = json.data || json || []; - if (!Array.isArray(list)) list = []; + const data = await getUserReservations(); + const list = Array.isArray(data) ? data : []; const enriched = await Promise.all(list.map(enrich)); setReservations(enriched); setFiltered(enriched); @@ -182,12 +238,25 @@ export default function UserReservationsPage() { const allStatuses = [...new Set(reservations.map(r => STATUS_MAP[r.status]))]; const counts = { all: reservations.length, ...Object.fromEntries(allStatuses.map(s => [s, reservations.filter(r => STATUS_MAP[r.status] === s).length])) }; + const handlePay = async (r) => { + setPayingId(r.id); + try { + await payDeposit({ reservationId: r.id }); + toast.success('تم دفع السلفة بنجاح!'); + loadReservations(); + } catch (err) { + toast.error(err?.message || 'فشل عملية الدفع'); + } finally { + setPayingId(null); + } + }; + if (loading) return
; return (
- setSelected(null)} /> + setSelected(null)} onPay={handlePay} payingId={payingId} />
@@ -217,7 +286,7 @@ export default function UserReservationsPage() {
) : (
- {filtered.map(r => )} + {filtered.map(r => )}
)}