refactor: replace revenue and geofence pages with new hub management system

This commit is contained in:
sazzadulalambd
2026-04-26 16:02:53 +06:00
parent f724a00453
commit 03062bfc48
8 changed files with 884 additions and 291 deletions

View File

@@ -100,7 +100,9 @@ interface Bike {
plateNumber: string;
status: 'available' | 'rented' | 'maintenance' | 'retired';
batteryLevel: number;
location: string;
location?: string; // deprecated - use hubId/hubName
hubId?: string;
hubName?: string;
assignedTo?: string;
investorId?: string;
investorName?: string;
@@ -126,7 +128,7 @@ interface Bike {
const mockBikes: Bike[] = [
{
id: 'EV001', model: 'Etron ET50', brand: 'Etron', image: '', plateNumber: 'Dhaka Metro Cha-A-1234', status: 'rented', batteryLevel: 78, location: 'Gulshan 1', assignedTo: 'Rahim Ahmed', investorId: 'inv1', investorName: 'Mr. Hasan (Investor)', purchaseDate: '2024-01-15', purchasePrice: 125000, currentRent: 350, totalRides: 156, totalDistance: 2340, totalEarnings: 54600, lastService: '2024-03-01', nextService: '2024-04-01', insuranceExpiry: '2025-01-15', registrationExpiry: '2026-01-15',
id: 'EV001', model: 'Etron ET50', brand: 'Etron', image: '', plateNumber: 'Dhaka Metro Cha-A-1234', status: 'rented', batteryLevel: 78, location: 'Gulshan 1', assignedTo: 'Rahim Ahmed', hubId: 'HUB-001', hubName: 'JAIBEN Head Office', investorId: 'inv1', investorName: 'Mr. Hasan (Investor)', purchaseDate: '2024-01-15', purchasePrice: 125000, currentRent: 350, totalRides: 156, totalDistance: 2340, totalEarnings: 54600, lastService: '2024-03-01', nextService: '2024-04-01', insuranceExpiry: '2025-01-15', registrationExpiry: '2026-01-15',
gpsDevice: { id: 'GPS001', phone: '01712345601', imei: '861234567890123', lastActive: '2024-03-21 14:30', signal: 85, battery: 72 },
documents: [
{ type: 'registration', number: 'REG-EV001-2024', issueDate: '2024-01-15', expiryDate: '2026-01-15', verified: true },
@@ -157,7 +159,7 @@ const mockBikes: Bike[] = [
]
},
{
id: 'EV002', model: 'Yadea DT3', brand: 'Yadea', image: '', plateNumber: 'Dhaka Metro Cha-A-5678', status: 'available', batteryLevel: 95, location: 'Banani', 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',
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',
gpsDevice: { id: 'GPS002', phone: '01712345602', imei: '861234567890124', lastActive: '2024-03-21 15:00', signal: 92, battery: 88 },
documents: [
{ type: 'registration', number: 'REG-EV002-2024', issueDate: '2024-02-01', expiryDate: '2026-02-01', verified: true },
@@ -173,7 +175,7 @@ const mockBikes: Bike[] = [
]
},
{
id: 'EV003', model: 'AIMA Lightning', brand: 'AIMA', image: '', plateNumber: 'Dhaka Metro Cha-A-9012', status: 'rented', batteryLevel: 62, location: 'Uttara', assignedTo: 'Karim Singh', investorId: 'inv1', investorName: 'Mr. Hasan (Investor)', purchaseDate: '2024-01-20', purchasePrice: 132000, currentRent: 400, totalRides: 203, totalDistance: 3890, totalEarnings: 71100, lastService: '2024-03-10', nextService: '2024-04-10', insuranceExpiry: '2025-01-20', registrationExpiry: '2026-01-20',
id: 'EV003', model: 'AIMA Lightning', brand: 'AIMA', image: '', plateNumber: 'Dhaka Metro Cha-A-9012', status: 'rented', batteryLevel: 62, hubId: 'HUB-003', hubName: 'Uttara Hub', investorId: 'inv1', investorName: 'Mr. Hasan (Investor)', purchaseDate: '2024-01-20', purchasePrice: 132000, currentRent: 400, totalRides: 203, totalDistance: 3890, totalEarnings: 71100, lastService: '2024-03-10', nextService: '2024-04-10', insuranceExpiry: '2025-01-20', registrationExpiry: '2026-01-20',
gpsDevice: { id: 'GPS003', phone: '01712345603', imei: '861234567890125', lastActive: '2024-03-21 14:45', signal: 78, battery: 55 },
documents: [
{ type: 'registration', number: 'REG-EV003-2024', issueDate: '2024-01-20', expiryDate: '2026-01-20', verified: true },
@@ -184,7 +186,7 @@ const mockBikes: Bike[] = [
activityLog: []
},
{
id: 'EV004', model: 'TVS iQube', brand: 'TVS', image: '', plateNumber: 'Dhaka Metro Cha-A-3456', status: 'maintenance', batteryLevel: 45, location: 'Workshop - Banani', investorId: 'inv2', investorName: 'Mrs. Rita (Investor)', purchaseDate: '2023-12-10', purchasePrice: 145000, totalRides: 312, totalDistance: 5670, totalEarnings: 98000, lastService: '2024-03-20', nextService: '2024-03-25', insuranceExpiry: '2024-12-10', registrationExpiry: '2025-12-10', notes: 'Motor issue - awaiting parts',
id: 'EV004', model: 'TVS iQube', brand: 'TVS', image: '', plateNumber: 'Dhaka Metro Cha-A-3456', status: 'maintenance', batteryLevel: 45, hubId: 'HUB-002', hubName: 'Banani Hub', investorId: 'inv2', investorName: 'Mrs. Rita (Investor)', purchaseDate: '2023-12-10', purchasePrice: 145000, totalRides: 312, totalDistance: 5670, totalEarnings: 98000, lastService: '2024-03-20', nextService: '2024-03-25', insuranceExpiry: '2024-12-10', registrationExpiry: '2025-12-10', notes: 'Motor issue - awaiting parts',
gpsDevice: { id: 'GPS004', phone: '01712345604', imei: '861234567890126', lastActive: '2024-03-20 10:00', signal: 0, battery: 12 },
documents: [
{ type: 'registration', number: 'REG-EV004-2023', issueDate: '2023-12-10', expiryDate: '2025-12-10', verified: true },
@@ -194,7 +196,7 @@ const mockBikes: Bike[] = [
activityLog: []
},
{
id: 'EV005', model: 'Bajaj Chetak', brand: 'Bajaj', image: '', plateNumber: 'Dhaka Metro Cha-A-7890', status: 'available', batteryLevel: 100, location: 'Dhanmondi', investorId: 'inv2', investorName: 'Mrs. Rita (Investor)', purchaseDate: '2024-02-15', purchasePrice: 138000, totalRides: 67, totalDistance: 890, totalEarnings: 23450, lastService: '2024-03-18', nextService: '2024-04-18', insuranceExpiry: '2025-02-15', registrationExpiry: '2026-02-15',
id: 'EV005', model: 'Bajaj Chetak', brand: 'Bajaj', image: '', plateNumber: 'Dhaka Metro Cha-A-7890', status: 'available', batteryLevel: 100, hubId: 'HUB-001', hubName: 'JAIBEN Head Office', investorId: 'inv2', investorName: 'Mrs. Rita (Investor)', purchaseDate: '2024-02-15', purchasePrice: 138000, totalRides: 67, totalDistance: 890, totalEarnings: 23450, lastService: '2024-03-18', nextService: '2024-04-18', insuranceExpiry: '2025-02-15', registrationExpiry: '2026-02-15',
gpsDevice: { id: 'GPS005', phone: '01712345605', imei: '861234567890127', lastActive: '2024-03-21 15:30', signal: 95, battery: 92 },
documents: [
{ type: 'registration', number: 'REG-EV005-2024', issueDate: '2024-02-15', expiryDate: '2026-02-15', verified: true },
@@ -341,7 +343,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
const tabs = [
{ id: 'overview', label: 'Overview', icon: Bike },
{ id: 'biker-assignment', label: 'Assign Bikers', icon: User },
// { id: 'biker-assignment', label: 'Assign Bikers', icon: User },
{ id: 'gps', label: 'GPS & Tracking', icon: Navigation2 },
{ id: 'documents', label: 'Documents', icon: FileText },
{ id: 'rental', label: 'Rental History', icon: History },
@@ -372,8 +374,8 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`px-3 py-2 rounded-lg text-sm font-medium whitespace-nowrap flex items-center gap-2 ${activeTab === tab.id
? 'bg-accent text-white'
: 'bg-white text-slate-600 border border-slate-200'
? 'bg-accent text-white'
: 'bg-white text-slate-600 border border-slate-200'
}`}
>
<tab.icon className="w-4 h-4" />
@@ -397,7 +399,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<h3 className="font-semibold text-slate-700 flex items-center gap-2">
<AlertTriangle className="w-5 h-5 text-accent" /> Damage History
</h3>
<button
<button
onClick={() => { setEditingDamage(null); setShowDamageModal(true); }}
className="px-4 py-2 bg-accent text-white text-sm rounded-lg hover:bg-accent-dark flex items-center gap-2"
>
@@ -432,25 +434,24 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<td className="px-4 py-3 text-sm text-slate-600">{damage.estimatedCost || 0}</td>
<td className="px-4 py-3 text-sm font-medium text-slate-700">{damage.actualCost || '-'}</td>
<td className="px-4 py-3">
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${
damage.status === 'repaired' ? 'bg-green-100 text-green-700' :
damage.status === 'under_repair' ? 'bg-amber-100 text-amber-700' :
damage.status === 'claim_rejected' ? 'bg-red-100 text-red-700' :
'bg-slate-100 text-slate-700'
}`}>
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${damage.status === 'repaired' ? 'bg-green-100 text-green-700' :
damage.status === 'under_repair' ? 'bg-amber-100 text-amber-700' :
damage.status === 'claim_rejected' ? 'bg-red-100 text-red-700' :
'bg-slate-100 text-slate-700'
}`}>
{damage.status.replace('_', ' ')}
</span>
</td>
<td className="px-4 py-3">
<div className="flex items-center gap-1">
<button
<button
onClick={() => { setEditingDamage(damage); setShowDamageModal(true); }}
className="p-2 hover:bg-slate-100 rounded-lg"
title="Edit"
>
<Edit className="w-4 h-4 text-slate-400" />
</button>
<button
<button
onClick={() => handleDeleteDamage(damage.id)}
className="p-2 hover:bg-red-50 rounded-lg"
title="Delete"
@@ -481,7 +482,7 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<h3 className="font-semibold text-slate-700 flex items-center gap-2">
<Wrench className="w-5 h-5 text-accent" /> Maintenance History
</h3>
<button
<button
onClick={() => { setEditingMaintenance(null); setShowMaintenanceModal(true); }}
className="px-4 py-2 bg-accent text-white text-sm rounded-lg hover:bg-accent-dark flex items-center gap-2"
>
@@ -516,24 +517,23 @@ export default function FleetDetailPage({ params }: { params: Promise<{ id: stri
<td className="px-4 py-3 text-sm font-medium text-slate-700">{maintenance.cost}</td>
<td className="px-4 py-3 text-sm text-slate-600">{maintenance.nextDueDate || '-'}</td>
<td className="px-4 py-3">
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${
maintenance.status === 'completed' ? 'bg-green-100 text-green-700' :
maintenance.status === 'in_progress' ? 'bg-amber-100 text-amber-700' :
'bg-slate-100 text-slate-700'
}`}>
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${maintenance.status === 'completed' ? 'bg-green-100 text-green-700' :
maintenance.status === 'in_progress' ? 'bg-amber-100 text-amber-700' :
'bg-slate-100 text-slate-700'
}`}>
{maintenance.status.replace('_', ' ')}
</span>
</td>
<td className="px-4 py-3">
<div className="flex items-center gap-1">
<button
<button
onClick={() => { setEditingMaintenance(maintenance); setShowMaintenanceModal(true); }}
className="p-2 hover:bg-slate-100 rounded-lg"
title="Edit"
>
<Edit className="w-4 h-4 text-slate-400" />
</button>
<button
<button
onClick={() => handleDeleteMaintenance(maintenance.id)}
className="p-2 hover:bg-red-50 rounded-lg"
title="Delete"
@@ -606,7 +606,7 @@ function OverviewTab({ bike }: { bike: Bike }) {
{bike.status}
</span>
<span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full bg-slate-100 text-slate-600">
<MapPin className="w-3 h-3" /> {bike.location}
<MapPin className="w-3 h-3" /> {bike.hubName || 'Not Assigned'}
</span>
</div>
</div>
@@ -857,8 +857,8 @@ function RentalTab({ bike }: { bike: Bike }) {
<p className="text-xs text-slate-500">ID: {rental.id}</p>
</div>
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${rental.status === 'active' ? 'bg-green-100 text-green-700' :
rental.status === 'completed' ? 'bg-blue-100 text-blue-700' :
'bg-red-100 text-red-700'
rental.status === 'completed' ? 'bg-blue-100 text-blue-700' :
'bg-red-100 text-red-700'
}`}>
{rental.status}
</span>
@@ -1098,8 +1098,8 @@ function BikerAssignmentTab({ bike }: { bike: Bike }) {
key={key}
onClick={() => { setRentalPlan(key as any); setAssignedBikers([]); }}
className={`p-4 rounded-lg border text-left transition-all ${rentalPlan === key
? 'border-accent bg-accent/5'
: 'border-slate-200 hover:border-accent/50'
? 'border-accent bg-accent/5'
: 'border-slate-200 hover:border-accent/50'
}`}
>
<p className="font-semibold text-slate-700">{plan.name}</p>
@@ -1204,7 +1204,7 @@ function BikerAssignmentTab({ bike }: { bike: Bike }) {
<div className="p-5 border-t border-slate-100 flex justify-end gap-3">
<button onClick={() => setShowAssignModal(false)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">Cancel</button>
<button
<button
onClick={handleSubmitAssignment}
disabled={assignedBikers.length === 0}
className="px-4 py-2 bg-accent text-white rounded-lg text-sm hover:bg-accent-dark disabled:opacity-50"
@@ -1239,7 +1239,7 @@ function InvestorTab({ bike }: { bike: Bike }) {
<p className="font-semibold text-slate-700">{bike.investorName || 'Investor'}</p>
<p className="text-sm text-slate-500">ID: {bike.investorId}</p>
</div>
<Link
<Link
href={`/admin/investors/${bike.investorId}`}
className="px-4 py-2 bg-purple-600 text-white text-sm rounded-lg hover:bg-purple-700 flex items-center gap-2"
>