refactor: replace revenue and geofence pages with new hub management system
This commit is contained in:
@@ -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"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user