"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, ImageIcon, User, } from "lucide-react"; import toast, { Toaster } from "react-hot-toast"; import AuthService from "../services/AuthService"; import { getPaymentTypes, getRentProperties, getUserReservations, payDeposit } from "../utils/api"; import { addPropertyRating } from "../utils/ratings"; import Loading from "../loading"; const API_BASE = process.env.NEXT_PUBLIC_API_URL || "https://45.93.137.91.nip.io/api"; const GOVERNORATES = ["دمشق", "ريف دمشق", "حلب", "حمص", "حماة", "اللاذقية", "طرطوس", "إدلب", "درعا", "السويداء", "القنيطرة", "دير الزور", "الرقة", "الحسكة"]; 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 }, }; 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?.() || ""; }; 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, paymentMethods, loadingPaymentMethods, receiptImage, onSetReceiptImage, haramDetails, onSetHaramDetails, }) { 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"); const selectedMethod = paymentMethods?.find((m) => String(m?.id ?? m?.name) === String(selectedPayment)); const isHaram = selectedMethod?.name?.toLowerCase?.() === "haram"; const hasSelectedMethod = !!selectedMethod; const isHaramFormValid = !isHaram || (haramDetails.fullName.trim() !== "" && haramDetails.governorate !== "" && haramDetails.phone.trim() !== ""); const canPay = payingId !== reservation.id && hasSelectedMethod && (!isHaram || (!!receiptImage && isHaramFormValid)); 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")}

{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 ( ); })}
)}
{isHaram && ( <>

بيانات حوالة الهرم

{haramDetails.split("\n").map((line, idx) => (
{line}
))}

{t("uploadReceipt", { defaultValue: "إيصال الدفع" })}

{receiptImage ? (
receipt

{receiptImage.name}

) : ( )}
)}
onConfirmPay(reservation)} 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), })} )}
{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 })}

{t("reportDescription")}