feat: implement automated double-entry accounting for investments and add transaction details modal

This commit is contained in:
sazzadulalambd
2026-04-26 14:56:12 +06:00
parent ae94ce0427
commit 7457b997ef
16 changed files with 8809 additions and 201 deletions

View File

@@ -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">