'use client'; import { useCallback, useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { CheckCircle, XCircle, Clock, User, Home, Calendar, DollarSign, AlertCircle, Key, DoorOpen, Shield, Phone, Mail, MessageCircle, ChevronDown, ChevronUp, FileText, Download, Printer, History, Loader2, CreditCard } from 'lucide-react'; import jsPDF from 'jspdf'; import html2canvas from 'html2canvas'; import toast, { Toaster } from 'react-hot-toast'; import AuthService from '../../services/AuthService'; import { adminConfirmDeposit, getRentProperty, getReservations } from '../../utils/api'; const RESERVATION_STATUS = Object.freeze({ pending: 0, ownerConfirmed: 1, depositPaid: 2, depositConfirmed: 3, completed: 4, cancelled: 5, }); const STATUS_KEY_BY_CODE = Object.freeze({ [RESERVATION_STATUS.pending]: 'pending', [RESERVATION_STATUS.ownerConfirmed]: 'ownerConfirmed', [RESERVATION_STATUS.depositPaid]: 'depositPaid', [RESERVATION_STATUS.depositConfirmed]: 'depositConfirmed', [RESERVATION_STATUS.completed]: 'completed', [RESERVATION_STATUS.cancelled]: 'cancelled', }); const STATUS_META = Object.freeze({ pending: { label: 'قيد الانتظار', card: 'border-yellow-400 bg-yellow-50', badge: 'bg-yellow-500 text-white', pdf: 'pending', tabActive: 'bg-yellow-600 text-white shadow-lg', tabIdle: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-200', }, ownerConfirmed: { label: 'موافقة المالك', card: 'border-blue-400 bg-blue-50', badge: 'bg-blue-500 text-white', pdf: 'ownerConfirmed', tabActive: 'bg-blue-600 text-white shadow-lg', tabIdle: 'bg-blue-100 text-blue-800 hover:bg-blue-200', }, depositPaid: { label: 'تم دفع العربون', card: 'border-indigo-400 bg-indigo-50', badge: 'bg-indigo-500 text-white', pdf: 'depositPaid', tabActive: 'bg-indigo-600 text-white shadow-lg', tabIdle: 'bg-indigo-100 text-indigo-800 hover:bg-indigo-200', }, depositConfirmed: { label: 'تم تأكيد العربون', card: 'border-green-400 bg-green-50', badge: 'bg-green-500 text-white', pdf: 'depositConfirmed', tabActive: 'bg-green-600 text-white shadow-lg', tabIdle: 'bg-green-100 text-green-800 hover:bg-green-200', }, completed: { label: 'منتهي', card: 'border-gray-400 bg-gray-50', badge: 'bg-gray-500 text-white', pdf: 'completed', tabActive: 'bg-gray-600 text-white shadow-lg', tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200', }, cancelled: { label: 'ملغي', card: 'border-red-400 bg-red-50', badge: 'bg-red-500 text-white', pdf: 'cancelled', tabActive: 'bg-red-600 text-white shadow-lg', tabIdle: 'bg-red-100 text-red-800 hover:bg-red-200', }, unknown: { label: 'غير معروف', card: 'border-gray-200 bg-white', badge: 'bg-gray-200 text-gray-700', pdf: 'unknown', tabActive: 'bg-gray-600 text-white shadow-lg', tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200', }, }); const FILTER_TABS = [ { id: 'all', label: 'الكل', tabActive: 'bg-gray-600 text-white shadow-lg', tabIdle: 'bg-gray-100 text-gray-800 hover:bg-gray-200', }, { id: 'pending', label: STATUS_META.pending.label, tabActive: STATUS_META.pending.tabActive, tabIdle: STATUS_META.pending.tabIdle, }, { id: 'ownerConfirmed', label: STATUS_META.ownerConfirmed.label, tabActive: STATUS_META.ownerConfirmed.tabActive, tabIdle: STATUS_META.ownerConfirmed.tabIdle, }, { id: 'depositPaid', label: STATUS_META.depositPaid.label, tabActive: STATUS_META.depositPaid.tabActive, tabIdle: STATUS_META.depositPaid.tabIdle, }, { id: 'depositConfirmed', label: STATUS_META.depositConfirmed.label, tabActive: STATUS_META.depositConfirmed.tabActive, tabIdle: STATUS_META.depositConfirmed.tabIdle, }, { id: 'completed', label: STATUS_META.completed.label, tabActive: STATUS_META.completed.tabActive, tabIdle: STATUS_META.completed.tabIdle, }, { id: 'cancelled', label: STATUS_META.cancelled.label, tabActive: STATUS_META.cancelled.tabActive, tabIdle: STATUS_META.cancelled.tabIdle, }, ]; function getStatusKey(status) { return STATUS_KEY_BY_CODE[Number(status)] ?? 'unknown'; } function getStatusMeta(status) { return STATUS_META[getStatusKey(status)] ?? STATUS_META.unknown; } function formatDisplayDate(value, locale = 'ar-SY') { if (!value) return '—'; const date = new Date(value); if (Number.isNaN(date.getTime())) { return String(value); } return date.toLocaleDateString(locale); } function formatInputDate(value) { if (!value) return '—'; const date = new Date(value); if (Number.isNaN(date.getTime())) { return String(value); } return date.toISOString().split('T')[0]; } function calculateReservationDays(startDate, endDate) { const start = new Date(startDate); const end = new Date(endDate); if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) { return 0; } const days = Math.ceil((end - start) / (1000 * 60 * 60 * 24)); return days > 0 ? days : 0; } function pickFirstNumber(...values) { for (const value of values) { const parsed = Number(value); if (Number.isFinite(parsed)) { return parsed; } } return 0; } function pickFirstText(...values) { for (const value of values) { if (typeof value === 'string' && value.trim()) { return value.trim(); } } return ''; } function normalizeCommissionType(value) { if (typeof value === 'string' && value.trim()) { return value.trim(); } if (value === 0) return 'من المالك'; if (value === 1) return 'من المستأجر'; if (value === 2) return 'من الطرفين'; return 'غير محدد'; } function buildReservationDisplayName(reservation) { return ( pickFirstText( reservation?.customer?.name, reservation?.tenant?.name, reservation?.user?.name, reservation?.customerName, reservation?.tenantName, reservation?.userName, reservation?.fullName, reservation?.name, ) || `الحجز #${reservation?.id ?? reservation?.reservationId ?? '—'}` ); } function normalizeReservation(reservation, property) { const info = property?.propertyInformation ?? property ?? {}; const status = Number.isFinite(Number(reservation?.status)) ? Number(reservation.status) : RESERVATION_STATUS.pending; const days = pickFirstNumber( reservation?.days, reservation?.durationInDays, calculateReservationDays(reservation?.startDate, reservation?.endDate), ); const totalAmount = pickFirstNumber( reservation?.totalPrice, reservation?.totalAmount, reservation?.price, ); const securityDeposit = pickFirstNumber( reservation?.securityDeposit, reservation?.deposit, property?.deposit, property?.securityDeposit, ); const dailyPrice = pickFirstNumber( reservation?.dailyPrice, reservation?.pricePerDay, days > 0 ? totalAmount / days : 0, ); const reservationId = pickFirstNumber(reservation?.reservationId, reservation?.id); return { ...reservation, id: reservationId || reservation?.id, reservationId: reservationId || reservation?.id, user: buildReservationDisplayName(reservation), userEmail: pickFirstText( reservation?.customer?.email, reservation?.tenant?.email, reservation?.user?.email, reservation?.customerEmail, reservation?.tenantEmail, reservation?.userEmail, ), userPhone: pickFirstText( reservation?.customer?.phoneNumber, reservation?.tenant?.phoneNumber, reservation?.user?.phoneNumber, reservation?.customerPhone, reservation?.tenantPhone, reservation?.userPhone, reservation?.phoneNumber, ), userType: pickFirstText( reservation?.customerTypeName, reservation?.customerType, reservation?.tenantType, 'غير محدد', ), identityNumber: pickFirstText( reservation?.customerIdentityNumber, reservation?.tenantIdentityNumber, reservation?.identityNumber, ), property: pickFirstText(info?.address, reservation?.propertyName) || `العقار #${reservation?.propertyId ?? '—'}`, propertyId: reservation?.propertyId, startDate: formatInputDate(reservation?.startDate), endDate: formatInputDate(reservation?.endDate), days, totalAmount, totalPrice: totalAmount, dailyPrice, commissionRate: pickFirstNumber(reservation?.commissionRate), commissionType: normalizeCommissionType(reservation?.commissionType), commissionAmount: pickFirstNumber(reservation?.commissionAmount), securityDeposit, status, requestDate: formatDisplayDate(reservation?.createdAt), createdAt: reservation?.createdAt, ownerApproved: status >= RESERVATION_STATUS.ownerConfirmed, adminApproved: status >= RESERVATION_STATUS.depositConfirmed, ownerDelivered: Boolean(reservation?.ownerDelivered), tenantReceived: Boolean(reservation?.tenantReceived), tenantLeft: Boolean(reservation?.tenantLeft), ownerReceived: Boolean(reservation?.ownerReceived), securityDepositPaid: status >= RESERVATION_STATUS.depositPaid, securityDepositReturned: reservation?.securityDepositReturned ?? null, contractSigned: Boolean(reservation?.contractSigned), notes: pickFirstText(reservation?.comment, reservation?.notes), actualStartDate: reservation?.actualStartDate ?? null, actualEndDate: reservation?.actualEndDate ?? null, _property: info, }; } const ReasonDialog = ({ isOpen, onClose, onConfirm, title, defaultReason = '' }) => { const [reason, setReason] = useState(defaultReason); const [otherReason, setOtherReason] = useState(''); const commonReasons = [ 'أعمال صيانة في العقار', 'العقار غير متاح في هذه التواريخ', 'مشكلة في وثائق المستأجر', 'المالك غير متاح للتسليم', 'تأخر في دفع الضمان', 'سبب آخر' ]; if (!isOpen) return null; return ( e.stopPropagation()} >

{title}

يرجى تحديد سبب الرفض

{commonReasons.map((r) => ( ))}