'use client'; import { useState, useEffect } from 'react'; import { FileText, Search, Filter, Bike, User, Calendar, DollarSign, Clock, MoreVertical, Eye, Plus, Phone, MessageCircle, X, CreditCard, Wallet, Building, Download, Printer, ChevronLeft, ChevronRight, CheckCircle, AlertTriangle, MapPin } from 'lucide-react'; import Link from 'next/link'; import { canRentalCreate, canRentalCancel, canRentalEdit, 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 Rental { id: string; bikeId: string; userId: string; userName: string; userPhone: string; bikeModel: string; bikePlate: string; bikeBattery: number; batteryId?: string; batteryName?: string; batteryRent?: number; batteryRentPending?: 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; location?: { lat: number; lng: number; address?: string; }; initialImages?: BikeImage[]; imagesApproved: boolean; bikerNote?: string; rejectNote?: string; createdAt: string; acceptedAt?: string; activatedAt?: string; batteryHistory?: { id: string; batteryId: string; batteryName: string; assignedAt: string; returnedAt?: string; monthlyRent: number; status: 'active' | 'returned'; }[]; } interface Bike { id: string; model: string; plate: string; battery: number; status: 'available' | 'rented' | 'maintenance'; } interface UserData { id: string; name: string; phone: string; kycStatus: 'approved' | 'pending' | 'rejected'; hasActiveRental: boolean; } const mockBikes: Bike[] = [ { id: 'BIKE-001', model: 'AIMA Lightning', plate: 'Dhaka Metro Cha-9012', battery: 87, status: 'available' }, { id: 'BIKE-002', model: 'Yadea DT3', plate: 'Dhaka Metro Ba-5521', battery: 65, status: 'available' }, { id: 'BIKE-003', model: 'AIMA EM5', plate: 'Dhaka Metro Ko-1234', battery: 92, status: 'available' }, { id: 'BIKE-005', model: 'Yadea G5', plate: 'Dhaka Metro Ha-5678', battery: 45, status: 'available' }, ]; const mockUsers: UserData[] = [ { id: 'USR-001', name: 'Rahim Ahmed', phone: '+8801712345678', kycStatus: 'approved', hasActiveRental: false }, { id: 'USR-002', name: 'Karim Hasan', phone: '+8801812345678', kycStatus: 'approved', hasActiveRental: false }, { id: 'USR-003', name: 'Jamal Uddin', phone: '+8801912345678', kycStatus: 'approved', hasActiveRental: true }, { id: 'USR-004', name: 'Rafiq Islam', phone: '+8801512345678', kycStatus: 'pending', hasActiveRental: false }, { id: 'USR-005', name: 'Farid Ahmed', phone: '+8801612345678', kycStatus: 'approved', hasActiveRental: false }, ]; 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' }, ]; interface Battery { id: string; brand: string; model: string; soc: number; monthlyRent: number; status: 'available' | 'in-use' | 'maintenance'; } const mockBatteries: Battery[] = [ { id: 'BAT-DH-001', brand: 'Galaxy', model: '72V 45Ah', soc: 85, monthlyRent: 1500, status: 'available' }, { id: 'BAT-DH-002', brand: 'Titan', model: '72V 50Ah', soc: 92, monthlyRent: 1800, status: 'available' }, { id: 'BAT-DH-003', brand: 'PowerMax', model: '60V 40Ah', soc: 78, monthlyRent: 1200, status: 'available' }, { id: 'BAT-DH-004', brand: 'UltraCell', model: '72V 55Ah', soc: 88, monthlyRent: 2000, status: 'available' }, { id: 'BAT-DH-005', brand: 'EcoVolt', model: '48V 30Ah', soc: 65, monthlyRent: 800, status: 'available' }, ]; const rentalSettings = { single: { Premium: { deposit: 5000, contractMonths: [1, 3, 6, 12], dailyRate: 200, weeklyRate: 1200, monthlyRate: 5000 }, Standard: { deposit: 3000, contractMonths: [1, 3, 6, 12], dailyRate: 150, weeklyRate: 900, monthlyRate: 3500 }, Economy: { deposit: 2000, contractMonths: [1, 3, 6, 12], dailyRate: 100, weeklyRate: 600, monthlyRate: 2500 }, }, shared: { Premium: { deposit: 4000, contractMonths: [1, 3, 6], dailyRate: 150, weeklyRate: 900, monthlyRate: 3500 }, Standard: { deposit: 2500, contractMonths: [1, 3, 6], dailyRate: 100, weeklyRate: 600, monthlyRate: 2200 }, Economy: { deposit: 2000, contractMonths: [1, 3, 6], dailyRate: 80, weeklyRate: 500, monthlyRate: 1800 }, }, 'rent-to-own': { Premium: { deposit: 15000, contractMonths: [12, 18, 24, 36], dailyRate: 350, weeklyRate: 2450, monthlyRate: 10500 }, Standard: { deposit: 12000, contractMonths: [12, 18, 24, 36], dailyRate: 250, weeklyRate: 1750, monthlyRate: 7000 }, Economy: { deposit: 10000, contractMonths: [12, 18, 24, 36], dailyRate: 200, weeklyRate: 1400, monthlyRate: 6000 }, }, }; 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, batteryRent: 1500, totalPaid: 38500, dueRental: 0, pendingRent: 0, pendingRentDays: 0, paymentStatus: 'paid', penaltyLevel: 'none', penaltyAmount: 0, hubId: 'HUB-001', hubName: 'Gulshan Hub', location: { lat: 23.7925, lng: 90.4074, address: 'Gulshan 1, Dhaka' }, imagesApproved: true, createdAt: '2024-01-15', acceptedAt: '2024-01-15', activatedAt: '2024-01-16', }, { 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', location: { lat: 23.8041, lng: 90.4152, address: 'Banani, Dhaka' }, imagesApproved: 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, batteryRent: 1500, 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', }, ]; export default function RentalsPage() { const [rentals, setRentals] = useState(mockRentals); const [showCreateModal, setShowCreateModal] = useState(false); const [statusFilter, setStatusFilter] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); const [hubFilter, setHubFilter] = useState('all'); const [createPermission, setCreatePermission] = useState(false); const [cancelPermission, setCancelPermission] = useState(false); const [editPermission, setEditPermission] = useState(false); const [lockPermission, setLockPermission] = useState(false); const [unlockPermission, setUnlockPermission] = useState(false); const [lockUnlockRental, setLockUnlockRental] = useState<{ id: string; action: 'lock' | 'unlock' } | null>(null); const [planConditions, setPlanConditions] = useState<{ single: { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[]; shared: { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[]; 'rent-to-own': { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[]; }>({ single: [ { name: 'Single Rent - Premium', deposit: 25000, dailyRate: 400, weeklyRate: 2800, monthlyRate: 12000, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Single Rent - Standard', deposit: 20000, dailyRate: 300, weeklyRate: 2100, monthlyRate: 9000, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Single Rent - Economy', deposit: 15000, dailyRate: 250, weeklyRate: 1750, monthlyRate: 7500, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, ], shared: [ { name: 'Share an EV - Premium', deposit: 20000, dailyRate: 300, weeklyRate: 2100, monthlyRate: 8400, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Share an EV - Standard', deposit: 15000, dailyRate: 200, weeklyRate: 1400, monthlyRate: 5600, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Share an EV - Economy', deposit: 12000, dailyRate: 150, weeklyRate: 1050, monthlyRate: 4200, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, ], 'rent-to-own': [ { name: 'Rent to Own - Premium', deposit: 25000, dailyRate: 350, weeklyRate: 2450, monthlyRate: 10500, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Rent to Own - Standard', deposit: 18000, dailyRate: 250, weeklyRate: 1750, monthlyRate: 7000, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, { name: 'Rent to Own - Economy', deposit: 15000, dailyRate: 200, weeklyRate: 1400, monthlyRate: 6000, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] }, ], }); const [createStep, setCreateStep] = useState(1); const [newRental, setNewRental] = useState<{ userId: string; type: RentalType; planConditionName: string; evModel: string; subscriptionType: 'daily' | 'weekly' | 'monthly'; contractMonths: number; bikeId: string; batteryId: string; batteryRent: number; startDate: string; hubId: string; depositAmount: number; depositPaymentMethod: PaymentMethod; }>({ userId: '', type: 'single', planConditionName: '', evModel: '', subscriptionType: 'daily', contractMonths: 0, bikeId: '', batteryId: '', batteryRent: 0, startDate: new Date().toISOString().split('T')[0], hubId: '', depositAmount: 0, depositPaymentMethod: 'cash', }); const availableBatteries = mockBatteries.filter(b => b.status === 'available'); const [showJournalPreview, setShowJournalPreview] = useState(false); useEffect(() => { setCreatePermission(canRentalCreate()); setCancelPermission(canRentalCancel()); setEditPermission(canRentalEdit()); setLockPermission(canRentalLock()); setUnlockPermission(canRentalUnlock()); }, []); 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 getPaymentStatusBadge = (status: PaymentStatus, pendingRent?: number, pendingRentDays?: number) => { 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, pendingRent: pendingRent || 0, pendingRentDays: pendingRentDays || 0 }; }; 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 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 filteredRentals = rentals.filter(r => { if (statusFilter !== 'all' && r.status !== statusFilter) return false; if (hubFilter !== 'all' && r.hubId !== hubFilter) return false; if (searchQuery) { const query = searchQuery.toLowerCase(); return ( r.id.toLowerCase().includes(query) || r.userName.toLowerCase().includes(query) || r.userPhone.includes(query) || r.bikeModel.toLowerCase().includes(query) || r.bikePlate.toLowerCase().includes(query) ); } return true; }); const eligibleUsers = mockUsers.filter(u => u.kycStatus === 'approved' && !u.hasActiveRental); const availableBikes = mockBikes.filter(b => b.status === 'available'); const selectedPlan = planConditions[newRental.type]?.find(p => p.name === newRental.planConditionName) || planConditions[newRental.type]?.[0]; const stats = { active: rentals.filter(r => r.status === 'active').length, pending: rentals.filter(r => r.status === 'pending').length, overdue: rentals.filter(r => r.paymentStatus === 'overdue' || r.paymentStatus === 'partial').length, locked: rentals.filter(r => r.status === 'locked').length, }; const handleCreateRental = () => { if (!newRental.userId || !newRental.bikeId || !newRental.hubId) return; setShowJournalPreview(true); }; const confirmCreateRental = () => { const bike = mockBikes.find(b => b.id === newRental.bikeId); const user = mockUsers.find(u => u.id === newRental.userId); const hub = mockHubs.find(h => h.id === newRental.hubId); const battery = mockBatteries.find(b => b.id === newRental.batteryId); const settings = planConditions[newRental.type]?.find(p => p.name === newRental.planConditionName) || planConditions[newRental.type]?.[0]; const rental: Rental = { id: `RNT-${String(rentals.length + 1).padStart(3, '0')}`, bikeId: newRental.bikeId, userId: newRental.userId, userName: user?.name || '', userPhone: user?.phone || '', bikeModel: bike?.model || '', bikePlate: bike?.plate || '', bikeBattery: bike?.battery || 0, batteryId: newRental.batteryId || undefined, batteryName: battery ? `${battery.brand} ${battery.model}` : undefined, batteryRent: newRental.batteryRent || undefined, type: newRental.type, status: 'pending', startDate: newRental.startDate, endDate: newRental.contractMonths > 0 ? new Date(new Date(newRental.startDate).setMonth(new Date(newRental.startDate).getMonth() + newRental.contractMonths)).toISOString().split('T')[0] : undefined, contractMonths: newRental.contractMonths > 0 ? newRental.contractMonths : undefined, subscriptionType: newRental.subscriptionType, deposit: newRental.depositAmount > 0 ? newRental.depositAmount : settings.deposit, depositPaymentMethod: newRental.depositPaymentMethod, depositPaid: newRental.depositAmount > 0 || settings.deposit > 0, dailyRate: settings.dailyRate, weeklyRate: settings.weeklyRate, monthlyRate: settings.monthlyRate, totalPaid: settings.deposit, dueRental: 0, paymentStatus: settings.deposit > 0 ? 'paid' : 'unpaid', penaltyLevel: 'none', penaltyAmount: 0, hubId: newRental.hubId, hubName: hub?.name || '', imagesApproved: false, createdAt: new Date().toISOString().split('T')[0], batteryHistory: newRental.batteryId ? [{ id: `BAT-RENT-${Date.now()}`, batteryId: newRental.batteryId, batteryName: battery ? `${battery.brand} ${battery.model}` : '', assignedAt: new Date().toISOString().split('T')[0], monthlyRent: newRental.batteryRent, status: 'active' as const, }] : undefined, }; setRentals([...rentals, rental]); setShowJournalPreview(false); setShowCreateModal(false); setCreateStep(1); setNewRental({ userId: '', type: 'single', planConditionName: planConditions.single[1]?.name || '', subscriptionType: 'daily', contractMonths: 0, bikeId: '', batteryId: '', batteryRent: 0, evModel: '', startDate: new Date().toISOString().split('T')[0], hubId: '', depositAmount: 0, depositPaymentMethod: 'cash', }); }; const selectedBike = mockBikes.find(b => b.id === newRental.bikeId); const selectedUser = mockUsers.find(u => u.id === newRental.userId); return (

Rentals

Manage rental transactions

{createPermission && ( )} Map View
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-slate-200 rounded-lg text-sm text-slate-600" />
{/* */} {filteredRentals.map(rental => { const statusBadge = getStatusBadge(rental.status); const paymentBadge = getPaymentStatusBadge(rental.paymentStatus, rental.pendingRent, rental.pendingRentDays); const typeBadge = getTypeBadge(rental.type); return ( {/* */} ); })}
ID Bike User Type Hub DepositSubscriptionRent Payment Status Actions
{rental.id}
{rental.bikeModel}

{rental.bikePlate}

{rental.userName}

{rental.userPhone}

{typeBadge.label} {rental.hubName}
৳{rental.deposit.toLocaleString()} {rental.depositPaid ? 'Paid' : 'Unpaid'}
{rental.subscriptionType} {(() => { const bikeRent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate']; const batteryRent = rental.batteryRent || 0; const batteryRentAdjusted = rental.subscriptionType === 'daily' ? Math.round(batteryRent / 30) : rental.subscriptionType === 'weekly' ? Math.round(batteryRent / 4) : batteryRent; const totalRent = bikeRent + batteryRentAdjusted; const due = (rental.pendingRent || 0) + (rental.batteryRentPending || 0); const penalty = rental.penaltyAmount || 0; const totalDue = due + penalty; const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo'; if (totalDue > 0) { return (
৳{totalDue.toLocaleString()} {batteryRent > 0 ? (

{rental.subscriptionType} ৳{bikeRent}{periodLabel} +Battery: ৳{batteryRentAdjusted}{periodLabel}

) : (

{rental.subscriptionType} ৳{totalRent}{periodLabel} +৳{penalty} penalty

)}
); } return (
৳{totalRent.toLocaleString()} {batteryRent > 0 ? (

{rental.subscriptionType} ৳{bikeRent}{periodLabel} +Battery: ৳{batteryRentAdjusted}{periodLabel}

) : (

{rental.subscriptionType}

)}
); })()}
{statusBadge.label}
{lockPermission && rental.status === 'active' && ( )} {unlockPermission && rental.status === 'locked' && ( )}
{filteredRentals.map(rental => { const statusBadge = getStatusBadge(rental.status); const typeBadge = getTypeBadge(rental.type); const rent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate']; const due = rental.pendingRent || 0; const penalty = rental.penaltyAmount || 0; const totalDue = due + penalty; return (
{rental.id} {statusBadge.label}

{rental.bikeModel}

{rental.bikePlate}

{rental.userName}

{rental.userPhone}

{typeBadge.label} {rental.subscriptionType} {rental.hubName}
Deposit: ৳{rental.deposit.toLocaleString()} {rental.depositPaid ? 'Paid' : 'Unpaid'}
{(() => { const batteryRent = rental.batteryRent || 0; const batteryRentAdjusted = rental.subscriptionType === 'daily' ? Math.round(batteryRent / 30) : rental.subscriptionType === 'weekly' ? Math.round(batteryRent / 4) : batteryRent; const totalRent = rent + batteryRentAdjusted; const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo'; return totalDue > 0 ? (
৳{totalDue.toLocaleString()} {batteryRent > 0 ? (

{rental.subscriptionType} ৳{rent}{periodLabel} +Battery: ৳{batteryRentAdjusted}{periodLabel} +৳{penalty} penalty

) : (

{rental.subscriptionType} ৳{totalRent}{periodLabel} +৳{penalty} penalty

)}
) : (
৳{totalRent.toLocaleString()} {batteryRent > 0 ? (

{rental.subscriptionType} ৳{rent}{periodLabel} +Battery: ৳{batteryRentAdjusted}{periodLabel}

) : (

{rental.subscriptionType}

)}
); })()}
View {lockPermission && rental.status === 'active' && ( )} {unlockPermission && rental.status === 'locked' && ( )} Call SMS
); })}
{showCreateModal && (

Create New Rental - Step {createStep} of 4

{createStep === 1 && (

Step 1: Start Date & Hub

setNewRental({ ...newRental, startDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />

Only KYC-approved users without active rentals are shown.

)} {createStep === 2 && (
{newRental.contractMonths > 0 && (

End Date: {new Date(new Date(newRental.startDate).setMonth(new Date(newRental.startDate).getMonth() + newRental.contractMonths)).toISOString().split('T')[0]}

)}
{(['daily', 'weekly', 'monthly'] as const).map(sub => ( ))}
{newRental.planConditionName}

Deposit: ৳{selectedPlan?.deposit.toLocaleString()}

Rate: {(() => { const bikeRate = newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate; const batteryRent = newRental.batteryRent || 0; const batteryRate = batteryRent > 0 ? (newRental.subscriptionType === 'daily' ? Math.round(batteryRent / 30) : newRental.subscriptionType === 'weekly' ? Math.round(batteryRent / 4) : batteryRent) : 0; const totalRate = bikeRate + batteryRate; return ( ৳{totalRate.toLocaleString()}/{newRental.subscriptionType === 'daily' ? 'day' : newRental.subscriptionType === 'weekly' ? 'week' : 'month'} {batteryRent > 0 && (Bike: ৳{bikeRate} + Battery: ৳{batteryRate})} ); })()}
)} {createStep === 3 && (

Step 3: Select Bike & Battery

{selectedBike && (
Battery: 70 ? 'bg-green-100 text-green-700' : selectedBike.battery > 40 ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}> {selectedBike.battery}%
)}

Biker can request for battery. It will be added to their rental.

{newRental.batteryId && (

Battery Monthly Rent: ৳{newRental.batteryRent}/month

Calculated Rate by Subscription:

• Daily: ৳{Math.round(newRental.batteryRent / 30)}/day (৳{newRental.batteryRent}/30)

• Weekly: ৳{Math.round(newRental.batteryRent / 4)}/week (৳{newRental.batteryRent}/4)

• Monthly (30 days): ৳{newRental.batteryRent}/month

• Monthly (31 days): ৳{newRental.batteryRent}/month

)}
)} {createStep === 4 && (

Step 4: Deposit Payment

Deposit Amount: ৳{selectedPlan?.deposit.toLocaleString()}

User: {selectedUser?.name}

Bike: {selectedBike?.model} ({selectedBike?.plate})

{newRental.batteryId && (

Battery: {availableBatteries.find(b => b.id === newRental.batteryId)?.brand} {availableBatteries.find(b => b.id === newRental.batteryId)?.model} (Monthly Rent: ৳{newRental.batteryRent})

)}

Hub: {mockHubs.find(h => h.id === newRental.hubId)?.name}

{newRental.batteryId && (

Total Rental Payment (Bike + Battery):

{(() => { const bikeRate = newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate; const batteryMonthlyRent = newRental.batteryRent || 0; const dailyRate = bikeRate + Math.round(batteryMonthlyRent / 30); const weeklyRate = bikeRate + Math.round(batteryMonthlyRent / 4); const monthly30Rate = bikeRate + batteryMonthlyRent; const monthly31Rate = bikeRate + batteryMonthlyRent; return (

Daily: ৳{dailyRate}/day (Bike: ৳{bikeRate} + Battery: ৳{Math.round(batteryMonthlyRent / 30)})

Weekly: ৳{weeklyRate}/week (Bike: ৳{bikeRate} + Battery: ৳{Math.round(batteryMonthlyRent / 4)})

Monthly (30 days): ৳{monthly30Rate}/month (Bike: ৳{bikeRate} + Battery: ৳{batteryMonthlyRent})

Monthly (31 days): ৳{monthly31Rate}/month (Bike: ৳{bikeRate} + Battery: ৳{batteryMonthlyRent})

); })()}
)}
{(['cash', 'bank', 'wallet'] as const).map(method => ( ))}
{(newRental.depositAmount > 0 || selectedPlan?.deposit > 0) && (

Proforma Invoice

Invoice No:PI-{Date.now().toString().slice(-8)}
Date:{new Date().toLocaleDateString()}
Customer:{selectedUser?.name}
Bike:{selectedBike?.model} ({selectedBike?.plate})
Description Debit (৳) Credit (৳)
{newRental.depositPaymentMethod === 'cash' && '1000 - Cash'} {newRental.depositPaymentMethod === 'bank' && '1100 - Bank'} {newRental.depositPaymentMethod === 'wallet' && '1200 - Biker Wallet'} {newRental.depositAmount > 0 ? newRental.depositAmount.toLocaleString() : selectedPlan?.deposit.toLocaleString()} -
2100 - Deposit Received - {newRental.depositAmount > 0 ? newRental.depositAmount.toLocaleString() : selectedPlan?.deposit.toLocaleString()}
Total {newRental.depositAmount > 0 ? newRental.depositAmount.toLocaleString() : selectedPlan?.deposit.toLocaleString()} {newRental.depositAmount > 0 ? newRental.depositAmount.toLocaleString() : selectedPlan?.deposit.toLocaleString()}
)}
)}
{createStep > 1 && ( )}
{createStep < 4 ? ( ) : ( <> {(newRental.depositAmount > 0 || selectedPlan?.deposit > 0) && ( )} )}
)} {lockUnlockRental && (
{lockUnlockRental.action === 'lock' ? : }

{lockUnlockRental.action === 'lock' ? 'Lock EV?' : 'Unlock EV?'}

{lockUnlockRental.action === 'lock' ? 'This will lock the EV and prevent the rider from starting it. Are you sure?' : 'This will unlock the EV and allow the rider to start it. Are you sure?'}

)}
); } function Lock({ className }: { className?: string }) { return ( ); } function Unlock({ className }: { className?: string }) { return ( ); }