'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) => (
))}
);
};
const DepositCommentDialog = ({
isOpen,
request,
isSubmitting,
onClose,
onConfirm,
}) => {
const [comment, setComment] = useState('');
useEffect(() => {
if (!isOpen) {
setComment('');
}
}, [isOpen, request?.id]);
if (!isOpen || !request) return null;
return (
e.stopPropagation()}
>
تأكيد العربون
يمكنك إضافة تعليق اختياري، أو ترك الحقل فارغاً ليتم إرسال null.
رقم الحجز: #{request.id}
العقار: {request.property}
المستأجر: {request.user}
);
};
const PDFExportButton = ({ request, onExportComplete }) => {
const [isExporting, setIsExporting] = useState(false);
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
};
const generatePDF = async () => {
if (!request) {
toast.error('لا توجد بيانات للتصدير');
return;
}
const statusMeta = getStatusMeta(request.status);
const userTypeLabel = request.userType || 'غير محدد';
setIsExporting(true);
const loadingToast = toast.loading('جاري إنشاء ملف PDF...', { id: 'pdf-export' });
try {
const printContent = document.createElement('div');
printContent.style.width = '800px';
printContent.style.padding = '40px';
printContent.style.backgroundColor = 'white';
printContent.style.fontFamily = 'Cairo, Arial, sans-serif';
printContent.style.direction = 'rtl';
printContent.style.position = 'absolute';
printContent.style.left = '-9999px';
printContent.style.top = '-9999px';
printContent.style.zIndex = '-9999';
printContent.innerHTML = `
معلومات المستأجر
الاسم الكامل:
${request.user || 'غير محدد'}
نوع الهوية:
${userTypeLabel}
رقم الهوية:
${request.identityNumber || 'غير محدد'}
البريد الإلكتروني:
${request.userEmail || 'غير محدد'}
رقم الهاتف:
${request.userPhone || 'غير محدد'}
معلومات العقار
العقار:
${request.property || 'غير محدد'}
السعر اليومي:
${formatCurrency(request.dailyPrice)}
تفاصيل الحجز
تاريخ البداية:
${request.startDate || 'غير محدد'}
تاريخ النهاية:
${request.endDate || 'غير محدد'}
عدد الأيام:
${request.days || 0} يوم
الحالة:
${statusMeta.label}
المعلومات المالية
سلفة الضمان:
${formatCurrency(request.securityDeposit)}
المبلغ الإجمالي:
${formatCurrency(request.totalAmount)}
نسبة العمولة:
${request.commissionRate || 0}%
نوع العمولة:
${request.commissionType || 'غير محدد'}
قيمة العمولة:
${formatCurrency(request.commissionAmount)}
${request.notes ? `
` : ''}
سجل الإجراءات
${request.requestDate}
تم إنشاء الطلب
${request.ownerApproved ? `
✓
تمت موافقة المالك
` : ''}
${request.adminApproved ? `
✓
تمت موافقة الإدارة
` : ''}
${request.ownerDelivered ? `
تم تسليم المفتاح
` : ''}
${request.tenantReceived ? `
تم استلام العقار
` : ''}
`;
document.body.appendChild(printContent);
await new Promise(resolve => setTimeout(resolve, 200));
const canvas = await html2canvas(printContent, {
scale: 2.5,
backgroundColor: '#ffffff',
logging: false,
useCORS: true,
windowWidth: printContent.scrollWidth,
windowHeight: printContent.scrollHeight,
onclone: (clonedDoc, element) => {
}
});
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4',
compress: true
});
const imgWidth = 210;
const pageHeight = 297;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
let pageCount = 1;
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
pageCount++;
}
const fileName = `تقرير_طلب_${request.id}_${new Date().toISOString().split('T')[0]}.pdf`;
pdf.save(fileName);
document.body.removeChild(printContent);
toast.success(`تم تصدير التقرير بنجاح! (${pageCount} صفحة)`, { id: 'pdf-export' });
if (onExportComplete) {
onExportComplete();
}
} catch (error) {
console.error('Error generating PDF:', error);
toast.error('حدث خطأ أثناء إنشاء ملف PDF', { id: 'pdf-export' });
} finally {
setIsExporting(false);
}
};
return (
);
};
const RequestDetailsDialog = ({ request, isOpen, onClose }) => {
if (!isOpen || !request) return null;
const formatCurrency = (amount) => {
return amount?.toLocaleString() + ' ل.س';
};
return (
e.stopPropagation()}
>
تفاصيل الطلب #{request.id}
جميع معلومات الطلب في مكان واحد
معلومات المستأجر
{request.user}
{request.userType || 'غير محدد'}
{request.identityNumber}
{request.userEmail}
معلومات العقار
{request.property}
{formatCurrency(request.dailyPrice)}
تفاصيل الحجز
{request.startDate}
{request.endDate}
{request.days} يوم
{formatCurrency(request.totalAmount)}
المعلومات المالية
{formatCurrency(request.securityDeposit)}
{request.securityDepositPaid ? '✓ تم الدفع' : '⏳ في انتظار الدفع'}
{request.commissionRate}%
{request.commissionType}
{formatCurrency(request.commissionAmount)}
سجل الإجراءات
تم إنشاء الطلب: {request.requestDate}
{request.ownerApproved && (
)}
{request.adminApproved && (
)}
{request.ownerDelivered && (
تم تسليم المفتاح من المالك
)}
{request.notes && (
)}
);
};
const RequestCard = ({ request, onConfirmDeposit, onViewDetails, confirmingDepositId }) => {
const [expanded, setExpanded] = useState(
Number(request.status) === RESERVATION_STATUS.depositPaid,
);
const isConfirmingDeposit = confirmingDepositId === request.id;
const statusMeta = getStatusMeta(request.status);
const canConfirmDeposit = Number(request.status) === RESERVATION_STATUS.depositPaid;
const formatCurrency = (amount) => {
const value = Number(amount) || 0;
return value.toLocaleString() + ' ل.س';
};
return (
setExpanded((prev) => !prev)}>
طلب #{request.id}
{statusMeta.label}
{request.requestDate}
{expanded ? (
) : (
)}
{request.user}
{request.property}
{request.days} أيام
{formatCurrency(request.totalAmount)}
{expanded && (
العربون
{formatCurrency(request.securityDeposit)}
حالة العربون
{request.securityDepositPaid ? 'تم دفع العربون' : 'بانتظار الدفع'}
العمولة
{request.commissionRate}% ({request.commissionType})
مدة الإيجار
{request.startDate} إلى {request.endDate}
{(request.userEmail || request.userPhone) && (
معلومات الاتصال
{request.userEmail || 'غير متوفر'}
{request.userPhone || 'غير متوفر'}
)}
{canConfirmDeposit && (
)}
{Number(request.status) === RESERVATION_STATUS.depositConfirmed && (
تم تأكيد العربون من قبل الإدارة.
)}
{request.notes && (
{request.notes}
)}
)}
);
};
export default function BookingRequests() {
/*
Legacy dummy data kept for reference only. The page now loads real
reservations from the API instead of rendering these local examples.
const DUMMY_REQUESTS = [
{
id: 'REQ001',
user: 'أحمد محمد',
userEmail: 'ahmed@example.com',
userPhone: '0938123456',
userType: 'syrian',
identityNumber: '123456789',
property: 'فيلا فاخرة في دمشق',
propertyId: 1,
startDate: '2024-03-01',
endDate: '2024-03-10',
days: 10,
totalAmount: 5000000,
dailyPrice: 500000,
commissionRate: 5,
commissionType: 'من المالك',
commissionAmount: 250000,
securityDeposit: 500000,
status: 'pending',
requestDate: '2024-02-25',
ownerApproved: false,
adminApproved: false,
ownerDelivered: false,
tenantReceived: false,
tenantLeft: false,
ownerReceived: false,
securityDepositPaid: false,
securityDepositReturned: null,
contractSigned: false,
notes: '',
actualStartDate: null,
actualEndDate: null
},
{
id: 'REQ002',
user: 'سارة أحمد',
userEmail: 'sara@example.com',
userPhone: '0945123789',
userType: 'passport',
identityNumber: 'AB123456',
property: 'شقة حديثة في حلب',
propertyId: 2,
startDate: '2024-03-05',
endDate: '2024-03-15',
days: 10,
totalAmount: 2500000,
dailyPrice: 250000,
commissionRate: 7,
commissionType: 'من المستأجر',
commissionAmount: 175000,
securityDeposit: 250000,
status: 'admin_approved',
requestDate: '2024-02-24',
ownerApproved: true,
adminApproved: true,
ownerDelivered: false,
tenantReceived: false,
tenantLeft: false,
ownerReceived: false,
securityDepositPaid: false,
securityDepositReturned: null,
contractSigned: false,
notes: '',
actualStartDate: null,
actualEndDate: null
},
{
id: 'REQ003',
user: 'محمد الحلبي',
userEmail: 'mohammed@example.com',
userPhone: '0956123456',
userType: 'syrian',
identityNumber: '987654321',
property: 'شقة بجانب البحر في اللاذقية',
propertyId: 3,
startDate: '2024-02-20',
endDate: '2024-03-20',
days: 30,
totalAmount: 9000000,
dailyPrice: 300000,
commissionRate: 5,
commissionType: 'من الاثنين',
commissionAmount: 450000,
securityDeposit: 500000,
status: 'active',
requestDate: '2024-02-15',
ownerApproved: true,
adminApproved: true,
ownerDelivered: true,
tenantReceived: true,
tenantLeft: false,
ownerReceived: false,
securityDepositPaid: true,
securityDepositReturned: null,
contractSigned: true,
notes: 'عقد موقع إلكترونياً',
actualStartDate: '2024-02-20',
actualEndDate: null
}
];
*/
const [requests, setRequests] = useState([]);
const [filter, setFilter] = useState('depositPaid');
const [detailsDialog, setDetailsDialog] = useState({ isOpen: false, request: null });
const [depositDialog, setDepositDialog] = useState({ isOpen: false, request: null });
const [confirmingDepositId, setConfirmingDepositId] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [loadError, setLoadError] = useState('');
const loadReservations = useCallback(async () => {
setIsLoading(true);
setLoadError('');
try {
if (!AuthService.isAdmin()) {
throw new Error('هذه الصفحة متاحة للإدارة فقط');
}
let list = await getReservations();
if (!Array.isArray(list)) {
list = [];
}
const propertyCache = new Map();
const uniquePropertyIds = [...new Set(list.map((item) => item?.propertyId).filter((id) => id != null))];
await Promise.all(
uniquePropertyIds.map(async (propertyId) => {
try {
const property = await getRentProperty(propertyId);
propertyCache.set(propertyId, property);
} catch (error) {
console.warn('[Admin] Failed to load property details for reservation:', propertyId, error);
propertyCache.set(propertyId, null);
}
}),
);
const normalizedRequests = list
.map((reservation) => normalizeReservation(reservation, propertyCache.get(reservation?.propertyId)))
.filter((reservation) => reservation?.id != null)
.sort((left, right) => {
const leftDate = new Date(left.createdAt || 0).getTime();
const rightDate = new Date(right.createdAt || 0).getTime();
return rightDate - leftDate;
});
setRequests(normalizedRequests);
} catch (error) {
console.error('[Admin] Failed to load reservations:', error);
setLoadError(error.message || 'فشل تحميل الحجوزات');
setRequests([]);
toast.error(error.message || 'فشل تحميل الحجوزات');
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
loadReservations();
}, [loadReservations]);
const openDepositConfirmationDialog = (request) => {
setDepositDialog({ isOpen: true, request });
};
const closeDepositConfirmationDialog = () => {
if (confirmingDepositId != null) return;
setDepositDialog({ isOpen: false, request: null });
};
const handleDepositConfirmation = async (request, commentInput = null) => {
if (!AuthService.isAdmin()) {
console.warn('[Admin] Deposit confirmation blocked: current user is not admin', {
user: AuthService.getUser(),
roles: AuthService.getRoles?.(),
request,
});
toast.error('هذا الإجراء متاح للإدارة فقط');
return;
}
if (Number(request?.status) !== RESERVATION_STATUS.depositPaid) {
console.warn('[Admin] Deposit confirmation blocked: reservation is not in depositPaid state', {
requestId: request?.id,
reservationId: request?.reservationId,
status: request?.status,
});
toast.error('يمكن تأكيد العربون فقط عندما تكون الحالة depositPaid');
return;
}
const reservationId = Number(request?.reservationId ?? request?.id);
if (!Number.isFinite(reservationId)) {
toast.error('تعذر تحديد رقم الحجز المطلوب');
return;
}
const adminUser = AuthService.getUser();
const parsedAdminId = Number(adminUser?.id);
const adminId = Number.isFinite(parsedAdminId) ? parsedAdminId : adminUser?.id;
const normalizedComment =
typeof commentInput === 'string' && commentInput.trim()
? commentInput.trim()
: null;
console.log('[Admin] Preparing admin confirm deposit request', {
requestId: request?.id,
reservationId,
adminId,
adminUser,
status: request?.status,
endpoint: '/Reservations/AdminConfirmDeposit/admin-confirm-deposit',
payload: {
reservationId,
adminId,
comment: normalizedComment,
},
});
if (adminId == null || adminId === '') {
console.warn('[Admin] Deposit confirmation blocked: adminId is missing', {
adminUser,
parsedAdminId,
});
toast.error('لم نتمكن من تحديد هوية المدير');
return;
}
setConfirmingDepositId(request.id);
try {
const result = await adminConfirmDeposit(reservationId, adminId, normalizedComment);
console.log('[Admin] Deposit confirmation response', {
requestId: request?.id,
reservationId,
adminId,
comment: normalizedComment,
status: result?.status,
ok: result?.ok,
message: result?.message,
data: result?.data,
});
if (!result.ok) {
throw new Error(result.message || result.data?.message || `HTTP ${result.status}`);
}
setRequests((prev) =>
prev.map((req) =>
req.id === request.id
? {
...req,
status: RESERVATION_STATUS.depositConfirmed,
adminApproved: true,
securityDepositPaid: true,
notes: normalizedComment || 'تم تأكيد العربون من قبل الإدارة',
}
: req,
),
);
setDepositDialog({ isOpen: false, request: null });
toast.success('تم تأكيد العربون بنجاح');
} catch (err) {
console.error('[Admin] Deposit confirmation failed:', err);
toast.error(err.message || 'فشل تأكيد العربون');
} finally {
setConfirmingDepositId(null);
}
};
const filteredRequests = requests.filter((req) =>
filter === 'all' ? true : getStatusKey(req.status) === filter,
);
const counts = FILTER_TABS.reduce((acc, tab) => {
acc[tab.id] =
tab.id === 'all'
? requests.length
: requests.filter((req) => getStatusKey(req.status) === tab.id).length;
return acc;
}, {});
const stats = {
total: requests.length,
depositPaid: counts.depositPaid || 0,
depositConfirmed: counts.depositConfirmed || 0,
completed: counts.completed || 0,
};
return (
متابعة حجوزات العربون
يتم جلب جميع الحجوزات من الخادم، مع فتح الحجوزات ذات الحالة depositPaid (2) بشكل افتراضي.
{loadError && (
{loadError}
)}
{stats.total}
إجمالي الحجوزات
{stats.depositPaid}
تم دفع العربون
{stats.depositConfirmed}
تم تأكيد العربون
{stats.completed}
منتهية
تصفية حسب الحالة
{filteredRequests.length} طلب
{FILTER_TABS.map((tab) => (
))}
{isLoading ? (
) : (
{filteredRequests.map((request) => (
setDetailsDialog({ isOpen: true, request: selectedRequest })}
confirmingDepositId={confirmingDepositId}
/>
))}
)}
{!isLoading && filteredRequests.length === 0 && (
لا توجد حجوزات مطابقة
لم يتم العثور على حجوزات ضمن الحالة الحالية.
)}
setDetailsDialog({ isOpen: false, request: null })}
/>
handleDepositConfirmation(depositDialog.request, comment)}
/>
);
}