'use client'; import { useState, useEffect } from 'react'; import { useParams } from 'next/navigation'; import Link from 'next/link'; import { ArrowLeft, Bike, User, Calendar, DollarSign, Wallet, Shield, CheckCircle, XCircle, Clock, Edit, Save, Plus, Trash2, Image, Upload, Lock, Unlock, AlertTriangle, MessageSquare, MapPin, Phone, MessageCircle, Play, Check, X, FileText, Download } from 'lucide-react'; import { canRentalAccept, canRentalReject, canRentalCancel, canRentalEdit, canRentalImageApprove, canRentalLock, canRentalUnlock } from '../../../../lib/auth'; type RentalStatus = 'pending' | 'accepted' | 'active' | 'completed' | 'cancelled' | 'locked' | 'disputed'; type RentalType = 'single' | 'shared' | 'rent-to-own'; type PaymentMethod = 'cash' | 'bank' | 'wallet'; type PaymentStatus = 'paid' | 'partial' | 'overdue' | 'unpaid'; type PenaltyLevel = 'none' | 'day1' | 'day2' | 'day3'; interface BikeImage { id: string; url: string; type: 'front' | 'back' | 'left' | 'right' | 'battery'; approved?: boolean; } interface Note { id: string; text: string; createdAt: string; } interface Rental { id: string; bikeId: string; userId: string; userName: string; userPhone: string; bikeModel: string; bikePlate: string; bikeBattery: number; type: RentalType; status: RentalStatus; startDate: string; endDate?: string; contractMonths?: number; subscriptionType: 'daily' | 'weekly' | 'monthly'; deposit: number; depositPaymentMethod: PaymentMethod; depositPaid: boolean; dailyRate: number; weeklyRate: number; monthlyRate: number; totalPaid: number; dueRental: number; pendingRent?: number; pendingRentDays?: number; paymentStatus: PaymentStatus; penaltyLevel: PenaltyLevel; penaltyAmount: number; lockedAt?: string; lockedReason?: string; hubId: string; hubName: string; initialImages?: BikeImage[]; imagesApproved: boolean; bikerNote?: string; rejectNote?: string; createdAt: string; acceptedAt?: string; activatedAt?: string; lockHistory?: LockEvent[]; } interface LockEvent { id: string; action: 'locked' | 'unlocked'; reason?: string; performedBy: string; performedAt: string; } const mockRentals: Rental[] = [ { id: 'RNT-001', bikeId: 'BIKE-001', userId: 'USR-003', userName: 'Jamal Uddin', userPhone: '+8801912345678', bikeModel: 'AIMA Lightning', bikePlate: 'Dhaka Metro Cha-9012', bikeBattery: 87, type: 'single', status: 'active', startDate: '2024-01-15', contractMonths: 12, subscriptionType: 'monthly', deposit: 3000, depositPaymentMethod: 'cash', depositPaid: true, dailyRate: 150, weeklyRate: 900, monthlyRate: 3500, totalPaid: 38500, dueRental: 0, pendingRent: 0, pendingRentDays: 0, paymentStatus: 'paid', penaltyLevel: 'none', penaltyAmount: 0, hubId: 'HUB-001', hubName: 'Gulshan Hub', imagesApproved: true, initialImages: [ { id: 'img1', type: 'front', url: 'https://picsum.photos/seed/bike-front/400/300', approved: true }, { id: 'img2', type: 'back', url: 'https://picsum.photos/seed/bike-back/400/300', approved: true }, { id: 'img3', type: 'left', url: 'https://picsum.photos/seed/bike-left/400/300', approved: true }, { id: 'img4', type: 'right', url: 'https://picsum.photos/seed/bike-right/400/300', approved: true }, { id: 'img5', type: 'battery', url: 'https://picsum.photos/seed/bike-battery/400/300', approved: true }, ], createdAt: '2024-01-15', acceptedAt: '2024-01-15', activatedAt: '2024-01-16', 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' }, { id: 'lh3', action: 'locked', reason: 'Second payment overdue - Day 2 penalty', performedBy: 'System', performedAt: '2024-03-01' }, { id: 'lh4', action: 'unlocked', reason: 'Payment cleared', performedBy: 'Admin Manager', performedAt: '2024-03-02' }, ], }, { id: 'RNT-002', bikeId: 'BIKE-002', userId: 'USR-002', userName: 'Karim Hasan', userPhone: '+8801812345678', bikeModel: 'Yadea DT3', bikePlate: 'Dhaka Metro Ba-5521', bikeBattery: 65, type: 'single', status: 'pending', startDate: '2024-02-10', contractMonths: 3, subscriptionType: 'monthly', deposit: 3000, depositPaymentMethod: 'bank', depositPaid: true, dailyRate: 150, weeklyRate: 900, monthlyRate: 3500, totalPaid: 3000, dueRental: 3500, pendingRent: 3500, pendingRentDays: 5, paymentStatus: 'partial', penaltyLevel: 'none', penaltyAmount: 0, hubId: 'HUB-002', hubName: 'Banani Hub', imagesApproved: false, initialImages: [ { id: 'img1', type: 'front', url: '', approved: false }, { id: 'img2', type: 'back', url: '', approved: false }, { id: 'img3', type: 'left', url: '', approved: false }, { id: 'img4', type: 'right', url: '', approved: false }, { id: 'img5', type: 'battery', url: '', approved: false }, ], createdAt: '2024-02-10', }, { id: 'RNT-003', bikeId: 'BIKE-003', userId: 'USR-001', userName: 'Rahim Ahmed', userPhone: '+8801712345678', bikeModel: 'AIMA EM5', bikePlate: 'Dhaka Metro Ko-1234', bikeBattery: 92, type: 'rent-to-own', status: 'completed', startDate: '2023-06-01', endDate: '2023-12-01', contractMonths: 6, subscriptionType: 'monthly', deposit: 10000, depositPaymentMethod: 'wallet', depositPaid: true, dailyRate: 500, weeklyRate: 3000, monthlyRate: 12000, totalPaid: 82000, dueRental: 0, pendingRent: 0, pendingRentDays: 0, paymentStatus: 'paid', penaltyLevel: 'none', penaltyAmount: 0, hubId: 'HUB-001', hubName: 'Gulshan Hub', imagesApproved: true, createdAt: '2023-06-01', acceptedAt: '2023-06-01', activatedAt: '2023-06-02', }, { id: 'RNT-004', bikeId: 'BIKE-005', userId: 'USR-005', userName: 'Farid Ahmed', userPhone: '+8801612345678', bikeModel: 'Yadea G5', bikePlate: 'Dhaka Metro Ha-5678', bikeBattery: 45, type: 'shared', status: 'locked', startDate: '2024-01-20', contractMonths: 1, subscriptionType: 'weekly', deposit: 2000, depositPaymentMethod: 'cash', depositPaid: true, dailyRate: 100, weeklyRate: 600, monthlyRate: 2200, totalPaid: 2600, dueRental: 600, pendingRent: 600, pendingRentDays: 3, paymentStatus: 'overdue', penaltyLevel: 'day3', penaltyAmount: 1000, lockedAt: '2024-02-05', lockedReason: 'Payment overdue - bike locked', hubId: 'HUB-003', hubName: 'Uttara Hub', imagesApproved: true, createdAt: '2024-01-20', acceptedAt: '2024-01-20', activatedAt: '2024-01-21', lockHistory: [ { id: 'lh1', action: 'locked', reason: 'Weekly payment overdue', performedBy: 'System', performedAt: '2024-02-05' }, ], }, ]; const mockHubs = [ { id: 'HUB-001', name: 'Gulshan Hub' }, { id: 'HUB-002', name: 'Banani Hub' }, { id: 'HUB-003', name: 'Uttara Hub' }, { id: 'HUB-004', name: 'Mirpur Hub' }, ]; const mockDamageHistory = [ { id: 'DMG-001', date: '2024-02-10', description: 'Minor scratch on left mirror', severity: 'minor', status: 'resolved' }, { id: 'DMG-002', date: '2024-03-05', description: 'Front fender dented', severity: 'moderate', status: 'reported' }, { id: 'DMG-003', date: '2024-03-15', description: 'Rear tire puncture', severity: 'minor', status: 'resolved' }, ]; const mockDocuments = [ { id: 'DOC-001', name: 'Rental Agreement', type: 'agreement', uploadedAt: '2024-01-15', downloaded: true }, { id: 'DOC-002', name: 'Bike Delivery Form', type: 'form', uploadedAt: '2024-01-16', downloaded: true }, { id: 'DOC-003', name: 'KYC Documents', type: 'kyc', uploadedAt: '2024-01-14', downloaded: true }, { id: 'DOC-004', name: 'Insurance Policy', type: 'insurance', uploadedAt: '2024-01-15', downloaded: false }, ]; const mockMessages = [ { id: 'MSG-001', date: '2024-03-15', text: 'Payment reminder sent', type: 'system' }, { id: 'MSG-002', date: '2024-03-10', text: 'Bike inspection scheduled', type: 'system' }, { id: 'MSG-003', date: '2024-02-20', text: 'User requested repair', type: 'user' }, ]; export default function RentalDetailPage() { const params = useParams(); const id = params.id as string; const [rental, setRental] = useState(null); const [editMode, setEditMode] = useState(false); const [notes, setNotes] = useState([ { id: 'n1', text: 'Rental started successfully. Bike in good condition.', createdAt: '2024-01-15' }, { id: 'n2', text: 'First monthly payment received.', createdAt: '2024-02-15' }, ]); const [newNote, setNewNote] = useState(''); const [editForm, setEditForm] = useState>({}); const [showLockModal, setShowLockModal] = useState(false); const [showUnlockModal, setShowUnlockModal] = useState(false); const [showCancelModal, setShowCancelModal] = useState(false); const [lockReason, setLockReason] = useState(''); const [rejectNote, setRejectNote] = useState(''); const [showRejectModal, setShowRejectModal] = useState(false); const [showDamageModal, setShowDamageModal] = useState(false); const [showSmsModal, setShowSmsModal] = useState(false); const [damageDesc, setDamageDesc] = useState(''); const [damageSeverity, setDamageSeverity] = useState<'minor' | 'moderate' | 'severe'>('minor'); const [smsText, setSmsText] = useState(''); const [damages, setDamages] = useState(mockDamageHistory); const [documents, setDocuments] = useState(mockDocuments); const [acceptPermission, setAcceptPermission] = useState(false); const [rejectPermission, setRejectPermission] = useState(false); const [cancelPermission, setCancelPermission] = useState(false); const [editPermission, setEditPermission] = useState(false); const [imageApprovePermission, setImageApprovePermission] = useState(false); const [lockPermission, setLockPermission] = useState(false); const [unlockPermission, setUnlockPermission] = useState(false); useEffect(() => { setAcceptPermission(canRentalAccept()); setRejectPermission(canRentalReject()); setCancelPermission(canRentalCancel()); setEditPermission(canRentalEdit()); setImageApprovePermission(canRentalImageApprove()); setLockPermission(canRentalLock()); setUnlockPermission(canRentalUnlock()); }, []); useEffect(() => { const found = mockRentals.find(r => r.id === id); if (found) { setRental(found); setEditForm(found); } }, [id]); if (!rental) { return (

Rental not found

Back to Rentals
); } const getStatusBadge = (status: RentalStatus) => { const styles: Record = { active: 'bg-green-100 text-green-700', pending: 'bg-amber-100 text-amber-700', accepted: 'bg-blue-100 text-blue-700', completed: 'bg-indigo-100 text-indigo-700', cancelled: 'bg-slate-100 text-slate-600', locked: 'bg-red-100 text-red-700', disputed: 'bg-orange-100 text-orange-700', }; const labels: Record = { active: 'Active', pending: 'Pending', accepted: 'Accepted', completed: 'Completed', cancelled: 'Cancelled', locked: 'Locked', disputed: 'Disputed', }; return { style: styles[status], label: labels[status] || status }; }; const getTypeBadge = (type: RentalType) => { const styles: Record = { single: 'bg-blue-100 text-blue-700', shared: 'bg-purple-100 text-purple-700', 'rent-to-own': 'bg-emerald-100 text-emerald-700', }; const labels: Record = { single: 'Single Rent', shared: 'Share EV', 'rent-to-own': 'Rent to Own', }; return { style: styles[type], label: labels[type] || type }; }; const getPaymentStatusBadge = (status: PaymentStatus) => { const styles: Record = { paid: 'bg-green-100 text-green-700', partial: 'bg-amber-100 text-amber-700', overdue: 'bg-red-100 text-red-700', unpaid: 'bg-slate-100 text-slate-600', }; const labels: Record = { paid: 'Paid', partial: 'Partial', overdue: 'Overdue', unpaid: 'Unpaid', }; return { style: styles[status], label: labels[status] || status }; }; const getPenaltyBadge = (level: PenaltyLevel) => { const styles: Record = { none: 'bg-slate-100 text-slate-500', day1: 'bg-amber-100 text-amber-700', day2: 'bg-orange-100 text-orange-700', day3: 'bg-red-100 text-red-700', }; const labels: Record = { none: 'None', day1: '1st Day', day2: '2nd Day', day3: 'Bike Lock', }; return { style: styles[level], label: labels[level] || level }; }; const handleSaveEdit = () => { setRental(prev => prev ? { ...prev, ...editForm } : null); setEditMode(false); }; const handleLockRental = () => { if (!lockReason.trim()) return; const newEvent: LockEvent = { id: `lh${Date.now()}`, action: 'locked', reason: lockReason, performedBy: 'Admin', performedAt: new Date().toISOString().split('T')[0] }; setRental(prev => prev ? { ...prev, status: 'locked', lockedAt: new Date().toISOString().split('T')[0], lockedReason: lockReason, lockHistory: [...(prev.lockHistory || []), newEvent] } : null); setShowLockModal(false); setLockReason(''); }; const handleUnlockRental = () => { const newEvent: LockEvent = { id: `lh${Date.now()}`, action: 'unlocked', performedBy: 'Admin', performedAt: new Date().toISOString().split('T')[0] }; setRental(prev => prev ? { ...prev, status: 'active', lockedAt: undefined, lockedReason: undefined, lockHistory: [...(prev.lockHistory || []), newEvent] } : null); setShowUnlockModal(false); }; const handleCancelRental = () => { setRental(prev => prev ? { ...prev, status: 'cancelled', endDate: new Date().toISOString().split('T')[0] } : null); setShowCancelModal(false); }; const handleAcceptRental = () => { setRental(prev => prev ? { ...prev, status: 'accepted', acceptedAt: new Date().toISOString().split('T')[0] } : null); }; const handleRejectRental = () => { if (!rejectNote.trim()) return; setRental(prev => prev ? { ...prev, status: 'cancelled', rejectNote } : null); setShowRejectModal(false); setRejectNote(''); }; const handleApproveImages = () => { setRental(prev => prev ? { ...prev, imagesApproved: true } : null); }; const handleActivateRental = () => { setRental(prev => prev ? { ...prev, status: 'active', activatedAt: new Date().toISOString().split('T')[0] } : null); }; const handleAddNote = () => { if (!newNote.trim()) return; setNotes(prev => [...prev, { id: `n${Date.now()}`, text: newNote, createdAt: new Date().toISOString().split('T')[0] }]); setNewNote(''); }; const handleReportDamage = () => { if (!damageDesc.trim()) return; setDamages(prev => [...prev, { id: `DMG-${Date.now()}`, date: new Date().toISOString().split('T')[0], description: damageDesc, severity: damageSeverity, status: 'reported' }]); setDamageDesc(''); setDamageSeverity('minor'); setShowDamageModal(false); }; const handleSendSms = () => { if (!smsText.trim()) return; alert(`SMS sent to ${rental.userPhone}: ${smsText}`); setSmsText(''); setShowSmsModal(false); }; const statusBadge = getStatusBadge(rental.status); const typeBadge = getTypeBadge(rental.type); const paymentBadge = getPaymentStatusBadge(rental.paymentStatus); const penaltyBadge = getPenaltyBadge(rental.penaltyLevel); const getBatteryColor = (level: number) => { if (level > 70) return 'text-green-600'; if (level > 40) return 'text-amber-600'; return 'text-red-600'; }; const timelineSteps = [ { key: 'created', label: 'Created', date: rental.createdAt, completed: true }, { key: 'accepted', label: 'Accepted', date: rental.acceptedAt, completed: !!rental.acceptedAt }, { key: 'imagesApproved', label: 'Images Approved', date: rental.imagesApproved ? rental.acceptedAt : undefined, completed: rental.imagesApproved }, { key: 'activated', label: 'Activated', date: rental.activatedAt, completed: !!rental.activatedAt }, ]; return (
Back to Rentals

{rental.id}

{statusBadge.label} {typeBadge.label}

Created: {rental.createdAt}

{editMode ? ( <> ) : ( <> {/* {editPermission && ( )} */} {cancelPermission && rental.status !== 'cancelled' && rental.status !== 'completed' && ( )} {lockPermission && rental.status === 'active' && ( )} {unlockPermission && rental.status === 'locked' && ( )} {rental.status === 'accepted' && ( )} )}

Total Paid

৳{rental.totalPaid.toLocaleString()}

Due Rental

৳{rental.dueRental.toLocaleString()}

Payment Status

{paymentBadge.label}
{rental.penaltyLevel !== 'none' && (

Penalty

৳{rental.penaltyAmount.toLocaleString()}

{penaltyBadge.label}
)}

Bike Info

{editMode ? ( setEditForm({ ...editForm, bikeModel: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Bike Model" /> ) : (
Model{rental.bikeModel}
)}
Plate{rental.bikePlate}
Battery {rental.bikeBattery}%

User Info

Name{rental.userName}
Call SMS

Hub Info

Hub{rental.hubName}

Duration

Start Date{rental.startDate}
{rental.endDate &&
End Date{rental.endDate}
} {rental.contractMonths &&
Contract{rental.contractMonths} Months
}

Subscription

Type{rental.subscriptionType}
Rate ৳{rental.subscriptionType === 'daily' ? rental.dailyRate : rental.subscriptionType === 'weekly' ? rental.weeklyRate : rental.monthlyRate}/ {rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'week' : 'month'}

Deposit

Amount৳{rental.deposit.toLocaleString()}
Method{rental.depositPaymentMethod}
Status {rental.depositPaid ? 'Paid' : 'Unpaid'}
{/* {(rental.status === 'pending' || rental.status === 'accepted') && rental.initialImages && ( */} {rental.initialImages && (

Initial Condition Images

{imageApprovePermission && !rental.imagesApproved && ( )} {rental.imagesApproved && ( Approved )}
{(['front', 'back', 'left', 'right', 'battery'] as const).map(type => { const img = rental.initialImages?.find(i => i.type === type); return (
{img?.url ? ( {type} ) : (

{type}

)}
); })}
)}

Notes ({notes.length})

{notes.length > 0 ? (
{notes.map(note => (

{note.text}

{note.createdAt}

))}
) : (

No notes yet.

)}
setNewNote(e.target.value)} placeholder="Add a note..." className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" />
{rental.status === 'locked' && (

Locked Info

Locked At{rental.lockedAt}
Reason{rental.lockedReason}
)}

Lock History

{rental.lockHistory && rental.lockHistory.length > 0 ? (
{rental.lockHistory.map(event => (
{event.action === 'locked' ? ( ) : ( )}
{event.action === 'locked' ? 'Locked' : 'Unlocked'} {event.performedAt}
{event.reason && (

{event.reason}

)}

By: {event.performedBy}

))}
) : (

No lock history available.

)}

Damage History

{damages.map(damage => (

{damage.description}

{damage.date}

{damage.severity} {damage.status}
))}

Rental Documents

{mockDocuments.map(doc => (

{doc.name}

{doc.uploadedAt}

))}
{rental.status === 'pending' && (

Biker Response

{acceptPermission && ( )} {rejectPermission && ( )}
)} {rental.status === 'accepted' && (
Accepted on {rental.acceptedAt}
)} {rental.status === 'cancelled' && rental.rejectNote && (
Rejected

Reason: {rental.rejectNote}

)}

Activity Timeline

{timelineSteps.map((step, idx) => (
{step.completed ? : {idx + 1}}

{step.label}

{step.date &&

{step.date}

}
))}
{showLockModal && (

Lock Rental