feat: implement automated double-entry accounting for investments and add transaction details modal
This commit is contained in:
@@ -7,8 +7,8 @@ import {
|
||||
User, Phone, Mail, MapPin, Calendar, Heart, Briefcase, Car, Navigation,
|
||||
FileText, Clock, TrendingUp, CreditCard, Shield, Award, Star, Activity,
|
||||
Eye, Edit, Trash2, ArrowLeft, PhoneCall, MessageCircle, CheckCircle, XCircle,
|
||||
AlertTriangle, DollarSign, Wallet, Bike as BikeIcon, Wrench, Ban, MoreHorizontal, Copy,
|
||||
ExternalLink, Download, Upload, Bell, MessageSquare, Send, RefreshCcw
|
||||
AlertTriangle, DollarSign as DollarSignIcon, Wallet, Bike as BikeIcon, Wrench, Ban, MoreHorizontal, Copy,
|
||||
ExternalLink, Download, Upload, Bell, MessageSquare, Send, RefreshCcw, Image
|
||||
} from 'lucide-react';
|
||||
|
||||
interface DrivingLicense {
|
||||
@@ -168,12 +168,31 @@ export default function BikerDetailPage({ params }: PageProps) {
|
||||
{ id: 'personal', label: 'Personal', icon: User },
|
||||
{ id: 'license', label: 'License & GPS', icon: Car },
|
||||
{ id: 'documents', label: 'Documents', icon: FileText },
|
||||
{ id: 'reviews', label: 'Reviews', icon: MessageCircle },
|
||||
{ id: 'stats', label: 'Statistics', icon: TrendingUp },
|
||||
{ id: 'rent', label: 'Rent History', icon: DollarSignIcon },
|
||||
{ id: 'bike', label: 'Bike', icon: BikeIcon },
|
||||
// { id: 'reviews', label: 'Reviews', icon: MessageCircle },
|
||||
// { id: 'stats', label: 'Statistics', icon: TrendingUp },
|
||||
{ id: 'account', label: 'Account', icon: CreditCard },
|
||||
{ id: 'activity', label: 'Activity', icon: Activity },
|
||||
];
|
||||
|
||||
const rentHistory = [
|
||||
{ id: 'R001', date: '2024-03-21', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R002', date: '2024-03-20', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R003', date: '2024-03-19', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R004', date: '2024-03-18', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R005', date: '2024-03-17', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R006', date: '2024-03-16', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R007', date: '2024-03-15', amount: 50, status: 'paid', bikeId: 'EV001', plan: 'Single' },
|
||||
{ id: 'R008', date: '2024-03-14', amount: 0, status: 'pending', bikeId: 'EV001', plan: 'Single' },
|
||||
];
|
||||
|
||||
const rentedBikesHistory = [
|
||||
{ id: 'RB001', bikeId: 'EV001', bikeName: 'AIMA Lightning', plate: 'Dhaka Metro Cha-9012', startDate: '2024-03-14', endDate: null, status: 'active', plan: 'Single', dailyRate: 50, totalDays: 8, totalRent: 400 },
|
||||
{ id: 'RB002', bikeId: 'EV003', bikeName: 'Yadea DT3', plate: 'Dhaka Metro Cha-5678', startDate: '2024-02-01', endDate: '2024-03-13', status: 'returned', plan: 'Rent-to-Own', dailyRate: 45, totalDays: 42, totalRent: 1890 },
|
||||
{ id: 'RB003', bikeId: 'EV005', bikeName: 'Etron ET50', plate: 'Dhaka Metro Cha-1234', startDate: '2024-01-10', endDate: '2024-01-31', status: 'returned', plan: 'Shared', dailyRate: 60, totalDays: 22, totalRent: 1320 },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-4 lg:p-6 min-h-screen">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
@@ -389,6 +408,249 @@ export default function BikerDetailPage({ params }: PageProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'rent' && (
|
||||
<div className="p-4 lg:p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<DollarSignIcon className="w-5 h-5 text-accent" /> Daily Rent History
|
||||
</h3>
|
||||
<div className="flex gap-2">
|
||||
<button className="px-3 py-1.5 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 flex items-center gap-1">
|
||||
<Download className="w-4 h-4" /> Export
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 mb-6">
|
||||
<div className="bg-green-50 border border-green-200 rounded-lg p-3">
|
||||
<p className="text-xs text-green-600 font-medium">Total Paid</p>
|
||||
<p className="text-lg font-bold text-green-700">৳{rentHistory.filter(r => r.status === 'paid').reduce((sum, r) => sum + r.amount, 0)}</p>
|
||||
</div>
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
|
||||
<p className="text-xs text-red-600 font-medium">Pending</p>
|
||||
<p className="text-lg font-bold text-red-700">৳{rentHistory.filter(r => r.status === 'pending').reduce((sum, r) => sum + r.amount, 0)}</p>
|
||||
</div>
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
|
||||
<p className="text-xs text-blue-600 font-medium">Total Days</p>
|
||||
<p className="text-lg font-bold text-blue-700">{rentHistory.length}</p>
|
||||
</div>
|
||||
<div className="bg-slate-50 border border-slate-200 rounded-lg p-3">
|
||||
<p className="text-xs text-slate-600 font-medium">Current Plan</p>
|
||||
<p className="text-lg font-bold text-slate-700">{biker.membershipType}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-slate-200 rounded-lg overflow-hidden">
|
||||
<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">Bike ID</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Plan</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Amount</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">
|
||||
{rentHistory.map(rent => (
|
||||
<tr key={rent.id} className="hover:bg-slate-50">
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rent.date}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rent.bikeId}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rent.plan}</td>
|
||||
<td className="px-4 py-3 text-sm font-semibold text-slate-700">৳{rent.amount}</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 ${rent.status === 'paid'
|
||||
? 'bg-green-100 text-green-700'
|
||||
: 'bg-amber-100 text-amber-700'
|
||||
}`}>
|
||||
{rent.status === 'paid' && <CheckCircle className="w-3 h-3" />}
|
||||
{rent.status === 'pending' && <Clock className="w-3 h-3" />}
|
||||
{rent.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
{rent.status === 'pending' ? (
|
||||
<button className="px-3 py-1.5 bg-accent text-white text-xs rounded-lg hover:bg-accent-dark">
|
||||
Collect
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-xs text-slate-400">-</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'bike' && (
|
||||
<div className="p-4 lg:p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<BikeIcon className="w-5 h-5 text-accent" /> Rented Bikes Details
|
||||
</h3>
|
||||
<div className="flex gap-2">
|
||||
<button className="px-3 py-1.5 bg-accent text-white text-sm rounded-lg hover:bg-accent-dark flex items-center gap-1">
|
||||
<Edit className="w-4 h-4" /> Update
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-lg bg-slate-100 flex items-center justify-center">
|
||||
<BikeIcon className="w-8 h-8 text-slate-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-700">{biker.currentBike || 'No Bike Assigned'}</p>
|
||||
<p className="text-sm text-slate-500">Plate: {biker.bikePlate || 'N/A'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4">
|
||||
<h4 className="font-medium text-slate-700 mb-4">Mileage</h4>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Current Odometer (km)</label>
|
||||
<input
|
||||
type="number"
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="Enter current reading"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Last Updated</label>
|
||||
<p className="text-sm text-slate-700">{biker.lastRideAt || 'N/A'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Total Distance</label>
|
||||
<p className="text-lg font-bold text-blue-600">{biker.totalDistance.toLocaleString()} km</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4">
|
||||
<h4 className="font-medium text-slate-700 mb-4">Battery Health</h4>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Battery Percentage</label>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="number"
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||
placeholder="0-100"
|
||||
/>
|
||||
<span className="text-slate-500">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Health Status</label>
|
||||
<span className="inline-flex items-center gap-1 text-sm font-medium px-2.5 py-1 rounded-full bg-green-100 text-green-700">
|
||||
<CheckCircle className="w-4 h-4" /> Good
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-500 block mb-1">Estimated Range</label>
|
||||
<p className="text-lg font-bold text-green-600">85 km</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4 mb-6">
|
||||
<h4 className="font-medium text-slate-700 mb-4">Bike Images</h4>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="aspect-square bg-slate-100 rounded-lg flex flex-col items-center justify-center">
|
||||
<Image className="w-8 h-8 text-slate-400 mb-2" />
|
||||
<span className="text-xs text-slate-500">Front</span>
|
||||
</div>
|
||||
<div className="aspect-square bg-slate-100 rounded-lg flex flex-col items-center justify-center">
|
||||
<Image className="w-8 h-8 text-slate-400 mb-2" />
|
||||
<span className="text-xs text-slate-500">Back</span>
|
||||
</div>
|
||||
<div className="aspect-square bg-slate-100 rounded-lg flex flex-col items-center justify-center">
|
||||
<Image className="w-8 h-8 text-slate-400 mb-2" />
|
||||
<span className="text-xs text-slate-500">Left Side</span>
|
||||
</div>
|
||||
<div className="aspect-square bg-slate-100 rounded-lg flex flex-col items-center justify-center">
|
||||
<Image className="w-8 h-8 text-slate-400 mb-2" />
|
||||
<span className="text-xs text-slate-500">Right Side</span>
|
||||
</div>
|
||||
</div>
|
||||
<button className="mt-4 px-4 py-2 border border-dashed border-slate-300 rounded-lg text-sm text-slate-500 hover:border-accent hover:text-accent w-full">
|
||||
+ Upload Images
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-slate-200 rounded-lg p-4">
|
||||
<h4 className="font-medium text-slate-700 mb-4">Notes</h4>
|
||||
<textarea
|
||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||
rows={4}
|
||||
placeholder="Add notes about the bike condition, issues, etc..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<h4 className="font-semibold text-slate-800 mb-4 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5 text-accent" /> Rented Bikes History
|
||||
</h4>
|
||||
<div className="bg-white border border-slate-200 rounded-lg overflow-hidden">
|
||||
<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">Bike</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Plate</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Plan</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Start Date</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">End Date</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Days</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Total Rent</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{rentedBikesHistory.map(rental => (
|
||||
<tr key={rental.id} className="hover:bg-slate-50">
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<BikeIcon className="w-4 h-4 text-slate-400" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-700">{rental.bikeName}</p>
|
||||
<p className="text-xs text-slate-400">{rental.bikeId}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rental.plate}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rental.plan}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rental.startDate}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rental.endDate || '-'}</td>
|
||||
<td className="px-4 py-3 text-sm text-slate-600">{rental.totalDays}</td>
|
||||
<td className="px-4 py-3 text-sm font-semibold text-green-600">৳{rental.totalRent}</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 ${rental.status === 'active'
|
||||
? 'bg-green-100 text-green-700'
|
||||
: 'bg-slate-100 text-slate-600'
|
||||
}`}>
|
||||
{rental.status === 'active' && <Activity className="w-3 h-3" />}
|
||||
{rental.status === 'returned' && <CheckCircle className="w-3 h-3" />}
|
||||
{rental.status === 'active' ? 'Active' : 'Returned'}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'stats' && (
|
||||
<div className="p-4 lg:p-6">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||
|
||||
Reference in New Issue
Block a user