From 4c25990e706dc64b56b23ed5e88392343f1206f7 Mon Sep 17 00:00:00 2001 From: sazzadulalambd Date: Sat, 16 May 2026 20:54:17 +0600 Subject: [PATCH] feat: add support for battery swapping status and bike image gallery management in fleet details --- src/app/admin/fleet/[id]/page.tsx | 91 ++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/src/app/admin/fleet/[id]/page.tsx b/src/app/admin/fleet/[id]/page.tsx index e12a689..b6ad357 100644 --- a/src/app/admin/fleet/[id]/page.tsx +++ b/src/app/admin/fleet/[id]/page.tsx @@ -9,7 +9,7 @@ import { GaugeCircle, CheckCircle, AlertTriangle, Activity, Award, TrendingUp, Wallet, MoreHorizontal, Map, Navigation2, Satellite, FileCheck, FileX, Clock3, History, CreditCard, User2, Phone, Mail, MapPinned, ExternalLink, Plus, - AlertCircle, Image as ImageIcon + AlertCircle, Image as ImageIcon, Camera } from 'lucide-react'; interface GPSDevice { @@ -104,7 +104,8 @@ interface BatteryHistory { serialNumber: string; assignedDate: string; returnedDate?: string; - status: 'active' | 'returned'; + swappedToBatteryId?: string; + status: 'active' | 'returned' | 'swapped'; socStart: number; socEnd?: number; monthlyRent?: number; @@ -152,6 +153,12 @@ interface Bike { damageHistory?: DamageRecord[]; maintenanceHistory?: MaintenanceRecord[]; batteryHistory?: BatteryHistory[]; + bikeImages?: { + front?: string; + back?: string; + left?: string; + right?: string; + }; } const mockBikes: Bike[] = [ @@ -188,8 +195,8 @@ const mockBikes: Bike[] = [ batteryHistory: [ { id: 'BH001', batteryId: 'BAT-001', brand: 'EVE Energy', model: 'Li-Ion 60V50Ah', serialNumber: 'SN-2024-00001', assignedDate: '2024-03-15', status: 'active', socStart: 85, monthlyRent: 1500 }, { id: 'BH002', batteryId: 'BAT-005', brand: 'Samsung SDI', model: 'Li-Ion 60V45Ah', serialNumber: 'SN-2023-00045', assignedDate: '2024-01-10', returnedDate: '2024-03-14', status: 'returned', socStart: 80, socEnd: 45, monthlyRent: 1200 }, - { id: 'BH003', batteryId: 'BAT-008', brand: 'LG Chem', model: 'Li-Ion 48V40Ah', serialNumber: 'SN-2023-00012', assignedDate: '2023-11-05', returnedDate: '2024-01-09', status: 'returned', socStart: 90, socEnd: 60, monthlyRent: 1000 }, - { id: 'BH004', batteryId: 'BAT-003', brand: 'Panasonic', model: 'Li-Ion 60V50Ah', serialNumber: 'SN-2023-00078', assignedDate: '2023-08-20', returnedDate: '2023-11-04', status: 'returned', socStart: 88, socEnd: 55, monthlyRent: 1500 }, + { id: 'BH003', batteryId: 'BAT-008', brand: 'LG Chem', model: 'Li-Ion 48V40Ah', serialNumber: 'SN-2023-00012', assignedDate: '2023-11-05', returnedDate: '2024-01-09', status: 'swapped', socStart: 90, socEnd: 60, monthlyRent: 1000 }, + { id: 'BH004', batteryId: 'BAT-003', brand: 'Panasonic', model: 'Li-Ion 60V50Ah', serialNumber: 'SN-2023-00078', assignedDate: '2023-08-20', returnedDate: '2023-11-04', status: 'swapped', socStart: 88, socEnd: 55, monthlyRent: 1500 }, { id: 'BH005', batteryId: 'BAT-002', brand: 'Sony', model: 'Li-Ion 48V35Ah', serialNumber: 'SN-2023-00034', assignedDate: '2023-05-15', returnedDate: '2023-08-19', status: 'returned', socStart: 75, socEnd: 40, monthlyRent: 900 }, { id: 'BH006', batteryId: 'BAT-012', brand: 'BYD', model: 'LiFePO4 60V40Ah', serialNumber: 'SN-2023-00056', assignedDate: '2024-04-20', returnedDate: '2024-06-15', status: 'returned', socStart: 92, socEnd: 35, monthlyRent: 1300 }, { id: 'BH007', batteryId: 'BAT-015', brand: 'CATL', model: 'Li-Ion 72V50Ah', serialNumber: 'SN-2024-00089', assignedDate: '2024-06-20', status: 'active', socStart: 88, monthlyRent: 1800 }, @@ -198,7 +205,15 @@ const mockBikes: Bike[] = [ { id: 'BH010', batteryId: 'BAT-007', brand: 'Kexin', model: 'Li-Ion 48V36Ah', serialNumber: 'SN-2022-00045', assignedDate: '2022-12-05', returnedDate: '2023-02-08', status: 'returned', socStart: 85, socEnd: 55, monthlyRent: 850 }, { id: 'BH011', batteryId: 'BAT-004', brand: 'Faraday', model: 'LiFePO4 48V42Ah', serialNumber: 'SN-2022-00089', assignedDate: '2022-09-15', returnedDate: '2022-12-04', status: 'returned', socStart: 90, socEnd: 48, monthlyRent: 1200 }, { id: 'BH012', batteryId: 'BAT-006', brand: 'Reliance', model: 'Lead Acid 48V32Ah', serialNumber: 'SN-2022-00034', assignedDate: '2022-06-20', returnedDate: '2022-09-14', status: 'returned', socStart: 95, socEnd: 30, monthlyRent: 600 }, - ] + { id: 'BH013', batteryId: 'BAT-020', brand: 'Maxell', model: 'Li-Ion 60V45Ah', serialNumber: 'SN-2024-00123', assignedDate: '2024-07-10', swappedToBatteryId: 'BAT-025', status: 'swapped', socStart: 75, socEnd: 65, monthlyRent: 1400 }, + { id: 'BH014', batteryId: 'BAT-018', brand: 'Nikola', model: 'LiFePO4 48V40Ah', serialNumber: 'SN-2023-00078', assignedDate: '2024-05-05', swappedToBatteryId: 'BAT-022', status: 'swapped', socStart: 82, socEnd: 55, monthlyRent: 1150 }, + ], + bikeImages: { + front: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop', + back: 'https://images.unsplash.com/photo-1591637333184-19aa84de3fbd?w=400&h=300&fit=crop', + left: 'https://images.unsplash.com/photo-1622185135505-2d795043906a?w=400&h=300&fit=crop', + right: 'https://images.unsplash.com/photo-1609630875171-b1321377ee53?w=400&h=300&fit=crop', + } }, { id: 'EV002', model: 'Yadea DT3', brand: 'Yadea', image: '', plateNumber: 'Dhaka Metro Cha-A-5678', status: 'available', batteryLevel: 95, location: 'Banani', hubId: 'HUB-002', hubName: 'Banani Hub', investorId: 'inv1', investorName: 'Mr. Hasan (Investor)', purchaseDate: '2024-02-01', purchasePrice: 118000, totalRides: 89, totalDistance: 1567, totalEarnings: 31150, lastService: '2024-03-15', nextService: '2024-04-15', insuranceExpiry: '2025-02-01', registrationExpiry: '2026-02-01', @@ -486,9 +501,9 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri ৳{damage.actualCost || '-'} {damage.status.replace('_', ' ')} @@ -571,8 +586,8 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri {maintenance.nextDueDate || '-'} {maintenance.status.replace('_', ' ')} @@ -773,7 +788,7 @@ function OverviewTab({ bike }: { bike: Bike }) { Battery ID Brand/Model Assigned Date - Returned Date + Returned/Swapped Start SOC End SOC Monthly Rent @@ -786,13 +801,20 @@ function OverviewTab({ bike }: { bike: Bike }) { {bh.batteryId} {bh.brand} {bh.model} {bh.assignedDate} - {bh.returnedDate || '-'} + + {bh.status === 'swapped' ? ( + <> + Swapped to {bh.swappedToBatteryId} + {bh.returnedDate} + + ) : bh.returnedDate || '-'} + {bh.socStart}% {bh.socEnd ? `${bh.socEnd}%` : '-'} ৳{bh.monthlyRent} - - {bh.status === 'active' ? 'Active' : 'Returned'} + + {bh.status === 'active' ? 'Active' : bh.status === 'swapped' ? 'Swapped' : 'Returned'} @@ -926,9 +948,50 @@ function GPSTab({ bike }: { bike: Bike }) { function DocumentsTab({ bike }: { bike: Bike }) { const docs = bike.documents || []; + const [images, setImages] = useState(bike.bikeImages || { front: '', back: '', left: '', right: '' }); + const [uploading, setUploading] = useState(null); + + const handleImageUpload = (view: 'front' | 'back' | 'left' | 'right', e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setUploading(view); + const reader = new FileReader(); + reader.onloadend = () => { + setImages(prev => ({ ...prev, [view]: reader.result as string })); + setUploading(null); + }; + reader.readAsDataURL(file); + } + }; return (
+
+

Bike Images

+
+ {(['front', 'back', 'left', 'right'] as const).map(view => ( +
+

{view} View

+
+ {images[view] ? ( + {`${view} + ) : ( +
+ +
+ )} + +
+
+ ))} +
+
+

Bike Documents

{docs.length === 0 ? (