diff --git a/app/components/admin/BookingRequests.js b/app/components/admin/BookingRequests.js index cf0a7a5..87f017e 100644 --- a/app/components/admin/BookingRequests.js +++ b/app/components/admin/BookingRequests.js @@ -419,6 +419,84 @@ const ReasonDialog = ({ isOpen, onClose, onConfirm, title, defaultReason = '' }) ); }; +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} + + + setComment(e.target.value)} + placeholder="اكتب تعليقاً إذا أردت..." + className="w-full min-h-32 p-4 border rounded-xl resize-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" + disabled={isSubmitting} + /> + + + onConfirm(comment)} + disabled={isSubmitting} + className="flex-1 bg-indigo-600 text-white py-3 rounded-xl font-medium hover:bg-indigo-700 transition-colors disabled:opacity-60 disabled:cursor-wait flex items-center justify-center gap-2" + > + {isSubmitting ? : } + {isSubmitting ? 'جاري الإرسال...' : 'تأكيد الإرسال'} + + + إلغاء + + + + + ); +}; + const PDFExportButton = ({ request, onExportComplete }) => { const [isExporting, setIsExporting] = useState(false); @@ -1023,6 +1101,7 @@ const RequestDetailsDialog = ({ request, isOpen, onClose }) => { )} + @@ -1307,6 +1386,7 @@ export default function BookingRequests() { 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(''); @@ -1364,7 +1444,16 @@ export default function BookingRequests() { loadReservations(); }, [loadReservations]); - const handleDepositConfirmation = async (request) => { + 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(), @@ -1394,6 +1483,10 @@ export default function BookingRequests() { 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, @@ -1405,7 +1498,7 @@ export default function BookingRequests() { payload: { reservationId, adminId, - comment: null, + comment: normalizedComment, }, }); @@ -1421,11 +1514,13 @@ export default function BookingRequests() { setConfirmingDepositId(request.id); try { - const result = await adminConfirmDeposit(reservationId, adminId, null); + 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, @@ -1444,12 +1539,13 @@ export default function BookingRequests() { status: RESERVATION_STATUS.depositConfirmed, adminApproved: true, securityDepositPaid: true, - notes: 'تم تأكيد العربون من قبل الإدارة', + notes: normalizedComment || 'تم تأكيد العربون من قبل الإدارة', } : req, ), ); + setDepositDialog({ isOpen: false, request: null }); toast.success('تم تأكيد العربون بنجاح'); } catch (err) { console.error('[Admin] Deposit confirmation failed:', err); @@ -1574,7 +1670,7 @@ export default function BookingRequests() { setDetailsDialog({ isOpen: true, request: selectedRequest })} confirmingDepositId={confirmingDepositId} /> @@ -1603,6 +1699,14 @@ export default function BookingRequests() { isOpen={detailsDialog.isOpen} onClose={() => setDetailsDialog({ isOpen: false, request: null })} /> + + handleDepositConfirmation(depositDialog.request, comment)} + /> ); } diff --git a/app/utils/api.js b/app/utils/api.js index 35de4e2..69493bc 100644 --- a/app/utils/api.js +++ b/app/utils/api.js @@ -383,12 +383,21 @@ export async function confirmDepositPayment(bookingId) { export async function adminConfirmDeposit(reservationId, adminId, comment = null) { const token = AuthService.getToken(); const endpoint = `${API_BASE}/Reservations/AdminConfirmDeposit/admin-confirm-deposit`; - const payload = { reservationId, adminId, comment }; + const normalizedComment = + typeof comment === 'string' && comment.trim() + ? comment.trim() + : null; + const payload = { + reservationId, + adminId, + comment: normalizedComment, + }; console.log('[API] AdminConfirmDeposit request', { method: 'PUT', endpoint, payload, + adminIdSource: 'jwt-user-id', hasToken: Boolean(token), tokenPreview: token ? `${token.slice(0, 18)}...${token.slice(-8)}` : null, });
+ يمكنك إضافة تعليق اختياري، أو ترك الحقل فارغاً ليتم إرسال null. +