This commit is contained in:
@ -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 (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||||
onClick={isSubmitting ? undefined : onClose}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.95, y: 20 }}
|
||||
animate={{ scale: 1, y: 0 }}
|
||||
exit={{ scale: 0.95, y: 20 }}
|
||||
className="bg-white rounded-2xl w-full max-w-lg p-6 shadow-2xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="text-center mb-5">
|
||||
<div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<MessageCircle className="w-8 h-8 text-indigo-600" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-900">تأكيد العربون</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
يمكنك إضافة تعليق اختياري، أو ترك الحقل فارغاً ليتم إرسال <span className="font-semibold">null</span>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl bg-gray-50 px-4 py-3 text-sm text-gray-700 mb-4 space-y-1">
|
||||
<div>رقم الحجز: <span className="font-semibold">#{request.id}</span></div>
|
||||
<div>العقار: <span className="font-semibold">{request.property}</span></div>
|
||||
<div>المستأجر: <span className="font-semibold">{request.user}</span></div>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
value={comment}
|
||||
onChange={(e) => 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}
|
||||
/>
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<button
|
||||
onClick={() => 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 ? <Loader2 className="w-5 h-5 animate-spin" /> : <CreditCard className="w-5 h-5" />}
|
||||
<span>{isSubmitting ? 'جاري الإرسال...' : 'تأكيد الإرسال'}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={onClose}
|
||||
disabled={isSubmitting}
|
||||
className="flex-1 bg-gray-100 text-gray-700 py-3 rounded-xl font-medium hover:bg-gray-200 transition-colors disabled:opacity-60"
|
||||
>
|
||||
إلغاء
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
const PDFExportButton = ({ request, onExportComplete }) => {
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
|
||||
@ -1023,6 +1101,7 @@ const RequestDetailsDialog = ({ request, isOpen, onClose }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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() {
|
||||
<RequestCard
|
||||
key={request.id}
|
||||
request={request}
|
||||
onConfirmDeposit={handleDepositConfirmation}
|
||||
onConfirmDeposit={openDepositConfirmationDialog}
|
||||
onViewDetails={(selectedRequest) => setDetailsDialog({ isOpen: true, request: selectedRequest })}
|
||||
confirmingDepositId={confirmingDepositId}
|
||||
/>
|
||||
@ -1603,6 +1699,14 @@ export default function BookingRequests() {
|
||||
isOpen={detailsDialog.isOpen}
|
||||
onClose={() => setDetailsDialog({ isOpen: false, request: null })}
|
||||
/>
|
||||
|
||||
<DepositCommentDialog
|
||||
isOpen={depositDialog.isOpen}
|
||||
request={depositDialog.request}
|
||||
isSubmitting={confirmingDepositId === depositDialog.request?.id}
|
||||
onClose={closeDepositConfirmationDialog}
|
||||
onConfirm={(comment) => handleDepositConfirmation(depositDialog.request, comment)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user