diff --git a/src/app/admin/rentals/[id]/page.tsx b/src/app/admin/rentals/[id]/page.tsx index 4fe5a4a..f930632 100644 --- a/src/app/admin/rentals/[id]/page.tsx +++ b/src/app/admin/rentals/[id]/page.tsx @@ -6,7 +6,7 @@ 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, Battery + Phone, MessageCircle, Play, Check, X, FileText, Download, Battery, Printer } from 'lucide-react'; import { canRentalAccept, canRentalReject, canRentalCancel, canRentalEdit, @@ -86,9 +86,35 @@ interface BatteryRentalHistory { assignedAt: string; returnedAt?: string; monthlyRent: number; + deposit: number; + depositMethod: 'cash' | 'bank' | 'bkash' | 'nagad'; + invoiceId: string; + invoiceGeneratedAt: string; status: 'active' | 'returned'; } +interface Invoice { + id: string; + type: 'ev_rental' | 'battery_rental'; + relatedId: string; // rental id or battery history id + amount: number; + deposit?: number; + generatedAt: string; + status: 'paid' | 'unpaid'; + description: string; +} + +interface JournalEntry { + id: string; + date: string; + description: string; + debit: string; + credit: string; + amount: number; + referenceId: string; + type: 'battery_deposit' | 'battery_rent' | 'ev_deposit' | 'ev_rent'; +} + interface LockEvent { id: string; action: 'locked' | 'unlocked'; @@ -162,8 +188,8 @@ const mockRentals: Rental[] = [ { id: 'lh4', action: 'unlocked', reason: 'Payment cleared', performedBy: 'Admin Manager', performedAt: '2024-03-02' }, ], batteryHistory: [ - { id: 'BAT-RENT-001', batteryId: 'BAT-DH-001', batteryName: 'Galaxy 72V 45Ah', assignedAt: '2024-01-16', monthlyRent: 1500, status: 'active' }, - { id: 'BAT-RENT-002', batteryId: 'BAT-DH-002', batteryName: 'Titan 72V 50Ah', assignedAt: '2024-02-20', returnedAt: '2024-03-15', monthlyRent: 1800, status: 'returned' }, + { id: 'BAT-RENT-001', batteryId: 'BAT-DH-001', batteryName: 'Galaxy 72V 45Ah', assignedAt: '2024-01-16', monthlyRent: 1500, deposit: 3000, depositMethod: 'bkash' as const, invoiceId: 'INV-BAT-001', invoiceGeneratedAt: '2024-01-16', status: 'active' as const }, + { id: 'BAT-RENT-002', batteryId: 'BAT-DH-002', batteryName: 'Titan 72V 50Ah', assignedAt: '2024-02-20', returnedAt: '2024-03-15', monthlyRent: 1800, deposit: 3500, depositMethod: 'cash' as const, invoiceId: 'INV-BAT-002', invoiceGeneratedAt: '2024-02-20', status: 'returned' as const }, ], }, { @@ -354,6 +380,21 @@ export default function RentalDetailPage() { const [uploadDocName, setUploadDocName] = useState(''); const [showAddBatteryModal, setShowAddBatteryModal] = useState(false); const [selectedBatteryId, setSelectedBatteryId] = useState(''); + const [batteryDeposit, setBatteryDeposit] = useState(0); + const [batteryDepositMethod, setBatteryDepositMethod] = useState<'cash' | 'bank' | 'bkash' | 'nagad'>('cash'); + const [showBatteryInvoicePreview, setShowBatteryInvoicePreview] = useState(null); + const [isDownloadingPDF, setIsDownloadingPDF] = useState(false); + const [downloadSuccess, setDownloadSuccess] = useState(false); + const [invoices, setInvoices] = useState([ + { id: 'INV-EV-001', type: 'ev_rental', relatedId: 'RNT-001', amount: 3000, deposit: 3000, generatedAt: '2024-01-15', status: 'paid', description: 'EV Rental Deposit — Jamal Uddin (AIMA Lightning)' }, + { id: 'INV-BAT-001', type: 'battery_rental', relatedId: 'BAT-RENT-001', amount: 1500, deposit: 3000, generatedAt: '2024-01-16', status: 'paid', description: 'Battery Rental — Galaxy 72V 45Ah (Deposit + 1st Month)' }, + { id: 'INV-BAT-002', type: 'battery_rental', relatedId: 'BAT-RENT-002', amount: 1800, deposit: 3500, generatedAt: '2024-02-20', status: 'paid', description: 'Battery Rental — Titan 72V 50Ah (Deposit + 1st Month)' }, + ]); + const [journalEntries, setJournalEntries] = useState([ + { id: 'JRN-001', date: '2024-01-15', description: 'EV Rental Deposit received from Jamal Uddin', debit: 'Cash / Bank', credit: 'Rental Deposit Liability', amount: 3000, referenceId: 'INV-EV-001', type: 'ev_deposit' }, + { id: 'JRN-002', date: '2024-01-16', description: 'Battery Deposit received — Galaxy 72V 45Ah', debit: 'Cash / Bank', credit: 'Battery Deposit Liability', amount: 3000, referenceId: 'INV-BAT-001', type: 'battery_deposit' }, + { id: 'JRN-003', date: '2024-02-20', description: 'Battery Deposit received — Titan 72V 50Ah', debit: 'Cash / Bank', credit: 'Battery Deposit Liability', amount: 3500, referenceId: 'INV-BAT-002', type: 'battery_deposit' }, + ]); const [acceptPermission, setAcceptPermission] = useState(false); const [rejectPermission, setRejectPermission] = useState(false); @@ -511,15 +552,60 @@ export default function RentalDetailPage() { const battery = mockBatteries.find(b => b.id === selectedBatteryId); if (!battery) return; + const today = new Date().toISOString().split('T')[0]; + const newInvId = `INV-BAT-${Date.now().toString().slice(-6)}`; + const newBatHistId = `BAT-RENT-${Date.now()}`; + + // 1. Create battery rental history entry const newBatteryHistory: BatteryRentalHistory = { - id: `BAT-RENT-${Date.now()}`, + id: newBatHistId, batteryId: battery.id, batteryName: `${battery.brand} ${battery.model}`, - assignedAt: new Date().toISOString().split('T')[0], + assignedAt: today, monthlyRent: battery.monthlyRent, + deposit: batteryDeposit, + depositMethod: batteryDepositMethod, + invoiceId: newInvId, + invoiceGeneratedAt: today, status: 'active', }; + // 2. Generate invoice + const newInvoice: Invoice = { + id: newInvId, + type: 'battery_rental', + relatedId: newBatHistId, + amount: battery.monthlyRent, + deposit: batteryDeposit, + generatedAt: today, + status: 'paid', + description: `Battery Rental — ${battery.brand} ${battery.model} (Deposit ৳${batteryDeposit} + 1st Month ৳${battery.monthlyRent})`, + }; + + // 3. Auto journal entries + const journalDeposit: JournalEntry = { + id: `JRN-${Date.now()}-DEP`, + date: today, + description: `Battery Deposit received — ${battery.brand} ${battery.model}`, + debit: 'Cash / Bank', + credit: 'Battery Deposit Liability', + amount: batteryDeposit, + referenceId: newInvId, + type: 'battery_deposit', + }; + const journalRent: JournalEntry = { + id: `JRN-${Date.now()}-RENT`, + date: today, + description: `1st Month Battery Rent — ${battery.brand} ${battery.model}`, + debit: 'Cash / Bank', + credit: 'Battery Rent Revenue', + amount: battery.monthlyRent, + referenceId: newInvId, + type: 'battery_rent', + }; + + setInvoices(prev => [...prev, newInvoice]); + setJournalEntries(prev => [...prev, journalDeposit, journalRent]); setRental(prev => prev ? { ...prev, batteryId: battery.id, @@ -527,8 +613,12 @@ export default function RentalDetailPage() { batteryRent: (prev.batteryRent || 0) + battery.monthlyRent, batteryHistory: [...(prev.batteryHistory || []), newBatteryHistory], } : null); + + setShowBatteryInvoicePreview(newInvoice); setShowAddBatteryModal(false); setSelectedBatteryId(''); + setBatteryDeposit(0); + setBatteryDepositMethod('cash'); }; const handleAddNote = () => { @@ -559,6 +649,25 @@ export default function RentalDetailPage() { setShowSmsModal(false); }; + const handlePrintInvoiceDirect = (inv: Invoice) => { + setShowBatteryInvoicePreview(inv); + setTimeout(() => { + window.print(); + }, 150); + }; + + const handleSimulatedPDFDownload = (invId: string) => { + setIsDownloadingPDF(true); + setDownloadSuccess(false); + setTimeout(() => { + setIsDownloadingPDF(false); + setDownloadSuccess(true); + setTimeout(() => { + setDownloadSuccess(false); + }, 2000); + }, 1500); + }; + const statusBadge = getStatusBadge(rental.status); const typeBadge = getTypeBadge(rental.type); const paymentBadge = getPaymentStatusBadge(rental.paymentStatus); @@ -579,6 +688,47 @@ export default function RentalDetailPage() { return (
+ {/* Global `@media print` style sheet overrides */} +