2026-05-16 12:10:49 +06:00
'use client' ;
import { useState , use } from 'react' ;
import Link from 'next/link' ;
import {
Battery , ArrowLeft , X , BatteryCharging , Activity , Gauge , MapPin , Bike , User , History ,
2026-05-16 12:36:00 +06:00
Calendar , DollarSign , CheckCircle , Clock , ArrowRightLeft , Handshake , TrendingUp , Edit ,
2026-05-16 19:33:28 +06:00
RefreshCw , AlertTriangle , Wrench , Plus , Trash2
2026-05-16 12:10:49 +06:00
} from 'lucide-react' ;
interface BMSData {
voltage : number ;
current : number ;
soc : number ;
temperature : number ;
cycles : number ;
health : number ;
timestamp : string ;
}
interface OwnershipLog {
id : string ;
batteryId : string ;
fromOwner : string ;
fromOwnerType : 'company' | 'biker' | 'hub' | 'swap-station' ;
toOwner : string ;
toOwnerType : 'company' | 'biker' | 'hub' | 'swap-station' ;
fromBikeId? : string ;
fromBikeModel? : string ;
toBikeId? : string ;
toBikeModel? : string ;
fromHubId? : string ;
fromHubName? : string ;
toHubId? : string ;
toHubName? : string ;
fromStationId? : string ;
fromStationName? : string ;
toStationId? : string ;
toStationName? : string ;
action : 'assigned' | 'rented' | 'swapped' | 'returned' | 'retired' ;
rentAmount? : number ;
notes? : string ;
timestamp : string ;
}
2026-05-16 19:33:28 +06:00
interface DamageRecord {
id : string ;
date : string ;
type : string ;
description : string ;
reportedBy : string ;
estimatedCost : number ;
actualCost? : number ;
status : 'reported' | 'in-progress' | 'resolved' ;
}
interface MaintenanceRecord {
id : string ;
date : string ;
type : 'routine' | 'repair' | 'replacement' | 'inspection' ;
description : string ;
cost : number ;
performedBy : string ;
nextDueDate? : string ;
status : 'completed' | 'pending' | 'overdue' ;
}
2026-05-16 12:10:49 +06:00
interface Battery {
id : string ;
serialNumber : string ;
brand : string ;
model : string ;
type : 'lithium-ion' | 'lifepo4' | 'lead-acid' ;
capacity : number ;
voltage : number ;
purchaseDate : string ;
purchasePrice : number ;
warrantyExpiry : string ;
status : 'available' | 'in-use' | 'maintenance' | 'retired' | 'charging' ;
currentSoc : number ;
health : number ;
cycleCount : number ;
assignedBikeId? : string ;
assignedBikeModel? : string ;
assignedBikePlate? : string ;
assignedBikerId? : string ;
assignedBikerName? : string ;
assignedBikerPhone? : string ;
currentHubId? : string ;
currentHubName? : string ;
currentStationId? : string ;
currentStationName? : string ;
lastMaintenance? : string ;
nextMaintenance? : string ;
bmsData? : BMSData ;
monthlyRent? : number ;
ownershipLogs : OwnershipLog [ ] ;
2026-05-16 19:33:28 +06:00
damageHistory? : DamageRecord [ ] ;
maintenanceHistory? : MaintenanceRecord [ ] ;
2026-05-16 12:10:49 +06:00
}
const mockBattery : Battery = {
id : 'BAT-001' ,
serialNumber : 'SN-2024-00001' ,
brand : 'EVE Energy' ,
model : 'Li-Ion 60V50Ah' ,
type : 'lithium-ion' ,
capacity : 50 ,
voltage : 60 ,
purchaseDate : '2024-01-15' ,
purchasePrice : 45000 ,
warrantyExpiry : '2027-01-15' ,
status : 'in-use' ,
currentSoc : 78 ,
health : 95 ,
cycleCount : 156 ,
assignedBikeId : 'EV001' ,
assignedBikeModel : 'Etron ET50' ,
assignedBikePlate : 'Dhaka Metro Cha-A-1234' ,
assignedBikerId : 'BK-001' ,
assignedBikerName : 'Rahim Ahmed' ,
assignedBikerPhone : '01712345678' ,
currentHubId : 'HUB-001' ,
currentHubName : 'JAIBEN Head Office' ,
monthlyRent : 1500 ,
lastMaintenance : '2024-03-01' ,
nextMaintenance : '2024-06-01' ,
bmsData : { voltage : 67.2 , current : - 2.5 , soc : 78 , temperature : 32 , cycles : 156 , health : 95 , timestamp : '2024-03-28 12:00:00' } ,
ownershipLogs : [
{ id : 'OL-001' , batteryId : 'BAT-001' , fromOwner : 'JAIBEN Hub' , fromOwnerType : 'hub' , toOwner : 'Rahim Ahmed' , toOwnerType : 'biker' , toBikeId : 'EV001' , toBikeModel : 'Etron ET50' , toHubId : 'HUB-001' , toHubName : 'JAIBEN Head Office' , action : 'rented' , rentAmount : 1500 , timestamp : '2024-03-15 08:30:00' } ,
{ id : 'OL-002' , batteryId : 'BAT-001' , fromOwner : 'Rahim Ahmed' , fromOwnerType : 'biker' , toOwner : 'JAIBEN Hub' , toOwnerType : 'hub' , fromBikeId : 'EV001' , fromBikeModel : 'Etron ET50' , fromHubId : 'HUB-001' , fromHubName : 'JAIBEN Head Office' , action : 'returned' , timestamp : '2024-03-28 18:00:00' , notes : 'Returned for charging' } ,
{ id : 'OL-003' , batteryId : 'BAT-001' , fromOwner : 'JAIBEN Hub' , fromOwnerType : 'hub' , toOwner : 'Rahim Ahmed' , toOwnerType : 'biker' , toBikeId : 'EV001' , toBikeModel : 'Etron ET50' , toHubId : 'HUB-001' , toHubName : 'JAIBEN Head Office' , action : 'rented' , rentAmount : 1500 , timestamp : '2024-03-28 18:30:00' } ,
2026-05-16 19:33:28 +06:00
] ,
damageHistory : [
{ id : 'DMG-001' , date : '2024-02-15' , type : 'Physical Damage' , description : 'Battery casing cracked due to drop' , reportedBy : 'Rahim Ahmed' , estimatedCost : 5000 , actualCost : 4500 , status : 'resolved' } ,
{ id : 'DMG-002' , date : '2024-03-20' , type : 'Connector Damage' , description : 'Charging connector bent' , reportedBy : 'Tech Staff' , estimatedCost : 1500 , status : 'in-progress' } ,
] ,
maintenanceHistory : [
{ id : 'MNT-001' , date : '2024-01-20' , type : 'routine' , description : 'General inspection and cleaning' , cost : 500 , performedBy : 'Tech Team' , nextDueDate : '2024-04-20' , status : 'completed' } ,
{ id : 'MNT-002' , date : '2024-03-01' , type : 'inspection' , description : 'BMS calibration and cell balancing' , cost : 800 , performedBy : 'Engineer Team' , nextDueDate : '2024-06-01' , status : 'completed' } ,
{ id : 'MNT-003' , date : '2024-04-15' , type : 'repair' , description : 'Replaced faulty connector' , cost : 1500 , performedBy : 'Tech Team' , status : 'pending' } ,
] ,
2026-05-16 12:10:49 +06:00
} ;
const statusColors : Record < string , string > = {
available : 'bg-green-100 text-green-700' ,
'in-use' : 'bg-blue-100 text-blue-700' ,
maintenance : 'bg-amber-100 text-amber-700' ,
retired : 'bg-slate-100 text-slate-500' ,
charging : 'bg-purple-100 text-purple-700' ,
} ;
const typeLabels : Record < string , string > = {
'lithium-ion' : 'Lithium-Ion' ,
'lifepo4' : 'LiFePO4' ,
'lead-acid' : 'Lead Acid' ,
} ;
export default function BatteryDetailPage ( { params } : { params : Promise < { id : string } > } ) {
const { id } = use ( params ) ;
2026-05-16 12:19:54 +06:00
const [ battery , setBattery ] = useState < Battery > ( mockBattery ) ;
2026-05-16 19:33:28 +06:00
const [ activeTab , setActiveTab ] = useState < 'info' | 'bms' | 'history' | 'rent' | 'damage' | 'maintenance' > ( 'info' ) ;
2026-05-16 12:19:54 +06:00
const [ showEditModal , setShowEditModal ] = useState ( false ) ;
2026-05-16 12:36:00 +06:00
const [ refreshing , setRefreshing ] = useState ( false ) ;
2026-05-16 12:19:54 +06:00
2026-05-16 19:33:28 +06:00
// Damage History State
const [ showDamageModal , setShowDamageModal ] = useState ( false ) ;
const [ editingDamage , setEditingDamage ] = useState < DamageRecord | null > ( null ) ;
const [ damageForm , setDamageForm ] = useState ( {
date : '' ,
type : '' ,
description : '' ,
reportedBy : '' ,
estimatedCost : 0 ,
actualCost : 0 ,
status : 'reported' as 'reported' | 'in-progress' | 'resolved' ,
hubId : '' ,
hubName : '' ,
} ) ;
// Maintenance State
const [ showMaintenanceModal , setShowMaintenanceModal ] = useState ( false ) ;
const [ editingMaintenance , setEditingMaintenance ] = useState < MaintenanceRecord | null > ( null ) ;
const [ maintenanceForm , setMaintenanceForm ] = useState ( {
date : '' ,
type : 'routine' as 'routine' | 'repair' | 'replacement' | 'inspection' ,
description : '' ,
cost : 0 ,
performedBy : '' ,
nextDueDate : '' ,
status : 'pending' as 'completed' | 'pending' | 'overdue' ,
hubId : '' ,
hubName : '' ,
} ) ;
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 damageTypes = [ 'Physical Damage' , 'Connector Damage' , 'Battery Cell Damage' , 'Charging Port Damage' , 'Water Damage' , 'Software Issue' , 'Other' ] ;
const maintenanceTypes = [ 'routine' , 'repair' , 'replacement' , 'inspection' ] as const ;
2026-05-16 12:19:54 +06:00
const handleSaveEdit = ( updatedBattery : Battery ) = > {
setBattery ( updatedBattery ) ;
setShowEditModal ( false ) ;
} ;
2026-05-16 12:36:00 +06:00
const handleRefreshBMS = ( ) = > {
if ( ! battery . bmsData ) return ;
setRefreshing ( true ) ;
setTimeout ( ( ) = > {
const bms = battery . bmsData ! ;
setBattery ( {
. . . battery ,
bmsData : {
. . . bms ,
voltage : Math.round ( ( 60 + Math . random ( ) * 10 ) * 10 ) / 10 ,
current : Math.round ( ( - 3 + Math . random ( ) * 4 ) * 10 ) / 10 ,
soc : Math.floor ( Math . random ( ) * 40 ) + 60 ,
temperature : 25 + Math . floor ( Math . random ( ) * 15 ) ,
cycles : bms.cycles + 1 ,
health : Math.max ( 70 , Math . min ( 100 , bms . health + ( Math . random ( ) > 0.7 ? 1 : 0 ) ) ) ,
timestamp : new Date ( ) . toISOString ( ) . replace ( 'T' , ' ' ) . substring ( 0 , 19 )
} ,
currentSoc : Math.floor ( Math . random ( ) * 40 ) + 60 ,
health : Math.max ( 70 , Math . min ( 100 , battery . health + ( Math . random ( ) > 0.7 ? 1 : 0 ) ) )
} ) ;
setRefreshing ( false ) ;
} , 1000 ) ;
} ;
2026-05-16 19:33:28 +06:00
// Damage CRUD Handlers
const handleAddDamage = ( ) = > {
const newDamage : DamageRecord = {
id : ` DMG- ${ Date . now ( ) } ` ,
. . . damageForm ,
estimatedCost : Number ( damageForm . estimatedCost ) ,
actualCost : damageForm.actualCost ? Number ( damageForm . actualCost ) : undefined ,
} ;
setBattery ( prev = > ( {
. . . prev ,
damageHistory : [ . . . ( prev . damageHistory || [ ] ) , newDamage ]
} ) ) ;
setShowDamageModal ( false ) ;
resetDamageForm ( ) ;
} ;
const handleUpdateDamage = ( ) = > {
if ( ! editingDamage ) return ;
const updatedDamage : DamageRecord = {
. . . editingDamage ,
. . . damageForm ,
estimatedCost : Number ( damageForm . estimatedCost ) ,
actualCost : damageForm.actualCost ? Number ( damageForm . actualCost ) : undefined ,
} ;
setBattery ( prev = > ( {
. . . prev ,
damageHistory : prev.damageHistory?.map ( d = > d . id === editingDamage . id ? updatedDamage : d ) || [ ]
} ) ) ;
setShowDamageModal ( false ) ;
setEditingDamage ( null ) ;
resetDamageForm ( ) ;
} ;
const handleDeleteDamage = ( damageId : string ) = > {
setBattery ( prev = > ( {
. . . prev ,
damageHistory : prev.damageHistory?.filter ( d = > d . id !== damageId ) || [ ]
} ) ) ;
} ;
const resetDamageForm = ( ) = > {
setDamageForm ( {
date : '' ,
type : '' ,
description : '' ,
reportedBy : '' ,
estimatedCost : 0 ,
actualCost : 0 ,
status : 'reported' ,
hubId : '' ,
hubName : '' ,
} ) ;
} ;
const openAddDamageModal = ( ) = > {
resetDamageForm ( ) ;
setDamageForm ( prev = > ( { . . . prev , date : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } ) ) ;
setEditingDamage ( null ) ;
setShowDamageModal ( true ) ;
} ;
const openEditDamageModal = ( damage : DamageRecord ) = > {
setDamageForm ( {
date : damage.date ,
type : damage . type ,
description : damage.description ,
reportedBy : damage.reportedBy ,
estimatedCost : damage.estimatedCost ,
actualCost : damage.actualCost || 0 ,
status : damage.status ,
hubId : '' ,
hubName : '' ,
} ) ;
setEditingDamage ( damage ) ;
setShowDamageModal ( true ) ;
} ;
// Maintenance CRUD Handlers
const handleAddMaintenance = ( ) = > {
const newMaintenance : MaintenanceRecord = {
id : ` MNT- ${ Date . now ( ) } ` ,
. . . maintenanceForm ,
cost : Number ( maintenanceForm . cost ) ,
nextDueDate : maintenanceForm.nextDueDate || undefined ,
} ;
setBattery ( prev = > ( {
. . . prev ,
maintenanceHistory : [ . . . ( prev . maintenanceHistory || [ ] ) , newMaintenance ]
} ) ) ;
setShowMaintenanceModal ( false ) ;
resetMaintenanceForm ( ) ;
} ;
const handleUpdateMaintenance = ( ) = > {
if ( ! editingMaintenance ) return ;
const updatedMaintenance : MaintenanceRecord = {
. . . editingMaintenance ,
. . . maintenanceForm ,
cost : Number ( maintenanceForm . cost ) ,
nextDueDate : maintenanceForm.nextDueDate || undefined ,
} ;
setBattery ( prev = > ( {
. . . prev ,
maintenanceHistory : prev.maintenanceHistory?.map ( m = > m . id === editingMaintenance . id ? updatedMaintenance : m ) || [ ]
} ) ) ;
setShowMaintenanceModal ( false ) ;
setEditingMaintenance ( null ) ;
resetMaintenanceForm ( ) ;
} ;
const handleDeleteMaintenance = ( maintenanceId : string ) = > {
setBattery ( prev = > ( {
. . . prev ,
maintenanceHistory : prev.maintenanceHistory?.filter ( m = > m . id !== maintenanceId ) || [ ]
} ) ) ;
} ;
const resetMaintenanceForm = ( ) = > {
setMaintenanceForm ( {
date : '' ,
type : 'routine' ,
description : '' ,
cost : 0 ,
performedBy : '' ,
nextDueDate : '' ,
status : 'pending' ,
hubId : '' ,
hubName : '' ,
} ) ;
} ;
const openAddMaintenanceModal = ( ) = > {
resetMaintenanceForm ( ) ;
setMaintenanceForm ( prev = > ( { . . . prev , date : new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } ) ) ;
setEditingMaintenance ( null ) ;
setShowMaintenanceModal ( true ) ;
} ;
const openEditMaintenanceModal = ( maintenance : MaintenanceRecord ) = > {
setMaintenanceForm ( {
date : maintenance.date ,
type : maintenance . type ,
description : maintenance.description ,
cost : maintenance.cost ,
performedBy : maintenance.performedBy ,
nextDueDate : maintenance.nextDueDate || '' ,
status : maintenance.status ,
hubId : '' ,
hubName : '' ,
} ) ;
setEditingMaintenance ( maintenance ) ;
setShowMaintenanceModal ( true ) ;
} ;
2026-05-16 12:10:49 +06:00
return (
< div className = "p-4 lg:p-6" >
< div className = "flex items-center gap-4 mb-6" >
< Link href = "/admin/batteries" className = "p-2 hover:bg-slate-100 rounded-lg" >
< ArrowLeft className = "w-5 h-5 text-slate-600" / >
< / Link >
< div className = "flex-1" >
< h1 className = "text-2xl lg:text-3xl font-extrabold text-slate-800" > Battery Details < / h1 >
< p className = "text-sm text-slate-500" > ID : { battery . id } < / p >
< / div >
2026-05-16 12:19:54 +06:00
< button
onClick = { ( ) = > setShowEditModal ( true ) }
className = "py-2 px-4 bg-accent text-white rounded-lg font-semibold text-sm hover:bg-accent-dark flex items-center gap-2"
>
< Edit className = "w-4 h-4" / > Edit
< / button >
2026-05-16 12:10:49 +06:00
< / div >
< div className = "bg-white rounded-xl shadow-sm border border-slate-100 mb-6" >
< div className = "p-5 border-b border-slate-100" >
< div className = "flex flex-col lg:flex-row lg:items-center gap-4" >
< div className = "flex items-center gap-4" >
< div className = { ` w-14 h-14 rounded-xl flex items-center justify-center ${ battery . type === 'lithium-ion' ? 'bg-blue-50' : battery . type === 'lifepo4' ? 'bg-green-50' : 'bg-amber-50' } ` } >
< Battery className = { ` w-7 h-7 ${ battery . type === 'lithium-ion' ? 'text-blue-600' : battery . type === 'lifepo4' ? 'text-green-600' : 'text-amber-600' } ` } / >
< / div >
< div >
< h2 className = "text-lg font-bold text-slate-800" > { battery . brand } { battery . model } < / h2 >
< p className = "text-sm text-slate-500" > { battery . serialNumber } < / p >
< span className = { ` inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full mt-1 ${ statusColors [ battery . status ] } ` } >
{ battery . status }
< / span >
< / div >
< / div >
< div className = "flex-1 grid grid-cols-2 md:grid-cols-4 gap-3 lg:ml-auto" >
< div className = "text-center" >
< p className = "text-xs text-slate-500" > SOC < / p >
< p className = { ` text-lg font-bold ${ battery . currentSoc > 50 ? 'text-green-600' : battery . currentSoc > 20 ? 'text-amber-600' : 'text-red-600' } ` } > { battery . currentSoc } % < / p >
< / div >
< div className = "text-center" >
< p className = "text-xs text-slate-500" > Health < / p >
< p className = { ` text-lg font-bold ${ battery . health > 80 ? 'text-green-600' : battery . health > 60 ? 'text-amber-600' : 'text-red-600' } ` } > { battery . health } % < / p >
< / div >
< div className = "text-center" >
< p className = "text-xs text-slate-500" > Cycles < / p >
< p className = "text-lg font-bold text-slate-700" > { battery . cycleCount } < / p >
< / div >
< div className = "text-center" >
< p className = "text-xs text-slate-500" > Price < / p >
< p className = "text-lg font-bold text-slate-700" > ৳ { battery . purchasePrice . toLocaleString ( ) } < / p >
< / div >
< / div >
< / div >
< / div >
{ battery . bmsData && (
< div className = "p-5 border-b border-slate-100 bg-gradient-to-r from-green-50 to-emerald-50" >
2026-05-16 12:36:00 +06:00
< div className = "flex items-center justify-between mb-3" >
< div className = "flex items-center gap-2" >
< Activity className = "w-4 h-4 text-green-600" / >
< span className = "font-medium text-green-800" > Live BMS Data < / span >
< span className = "text-xs text-green-600 bg-green-100 px-2 py-0.5 rounded-full" > Real - time < / span >
< / div >
< div className = "flex items-center gap-2" >
< span className = "text-xs text-green-600" > Updated : { battery . bmsData . timestamp } < / span >
< button onClick = { handleRefreshBMS } disabled = { refreshing } className = "p-1.5 text-green-600 hover:bg-green-100 rounded-lg" >
< RefreshCw className = { ` w-4 h-4 ${ refreshing ? 'animate-spin' : '' } ` } / >
< / button >
< / div >
2026-05-16 12:10:49 +06:00
< / div >
< div className = "grid grid-cols-3 md:grid-cols-6 gap-2" >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > Voltage < / p >
< p className = "font-semibold text-slate-700" > { battery . bmsData . voltage } V < / p >
< / div >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > Current < / p >
< p className = "font-semibold text-slate-700" > { battery . bmsData . current } A < / p >
< / div >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > SOC < / p >
< p className = "font-semibold text-green-600" > { battery . bmsData . soc } % < / p >
< / div >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > Temp < / p >
< p className = "font-semibold text-slate-700" > { battery . bmsData . temperature } ° C < / p >
< / div >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > Cycles < / p >
< p className = "font-semibold text-slate-700" > { battery . bmsData . cycles } < / p >
< / div >
< div className = "bg-white rounded-lg p-2 text-center" >
< p className = "text-xs text-slate-500" > Health < / p >
< p className = { ` font-semibold ${ battery . bmsData . health > 80 ? 'text-green-600' : 'text-amber-600' } ` } > { battery . bmsData . health } % < / p >
< / div >
< / div >
< / div >
) }
< div className = "border-b border-slate-100" >
2026-05-16 19:33:28 +06:00
< div className = "flex gap-1 p-1 overflow-x-auto" >
< button onClick = { ( ) = > setActiveTab ( 'info' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'info' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > Info < / button >
< button onClick = { ( ) = > setActiveTab ( 'bms' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'bms' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > BMS < / button >
< button onClick = { ( ) = > setActiveTab ( 'history' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'history' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > History < / button >
< button onClick = { ( ) = > setActiveTab ( 'rent' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'rent' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > Rent < / button >
< button onClick = { ( ) = > setActiveTab ( 'damage' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'damage' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > Damage History < / button >
< button onClick = { ( ) = > setActiveTab ( 'maintenance' ) } className = { ` px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap ${ activeTab === 'maintenance' ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' } ` } > Maintenance < / button >
2026-05-16 12:10:49 +06:00
< / div >
< / div >
< div className = "p-5" >
{ activeTab === 'info' && (
< div className = "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4" >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Brand < / p >
< p className = "font-medium text-slate-700" > { battery . brand } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Model < / p >
< p className = "font-medium text-slate-700" > { battery . model } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Serial Number < / p >
< p className = "font-medium text-slate-700" > { battery . serialNumber } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Type < / p >
< p className = "font-medium text-slate-700" > { typeLabels [ battery . type ] } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Capacity < / p >
< p className = "font-medium text-slate-700" > { battery . capacity } Ah < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Voltage < / p >
< p className = "font-medium text-slate-700" > { battery . voltage } V < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Purchase Date < / p >
< p className = "font-medium text-slate-700" > { battery . purchaseDate } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Warranty Expiry < / p >
< p className = "font-medium text-slate-700" > { battery . warrantyExpiry } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Last Maintenance < / p >
< p className = "font-medium text-slate-700" > { battery . lastMaintenance || 'N/A' } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Next Maintenance < / p >
< p className = "font-medium text-slate-700" > { battery . nextMaintenance || 'N/A' } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Purchase Price < / p >
< p className = "font-medium text-slate-700" > ৳ { battery . purchasePrice . toLocaleString ( ) } < / p >
< / div >
{ battery . monthlyRent && (
< div className = "bg-slate-50 rounded-lg p-3" >
< p className = "text-xs text-slate-500" > Monthly Rent < / p >
< p className = "font-medium text-green-600" > ৳ { battery . monthlyRent } / month < / p >
< / div >
) }
< / div >
) }
{ activeTab === 'bms' && battery . bmsData && (
< div className = "space-y-4" >
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-2" >
< Activity className = "w-5 h-5 text-green-600" / >
< h4 className = "font-medium text-slate-700" > BMS Real - time Data < / h4 >
< / div >
< span className = "text-xs text-slate-400" > Updated : { battery . bmsData . timestamp } < / span >
< / div >
< div className = "grid grid-cols-2 md:grid-cols-3 gap-4" >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > Voltage < / p >
< p className = "text-2xl font-bold text-slate-700" > { battery . bmsData . voltage } V < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > Current < / p >
< p className = "text-2xl font-bold text-slate-700" > { battery . bmsData . current } A < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > SOC < / p >
< p className = "text-2xl font-bold text-green-600" > { battery . bmsData . soc } % < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > Temperature < / p >
< p className = "text-2xl font-bold text-slate-700" > { battery . bmsData . temperature } ° C < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > Cycles < / p >
< p className = "text-2xl font-bold text-slate-700" > { battery . bmsData . cycles } < / p >
< / div >
< div className = "bg-slate-50 rounded-lg p-4 text-center" >
< p className = "text-xs text-slate-500 mb-1" > Health < / p >
< p className = { ` text-2xl font-bold ${ battery . bmsData . health > 80 ? 'text-green-600' : 'text-amber-600' } ` } > { battery . bmsData . health } % < / p >
< / div >
< / div >
< / div >
) }
{ activeTab === 'history' && (
< div >
< div className = "flex items-center gap-2 mb-4" >
< History className = "w-5 h-5 text-purple-600" / >
< h4 className = "font-medium text-slate-700" > Ownership History < / h4 >
< / div >
{ battery . ownershipLogs . length === 0 ? (
< div className = "text-center py-8 text-slate-400" > No history records < / div >
) : (
< div className = "space-y-3" >
{ battery . ownershipLogs . map ( ( log , index ) = > (
< div key = { log . id } className = "relative pl-6 pb-4 border-l-2 border-slate-200 last:border-0" >
< div className = { ` absolute -left-2 top-0 w-4 h-4 rounded-full ${ log . action === 'rented' ? 'bg-green-500' : log . action === 'swapped' ? 'bg-blue-500' : log . action === 'returned' ? 'bg-amber-500' : 'bg-slate-400' } ` } / >
< div className = "bg-slate-50 rounded-lg p-3" >
< div className = "flex items-center justify-between mb-2" >
< span className = { ` text-xs font-medium px-2 py-0.5 rounded-full ${ log . action === 'rented' ? 'bg-green-100 text-green-700' : log . action === 'swapped' ? 'bg-blue-100 text-blue-700' : log . action === 'returned' ? 'bg-amber-100 text-amber-700' : 'bg-slate-100 text-slate-500' } ` } >
{ log . action }
< / span >
< span className = "text-xs text-slate-400" > { log . timestamp } < / span >
< / div >
< div className = "grid grid-cols-2 gap-2 text-sm" >
< div >
< p className = "text-xs text-slate-500" > From < / p >
< p className = "font-medium text-slate-700" > { log . fromOwner } < / p >
< / div >
< div >
< p className = "text-xs text-slate-500" > To < / p >
< p className = "font-medium text-slate-700" > { log . toOwner } < / p >
< / div >
< / div >
{ log . rentAmount && (
< p className = "text-xs text-green-600 mt-2" > Rent : ৳ { log . rentAmount } / month < / p >
) }
{ log . notes && (
< p className = "text-xs text-slate-400 mt-1 italic" > { log . notes } < / p >
) }
< / div >
< / div >
) ) }
< / div >
) }
< / div >
) }
{ activeTab === 'rent' && (
< div >
< div className = "flex items-center gap-2 mb-4" >
< DollarSign className = "w-5 h-5 text-green-600" / >
< h4 className = "font-medium text-slate-700" > Rent Information < / h4 >
< / div >
{ battery . monthlyRent ? (
< div className = "bg-green-50 rounded-lg p-5 border border-green-100" >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
< div >
< p className = "text-sm text-green-600 mb-1" > Monthly Rent Amount < / p >
< p className = "text-3xl font-bold text-green-700" > ৳ { battery . monthlyRent } < / p >
< p className = "text-xs text-green-500 mt-1" > per month < / p >
< / div >
< div >
< p className = "text-sm text-green-600 mb-1" > Status < / p >
< p className = "text-lg font-semibold text-green-700" > { battery . assignedBikerName ? 'Rented' : 'Not Rented' } < / p >
< p className = "text-xs text-green-500 mt-1" > { battery . assignedBikerName ? ` to ${ battery . assignedBikerName } ` : 'Available for rental' } < / p >
< / div >
< / div >
< / div >
) : (
< div className = "bg-slate-50 rounded-lg p-5 text-center text-slate-500" >
This battery is not set up for rental .
< / div >
) }
< / div >
) }
2026-05-16 19:33:28 +06:00
{ activeTab === 'damage' && (
< div className = "space-y-4" >
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-2" >
< AlertTriangle className = "w-5 h-5 text-red-500" / >
< h4 className = "font-medium text-slate-700" > Damage History < / h4 >
< / div >
< button onClick = { openAddDamageModal } className = "px-4 py-2 bg-accent text-white text-sm rounded-lg hover:bg-accent-dark flex items-center gap-2" >
< Plus className = "w-4 h-4" / > Add Damage
< / button >
< / div >
{ ( battery . damageHistory || [ ] ) . length > 0 ? (
< div className = "overflow-x-auto" >
< table className = "w-full" >
< thead className = "bg-slate-50" >
< tr >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Date < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Type < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Description < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Hub < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Est . Cost < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Actual Cost < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Status < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Actions < / th >
< / tr >
< / thead >
< tbody className = "divide-y divide-slate-100" >
{ battery . damageHistory ? . map ( damage = > (
< tr key = { damage . id } className = "hover:bg-slate-50" >
< td className = "px-4 py-3 text-sm text-slate-600" > { damage . date } < / td >
< td className = "px-4 py-3 text-sm text-slate-700" > { damage . type } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { damage . description } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { damageForm . hubName || '-' } < / td >
< td className = "px-4 py-3 text-sm font-medium text-amber-600" > ৳ { damage . estimatedCost . toLocaleString ( ) } < / td >
< td className = "px-4 py-3 text-sm font-medium text-green-600" > { damage . actualCost ? ` ৳ ${ damage . actualCost . toLocaleString ( ) } ` : '-' } < / td >
< td className = "px-4 py-3" >
< span className = { ` text-xs font-medium px-2 py-1 rounded-full ${ damage . status === 'resolved' ? 'bg-green-100 text-green-700' : damage . status === 'in-progress' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700' } ` } >
{ damage . status === 'resolved' ? 'Resolved' : damage . status === 'in-progress' ? 'In Progress' : 'Reported' }
< / span >
< / td >
< td className = "px-4 py-3" >
< div className = "flex items-center gap-1" >
< button onClick = { ( ) = > openEditDamageModal ( damage ) } className = "p-1.5 hover:bg-blue-100 rounded-lg" >
< Edit className = "w-4 h-4 text-blue-500" / >
< / button >
< button onClick = { ( ) = > handleDeleteDamage ( damage . id ) } className = "p-1.5 hover:bg-red-100 rounded-lg" >
< Trash2 className = "w-4 h-4 text-red-500" / >
< / button >
< / div >
< / td >
< / tr >
) ) }
< / tbody >
< / table >
< / div >
) : (
< div className = "bg-slate-50 rounded-lg p-5 text-center text-slate-500" >
No damage history found .
< / div >
) }
< / div >
) }
{ activeTab === 'maintenance' && (
< div className = "space-y-4" >
< div className = "flex items-center justify-between" >
< div className = "flex items-center gap-2" >
< Wrench className = "w-5 h-5 text-blue-500" / >
< h4 className = "font-medium text-slate-700" > Maintenance History < / h4 >
< / div >
< button onClick = { openAddMaintenanceModal } className = "px-4 py-2 bg-accent text-white text-sm rounded-lg hover:bg-accent-dark flex items-center gap-2" >
< Plus className = "w-4 h-4" / > Add Maintenance
< / button >
< / div >
{ ( battery . maintenanceHistory || [ ] ) . length > 0 ? (
< div className = "overflow-x-auto" >
< table className = "w-full" >
< thead className = "bg-slate-50" >
< tr >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Date < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Type < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Description < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Hub < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Cost < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Performed By < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Next Due < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Status < / th >
< th className = "px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase" > Actions < / th >
< / tr >
< / thead >
< tbody className = "divide-y divide-slate-100" >
{ battery . maintenanceHistory ? . map ( maint = > (
< tr key = { maint . id } className = "hover:bg-slate-50" >
< td className = "px-4 py-3 text-sm text-slate-600" > { maint . date } < / td >
< td className = "px-4 py-3 text-sm text-slate-700 capitalize" > { maint . type } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { maint . description } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { maintenanceForm . hubName || '-' } < / td >
< td className = "px-4 py-3 text-sm font-medium text-green-600" > ৳ { maint . cost . toLocaleString ( ) } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { maint . performedBy } < / td >
< td className = "px-4 py-3 text-sm text-slate-600" > { maint . nextDueDate || '-' } < / td >
< td className = "px-4 py-3" >
< span className = { ` text-xs font-medium px-2 py-1 rounded-full ${ maint . status === 'completed' ? 'bg-green-100 text-green-700' : maint . status === 'pending' ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700' } ` } >
{ maint . status === 'completed' ? 'Completed' : maint . status === 'pending' ? 'Pending' : 'Overdue' }
< / span >
< / td >
< td className = "px-4 py-3" >
< div className = "flex items-center gap-1" >
< button onClick = { ( ) = > openEditMaintenanceModal ( maint ) } className = "p-1.5 hover:bg-blue-100 rounded-lg" >
< Edit className = "w-4 h-4 text-blue-500" / >
< / button >
< button onClick = { ( ) = > handleDeleteMaintenance ( maint . id ) } className = "p-1.5 hover:bg-red-100 rounded-lg" >
< Trash2 className = "w-4 h-4 text-red-500" / >
< / button >
< / div >
< / td >
< / tr >
) ) }
< / tbody >
< / table >
< / div >
) : (
< div className = "bg-slate-50 rounded-lg p-5 text-center text-slate-500" >
No maintenance history found .
< / div >
) }
< / div >
) }
2026-05-16 12:10:49 +06:00
< / div >
< / div >
2026-05-16 19:33:28 +06:00
{ /* Damage Modal */ }
{ showDamageModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto" >
< div className = "p-4 border-b border-slate-100 flex justify-between items-center" >
< h3 className = "font-semibold text-slate-800" > { editingDamage ? 'Edit Damage' : 'Add Damage' } < / h3 >
< button onClick = { ( ) = > { setShowDamageModal ( false ) ; setEditingDamage ( null ) ; } } className = "text-slate-400 hover:text-slate-600" >
< X className = "w-5 h-5" / >
< / button >
< / div >
< div className = "p-4 space-y-4" >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Date < / label >
< input type = "date" value = { damageForm . date } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , date : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Type < / label >
< select value = { damageForm . type } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , type : e . target . value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select Type < / option >
{ damageTypes . map ( type = > (
< option key = { type } value = { type } > { type } < / option >
) ) }
< / select >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Description < / label >
< textarea value = { damageForm . description } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , description : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" rows = { 2 } / >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Reported By < / label >
< input type = "text" value = { damageForm . reportedBy } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , reportedBy : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Hub < / label >
< select value = { damageForm . hubId } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , hubId : e.target.value , hubName : mockHubs.find ( h = > h . id === e . target . value ) ? . name || '' } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select Hub < / option >
{ mockHubs . map ( hub = > (
< option key = { hub . id } value = { hub . id } > { hub . name } < / option >
) ) }
< / select >
< / div >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Estimated Cost ( ৳ ) < / label >
< input type = "number" value = { damageForm . estimatedCost } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , estimatedCost : Number ( e . target . value ) } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Actual Cost ( ৳ ) < / label >
< input type = "number" value = { damageForm . actualCost } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , actualCost : Number ( e . target . value ) } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Status < / label >
< select value = { damageForm . status } onChange = { ( e ) = > setDamageForm ( { . . . damageForm , status : e.target.value as any } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "reported" > Reported < / option >
< option value = "in-progress" > In Progress < / option >
< option value = "resolved" > Resolved < / option >
< / select >
< / div >
< div className = "flex gap-2 pt-4" >
< button onClick = { ( ) = > { setShowDamageModal ( false ) ; setEditingDamage ( null ) ; } } className = "flex-1 py-2 px-4 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" > Cancel < / button >
< button onClick = { editingDamage ? handleUpdateDamage : handleAddDamage } className = "flex-1 py-2 px-4 bg-accent text-white rounded-lg text-sm hover:bg-accent-dark" > { editingDamage ? 'Update' : 'Add' } < / button >
< / div >
< / div >
< / div >
< / div >
) }
{ /* Maintenance Modal */ }
{ showMaintenanceModal && (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto" >
< div className = "p-4 border-b border-slate-100 flex justify-between items-center" >
< h3 className = "font-semibold text-slate-800" > { editingMaintenance ? 'Edit Maintenance' : 'Add Maintenance' } < / h3 >
< button onClick = { ( ) = > { setShowMaintenanceModal ( false ) ; setEditingMaintenance ( null ) ; } } className = "text-slate-400 hover:text-slate-600" >
< X className = "w-5 h-5" / >
< / button >
< / div >
< div className = "p-4 space-y-4" >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Date < / label >
< input type = "date" value = { maintenanceForm . date } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , date : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Type < / label >
< select value = { maintenanceForm . type } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , type : e . target . value as any } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
{ maintenanceTypes . map ( type = > (
< option key = { type } value = { type } > { type . charAt ( 0 ) . toUpperCase ( ) + type . slice ( 1 ) } < / option >
) ) }
< / select >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Description < / label >
< textarea value = { maintenanceForm . description } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , description : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" rows = { 2 } / >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Hub < / label >
< select value = { maintenanceForm . hubId } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , hubId : e.target.value , hubName : mockHubs.find ( h = > h . id === e . target . value ) ? . name || '' } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "" > Select Hub < / option >
{ mockHubs . map ( hub = > (
< option key = { hub . id } value = { hub . id } > { hub . name } < / option >
) ) }
< / select >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Cost ( ৳ ) < / label >
< input type = "number" value = { maintenanceForm . cost } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , cost : Number ( e . target . value ) } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Performed By < / label >
< input type = "text" value = { maintenanceForm . performedBy } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , performedBy : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Next Due Date < / label >
< input type = "date" value = { maintenanceForm . nextDueDate } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , nextDueDate : e.target.value } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" / >
< / div >
< div >
< label className = "text-sm font-medium text-slate-600 mb-1 block" > Status < / label >
< select value = { maintenanceForm . status } onChange = { ( e ) = > setMaintenanceForm ( { . . . maintenanceForm , status : e.target.value as any } ) } className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" >
< option value = "completed" > Completed < / option >
< option value = "pending" > Pending < / option >
< option value = "overdue" > Overdue < / option >
< / select >
< / div >
< / div >
< div className = "flex gap-2 pt-4" >
< button onClick = { ( ) = > { setShowMaintenanceModal ( false ) ; setEditingMaintenance ( null ) ; } } className = "flex-1 py-2 px-4 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50" > Cancel < / button >
< button onClick = { editingMaintenance ? handleUpdateMaintenance : handleAddMaintenance } className = "flex-1 py-2 px-4 bg-accent text-white rounded-lg text-sm hover:bg-accent-dark" > { editingMaintenance ? 'Update' : 'Add' } < / button >
< / div >
< / div >
< / div >
< / div >
) }
2026-05-16 12:10:49 +06:00
{ battery . status === 'in-use' && (
< div className = "bg-blue-50 rounded-xl border border-blue-200 p-5 mb-6" >
< div className = "flex items-center gap-3 mb-4" >
< div className = "w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center" >
< User className = "w-5 h-5 text-blue-600" / >
< / div >
< div >
< h3 className = "font-semibold text-blue-800" > Currently Rented to Biker < / h3 >
< span className = "text-xs text-blue-600" > Active Rental < / span >
< / div >
< / div >
< div className = "grid grid-cols-2 md:grid-cols-4 gap-4" >
< div className = "bg-white rounded-lg p-3" >
< p className = "text-xs text-blue-500 mb-1" > Biker < / p >
< p className = "font-medium text-blue-900" > { battery . assignedBikerName } < / p >
< p className = "text-xs text-blue-400" > { battery . assignedBikerPhone } < / p >
< / div >
< div className = "bg-white rounded-lg p-3" >
< p className = "text-xs text-blue-500 mb-1" > Bike < / p >
< p className = "font-medium text-blue-900" > { battery . assignedBikeModel } < / p >
< p className = "text-xs text-blue-400" > { battery . assignedBikePlate } < / p >
< / div >
< div className = "bg-white rounded-lg p-3" >
< p className = "text-xs text-blue-500 mb-1" > Hub / Station < / p >
< p className = "font-medium text-blue-900" > { battery . currentHubName || battery . currentStationName || 'Not Assigned' } < / p >
< / div >
< div className = "bg-white rounded-lg p-3" >
< p className = "text-xs text-blue-500 mb-1" > Monthly Rent < / p >
< p className = "font-medium text-green-600" > ৳ { battery . monthlyRent } / month < / p >
< / div >
< / div >
< / div >
) }
2026-05-16 12:19:54 +06:00
{ showEditModal && (
< EditBatteryModal
battery = { battery }
onSave = { handleSaveEdit }
onClose = { ( ) = > setShowEditModal ( false ) }
/ >
) }
< / div >
) ;
}
function EditBatteryModal ( {
battery ,
onSave ,
onClose
} : {
battery : Battery ;
onSave : ( battery : Battery ) = > void ;
onClose : ( ) = > void ;
} ) {
const [ formData , setFormData ] = useState < Battery > ( { . . . battery } ) ;
const handleChange = ( field : keyof Battery , value : any ) = > {
setFormData ( { . . . formData , [ field ] : value } ) ;
} ;
return (
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4" >
< div className = "bg-white rounded-xl shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto" >
< div className = "p-5 border-b border-slate-100 flex items-center justify-between sticky top-0 bg-white" >
< h2 className = "text-lg font-bold text-slate-800" > Edit Battery < / h2 >
< button onClick = { onClose } className = "p-2 hover:bg-slate-100 rounded-lg" >
< X className = "w-5 h-5 text-slate-400" / >
< / button >
< / div >
< div className = "p-5 space-y-4" >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Brand < / label >
< input
type = "text"
value = { formData . brand }
onChange = { ( e ) = > handleChange ( 'brand' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Model < / label >
< input
type = "text"
value = { formData . model }
onChange = { ( e ) = > handleChange ( 'model' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Serial Number < / label >
< input
type = "text"
value = { formData . serialNumber }
onChange = { ( e ) = > handleChange ( 'serialNumber' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Type < / label >
< select
value = { formData . type }
onChange = { ( e ) = > handleChange ( 'type' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "lithium-ion" > Lithium - Ion < / option >
< option value = "lifepo4" > LiFePO4 < / option >
< option value = "lead-acid" > Lead Acid < / option >
< / select >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Capacity ( Ah ) < / label >
< input
type = "number"
value = { formData . capacity }
onChange = { ( e ) = > handleChange ( 'capacity' , parseInt ( e . target . value ) ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Voltage ( V ) < / label >
< input
type = "number"
value = { formData . voltage }
onChange = { ( e ) = > handleChange ( 'voltage' , parseInt ( e . target . value ) ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Purchase Date < / label >
< input
type = "date"
value = { formData . purchaseDate }
onChange = { ( e ) = > handleChange ( 'purchaseDate' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Purchase Price ( ৳ ) < / label >
< input
type = "number"
value = { formData . purchasePrice }
onChange = { ( e ) = > handleChange ( 'purchasePrice' , parseInt ( e . target . value ) ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Warranty Expiry < / label >
< input
type = "date"
value = { formData . warrantyExpiry }
onChange = { ( e ) = > handleChange ( 'warrantyExpiry' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Monthly Rent ( ৳ ) < / label >
< input
type = "number"
value = { formData . monthlyRent || 0 }
onChange = { ( e ) = > handleChange ( 'monthlyRent' , parseInt ( e . target . value ) ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Status < / label >
< select
value = { formData . status }
onChange = { ( e ) = > handleChange ( 'status' , e . target . value ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
< option value = "available" > Available < / option >
< option value = "in-use" > In Use < / option >
< option value = "charging" > Charging < / option >
< option value = "maintenance" > Maintenance < / option >
< option value = "retired" > Retired < / option >
< / select >
< / div >
< div >
< label className = "block text-sm font-medium text-slate-700 mb-1" > Current SOC ( % ) < / label >
< input
type = "number"
value = { formData . currentSoc }
onChange = { ( e ) = > handleChange ( 'currentSoc' , parseInt ( e . target . value ) ) }
className = "w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
/ >
< / div >
< / div >
< / div >
< div className = "p-5 border-t border-slate-100 flex gap-3 sticky bottom-0 bg-white" >
< button onClick = { onClose } className = "flex-1 py-2.5 px-4 border border-slate-200 rounded-lg font-semibold text-sm hover:bg-slate-50" >
Cancel
< / button >
< button
onClick = { ( ) = > onSave ( formData ) }
className = "flex-1 py-2.5 px-4 bg-accent text-white rounded-lg font-semibold text-sm hover:bg-accent-dark"
>
Save Changes
< / button >
< / div >
< / div >
2026-05-16 12:10:49 +06:00
< / div >
) ;
}