added a swicher on the properties
All checks were successful
Build frontend / build (push) Successful in 46s
All checks were successful
Build frontend / build (push) Successful in 46s
This commit is contained in:
@ -68,6 +68,24 @@ function parseTimeSpan(str) {
|
|||||||
return ((days * 86400) + (hh * 3600) + (mm * 60) + ss) * 1000;
|
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 }) {
|
function CountdownTimer({ deadline }) {
|
||||||
const [remaining, setRemaining] = useState(deadline ? Math.max(0, deadline - Date.now()) : 0);
|
const [remaining, setRemaining] = useState(deadline ? Math.max(0, deadline - Date.now()) : 0);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -96,6 +114,7 @@ function ReservationCard({ r, onViewDetails, onPay, payingId }) {
|
|||||||
const deadline = isOwnerConfirmed && r.ownerApprovalDate
|
const deadline = isOwnerConfirmed && r.ownerApprovalDate
|
||||||
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod)
|
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod)
|
||||||
: null;
|
: null;
|
||||||
|
const isExpired = deadline ? Date.now() > deadline : false;
|
||||||
const isPaying = payingId === r.id;
|
const isPaying = payingId === r.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -124,16 +143,19 @@ function ReservationCard({ r, onViewDetails, onPay, payingId }) {
|
|||||||
<div className="text-sm font-medium">{new Date(r.endDate).toLocaleDateString('ar')}</div>
|
<div className="text-sm font-medium">{new Date(r.endDate).toLocaleDateString('ar')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isOwnerConfirmed && <div className="flex items-center justify-between bg-blue-50 p-3 rounded-xl mb-3">
|
{isOwnerConfirmed && <div className="bg-blue-50 p-3 rounded-xl mb-3">
|
||||||
<span className="text-sm text-blue-800 font-medium flex items-center gap-1"><Timer className="w-4 h-4"/> متبقي للدفع:</span>
|
<div className="flex items-center justify-between mb-1">
|
||||||
<CountdownTimer deadline={deadline} />
|
<span className="text-sm text-blue-800 font-medium flex items-center gap-1"><Timer className="w-4 h-4"/> متبقي للدفع:</span>
|
||||||
|
<CountdownTimer deadline={deadline} />
|
||||||
|
</div>
|
||||||
|
{r.allowedPaymentPeriod && <div className="text-xs text-blue-600">مدة الدفع: {formatWindowDuration(r.allowedPaymentPeriod)}</div>}
|
||||||
</div>}
|
</div>}
|
||||||
<div className="flex gap-3 pt-3 border-t border-gray-100">
|
<div className="flex gap-3 pt-3 border-t border-gray-100">
|
||||||
<button onClick={() => onViewDetails(r)}
|
<button onClick={() => onViewDetails(r)}
|
||||||
className="flex-1 bg-gray-100 text-gray-700 py-2 rounded-xl text-sm font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2">
|
className="flex-1 bg-gray-100 text-gray-700 py-2 rounded-xl text-sm font-medium hover:bg-gray-200 transition-colors flex items-center justify-center gap-2">
|
||||||
<Eye className="w-4 h-4"/> التفاصيل
|
<Eye className="w-4 h-4"/> التفاصيل
|
||||||
</button>
|
</button>
|
||||||
{isOwnerConfirmed && <button onClick={() => onPay(r)} disabled={isPaying}
|
{isOwnerConfirmed && !isExpired && <button onClick={() => onPay(r)} disabled={isPaying}
|
||||||
className={`flex-1 py-2 rounded-xl text-sm font-medium transition-colors flex items-center justify-center gap-2 ${isPaying ? 'bg-gray-300 text-gray-500 cursor-not-allowed' : 'bg-amber-500 text-white hover:bg-amber-600'}`}>
|
className={`flex-1 py-2 rounded-xl text-sm font-medium transition-colors flex items-center justify-center gap-2 ${isPaying ? 'bg-gray-300 text-gray-500 cursor-not-allowed' : 'bg-amber-500 text-white hover:bg-amber-600'}`}>
|
||||||
{isPaying ? <Loader2 className="w-4 h-4 animate-spin"/> : <CreditCard className="w-4 h-4"/>} {isPaying ? 'جاري الدفع...' : 'ادفع الآن'}
|
{isPaying ? <Loader2 className="w-4 h-4 animate-spin"/> : <CreditCard className="w-4 h-4"/>} {isPaying ? 'جاري الدفع...' : 'ادفع الآن'}
|
||||||
</button>}
|
</button>}
|
||||||
@ -150,6 +172,7 @@ function DetailsModal({ r, isOpen, onClose, onPay, payingId }) {
|
|||||||
const deadline = isOwnerConfirmed && r.ownerApprovalDate
|
const deadline = isOwnerConfirmed && r.ownerApprovalDate
|
||||||
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod)
|
? new Date(r.ownerApprovalDate).getTime() + parseTimeSpan(r.allowedPaymentPeriod)
|
||||||
: null;
|
: null;
|
||||||
|
const isExpired = deadline ? Date.now() > deadline : false;
|
||||||
const isPaying = payingId === r.id;
|
const isPaying = payingId === r.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -186,13 +209,16 @@ function DetailsModal({ r, isOpen, onClose, onPay, payingId }) {
|
|||||||
<h3 className="font-bold text-amber-700 mb-3 flex items-center gap-2"><DollarSign className="w-5 h-5"/> المعلومات المالية</h3>
|
<h3 className="font-bold text-amber-700 mb-3 flex items-center gap-2"><DollarSign className="w-5 h-5"/> المعلومات المالية</h3>
|
||||||
<div className="flex justify-between font-bold"><span className="text-gray-900">الإجمالي</span><span className="text-amber-600 text-lg">{r.totalPrice?.toLocaleString()??'—'}</span></div>
|
<div className="flex justify-between font-bold"><span className="text-gray-900">الإجمالي</span><span className="text-amber-600 text-lg">{r.totalPrice?.toLocaleString()??'—'}</span></div>
|
||||||
</div>
|
</div>
|
||||||
{isOwnerConfirmed && <div className="flex items-center justify-between bg-blue-50 p-4 rounded-xl">
|
{isOwnerConfirmed && <div className="bg-blue-50 p-4 rounded-xl">
|
||||||
<span className="text-blue-800 font-medium flex items-center gap-2"><Timer className="w-5 h-5"/> متبقي للدفع:</span>
|
<div className="flex items-center justify-between mb-2">
|
||||||
<CountdownTimer deadline={deadline} />
|
<span className="text-blue-800 font-medium flex items-center gap-2"><Timer className="w-5 h-5"/> متبقي للدفع:</span>
|
||||||
<button onClick={() => { onPay(r); onClose(); }} disabled={isPaying}
|
<CountdownTimer deadline={deadline} />
|
||||||
className={`px-6 py-2 rounded-xl font-medium transition-colors flex items-center gap-2 ${isPaying ? 'bg-gray-300 text-gray-500 cursor-not-allowed' : 'bg-amber-500 text-white hover:bg-amber-600'}`}>
|
</div>
|
||||||
|
{r.allowedPaymentPeriod && <div className="text-xs text-blue-600 mb-3">مدة الدفع: {formatWindowDuration(r.allowedPaymentPeriod)}</div>}
|
||||||
|
{!isExpired && <button onClick={() => { onPay(r); onClose(); }} disabled={isPaying}
|
||||||
|
className={`w-full py-2 rounded-xl font-medium transition-colors flex items-center justify-center gap-2 ${isPaying ? 'bg-gray-300 text-gray-500 cursor-not-allowed' : 'bg-amber-500 text-white hover:bg-amber-600'}`}>
|
||||||
{isPaying ? <Loader2 className="w-5 h-5 animate-spin"/> : <CreditCard className="w-5 h-5"/>} {isPaying ? 'جاري الدفع...' : 'ادفع الآن'}
|
{isPaying ? <Loader2 className="w-5 h-5 animate-spin"/> : <CreditCard className="w-5 h-5"/>} {isPaying ? 'جاري الدفع...' : 'ادفع الآن'}
|
||||||
</button>
|
</button>}
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@ -241,7 +267,12 @@ export default function UserReservationsPage() {
|
|||||||
const handlePay = async (r) => {
|
const handlePay = async (r) => {
|
||||||
setPayingId(r.id);
|
setPayingId(r.id);
|
||||||
try {
|
try {
|
||||||
await payDeposit({ reservationId: r.id });
|
await payDeposit({
|
||||||
|
reservationId: r.id,
|
||||||
|
paymentTypeId: 1,
|
||||||
|
transactionType: 1,
|
||||||
|
comment: null,
|
||||||
|
});
|
||||||
toast.success('تم دفع السلفة بنجاح!');
|
toast.success('تم دفع السلفة بنجاح!');
|
||||||
loadReservations();
|
loadReservations();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user