'use client';
import { useState, useEffect, useCallback } from 'react';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import {
Calendar, Clock, CheckCircle, XCircle, Eye, Loader2, Search,
MapPin, DollarSign, Home, ArrowLeft, CreditCard, Timer, Star,
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
import AuthService from '../services/AuthService';
import { 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';
const STATUS_MAP = ['pending','ownerConfirmed','depositPaid','depositConfirmed','completed','cancelled'];
const STATUS_UI = {
pending: { label: 'قيد الانتظار', color: 'bg-yellow-100 text-yellow-800', icon: Clock },
ownerConfirmed: { label: 'مؤكد من المالك', color: 'bg-blue-100 text-blue-800', icon: CheckCircle },
depositPaid: { label: 'تم دفع السلفة', color: 'bg-indigo-100 text-indigo-800', icon: DollarSign },
depositConfirmed: { label: 'مؤكد', color: 'bg-green-100 text-green-800', icon: CheckCircle },
completed: { label: 'منتهي', color: 'bg-green-100 text-green-800', icon: CheckCircle },
cancelled: { label: 'ملغي', color: 'bg-gray-100 text-gray-800', icon: XCircle },
};
function statusLabel(code) { return STATUS_UI[STATUS_MAP[code]]?.label ?? String(code); }
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; }
function StatusBadge({ code }) {
const Icon = statusIcon(code);
return (
{statusLabel(code)}
);
}
const propAddr = (p, r) => p?.address ?? r?.propertyAddress ?? '';
const propImages = (p, r) => {
if (p?.images && Array.isArray(p.images)) return p.images;
if (r?.property?.images && Array.isArray(r.property.images)) return r.property.images;
return [];
};
const propBeds = (p, r) => p?.numberOfBedRooms ?? r?.property?.numberOfBedRooms ?? 0;
const propBaths = (p, r) => p?.numberOfBathRooms ?? r?.property?.numberOfBathRooms ?? 0;
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 formatWindowDuration(str) {
if (!str) return '';
const clean = str.replace(/-/g, '');
const dotIdx = clean.indexOf('.');
let totalHours = 0, timePart = clean;
if (dotIdx !== -1) {
const days = parseInt(clean.substring(0, dotIdx), 10) || 0;
totalHours += days * 24;
timePart = clean.substring(dotIdx + 1);
}
const parts = timePart.split(':');
if (parts.length >= 2) {
totalHours += parseInt(parts[0], 10) || 0;
}
if (totalHours > 0) return `${String(totalHours).padStart(2, '0')}:00:00`;
return timePart.substring(0, 8);
}
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, r);
const img = imgs.length > 0 ? `${API_BASE}${imgs[0]}` : null;
const addr = propAddr(p, r);
const beds = propBeds(p, r);
const baths = propBaths(p, r);
const isOwnerConfirmed = STATUS_MAP[r.status] === 'ownerConfirmed';
const canRate = STATUS_MAP[r.status] === 'depositPaid' || STATUS_MAP[r.status] === 'completed';
const hasTimeWindow = r.ownerApprovalDate && p?.allowedPaymentPeriod;
const deadline = hasTimeWindow
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(p.allowedPaymentPeriod)
: null;
const isExpired = deadline ? Date.now() > deadline : false;
const isPaying = payingId === r.id;
const [showRating, setShowRating] = useState(false);
const [ratingValue, setRatingValue] = useState(0);
const [ratingComment, setRatingComment] = useState('');
const [submittingRating, setSubmittingRating] = useState(false);
return (
{img &&
}
{r.totalPrice?.toLocaleString() ?? '—'}
السعر الإجمالي
{(beds||baths) &&
{beds>0&&{beds} غرف}{baths>0&&{baths} حمامات}
}
من
{new Date(r.startDate).toLocaleDateString('ar')}
إلى
{new Date(r.endDate).toLocaleDateString('ar')}
{isOwnerConfirmed && hasTimeWindow &&
متبقي للدفع:
مدة الدفع: {formatWindowDuration(p.allowedPaymentPeriod)}
}
{isOwnerConfirmed && !isExpired && }
{canRate && !showRating &&
}
{canRate && showRating &&
{[1,2,3,4,5].map(n => (
))}
}
);
}
function DetailsModal({ r, isOpen, onClose, onPay, payingId }) {
if (!isOpen || !r) return null;
const p = r._prop;
const isOwnerConfirmed = STATUS_MAP[r.status] === 'ownerConfirmed';
const hasTimeWindow = r.ownerApprovalDate && p?.allowedPaymentPeriod;
const deadline = hasTimeWindow
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(p.allowedPaymentPeriod)
: null;
const isExpired = deadline ? Date.now() > deadline : false;
const isPaying = payingId === r.id;
return (
e.stopPropagation()}>
تفاصيل الحجز
رقم الحجز: #{r.id}
{p &&
معلومات العقار
العنوان: {propAddr(p, r)||'—'}
{(propBeds(p, r)||propBaths(p, r)) &&
{propBeds(p, r)>0&&{propBeds(p, r)} غرف}
{propBaths(p, r)>0&&{propBaths(p, r)} حمامات}
}
}
تفاصيل الحجز
تاريخ البداية
{new Date(r.startDate).toLocaleDateString('ar')}
تاريخ النهاية
{new Date(r.endDate).toLocaleDateString('ar')}
تاريخ الإنشاء
{new Date(r.createdAt).toLocaleDateString('ar')}
المعلومات المالية
الإجمالي{r.totalPrice?.toLocaleString()??'—'}
{isOwnerConfirmed && hasTimeWindow &&
متبقي للدفع:
مدة الدفع: {formatWindowDuration(p.allowedPaymentPeriod)}
{!isExpired &&
}
}
);
}
export default function UserReservationsPage() {
const router = useRouter();
const [reservations, setReservations] = useState([]);
const [filtered, setFiltered] = useState([]);
const [loading, setLoading] = useState(true);
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 [data, rentProps] = await Promise.all([
getUserReservations(),
getRentProperties().catch(() => []),
]);
const list = Array.isArray(data) ? data : [];
const propsList = Array.isArray(rentProps) ? rentProps : [];
const propMap = {};
propsList.forEach(rp => {
const info = rp?.propertyInformation ?? {};
if (rp?.allowedPaymentPeriod) info.allowedPaymentPeriod = rp.allowedPaymentPeriod;
propMap[rp.propertyInformationId] = info;
propMap[rp.propertyInformation?.id] = info;
});
const enriched = list.map(r => {
if (r.propertyId && propMap[r.propertyId]) r._prop = propMap[r.propertyId];
return r;
});
setReservations(enriched);
setFiltered(enriched);
} catch (err) {
console.error(err);
toast.error('فشل تحميل الحجوزات');
setReservations([]);
setFiltered([]);
}
setLoading(false);
}, []);
useEffect(() => {
let r = reservations;
if (filterStatus !== 'all') r = r.filter(x => STATUS_MAP[x.status] === filterStatus);
if (searchTerm) { const q = searchTerm.toLowerCase(); r = r.filter(x => propAddr(x._prop, x).toLowerCase().includes(q) || String(x.id).includes(q)); }
setFiltered(r);
}, [reservations, filterStatus, searchTerm]);
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,
paymentTypeId: 1,
transactionType: 1,
comment: null,
});
toast.success('تم دفع السلفة بنجاح!');
loadReservations();
} catch (err) {
toast.error(err?.message || 'فشل عملية الدفع');
} finally {
setPayingId(null);
}
};
if (loading) return
;
return (
setSelected(null)} onPay={handlePay} payingId={payingId} />
حجوزاتي
لديك {reservations.length} حجز
{Object.entries(counts).map(([s, c]) => (
setFilterStatus(s)}>
{c}
{s==='all'?'الكل':(STATUS_UI[s]?.label||s)}
))}
setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500"/>
{filtered.length === 0 ? (
لا توجد حجوزات
لم تقم بأي حجز حتى الآن
) : (
{filtered.map(r => )}
)}
);
}