Add full FOCO investor management system with CRUD, investments, and transactions

This commit is contained in:
sazzadulalambd
2026-04-22 01:02:45 +06:00
parent 5338038ea2
commit dab0c11b15
32 changed files with 7673 additions and 89 deletions

View File

@@ -0,0 +1,689 @@
'use client';
import { useState, use } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import {
Bike, MapPin, Battery, User, Wrench, Eye, Edit, Trash2, X, ArrowLeft, PhoneCall,
MessageCircle, Calendar, DollarSign, Clock, Navigation, Car, FileText, Shield, Zap,
GaugeCircle, CheckCircle, AlertTriangle, Activity, Award, TrendingUp, Wallet,
MoreHorizontal, Map, Navigation2, Satellite, FileCheck, FileX, Clock3,
History, CreditCard, User2, Phone, Mail, MapPinned
} from 'lucide-react';
interface GPSDevice {
id: string;
phone: string;
imei: string;
lastActive: string;
signal: number;
battery: number;
}
interface BikeDocument {
type: 'registration' | 'insurance' | 'fitness' | 'permit' | 'other';
number: string;
issueDate: string;
expiryDate: string;
verified: boolean;
}
interface RentalHistory {
id: string;
bikerId: string;
bikerName: string;
type: 'single' | 'shared' | 'rent-to-own';
status: 'active' | 'completed' | 'disputed';
startDate: string;
endDate?: string;
dailyRate: number;
totalPaid: number;
rideCount: number;
}
interface ActivityLog {
id: string;
action: string;
details: string;
date: string;
by: string;
}
interface Bike {
id: string;
model: string;
brand: string;
image: string;
plateNumber: string;
status: 'available' | 'rented' | 'maintenance' | 'retired';
batteryLevel: number;
location: string;
assignedTo?: string;
investorId?: string;
investorName?: string;
purchaseDate?: string;
purchasePrice?: number;
currentRent?: number;
totalRides?: number;
totalDistance?: number;
totalEarnings?: number;
lastService?: string;
nextService?: string;
insuranceExpiry?: string;
registrationExpiry?: string;
notes?: string;
gpsDevice?: GPSDevice;
documents?: BikeDocument[];
rentalHistory?: RentalHistory[];
activityLog?: ActivityLog[];
}
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',
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 },
{ type: 'insurance', number: 'INS-EV001-2024', issueDate: '2024-01-15', expiryDate: '2025-01-15', verified: true },
{ type: 'fitness', number: 'FIT-EV001-2024', issueDate: '2024-01-15', expiryDate: '2025-01-15', verified: true },
{ type: 'permit', number: 'PMT-EV001-2024', issueDate: '2024-01-15', expiryDate: '2026-01-15', verified: true },
],
rentalHistory: [
{ id: 'R001', bikerId: 'B001', bikerName: 'Rahim Ahmed', type: 'single', status: 'active', startDate: '2024-03-01', dailyRate: 350, totalPaid: 7350, rideCount: 21 },
{ id: 'R002', bikerId: 'B003', bikerName: 'Jamal Khan', type: 'rent-to-own', status: 'completed', startDate: '2024-02-01', endDate: '2024-02-28', dailyRate: 400, totalPaid: 11200, rideCount: 28 },
{ id: 'R003', bikerId: 'B005', bikerName: 'Mahir Islam', type: 'shared', status: 'completed', startDate: '2024-01-15', endDate: '2024-01-31', dailyRate: 60, totalPaid: 2700, rideCount: 45 },
],
activityLog: [
{ id: 'A001', action: 'Rental Started', details: 'Single rental by Rahim Ahmed', date: '2024-03-01 08:00', by: 'System' },
{ id: 'A002', action: 'Service', details: 'Regular maintenance completed', date: '2024-03-01', by: 'Admin' },
{ id: 'A003', action: 'GPS Update', details: 'New GPS device installed', date: '2024-02-15', by: 'Admin' },
{ id: 'A004', action: 'Rental Completed', details: 'Rent-to-own by Jamal Khan', date: '2024-02-28 23:59', by: 'System' },
{ id: 'A005', action: 'Insurance Renewed', details: 'Insurance renewed for 1 year', date: '2024-01-15', by: 'Admin' },
]
},
{
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',
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 },
{ type: 'insurance', number: 'INS-EV002-2024', issueDate: '2024-02-01', expiryDate: '2025-02-01', verified: true },
{ type: 'fitness', number: 'FIT-EV002-2024', issueDate: '2024-02-01', expiryDate: '2025-02-01', verified: true },
],
rentalHistory: [
{ id: 'R004', bikerId: 'B002', bikerName: 'Karim Singh', type: 'single', status: 'completed', startDate: '2024-02-15', endDate: '2024-02-28', dailyRate: 350, totalPaid: 4900, rideCount: 14 },
],
activityLog: [
{ id: 'A006', action: 'Service', details: 'Regular maintenance', date: '2024-03-15', by: 'Admin' },
{ id: 'A007', action: 'Added to Fleet', details: 'Bike registered in system', date: '2024-02-01', by: 'Admin' },
]
},
{
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',
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 },
{ type: 'insurance', number: 'INS-EV003-2024', issueDate: '2024-01-20', expiryDate: '2025-01-20', verified: true },
{ type: 'fitness', number: 'FIT-EV003-2024', issueDate: '2024-01-20', expiryDate: '2025-01-20', verified: true },
],
rentalHistory: [],
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',
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 },
{ type: 'insurance', number: 'INS-EV004-2023', issueDate: '2023-12-10', expiryDate: '2024-12-10', verified: true },
],
rentalHistory: [],
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',
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 },
{ type: 'insurance', number: 'INS-EV005-2024', issueDate: '2024-02-15', expiryDate: '2025-02-15', verified: true },
],
rentalHistory: [],
activityLog: []
},
];
function getBikeById(id: string): Bike | undefined {
return mockBikes.find(b => b.id === id);
}
const statusColors: Record<string, string> = {
available: 'bg-green-100 text-green-700',
rented: 'bg-blue-100 text-blue-700',
maintenance: 'bg-amber-100 text-amber-700',
retired: 'bg-slate-100 text-slate-500',
};
const docTypeLabels: Record<string, string> = {
registration: 'Registration Certificate',
insurance: 'Insurance',
fitness: 'Fitness Certificate',
permit: 'Road Permit',
other: 'Other',
};
export default function FleetDetailPage({ params }: { params: Promise<{ id: string }> }) {
const resolvedParams = use(params);
const router = useRouter();
const bikeData = getBikeById(resolvedParams.id);
const [activeTab, setActiveTab] = useState('overview');
if (!bikeData) {
return (
<div className="p-4 lg:p-6">
<div className="bg-white rounded-xl p-8 text-center">
<Bike className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<h2 className="text-lg font-bold text-slate-700">Bike Not Found</h2>
<p className="text-sm text-slate-500 mb-4">The bike ID "{resolvedParams.id}" was not found.</p>
<Link href="/admin/fleet" className="text-accent hover:underline">Back to Fleet</Link>
</div>
</div>
);
}
const bike = bikeData;
const tabs = [
{ id: 'overview', label: 'Overview', icon: Bike },
{ id: 'gps', label: 'GPS & Tracking', icon: Navigation2 },
{ id: 'documents', label: 'Documents', icon: FileText },
{ id: 'rental', label: 'Rental History', icon: History },
{ id: 'activity', label: 'Activity Log', icon: Clock3 },
{ id: 'investor', label: 'Investor Info', icon: User2 },
];
return (
<div className="p-4 lg:p-6 min-h-screen">
<div className="flex items-center gap-3 mb-4">
<button onClick={() => router.back()} className="p-2 hover:bg-slate-100 rounded-lg lg:hidden">
<ArrowLeft className="w-5 h-5 text-slate-600" />
</button>
<Link href="/admin/fleet" className="p-2 hover:bg-slate-100 rounded-lg hidden lg:block">
<ArrowLeft className="w-5 h-5 text-slate-600" />
</Link>
<div className="flex-1">
<h1 className="text-xl lg:text-2xl font-extrabold text-slate-800">Bike Details</h1>
<p className="text-sm text-slate-500">ID: {bike.id}</p>
</div>
</div>
<div className="flex flex-wrap gap-2 mb-4 overflow-x-auto pb-2">
{tabs.map(tab => (
<button
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'
}`}
>
<tab.icon className="w-4 h-4" />
{tab.label}
</button>
))}
</div>
{activeTab === 'overview' && <OverviewTab bike={bike} />}
{activeTab === 'gps' && <GPSTab bike={bike} />}
{activeTab === 'documents' && <DocumentsTab bike={bike} />}
{activeTab === 'rental' && <RentalTab bike={bike} />}
{activeTab === 'activity' && <ActivityTab bike={bike} />}
{activeTab === 'investor' && <InvestorTab bike={bike} />}
<div className="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-slate-200 lg:relative lg:bg-transparent lg:border-0 lg:p-0 z-50">
<div className="flex gap-2 max-w-2xl mx-auto">
<button className="flex-1 py-2 px-4 bg-accent text-white rounded-lg font-semibold text-sm flex items-center justify-center gap-2 hover:bg-accent-dark transition-colors">
<Edit className="w-4 h-4" />
<span>Edit Bike</span>
</button>
<button className="flex-1 py-2 px-4 border border-red-200 text-red-600 rounded-lg font-semibold text-sm flex items-center justify-center gap-2 hover:bg-red-50 transition-colors">
<Trash2 className="w-4 h-4" />
<span>Delete</span>
</button>
</div>
</div>
</div>
);
}
function OverviewTab({ bike }: { bike: Bike }) {
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<div className="flex items-start gap-4 mb-4">
<div className="w-16 h-16 rounded-xl bg-blue-50 flex items-center justify-center">
<Bike className="w-8 h-8 text-blue-600" />
</div>
<div className="flex-1">
<h2 className="text-xl font-bold text-slate-800">{bike.model}</h2>
<p className="text-sm text-slate-500">{bike.brand} {bike.id}</p>
<div className="flex flex-wrap gap-2 mt-2">
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${statusColors[bike.status]}`}>
{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}
</span>
</div>
</div>
</div>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Plate Number</p>
<p className="font-semibold text-slate-700">{bike.plateNumber}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Battery</p>
<p className={`font-semibold ${bike.batteryLevel > 50 ? 'text-green-600' : bike.batteryLevel > 20 ? 'text-amber-600' : 'text-red-600'}`}>{bike.batteryLevel}%</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Current Renter</p>
<p className="font-semibold text-slate-700">{bike.assignedTo || 'Available'}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Daily Rate</p>
<p className="font-semibold text-green-600">{bike.currentRent || 0}</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Performance Metrics</h3>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Total Rides</p>
<p className="text-xl font-bold text-slate-700">{bike.totalRides || 0}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Distance</p>
<p className="text-xl font-bold text-slate-700">{(bike.totalDistance || 0).toLocaleString()} km</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Total Earnings</p>
<p className="text-xl font-bold text-green-600">{bike.totalEarnings?.toLocaleString() || 0}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Purchase Price</p>
<p className="text-xl font-bold text-slate-700">{bike.purchasePrice?.toLocaleString() || 0}</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Maintenance</h3>
<div className="grid grid-cols-2 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Last Service</p>
<p className="font-medium text-slate-700">{bike.lastService || 'N/A'}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Next Service</p>
<p className="font-medium text-slate-700">{bike.nextService || 'N/A'}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Insurance Expiry</p>
<p className="font-medium text-slate-700">{bike.insuranceExpiry || 'N/A'}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Registration Expiry</p>
<p className="font-medium text-slate-700">{bike.registrationExpiry || 'N/A'}</p>
</div>
</div>
{bike.notes && (
<div className="mt-3 p-3 bg-amber-50 rounded-lg">
<p className="text-xs text-amber-600 font-medium">Note</p>
<p className="text-sm text-amber-800">{bike.notes}</p>
</div>
)}
</div>
</div>
);
}
function GPSTab({ bike }: { bike: Bike }) {
const gps = bike.gpsDevice;
if (!gps) {
return (
<div className="bg-white rounded-xl p-6 shadow-sm border border-slate-100 text-center">
<Satellite className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<h3 className="font-semibold text-slate-700">No GPS Device</h3>
<p className="text-sm text-slate-500">This bike doesn't have a GPS device installed.</p>
</div>
);
}
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<div className="flex items-center gap-3 mb-4">
<div className="w-12 h-12 rounded-xl bg-purple-50 flex items-center justify-center">
<Satellite className="w-6 h-6 text-purple-600" />
</div>
<div>
<h3 className="font-semibold text-slate-700">GPS Device</h3>
<p className="text-sm text-slate-500">ID: {gps.id}</p>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Phone Number</p>
<p className="font-semibold text-slate-700 flex items-center gap-2">
<Phone className="w-4 h-4 text-slate-400" /> {gps.phone}
</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">IMEI</p>
<p className="font-semibold text-slate-700 font-mono text-sm">{gps.imei}</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Status</h3>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Last Active</p>
<p className="font-medium text-slate-700">{gps.lastActive}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Signal Strength</p>
<p className={`font-semibold ${gps.signal > 70 ? 'text-green-600' : gps.signal > 40 ? 'text-amber-600' : 'text-red-600'}`}>
{gps.signal}%
</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">GPS Battery</p>
<p className={`font-semibold ${gps.battery > 50 ? 'text-green-600' : gps.battery > 20 ? 'text-amber-600' : 'text-red-600'}`}>
{gps.battery}%
</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Status</p>
<p className="font-semibold text-green-600 flex items-center gap-1">
<CheckCircle className="w-4 h-4" /> Active
</p>
</div>
</div>
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Live Location</h3>
<div className="bg-slate-100 rounded-lg h-48 flex items-center justify-center">
<div className="text-center">
<MapPinned className="w-8 h-8 text-slate-400 mx-auto mb-2" />
<p className="text-sm text-slate-500">{bike.location}</p>
</div>
</div>
</div>
</div>
);
}
function DocumentsTab({ bike }: { bike: Bike }) {
const docs = bike.documents || [];
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-4">Bike Documents</h3>
{docs.length === 0 ? (
<div className="text-center py-8">
<FileText className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<p className="text-sm text-slate-500">No documents uploaded.</p>
</div>
) : (
<div className="space-y-3">
{docs.map((doc, idx) => (
<div key={idx} className="flex items-center justify-between p-4 border border-slate-200 rounded-lg">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-blue-50 flex items-center justify-center">
<FileText className="w-5 h-5 text-blue-600" />
</div>
<div>
<p className="font-medium text-slate-700">{docTypeLabels[doc.type]}</p>
<p className="text-xs text-slate-500">Number: {doc.number}</p>
<p className="text-xs text-slate-400">Issued: {doc.issueDate} • Expires: {doc.expiryDate}</p>
</div>
</div>
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${
doc.verified ? 'bg-green-100 text-green-700' : 'bg-amber-100 text-amber-700'
}`}>
{doc.verified ? <CheckCircle className="w-3 h-3" /> : <Clock3 className="w-3 h-3" />}
{doc.verified ? 'Verified' : 'Pending'}
</span>
</div>
))}
</div>
)}
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Add New Document</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<select className="px-3 py-2 border border-slate-200 rounded-lg text-sm">
<option value="">Select Document Type</option>
<option value="registration">Registration Certificate</option>
<option value="insurance">Insurance</option>
<option value="fitness">Fitness Certificate</option>
<option value="permit">Road Permit</option>
<option value="other">Other</option>
</select>
<input type="text" placeholder="Document Number" className="px-3 py-2 border border-slate-200 rounded-lg text-sm" />
<input type="date" placeholder="Issue Date" className="px-3 py-2 border border-slate-200 rounded-lg text-sm" />
<input type="date" placeholder="Expiry Date" className="px-3 py-2 border border-slate-200 rounded-lg text-sm" />
</div>
<button className="w-full mt-3 py-2 bg-accent text-white rounded-lg font-semibold text-sm hover:bg-accent-dark">
Upload Document
</button>
</div>
</div>
);
}
function RentalTab({ bike }: { bike: Bike }) {
const history = bike.rentalHistory || [];
const getRateDisplay = (type: string, rate: number) => {
switch (type) {
case 'single': return `৳${rate}/day`;
case 'shared': return `৳${rate/2}+${rate/2} (2 person)`;
case 'rent-to-own': return `৳${rate}/day`;
default: return `৳${rate}`;
}
};
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Rental History</h3>
{history.length === 0 ? (
<div className="text-center py-8">
<History className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<p className="text-sm text-slate-500">No rental history yet.</p>
</div>
) : (
<div className="space-y-3">
{history.map(rental => (
<div key={rental.id} className="p-4 border border-slate-200 rounded-lg">
<div className="flex items-start justify-between mb-2">
<div>
<p className="font-medium text-slate-700">{rental.bikerName}</p>
<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}
</span>
</div>
<div className="flex flex-wrap gap-3 text-xs">
<span className="bg-slate-100 px-2 py-1 rounded text-slate-600">
{rental.type === 'single' ? 'Single (350/day)' :
rental.type === 'shared' ? 'Shared (60/day)' :
'Rent-to-Own (450/day)'}
</span>
<span className="text-slate-500">
{rental.startDate} {rental.endDate && `to ${rental.endDate}`}
</span>
</div>
<div className="flex justify-between mt-2 pt-2 border-t border-slate-100">
<span className="text-xs text-slate-500">{rental.rideCount} rides</span>
<span className="text-sm font-semibold text-green-600">৳{rental.totalPaid.toLocaleString()}</span>
</div>
</div>
))}
</div>
)}
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Rental Rates Info</h3>
<div className="space-y-2">
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center">
<span className="text-xs font-bold text-green-600">1</span>
</div>
<span className="font-medium text-slate-700">Single</span>
</div>
<span className="font-semibold text-green-600">৳350/day</span>
</div>
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center">
<span className="text-xs font-bold text-blue-600">2</span>
</div>
<span className="font-medium text-slate-700">Shared (2 Person)</span>
</div>
<span className="font-semibold text-green-600">৳60/day (৳30+৳30)</span>
</div>
<div className="flex items-center justify-between p-3 bg-purple-50 rounded-lg">
<div className="flex items-center gap-2">
<div className="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center">
<span className="text-xs font-bold text-purple-600">3</span>
</div>
<span className="font-medium text-slate-700">Rent-to-Own</span>
</div>
<span className="font-semibold text-green-600">৳450/day</span>
</div>
</div>
</div>
</div>
);
}
function ActivityTab({ bike }: { bike: Bike }) {
const logs = bike.activityLog || [];
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Activity Log</h3>
{logs.length === 0 ? (
<div className="text-center py-8">
<Clock3 className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<p className="text-sm text-slate-500">No activity yet.</p>
</div>
) : (
<div className="space-y-3">
{logs.map(log => (
<div key={log.id} className="flex items-start gap-3 p-3 bg-slate-50 rounded-lg">
<div className="w-8 h-8 rounded-full bg-white flex items-center justify-center">
<Activity className="w-4 h-4 text-slate-400" />
</div>
<div className="flex-1">
<p className="text-sm font-medium text-slate-700">{log.action}</p>
<p className="text-xs text-slate-500">{log.details}</p>
<p className="text-xs text-slate-400">{log.date} by {log.by}</p>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
function InvestorTab({ bike }: { bike: Bike }) {
return (
<div className="space-y-4">
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Investor Information</h3>
{!bike.investorId ? (
<div className="text-center py-8">
<User2 className="w-12 h-12 text-slate-300 mx-auto mb-4" />
<p className="text-sm text-slate-500">No investor assigned.</p>
</div>
) : (
<div className="space-y-4">
<div className="flex items-center gap-4 p-4 bg-purple-50 rounded-lg">
<div className="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center">
<User2 className="w-6 h-6 text-purple-600" />
</div>
<div>
<p className="font-semibold text-slate-700">{bike.investorName || 'Investor'}</p>
<p className="text-sm text-slate-500">ID: {bike.investorId}</p>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Purchase Date</p>
<p className="font-semibold text-slate-700">{bike.purchaseDate || '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-semibold text-green-600">{bike.purchasePrice?.toLocaleString() || 0}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">Total Earnings</p>
<p className="font-semibold text-green-600">{bike.totalEarnings?.toLocaleString() || 0}</p>
</div>
<div className="bg-slate-50 rounded-lg p-3">
<p className="text-xs text-slate-500">ROI</p>
<p className="font-semibold text-amber-600">
{bike.purchasePrice && bike.totalEarnings
? ((bike.totalEarnings / bike.purchasePrice) * 100).toFixed(1)
: 0}%
</p>
</div>
</div>
</div>
)}
</div>
<div className="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-slate-100">
<h3 className="font-semibold text-slate-700 mb-3">Payment Summary</h3>
<div className="space-y-2">
<div className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
<span className="text-sm text-slate-600">Total Paid by Renters</span>
<span className="font-semibold text-green-600">{bike.totalEarnings?.toLocaleString() || 0}</span>
</div>
<div className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
<span className="text-sm text-slate-600">Investor Share (80%)</span>
<span className="font-semibold text-purple-600">{Math.round((bike.totalEarnings || 0) * 0.8).toLocaleString()}</span>
</div>
<div className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
<span className="text-sm text-slate-600">Company Share (20%)</span>
<span className="font-semibold text-blue-600">{Math.round((bike.totalEarnings || 0) * 0.2).toLocaleString()}</span>
</div>
</div>
</div>
</div>
);
}