// '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 [ratings, setRatings] = useState({ clean: 0, services: 0, ownerBehavior: 0, experience: 0 });
// const [ratingComment, setRatingComment] = useState('');
// const [submittingRating, setSubmittingRating] = useState(false);
// return (
//
//
// {img &&
}
//
//
//
// {addr &&
{addr}
}
//
//
//
{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 &&
//
// {[
// { key: 'clean', label: 'النظافة' },
// { key: 'services', label: 'الخدمات' },
// { key: 'ownerBehavior', label: 'تعامل المالك' },
// { key: 'experience', label: 'التجربة العامة' },
// ].map(cat =>
//
{cat.label}
//
// {[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 => )}
//
// )}
//
//
// );
// }
'use client';
import { useState, useEffect, useCallback } from 'react';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { useTranslation } from 'react-i18next';
import {
Calendar,
Clock,
CheckCircle,
XCircle,
Eye,
Loader2,
Search,
MapPin,
DollarSign,
Home,
ArrowLeft,
CreditCard,
Timer,
Star,
Flag,
Banknote,
Building,
ArrowLeftRight,
Check,
X,
Info,
Landmark,
Phone,
Wallet,
} 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: { color: 'bg-yellow-100 text-yellow-800', icon: Clock },
ownerConfirmed: { color: 'bg-blue-100 text-blue-800', icon: CheckCircle },
depositPaid: { color: 'bg-indigo-100 text-indigo-800', icon: DollarSign },
depositConfirmed: { color: 'bg-green-100 text-green-800', icon: CheckCircle },
completed: { color: 'bg-green-100 text-green-800', icon: CheckCircle },
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; }
function formatCurrency(v, sign = '') {
return `${sign} ${Number(v ?? 0).toLocaleString()}`;
}
function formatDate(date) {
if (!date) return '';
const d = new Date(date);
if (Number.isNaN(d.getTime())) return '';
return d.toLocaleDateString('en-GB');
}
function StatusBadge({ code }) {
const { t } = useTranslation();
const Icon = statusIcon(code);
const key = STATUS_MAP[code] || 'pending';
return (
{t(`bookingStatus.${key}`)}
);
}
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;
const getAuthToken = () => {
if (typeof window === 'undefined') return '';
return (
AuthService.getToken?.() ||
AuthService.getAccessToken?.() ||
localStorage.getItem('token') ||
localStorage.getItem('accessToken') ||
localStorage.getItem('authToken') ||
''
);
};
const readStoredUser = () => {
if (typeof window === 'undefined') return null;
const keys = ['user', 'currentUser', 'authUser', 'profile'];
for (const key of keys) {
const raw = localStorage.getItem(key);
if (!raw) continue;
try {
return JSON.parse(raw);
} catch {
return raw;
}
}
return null;
};
const extractNumericUserId = (value) => {
if (!value) return null;
if (typeof value === 'number') return Number.isInteger(value) ? value : null;
if (typeof value === 'string') {
const n = Number(value);
return Number.isInteger(n) ? n : null;
}
if (typeof value === 'object') {
const candidates = [
value.id,
value.userId,
value.userID,
value.user?.id,
value.user?.userId,
value.profile?.id,
value.profile?.userId,
value.data?.id,
];
for (const candidate of candidates) {
const id = extractNumericUserId(candidate);
if (id !== null) return id;
}
}
return null;
};
async function reportReservation(reservationId, message) {
const user = AuthService.getUser?.() ?? readStoredUser();
const reporter = extractNumericUserId(user);
const rid = Number(reservationId);
if (!Number.isInteger(rid)) {
throw new Error('Invalid reservation ID');
}
if (!Number.isInteger(reporter)) {
throw new Error('Could not identify current user');
}
const token = getAuthToken();
const res = await fetch(`${API_BASE}/ReservationReports/ReportReservation`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify({
reservationId: rid,
message: message ?? null,
reporter,
}),
});
if (!res.ok) {
let errorMessage = 'Failed to submit report';
try {
const data = await res.json();
errorMessage = data?.message || data?.title || errorMessage;
} catch (_) {
try {
const text = await res.text();
if (text) errorMessage = text;
} catch (_) {}
}
throw new Error(errorMessage);
}
try {
return await res.json();
} catch {
return null;
}
}
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 { t } = useTranslation();
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 {t('timeExpired')};
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 PaymentDialog({
isOpen,
reservation,
onClose,
selectedPayment,
onSelectPayment,
onConfirmPay,
payingId,
}) {
const { t } = useTranslation();
const [showCashDialog, setShowCashDialog] = useState(false);
useEffect(() => {
if (isOpen) setShowCashDialog(false);
}, [isOpen, reservation?.id]);
if (!isOpen || !reservation) return null;
const amount = reservation.depositAmount || reservation.totalPrice || 0;
const currencySign = reservation.currencySign || t('sypSymbol');
return (
<>
e.stopPropagation()}
>
{reservation.propertyName}
{(reservation.propertyAddress || reservation.propertyCity) && (
{[reservation.propertyCity, reservation.propertyAddress].filter(Boolean).join(' - ')}
)}
{formatDate(reservation.startDate)} - {formatDate(reservation.endDate)}
#{reservation.reservationId || reservation.id}
{t('depositAmount')}
{formatCurrency(amount, currencySign)}
{t('depositPaidToPlatform')}
{t('paymentMethod')}
{PAYMENT_METHODS.map((method) => {
const isSelected = selectedPayment === method.id;
const Icon = method.icon;
return (
);
})}
onConfirmPay(reservation)}
disabled={payingId === reservation.id}
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
className="w-full bg-amber-500 hover:bg-amber-600 disabled:bg-amber-300 text-white font-bold py-4 px-6 rounded-2xl text-lg transition-all shadow-lg hover:shadow-xl flex items-center justify-center gap-3"
>
{payingId === reservation.id ? (
<>
{t('processingPayment')}
>
) : (
<>
{t('payDeposit', { amount: formatCurrency(amount, currencySign) })}
>
)}
{t('cashPaymentLocation')}
{t('platformOfficeAddress')}
+963567823411
{showCashDialog && (
{t('cashPaymentPending')}
{t('cashPaymentInstructions')}
{t('platformLocation')}
{t('platformOfficeAddress')}
+963567823411
{t('myBookings')}
)}
>
);
}
function ReportDialog({ isOpen, reservation, onClose, onSubmit, submitting }) {
const { t } = useTranslation();
const [message, setMessage] = useState('');
useEffect(() => {
if (isOpen) setMessage('');
}, [isOpen, reservation?.id]);
if (!isOpen || !reservation) return null;
return (
e.stopPropagation()}
>
{t('reportReservation')}
{t('reservationNumber', { id: reservation.id })}
);
}
function ReservationCard({ r, onViewDetails, onPay, onReport, payingId, reportingId }) {
const { t } = useTranslation();
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 isReporting = reportingId === r.id;
const [showRating, setShowRating] = useState(false);
const [ratings, setRatings] = useState({ clean: 0, services: 0, ownerBehavior: 0, experience: 0 });
const [ratingComment, setRatingComment] = useState('');
const [submittingRating, setSubmittingRating] = useState(false);
return (
{img &&
}
{r.totalPrice?.toLocaleString() ?? '—'}
{t('totalPrice')}
{(beds||baths) &&
{beds>0&&{beds} {t('rooms')}}{baths>0&&{baths} {t('bathrooms')}}
}
{t('from')}
{new Date(r.startDate).toLocaleDateString('ar')}
{t('to')}
{new Date(r.endDate).toLocaleDateString('ar')}
{isOwnerConfirmed && hasTimeWindow &&
{t('remainingForPayment')}
{t('paymentDuration', { duration: formatWindowDuration(p.allowedPaymentPeriod) })}
}
{isOwnerConfirmed && !isExpired && }
{canRate && !showRating &&
}
{canRate && showRating &&
{[
{ key: 'clean', label: 'ratingCategory.clean' },
{ key: 'services', label: 'ratingCategory.services' },
{ key: 'ownerBehavior', label: 'ratingCategory.ownerBehavior' },
{ key: 'experience', label: 'ratingCategory.experience' },
].map(cat =>
{t(cat.label)}
{[1,2,3,4,5].map(n => (
))}
)}
}
);
}
function DetailsModal({ r, isOpen, onClose, onPay, onReport, payingId, reportingId }) {
const { t } = useTranslation();
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;
const isReporting = reportingId === r.id;
return (
e.stopPropagation()}>
{t('bookingDetails')}
{t('reservationNumber', { id: r.id })}
{p &&
{t('propertyInfo')}
{t('addressLabel')} {propAddr(p, r)||'—'}
{(propBeds(p, r)||propBaths(p, r)) &&
{propBeds(p, r)>0&&{propBeds(p, r)} {t('rooms')}}
{propBaths(p, r)>0&&{propBaths(p, r)} {t('bathrooms')}}
}
}
{t('bookingDetails')}
{t('startDateLabel')}
{new Date(r.startDate).toLocaleDateString('ar')}
{t('endDateLabel')}
{new Date(r.endDate).toLocaleDateString('ar')}
{t('createdAtLabel')}
{new Date(r.createdAt).toLocaleDateString('ar')}
{t('financialInfo')}
{t('total')}{r.totalPrice?.toLocaleString()??'—'}
{isOwnerConfirmed && hasTimeWindow &&
{t('remainingForPayment')}
{t('paymentDuration', { duration: formatWindowDuration(p.allowedPaymentPeriod) })}
{!isExpired &&
}
}
);
}
export default function UserReservationsPage() {
const { t } = useTranslation();
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);
const [reportDialog, setReportDialog] = useState({ open: false, reservation: null });
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 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(t('failedToLoadBookings'));
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 openPaymentDialog = (r) => {
setPaymentDialog({ open: true, reservation: r });
};
const closePaymentDialog = () => {
setPaymentDialog({ open: false, reservation: null });
};
const handleConfirmPay = async (r) => {
setPayingId(r.id);
try {
await payDeposit({
reservationId: r.id,
paymentTypeId: 1,
transactionType: 1,
comment: null,
});
toast.success(t('depositPaidSuccess'));
closePaymentDialog();
loadReservations();
} catch (err) {
toast.error(err?.message || t('paymentFailed'));
} finally {
setPayingId(null);
}
};
const openReportDialog = (r) => {
setReportDialog({ open: true, reservation: r });
};
const closeReportDialog = () => {
setReportDialog({ open: false, reservation: null });
};
const handleSubmitReport = async (message) => {
if (!reportDialog.reservation) return;
setReportingId(reportDialog.reservation.id);
try {
await reportReservation(reportDialog.reservation.id, message.trim() || null);
toast.success(t('reportSubmittedSuccess'));
closeReportDialog();
} catch (err) {
toast.error(err?.message || t('reportFailed'));
} finally {
setReportingId(null);
}
};
if (loading) return
;
return (
setSelected(null)}
onPay={openPaymentDialog}
onReport={openReportDialog}
payingId={payingId}
reportingId={reportingId}
/>
{t('myBookings')}
{t('youHaveBookings', { count: reservations.length })}
{Object.entries(counts).map(([s, c]) => (
setFilterStatus(s)}>
{c}
{s==='all'?t('all'):t(`bookingStatus.${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 ? (
{t('noBookings')}
{t('noReservationsDesc')}
) : (
{filtered.map(r => (
))}
)}
);
}