updates
This commit is contained in:
@@ -214,9 +214,37 @@ const BookingManagementPage: React.FC = () => {
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
<div className="text-sm font-bold text-slate-900 bg-gradient-to-r from-amber-600 to-amber-700 bg-clip-text text-transparent">
|
||||
{formatCurrency(booking.total_price)}
|
||||
</div>
|
||||
{(() => {
|
||||
const completedPayments = booking.payments?.filter(
|
||||
(p) => p.payment_status === 'completed'
|
||||
) || [];
|
||||
const amountPaid = completedPayments.reduce(
|
||||
(sum, p) => sum + (p.amount || 0),
|
||||
0
|
||||
);
|
||||
const remainingDue = booking.total_price - amountPaid;
|
||||
const hasPayments = completedPayments.length > 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="text-sm font-bold text-slate-900 bg-gradient-to-r from-amber-600 to-amber-700 bg-clip-text text-transparent">
|
||||
{formatCurrency(booking.total_price)}
|
||||
</div>
|
||||
{hasPayments && (
|
||||
<div className="text-xs mt-1">
|
||||
<div className="text-green-600 font-medium">
|
||||
Paid: {formatCurrency(amountPaid)}
|
||||
</div>
|
||||
{remainingDue > 0 && (
|
||||
<div className="text-amber-600 font-medium">
|
||||
Due: {formatCurrency(remainingDue)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</td>
|
||||
<td className="px-8 py-5 whitespace-nowrap">
|
||||
{getStatusBadge(booking.status)}
|
||||
@@ -369,12 +397,219 @@ const BookingManagementPage: React.FC = () => {
|
||||
<p className="text-lg font-semibold text-slate-900">{selectedBooking.guest_count} guest{selectedBooking.guest_count !== 1 ? 's' : ''}</p>
|
||||
</div>
|
||||
|
||||
{/* Total Price - Highlighted */}
|
||||
<div className="bg-gradient-to-br from-amber-50 via-yellow-50 to-amber-50 p-6 rounded-xl border-2 border-amber-200 shadow-lg">
|
||||
<label className="text-xs font-semibold text-amber-700 uppercase tracking-wider mb-2 block">Total Price</label>
|
||||
<p className="text-4xl font-bold bg-gradient-to-r from-amber-600 via-amber-700 to-amber-600 bg-clip-text text-transparent">
|
||||
{formatCurrency(selectedBooking.total_price)}
|
||||
</p>
|
||||
{/* Payment Method & Status */}
|
||||
<div className="bg-gradient-to-br from-indigo-50/50 to-purple-50/50 p-6 rounded-xl border border-indigo-100">
|
||||
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
|
||||
<div className="w-1 h-4 bg-gradient-to-b from-indigo-400 to-indigo-600 rounded-full"></div>
|
||||
Payment Information
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Payment Method</p>
|
||||
<p className="text-base font-semibold text-slate-900">
|
||||
{selectedBooking.payment_method === 'cash'
|
||||
? '💵 Pay at Hotel'
|
||||
: selectedBooking.payment_method === 'stripe'
|
||||
? '💳 Stripe (Card)'
|
||||
: selectedBooking.payment_method === 'paypal'
|
||||
? '💳 PayPal'
|
||||
: selectedBooking.payment_method || 'N/A'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Payment Status</p>
|
||||
<p className={`text-base font-semibold ${
|
||||
selectedBooking.payment_status === 'paid' || selectedBooking.payment_status === 'completed'
|
||||
? 'text-green-600'
|
||||
: selectedBooking.payment_status === 'pending'
|
||||
? 'text-yellow-600'
|
||||
: 'text-red-600'
|
||||
}`}>
|
||||
{selectedBooking.payment_status === 'paid' || selectedBooking.payment_status === 'completed'
|
||||
? '✅ Paid'
|
||||
: selectedBooking.payment_status === 'pending'
|
||||
? '⏳ Pending'
|
||||
: selectedBooking.payment_status === 'failed'
|
||||
? '❌ Failed'
|
||||
: selectedBooking.payment_status || 'Unpaid'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Service Usages */}
|
||||
{selectedBooking.service_usages && selectedBooking.service_usages.length > 0 && (
|
||||
<div className="bg-gradient-to-br from-purple-50/50 to-pink-50/50 p-6 rounded-xl border border-purple-100">
|
||||
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
|
||||
<div className="w-1 h-4 bg-gradient-to-b from-purple-400 to-purple-600 rounded-full"></div>
|
||||
Additional Services
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
{selectedBooking.service_usages.map((service: any, idx: number) => (
|
||||
<div key={service.id || idx} className="flex justify-between items-center py-2 border-b border-purple-100 last:border-0">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-900">{service.service_name || service.name || 'Service'}</p>
|
||||
<p className="text-xs text-slate-500">
|
||||
{formatCurrency(service.unit_price || service.price || 0)} × {service.quantity || 1}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-slate-900">
|
||||
{formatCurrency(service.total_price || (service.unit_price || service.price || 0) * (service.quantity || 1))}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Payment Breakdown */}
|
||||
{(() => {
|
||||
const completedPayments = selectedBooking.payments?.filter(
|
||||
(p) => p.payment_status === 'completed'
|
||||
) || [];
|
||||
const allPayments = selectedBooking.payments || [];
|
||||
const amountPaid = completedPayments.reduce(
|
||||
(sum, p) => sum + (p.amount || 0),
|
||||
0
|
||||
);
|
||||
const remainingDue = selectedBooking.total_price - amountPaid;
|
||||
const hasPayments = allPayments.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasPayments && (
|
||||
<div className="bg-gradient-to-br from-teal-50/50 to-cyan-50/50 p-6 rounded-xl border border-teal-100">
|
||||
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 block flex items-center gap-2">
|
||||
<div className="w-1 h-4 bg-gradient-to-b from-teal-400 to-teal-600 rounded-full"></div>
|
||||
Payment History
|
||||
</label>
|
||||
<div className="space-y-3">
|
||||
{allPayments.map((payment: any, idx: number) => (
|
||||
<div key={payment.id || idx} className="p-3 bg-white rounded-lg border border-teal-100">
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-900">
|
||||
{formatCurrency(payment.amount || 0)}
|
||||
</p>
|
||||
<p className="text-xs text-slate-500 mt-1">
|
||||
{payment.payment_type === 'deposit' ? 'Deposit (20%)' : payment.payment_type === 'remaining' ? 'Remaining Payment' : 'Full Payment'}
|
||||
{' • '}
|
||||
{payment.payment_method === 'stripe' ? 'Stripe' : payment.payment_method === 'paypal' ? 'PayPal' : payment.payment_method || 'Cash'}
|
||||
</p>
|
||||
</div>
|
||||
<span className={`text-xs px-2 py-1 rounded-full font-medium ${
|
||||
payment.payment_status === 'completed' || payment.payment_status === 'paid'
|
||||
? 'bg-green-100 text-green-700'
|
||||
: payment.payment_status === 'pending'
|
||||
? 'bg-yellow-100 text-yellow-700'
|
||||
: 'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{payment.payment_status === 'completed' || payment.payment_status === 'paid' ? 'Paid' : payment.payment_status || 'Pending'}
|
||||
</span>
|
||||
</div>
|
||||
{payment.transaction_id && (
|
||||
<p className="text-xs text-slate-400 font-mono">ID: {payment.transaction_id}</p>
|
||||
)}
|
||||
{payment.payment_date && (
|
||||
<p className="text-xs text-slate-400 mt-1">
|
||||
{new Date(payment.payment_date).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Payment Summary - Always show, even if no payments */}
|
||||
<div className="bg-gradient-to-br from-green-50 via-emerald-50 to-green-50 p-6 rounded-xl border-2 border-green-200 shadow-lg mb-4">
|
||||
<label className="text-xs font-semibold text-green-700 uppercase tracking-wider mb-2 block">Amount Paid</label>
|
||||
<p className="text-3xl font-bold bg-gradient-to-r from-green-600 via-emerald-700 to-green-600 bg-clip-text text-transparent">
|
||||
{formatCurrency(amountPaid)}
|
||||
</p>
|
||||
{hasPayments && completedPayments.length > 0 && (
|
||||
<p className="text-xs text-green-600 mt-2">
|
||||
{completedPayments.length} payment{completedPayments.length !== 1 ? 's' : ''} completed
|
||||
{amountPaid > 0 && selectedBooking.total_price > 0 && (
|
||||
<span className="ml-2">
|
||||
({((amountPaid / selectedBooking.total_price) * 100).toFixed(0)}% of total)
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{amountPaid === 0 && !hasPayments && (
|
||||
<p className="text-sm text-gray-500 mt-2">No payments made yet</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Remaining Due - Show prominently if there's remaining balance */}
|
||||
{remainingDue > 0 && (
|
||||
<div className="bg-gradient-to-br from-amber-50 via-yellow-50 to-amber-50 p-6 rounded-xl border-2 border-amber-200 shadow-lg mb-4">
|
||||
<label className="text-xs font-semibold text-amber-700 uppercase tracking-wider mb-2 block">Remaining Due (To be paid)</label>
|
||||
<p className="text-3xl font-bold text-amber-600">
|
||||
{formatCurrency(remainingDue)}
|
||||
</p>
|
||||
{selectedBooking.total_price > 0 && (
|
||||
<p className="text-xs text-amber-600 mt-2">
|
||||
({((remainingDue / selectedBooking.total_price) * 100).toFixed(0)}% of total)
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Total Booking Price - Show as reference */}
|
||||
<div className="bg-gradient-to-br from-slate-50 to-gray-50 p-6 rounded-xl border-2 border-slate-200 shadow-lg">
|
||||
<label className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2 block">Total Booking Price</label>
|
||||
<p className="text-2xl font-bold text-slate-700">
|
||||
{formatCurrency(selectedBooking.total_price)}
|
||||
</p>
|
||||
<p className="text-xs text-slate-500 mt-2">
|
||||
This is the total amount for the booking
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Booking Metadata */}
|
||||
<div className="bg-gradient-to-br from-slate-50 to-white p-6 rounded-xl border border-slate-200">
|
||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-4 block flex items-center gap-2">
|
||||
<div className="w-1 h-4 bg-gradient-to-b from-slate-400 to-slate-600 rounded-full"></div>
|
||||
Booking Metadata
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{selectedBooking.createdAt && (
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Created At</p>
|
||||
<p className="text-sm font-medium text-slate-900">
|
||||
{new Date(selectedBooking.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{selectedBooking.updatedAt && (
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Last Updated</p>
|
||||
<p className="text-sm font-medium text-slate-900">
|
||||
{new Date(selectedBooking.updatedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{selectedBooking.requires_deposit !== undefined && (
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Deposit Required</p>
|
||||
<p className="text-sm font-medium text-slate-900">
|
||||
{selectedBooking.requires_deposit ? 'Yes (20%)' : 'No'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{selectedBooking.deposit_paid !== undefined && (
|
||||
<div>
|
||||
<p className="text-xs text-slate-500 mb-1">Deposit Paid</p>
|
||||
<p className={`text-sm font-medium ${selectedBooking.deposit_paid ? 'text-green-600' : 'text-amber-600'}`}>
|
||||
{selectedBooking.deposit_paid ? '✅ Yes' : '❌ No'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Notes */}
|
||||
|
||||
Reference in New Issue
Block a user