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 => )}
)}