feat: add rental payment history section and interface to admin rental details page
This commit is contained in:
@@ -72,6 +72,7 @@ interface Rental {
|
||||
acceptedAt?: string;
|
||||
activatedAt?: string;
|
||||
lockHistory?: LockEvent[];
|
||||
paymentHistory?: PaymentHistory[];
|
||||
}
|
||||
|
||||
interface LockEvent {
|
||||
@@ -82,6 +83,16 @@ interface LockEvent {
|
||||
performedAt: string;
|
||||
}
|
||||
|
||||
interface PaymentHistory {
|
||||
id: string;
|
||||
amount: number;
|
||||
type: 'daily' | 'weekly' | 'monthly' | 'deposit' | 'penalty';
|
||||
date: string;
|
||||
method: 'cash' | 'bank' | 'wallet' | 'bkash' | 'nagad';
|
||||
status: 'paid' | 'pending' | 'failed';
|
||||
note?: string;
|
||||
}
|
||||
|
||||
const mockRentals: Rental[] = [
|
||||
{
|
||||
id: 'RNT-001',
|
||||
@@ -123,6 +134,12 @@ const mockRentals: Rental[] = [
|
||||
createdAt: '2024-01-15',
|
||||
acceptedAt: '2024-01-15',
|
||||
activatedAt: '2024-01-16',
|
||||
paymentHistory: [
|
||||
{ id: 'PAY-001', amount: 3000, type: 'deposit', date: '2024-01-15', method: 'bkash', status: 'paid', note: 'Deposit payment' },
|
||||
{ id: 'PAY-002', amount: 12000, type: 'monthly', date: '2024-02-15', method: 'bank', status: 'paid', note: 'February rent' },
|
||||
{ id: 'PAY-003', amount: 12000, type: 'monthly', date: '2024-03-15', method: 'bank', status: 'paid', note: 'March rent' },
|
||||
{ id: 'PAY-004', amount: 12000, type: 'monthly', date: '2024-04-15', method: 'bank', status: 'pending', note: 'April rent - pending' },
|
||||
],
|
||||
lockHistory: [
|
||||
{ id: 'lh1', action: 'locked', reason: 'First payment overdue - Day 1 penalty', performedBy: 'System', performedAt: '2024-02-01' },
|
||||
{ id: 'lh2', action: 'unlocked', reason: 'Payment received', performedBy: 'Admin Manager', performedAt: '2024-02-03' },
|
||||
@@ -282,6 +299,7 @@ export default function RentalDetailPage() {
|
||||
{ id: 'n2', text: 'First monthly payment received.', createdAt: '2024-02-15' },
|
||||
]);
|
||||
const [newNote, setNewNote] = useState('');
|
||||
const [paymentPage, setPaymentPage] = useState(0);
|
||||
const [editForm, setEditForm] = useState<Partial<Rental>>({});
|
||||
const [showLockModal, setShowLockModal] = useState(false);
|
||||
const [showUnlockModal, setShowUnlockModal] = useState(false);
|
||||
@@ -692,6 +710,7 @@ export default function RentalDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Initial Condition Images */}
|
||||
{/* {(rental.status === 'pending' || rental.status === 'accepted') && rental.initialImages && ( */}
|
||||
{rental.initialImages && (
|
||||
<div className="bg-white p-4 rounded-xl border border-slate-200">
|
||||
@@ -710,7 +729,7 @@ export default function RentalDetailPage() {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-5 gap-2">
|
||||
{(['front', 'back', 'left', 'right', 'battery'] as const).map(type => {
|
||||
const img = rental.initialImages?.find(i => i.type === type);
|
||||
return (
|
||||
@@ -730,8 +749,7 @@ export default function RentalDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
{/* Notes */}
|
||||
<div className="bg-slate-50 p-4 rounded-xl border border-slate-100">
|
||||
<h3 className="font-semibold text-slate-700 mb-3 flex items-center gap-2">
|
||||
<MessageSquare className="w-5 h-5" /> Notes ({notes.length})
|
||||
@@ -761,6 +779,61 @@ export default function RentalDetailPage() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rental Payment History */}
|
||||
{rental.paymentHistory && rental.paymentHistory.length > 0 && (
|
||||
<div className="bg-white p-4 rounded-xl border border-slate-200">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-slate-700 flex items-center gap-2">
|
||||
<DollarSign className="w-5 h-5 text-emerald-500" /> Rental Payment History
|
||||
</h3>
|
||||
<span className="text-xs text-slate-500">{rental.paymentHistory.length} payments</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{rental.paymentHistory.slice(paymentPage, paymentPage + 10).map(payment => (
|
||||
<div key={payment.id} className="p-3 bg-slate-50 rounded-lg border border-slate-100">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`text-xs font-semibold px-2 py-1 rounded-full ${payment.type === 'deposit' ? 'bg-purple-100 text-purple-700' : payment.type === 'penalty' ? 'bg-red-100 text-red-700' : 'bg-emerald-100 text-emerald-700'}`}>
|
||||
{payment.type.charAt(0).toUpperCase() + payment.type.slice(1)}
|
||||
</span>
|
||||
<span className={`text-xs font-medium px-2 py-1 rounded-full ${payment.status === 'paid' ? 'bg-green-100 text-green-700' : payment.status === 'pending' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}>
|
||||
{payment.status.charAt(0).toUpperCase() + payment.status.slice(1)}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold text-slate-800">৳{payment.amount.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-xs text-slate-500">
|
||||
<span>{payment.date}</span>
|
||||
<span className="capitalize">{payment.method}</span>
|
||||
</div>
|
||||
{payment.note && <p className="text-xs text-slate-500 mt-1">{payment.note}</p>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{rental.paymentHistory.length > 1 && (
|
||||
<div className="flex items-center justify-between mt-4 pt-3 border-t border-slate-100">
|
||||
<button
|
||||
onClick={() => setPaymentPage(p => Math.max(0, p - 1))}
|
||||
disabled={paymentPage === 0}
|
||||
className="px-3 py-1.5 text-sm border border-slate-200 rounded-lg disabled:opacity-40 hover:bg-slate-50"
|
||||
>
|
||||
← Prev
|
||||
</button>
|
||||
<span className="text-sm text-slate-500">
|
||||
Showing {paymentPage + 1}-{Math.min(paymentPage + 10, rental.paymentHistory.length)} of {rental.paymentHistory.length}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setPaymentPage(p => Math.min(Math.ceil((rental.paymentHistory?.length ?? 1) / 10) * 10 - 10, p + 10))}
|
||||
disabled={paymentPage + 10 >= (rental.paymentHistory?.length ?? 1)}
|
||||
className="px-3 py-1.5 text-sm border border-slate-200 rounded-lg disabled:opacity-40 hover:bg-slate-50"
|
||||
>
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
|
||||
Reference in New Issue
Block a user