From cc8663a05c20a7179ef086bd39ac7d684c2f90ea Mon Sep 17 00:00:00 2001 From: sazzadulalambd Date: Wed, 13 May 2026 02:06:28 +0600 Subject: [PATCH] feat: extend Biker interface with battery details and refactor plan selection UI --- src/app/admin/bikers/[id]/page.tsx | 3313 +++++++++++++---- src/app/admin/bikers/page.tsx | 31 +- .../settings/components/PlanSelection.tsx | 690 ++-- src/app/admin/settings/page.tsx | 1 - 4 files changed, 3106 insertions(+), 929 deletions(-) diff --git a/src/app/admin/bikers/[id]/page.tsx b/src/app/admin/bikers/[id]/page.tsx index 5ac2901..e37154b 100644 --- a/src/app/admin/bikers/[id]/page.tsx +++ b/src/app/admin/bikers/[id]/page.tsx @@ -1,136 +1,637 @@ 'use client'; -import { useState, use } from 'react'; +import { useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; 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 as DollarSignIcon, Wallet, Bike as BikeIcon, Wrench, Ban, MoreHorizontal, Copy, - ExternalLink, Download, Upload, Bell, MessageSquare, Send, RefreshCcw, Image + Eye, ArrowLeft, PhoneCall, MessageCircle, CheckCircle, XCircle, + AlertTriangle, DollarSign, Wallet, Bike as BikeIcon, Wrench, Ban, Copy, + ExternalLink, Download, Upload, Bell, MessageSquare, Send, RefreshCcw, Image, + Plus, X, Check, AlertCircle, Map, Camera, FileCheck, FileX, Users, + Bike, AlertOctagon, ShieldAlert, History, Banknote, Building2, + ChevronDown, ChevronRight, MapPinHouse, Home, Hash, Globe, PhoneIncoming, + Tag, Save, Trash2, EyeOff, LayoutList, Grid3X3, MoreHorizontal, + Package, Fuel, Gauge, Timer, Coins, Receipt, ArrowRightLeft, + Truck, Battery, Zap, Settings2, Siren, Printer, Clock3, UserCheck, + Globe2, Building, HeartPulse, UsersRound, MessageSquareDashed, BellRing, + ListTodo, ClipboardList, BugOff, Info, SendHorizontal, PhoneOutgoing, Edit } from 'lucide-react'; +import toast from 'react-hot-toast'; -interface DrivingLicense { - number: string; - issueDate: string; - expiryDate: string; - class: string; - status: 'valid' | 'expired' | 'suspended'; +interface KYCData { + fullName: string; + phone: string; + alternatePhone?: string; + email: string; + nid: string; + dateOfBirth: string; + gender: string; + bloodGroup: string; + maritalStatus: string; + religion: string; + nationality: string; + presentAddress: { + houseFlat: string; + floor: string; + road: string; + block: string; + area: string; + thana: string; + district: string; + division: string; + postalCode: string; + landmark?: string; + }; + permanentAddress: { + houseFlat: string; + floor: string; + road: string; + block: string; + area: string; + thana: string; + district: string; + division: string; + postalCode: string; + }; + isPermanentSame: boolean; + drivingLicense: { + number: string; + licenseType: string; + expiryDate: string; + issueDate: string; + issuingAuthority: string; + frontImage?: string; + backImage?: string; + }; + employment: { + status: string; + companyName: string; + department: string; + designation: string; + monthlyIncome: string; + yearsOfExperience: string; + businessAddress: string; + }; + nominee: { + name: string; + relationship: string; + nid: string; + phone: string; + email: string; + address: string; + dateOfBirth: string; + bloodGroup: string; + sharePercentage: string; + photo?: string; + nidFront?: string; + nidBack?: string; + }; + emergencyContact: { + name: string; + relationship: string; + phone: string; + email: string; + address: string; + }; + nidFrontImage?: string; + nidBackImage?: string; + kycTimeline: Array<{ + stage: string; + status: string; + timestamp: string; + adminName: string; + notes?: string; + }>; } -interface Document { - type: 'nid' | 'passport' | 'driving_license' | 'other'; - number: string; - verified: boolean; +interface Rental { + id: string; + planName: string; + subscriptionType: string; + startDate: string; + endDate?: string; + dailyRate: number; + weeklyRate?: number; + monthlyRate?: number; + depositAmount: number; + depositStatus: string; + dueAmount: number; + pendingDays: number; + rentPaymentStatus: string; + evModel: string; + batteryPercent: number; + currentLocation: string; + odometerReading: number; + contractEndDate: string; + initialConditionImages: string[]; + status: string; + totalAmount: number; + penalty: number; + plate: string; +} + +interface Transaction { + id: string; + date: string; + type: string; + amount: number; + method: string; + status: string; + description: string; +} + +interface ActivityLog { + id: string; + type: string; + action: string; + timestamp: string; + adminName?: string; + details?: string; + icon: any; +} + +interface DamageReport { + id: string; + date: string; + rentalId: string; + bike: string; + damageType: string; + severity: string; + estimatedCost: number; + actualCost?: number; + status: string; + resolution?: string; + description: string; + location: string; + images: string[]; +} + +interface MessageHistory { + id: string; + direction: 'sent' | 'received'; + channel: 'sms' | 'push' | 'in_app'; + message: string; + timestamp: string; + status: string; +} + +interface Note { + id: string; + text: string; + createdAt: string; + createdBy: string; } interface Biker { id: string; - name: string; - email: string; - phone: string; - alternatePhone?: string; - status: 'active' | 'pending' | 'inactive' | 'blocked'; - createdAt: string; - location: string; - address: string; - dateOfBirth: string; - gender: 'male' | 'female' | 'other'; - bloodGroup: string; - occupation: string; - emergencyContact: string; - emergencyPhone: string; - emergencyRelation: string; - drivingLicense: DrivingLicense; - documents: Document[]; - gpsDeviceId?: string; - gpsPhone?: string; - totalRides: number; - totalDistance: number; - totalRideHours: number; - totalSpent: number; - currentBike?: string; - bikePlate?: string; - depositPaid: number; + kyc: KYCData; + gpsDeviceId: string; + gpsStatus: string; + lastLocation: string; + location?: string; + currentRental: Rental; + previousRentals: Rental[]; + bikeSpecs: { + model: string; + brand: string; + year: string; + color: string; + vin: string; + engineNumber: string; + plateNumber: string; + batteryCapacity: string; + maxRange: string; + chargingTime: string; + lastServiceDate: string; + nextServiceDue: string; + totalKmRun: number; + }; + bikes: { + current: { + id: string; + model: string; + brand: string; + plate: string; + vin: string; + color: string; + year: string; + batteryCapacity: string; + maxRange: string; + totalKmRun: number; + lastService: string; + nextService: string; + }; + batteries: Array<{ + id: string; + name: string; + percent: number; + status: 'active' | 'available' | 'charging' | 'swapped'; + swappedAt: string; + location: string; + odometer: number; + }>; + }; + transactions: Transaction[]; + activityLog: ActivityLog[]; + damageReports: DamageReport[]; + messageHistory: MessageHistory[]; + notes: Note[]; walletBalance: number; + totalPaid: number; + totalDue: number; + pendingAmount: number; + totalPenaltiesPaid: number; + bankAccount: { + bankName: string; + accountNumber: string; + routingNumber: string; + }; + billingAddress: { + houseFlat: string; + floor: string; + road: string; + block: string; + area: string; + thana: string; + district: string; + division: string; + postalCode: string; + }; + status: string; + kycStatus: string; + membershipType: string; rating: number; totalRatings: number; - responseTime: number; - cancellationRate: number; - kycStatus: 'verified' | 'pending' | 'rejected'; - membershipType: 'free' | 'basic' | 'premium' | 'vip'; - insuranceStatus: 'active' | 'expired' | 'none'; - insuranceExpiry?: string; - notes?: string; - lastRideAt?: string; - firstRideAt?: string; - joinedFrom: string; + role: string; + totalRentals: number; + activeRentals: number; + completedRentals: number; + cancelledRentals: number; } -const mockBikers: Biker[] = [ - { - id: 'B001', name: 'Rahim Ahmed', email: 'rahim@email.com', phone: '01712345678', alternatePhone: '01912345678', - status: 'active', createdAt: '2024-01-15', location: 'Gulshan, Dhaka', - address: 'House 12, Road 5, Gulshan 1, Dhaka 1212', dateOfBirth: '1995-03-15', gender: 'male', bloodGroup: 'O+', - occupation: 'Student', emergencyContact: 'Karim Ahmed', emergencyPhone: '01712345679', emergencyRelation: 'Brother', - drivingLicense: { number: 'DL2024001234', issueDate: '2023-01-15', expiryDate: '2033-01-14', class: 'M', status: 'valid' }, - documents: [{ type: 'nid', number: '1234567890', verified: true }], - gpsDeviceId: 'GP-001234', gpsPhone: '01712345678', - totalRides: 156, totalDistance: 2340, totalRideHours: 468, totalSpent: 54500, - currentBike: 'Etron ET50', bikePlate: 'Dhaka Metro Cha-1234', depositPaid: 5000, walletBalance: 1250, - rating: 4.8, totalRatings: 156, responseTime: 2.5, cancellationRate: 2.1, - kycStatus: 'verified', membershipType: 'premium', insuranceStatus: 'active', insuranceExpiry: '2025-01-14', - notes: 'Reliable rider, frequently uses premium service', lastRideAt: '2024-03-21', firstRideAt: '2024-01-15', joinedFrom: 'App Store', +const mockBiker: Biker = { + id: 'B001', + status: 'active', + role: 'rider', + totalRentals: 45, + activeRentals: 1, + completedRentals: 42, + cancelledRentals: 2, + location: 'Gulshan 1, Dhaka', + kycStatus: 'verified', + membershipType: 'Premium', + rating: 4.8, + totalRatings: 156, + kyc: { + fullName: 'Mohammad Rafiqul Islam', + phone: '01712345678', + alternatePhone: '01987654321', + email: 'rafiqul.islam@gmail.com', + nid: '19951234567890123', + dateOfBirth: '1995-06-15', + gender: 'Male', + bloodGroup: 'O+', + maritalStatus: 'Married', + religion: 'Islam', + nationality: 'Bangladeshi', + presentAddress: { + houseFlat: 'House 42', + floor: '3rd Floor', + road: 'Road 11', + block: 'Block D', + area: 'Gulshan 1', + thana: 'Gulshan', + district: 'Dhaka', + division: 'Dhaka', + postalCode: '1212', + landmark: 'Near BIBM', + }, + permanentAddress: { + houseFlat: 'House 42', + floor: '3rd Floor', + road: 'Road 11', + block: 'Block D', + area: 'Gulshan 1', + thana: 'Gulshan', + district: 'Dhaka', + division: 'Dhaka', + postalCode: '1212', + }, + isPermanentSame: true, + drivingLicense: { + number: 'DL-2020-DH-1234567', + licenseType: 'Motorcycle', + expiryDate: '2030-06-14', + issueDate: '2020-06-15', + issuingAuthority: 'Bangladesh Road Transport Authority (BRTA), Dhaka', + frontImage: 'https://picsum.photos/400/250?random=dl1', + backImage: 'https://picsum.photos/400/250?random=dl2', + }, + employment: { + status: 'Self Employed', + companyName: 'FoodPanda Bangladesh', + department: 'Delivery Operations', + designation: 'Delivery Rider', + monthlyIncome: '৳35,000', + yearsOfExperience: '4 years', + businessAddress: 'House 12, Road 5, Gulshan 1, Dhaka', + }, + nominee: { + name: 'Fatema Begum', + relationship: 'Wife', + nid: '19952345678901234', + phone: '01812345678', + email: 'fatema.begum@yahoo.com', + address: 'House 42, Road 11, Gulshan 1, Dhaka', + dateOfBirth: '1998-03-20', + bloodGroup: 'A+', + sharePercentage: '100', + photo: 'https://picsum.photos/200/200?random=nominee', + nidFront: 'https://picsum.photos/400/250?random=nf1', + nidBack: 'https://picsum.photos/400/250?random=nf2', + }, + emergencyContact: { + name: 'Abdul Karim', + relationship: 'Brother', + phone: '01798765432', + email: 'abdul.karim@outlook.com', + address: 'House 15, Road 8, Banani, Dhaka', + }, + nidFrontImage: 'https://picsum.photos/400/250?random=nid1', + nidBackImage: 'https://picsum.photos/400/250?random=nid2', + kycTimeline: [ + { stage: 'Application Submitted', status: 'completed', timestamp: '2024-01-10 09:30 AM', adminName: 'System' }, + { stage: 'ID Verification', status: 'completed', timestamp: '2024-01-11 10:15 AM', adminName: 'Asaduzzaman', notes: 'NID verified successfully' }, + { stage: 'Address Verification', status: 'completed', timestamp: '2024-01-12 02:30 PM', adminName: 'Rahima Begum', notes: 'Address confirmed via phone call' }, + { stage: 'Interview Scheduled', status: 'completed', timestamp: '2024-01-13 09:00 AM', adminName: 'System' }, + { stage: 'Interview Completed', status: 'completed', timestamp: '2024-01-15 11:30 AM', adminName: 'Karim Ahmed', notes: 'All documents verified, approved for onboarding' }, + { stage: 'KYC Approved', status: 'completed', timestamp: '2024-01-16 03:45 PM', adminName: 'Supervisor Rahman' }, + ], }, - { - id: 'B002', name: 'Karim Hasan', email: 'karim@email.com', phone: '01712345679', - status: 'active', createdAt: '2024-02-20', location: 'Banani, Dhaka', - address: 'House 5, Road 11, Banani, Dhaka 1213', dateOfBirth: '1990-07-22', gender: 'male', bloodGroup: 'B+', - occupation: 'Business', emergencyContact: 'Rahim Hasan', emergencyPhone: '01712345678', emergencyRelation: 'Friend', - drivingLicense: { number: 'DL2024005678', issueDate: '2023-06-20', expiryDate: '2033-06-19', class: 'M', status: 'valid' }, - documents: [{ type: 'nid', number: '1234567891', verified: true }], - gpsDeviceId: 'GP-001235', - totalRides: 89, totalDistance: 1335, totalRideHours: 267, totalSpent: 31200, - currentBike: 'Yadea DT3', bikePlate: 'Dhaka Metro Cha-5678', depositPaid: 5000, walletBalance: 800, - rating: 4.5, totalRatings: 89, responseTime: 3.2, cancellationRate: 4.5, - kycStatus: 'verified', membershipType: 'basic', insuranceStatus: 'active', insuranceExpiry: '2025-02-19', joinedFrom: 'Website', + gpsDeviceId: 'GPS-DH-2024-001234', + gpsStatus: 'Online', + lastLocation: '23.8103, 90.4125', + currentRental: { + id: 'RNT-2024-0045', + planName: 'Monthly Rental', + subscriptionType: 'Premium', + startDate: '2024-03-01', + contractEndDate: '2024-04-30', + dailyRate: 0, + monthlyRate: 4500, + depositAmount: 5000, + depositStatus: 'Paid', + dueAmount: 0, + pendingDays: 0, + rentPaymentStatus: 'Paid', + evModel: 'AIMA Lightning EV', + batteryPercent: 78, + currentLocation: '23.7521, 90.3756', + odometerReading: 2456, + initialConditionImages: [ + 'https://picsum.photos/400/300?random=cond1', + 'https://picsum.photos/400/300?random=cond2', + 'https://picsum.photos/400/300?random=cond3', + 'https://picsum.photos/400/300?random=cond4', + ], + status: 'Active', + totalAmount: 4500, + penalty: 0, + plate: 'Dhaka Metro Cha-4521', }, - { - id: 'B003', name: 'Jamal Mahmud', email: 'jamal@email.com', phone: '01712345680', - status: 'pending', createdAt: '2024-03-18', location: 'Uttara, Dhaka', - address: 'Sector 10, Uttara, Dhaka 1230', dateOfBirth: '1988-11-05', gender: 'male', bloodGroup: 'A+', - occupation: 'Job Holder', emergencyContact: 'Mahmud Ali', emergencyPhone: '01712345681', emergencyRelation: 'Father', - drivingLicense: { number: '', issueDate: '', expiryDate: '', class: 'M', status: 'valid' }, - documents: [{ type: 'nid', number: '1234567892', verified: false }], - totalRides: 0, totalDistance: 0, totalRideHours: 0, totalSpent: 0, - depositPaid: 0, walletBalance: 0, rating: 0, totalRatings: 0, responseTime: 0, cancellationRate: 0, - kycStatus: 'pending', membershipType: 'free', insuranceStatus: 'none', joinedFrom: 'Referral', + previousRentals: [ + { + id: 'RNT-2024-0020', + planName: 'Weekly Rental', + subscriptionType: 'Basic', + startDate: '2024-02-01', + contractEndDate: '2024-02-07', + endDate: '2024-02-28', + dailyRate: 0, + weeklyRate: 1200, + depositAmount: 3000, + depositStatus: 'Returned', + dueAmount: 0, + pendingDays: 0, + rentPaymentStatus: 'Paid', + evModel: 'Yadea DT3', + batteryPercent: 0, + currentLocation: '', + odometerReading: 1850, + initialConditionImages: [], + status: 'Completed', + totalAmount: 4800, + penalty: 0, + plate: 'Dhaka Metro Cha-3890', + }, + { + id: 'RNT-2024-0010', + planName: 'Monthly Rental', + subscriptionType: 'Standard', + startDate: '2024-01-01', + contractEndDate: '2024-01-31', + endDate: '2024-01-31', + dailyRate: 0, + monthlyRate: 3500, + depositAmount: 3000, + depositStatus: 'Returned', + dueAmount: 500, + pendingDays: 0, + rentPaymentStatus: 'Paid with Penalty', + evModel: 'Etron ET50', + batteryPercent: 0, + currentLocation: '', + odometerReading: 1200, + initialConditionImages: [], + status: 'Completed', + totalAmount: 4000, + penalty: 500, + plate: 'Dhaka Metro Cha-2156', + }, + { + id: 'RNT-2023-0045', + planName: 'Weekly Rental', + subscriptionType: 'Basic', + startDate: '2023-12-15', + contractEndDate: '2023-12-21', + endDate: '2023-12-22', + dailyRate: 0, + weeklyRate: 1200, + depositAmount: 3000, + depositStatus: 'Returned', + dueAmount: 0, + pendingDays: 0, + rentPaymentStatus: 'Cancelled', + evModel: 'AIMA Lightning EV', + batteryPercent: 0, + currentLocation: '', + odometerReading: 180, + initialConditionImages: [], + status: 'Cancelled', + totalAmount: 1200, + penalty: 0, + plate: 'Dhaka Metro Cha-4521', + }, + ], + bikeSpecs: { + model: 'AIMA Lightning EV', + brand: 'AIMA', + year: '2024', + color: 'Matte Black', + vin: 'VIN1234567890ABCD', + engineNumber: 'ENG-2024-AIMA-5678', + plateNumber: 'Dhaka Metro Cha-4521', + batteryCapacity: '72V 40Ah', + maxRange: '120 km', + chargingTime: '4-6 hours', + lastServiceDate: '2024-03-15', + nextServiceDue: '2024-04-15', + totalKmRun: 2456, }, - { - id: 'B004', name: 'Ali Rahman', email: 'ali@email.com', phone: '01712345681', - status: 'active', createdAt: '2023-12-01', location: 'Dhanmondi, Dhaka', - address: 'House 27, Road 8, Dhanmondi, Dhaka 1205', dateOfBirth: '1992-06-10', gender: 'male', bloodGroup: 'AB+', - occupation: 'Engineer', emergencyContact: 'Rahman Ali', emergencyPhone: '01712345682', emergencyRelation: 'Brother', - drivingLicense: { number: 'DL202301234', issueDate: '2023-05-10', expiryDate: '2033-05-09', class: 'M', status: 'valid' }, - documents: [{ type: 'nid', number: '1234567893', verified: true }], - gpsDeviceId: 'GP-001236', - totalRides: 234, totalDistance: 3510, totalRideHours: 702, totalSpent: 81900, - currentBike: 'AIMA Lightning', bikePlate: 'Dhaka Metro Cha-9012', depositPaid: 5000, walletBalance: 2100, - rating: 4.9, totalRatings: 234, responseTime: 1.8, cancellationRate: 1.2, - kycStatus: 'verified', membershipType: 'vip', insuranceStatus: 'active', insuranceExpiry: '2024-12-01', joinedFrom: 'Facebook', + bikes: { + current: { + id: 'BIKE-001', + model: 'AIMA Lightning EV', + brand: 'AIMA', + plate: 'Dhaka Metro Cha-4521', + vin: 'VIN123456789ABCDE', + color: 'Black', + year: '2023', + batteryCapacity: '72V 45Ah', + maxRange: '120 km', + totalKmRun: 2456, + lastService: '2024-03-15', + nextService: '2024-06-15', + }, + batteries: [ + { id: 'BAT-DH-001', name: 'Battery A', percent: 78, status: 'active', swappedAt: '2024-04-15 10:30', location: 'Swap Station - Gulshan', odometer: 2456 }, + { id: 'BAT-DH-002', name: 'Battery B', percent: 92, status: 'available', swappedAt: '2024-04-10 08:00', location: 'Swap Station - Banani', odometer: 1890 }, + { id: 'BAT-DH-003', name: 'Battery C', percent: 15, status: 'charging', swappedAt: '2024-04-05 14:00', location: 'Swap Station - Gulshan', odometer: 1200 }, + ], }, - { - id: 'B005', name: 'Mostafa Kamal', email: 'mostafa@email.com', phone: '01712345682', - status: 'inactive', createdAt: '2023-08-15', location: 'Mirpur, Dhaka', - address: 'Mirpur 1, Dhaka 1216', dateOfBirth: '1997-02-28', gender: 'male', bloodGroup: 'O-', - occupation: 'Teacher', emergencyContact: 'Kamal Mostafa', emergencyPhone: '01712345683', emergencyRelation: 'Father', - drivingLicense: { number: 'DL2022009876', issueDate: '2022-08-15', expiryDate: '2024-08-14', class: 'M', status: 'expired' }, - documents: [{ type: 'nid', number: '1234567894', verified: true }], - totalRides: 45, totalDistance: 675, totalRideHours: 135, totalSpent: 15750, - depositPaid: 5000, walletBalance: 0, rating: 3.8, totalRatings: 45, responseTime: 4.5, cancellationRate: 8.9, - kycStatus: 'verified', membershipType: 'free', insuranceStatus: 'expired', insuranceExpiry: '2024-01-14', joinedFrom: 'App Store', + transactions: [ + { id: 'TXN-001', date: '2024-03-28', type: 'Rent Payment', amount: 4500, method: 'bKash', status: 'Completed', description: 'March 2024 monthly rent' }, + { id: 'TXN-002', date: '2024-03-01', type: 'Deposit', amount: 5000, method: 'bKash', status: 'Completed', description: 'Security deposit for new rental' }, + { id: 'TXN-003', date: '2024-02-28', type: 'Rent Payment', amount: 1200, method: 'Rocket', status: 'Completed', description: 'Final week rent - February' }, + { id: 'TXN-004', date: '2024-02-21', type: 'Rent Payment', amount: 1200, method: 'bKash', status: 'Completed', description: 'Week 4 rent - February' }, + { id: 'TXN-005', date: '2024-02-14', type: 'Rent Payment', amount: 1200, method: 'bKash', status: 'Completed', description: 'Week 3 rent - February' }, + { id: 'TXN-006', date: '2024-02-07', type: 'Rent Payment', amount: 1200, method: 'Rocket', status: 'Completed', description: 'Week 2 rent - February' }, + { id: 'TXN-007', date: '2024-02-01', type: 'Deposit Return', amount: -3000, method: 'Bank Transfer', status: 'Completed', description: 'Deposit returned from previous rental' }, + { id: 'TXN-008', date: '2024-01-31', type: 'Penalty', amount: 500, method: 'Wallet', status: 'Completed', description: 'Late payment penalty - January' }, + { id: 'TXN-009', date: '2024-01-28', type: 'Rent Payment', amount: 3500, method: 'bKash', status: 'Completed', description: 'January 2024 monthly rent' }, + { id: 'TXN-010', date: '2024-01-15', type: 'Wallet Top-up', amount: 5000, method: 'bKash', status: 'Completed', description: 'Wallet recharge' }, + { id: 'TXN-011', date: '2024-01-01', type: 'Deposit', amount: 3000, method: 'Cash', status: 'Completed', description: 'Security deposit' }, + { id: 'TXN-012', date: '2023-12-28', type: 'Rent Payment', amount: 800, method: 'bKash', status: 'Completed', description: 'Partial rent - December' }, + ], + activityLog: [ + { id: 'ACT-001', type: 'login', action: 'App login from Android device', timestamp: '2024-03-28 08:15 AM', icon: User }, + { id: 'ACT-002', type: 'rental', action: 'Battery swap at Gulshan Swap Station', timestamp: '2024-03-28 07:45 AM', icon: Battery, details: 'Swapped from 35% to 92%' }, + { id: 'ACT-003', type: 'payment', action: 'Monthly rent payment completed', timestamp: '2024-03-28 07:30 AM', adminName: 'System', icon: DollarSign }, + { id: 'ACT-004', type: 'rental', action: 'Rental started - AIMA Lightning EV', timestamp: '2024-03-01 10:00 AM', icon: BikeIcon }, + { id: 'ACT-005', type: 'kyc', action: 'KYC verification completed', timestamp: '2024-01-16 03:45 PM', adminName: 'Supervisor Rahman', icon: Shield }, + { id: 'ACT-006', type: 'document', action: 'Driving license image uploaded', timestamp: '2024-01-10 11:20 AM', icon: FileText }, + { id: 'ACT-007', type: 'penalty', action: 'Late payment penalty applied', timestamp: '2024-01-31 11:59 PM', adminName: 'System', icon: AlertTriangle, details: '৳500 for late rent payment' }, + { id: 'ACT-008', type: 'bike', action: 'Bike changed from Etron ET50 to AIMA Lightning', timestamp: '2024-03-01 09:30 AM', adminName: 'Staff: Kamal', icon: ArrowRightLeft }, + { id: 'ACT-009', type: 'message', action: 'Received SMS: Rent reminder', timestamp: '2024-02-28 09:00 AM', icon: MessageSquare }, + { id: 'ACT-010', type: 'login', action: 'App login from iOS device', timestamp: '2024-02-15 06:30 PM', icon: User }, + { id: 'ACT-011', type: 'rental', action: 'Battery swap at Banani Swap Station', timestamp: '2024-02-15 05:45 PM', icon: Battery, details: 'Swapped from 28% to 95%' }, + { id: 'ACT-012', type: 'document', action: 'NID image uploaded', timestamp: '2024-01-10 09:45 AM', icon: FileText }, + ], + damageReports: [ + { + id: 'DMG-001', + date: '2024-02-10', + rentalId: 'RNT-2024-0020', + bike: 'Yadea DT3', + damageType: 'Body Damage', + severity: 'Medium', + estimatedCost: 2000, + actualCost: 1800, + status: 'Resolved', + resolution: 'Panel repaired and repainted', + description: 'Minor scratch on left rear fender noticed during routine inspection', + location: 'Gulshan 1, Dhaka', + images: ['https://picsum.photos/400/300?random=dm1', 'https://picsum.photos/400/300?random=dm2'], + }, + { + id: 'DMG-002', + date: '2024-01-20', + rentalId: 'RNT-2024-0010', + bike: 'Etron ET50', + damageType: 'Tire Damage', + severity: 'Low', + estimatedCost: 800, + actualCost: 750, + status: 'Resolved', + resolution: 'Rear tire replaced', + description: 'Puncture in rear tire caused by sharp object on road', + location: 'Dhanmondi 32, Dhaka', + images: ['https://picsum.photos/400/300?random=dm3'], + }, + { + id: 'DMG-003', + date: '2024-03-25', + rentalId: 'RNT-2024-0045', + bike: 'AIMA Lightning EV', + damageType: 'Battery Damage', + severity: 'High', + estimatedCost: 5000, + status: 'Pending', + description: 'Battery not holding charge properly, discharging faster than normal', + location: 'Uttara Sector 10, Dhaka', + images: ['https://picsum.photos/400/300?random=dm4', 'https://picsum.photos/400/300?random=dm5'], + }, + ], + messageHistory: [ + { id: 'MSG-001', direction: 'sent', channel: 'sms', message: 'Your monthly rent of ৳4,500 is due on March 28. Please ensure sufficient balance in your wallet.', timestamp: '2024-03-25 10:00 AM', status: 'Delivered' }, + { id: 'MSG-002', direction: 'received', channel: 'sms', message: 'Okay, I will pay today.', timestamp: '2024-03-25 10:15 AM', status: 'Received' }, + { id: 'MSG-003', direction: 'sent', channel: 'sms', message: 'Thank you. You can make payment via bKash or Rocket to 01712345678.', timestamp: '2024-03-25 10:20 AM', status: 'Delivered' }, + { id: 'MSG-004', direction: 'received', channel: 'sms', message: 'Payment done. Transaction ID: BKP123456789.', timestamp: '2024-03-28 07:25 AM', status: 'Received' }, + { id: 'MSG-005', direction: 'sent', channel: 'push', message: 'Your rent payment has been received. Thank you!', timestamp: '2024-03-28 07:30 AM', status: 'Delivered' }, + { id: 'MSG-006', direction: 'sent', channel: 'sms', message: 'Reminder: Your bike rental contract ends on April 30. Please renew or return the bike.', timestamp: '2024-03-20 09:00 AM', status: 'Delivered' }, + { id: 'MSG-007', direction: 'received', channel: 'sms', message: 'I want to renew the contract. What are the new rates?', timestamp: '2024-03-20 02:30 PM', status: 'Received' }, + { id: 'MSG-008', direction: 'sent', channel: 'sms', message: 'Great! Current rates are ৳4,500/month for the same plan. We will send renewal documents soon.', timestamp: '2024-03-20 03:00 PM', status: 'Delivered' }, + ], + notes: [ + { id: 'NOTE-001', text: 'Very reliable biker, always pays rent on time. Low maintenance issues.', createdAt: '2024-03-15 11:30 AM', createdBy: 'Karim (Staff)' }, + { id: 'NOTE-002', text: 'Battery performance issue reported on March 25. Needs inspection.', createdAt: '2024-03-25 04:00 PM', createdBy: 'Rahim (Manager)' }, + { id: 'NOTE-003', text: 'VIP customer - priority support required.', createdAt: '2024-02-01 09:00 AM', createdBy: 'Supervisor Rahman' }, + ], + walletBalance: 1250, + totalPaid: 27800, + totalDue: 0, + pendingAmount: 0, + totalPenaltiesPaid: 500, + bankAccount: { + bankName: 'Dutch-Bangla Bank Limited', + accountNumber: '1511234567890', + routingNumber: '9032612', }, + billingAddress: { + houseFlat: 'House 42', + floor: '3rd Floor', + road: 'Road 11', + block: 'Block D', + area: 'Gulshan 1', + thana: 'Gulshan', + district: 'Dhaka', + division: 'Dhaka', + postalCode: '1212', + }, +}; + +const tabs = [ + { id: 'personal', label: 'Personal', icon: User }, + { id: 'license', label: 'License & GPS', icon: Car }, + { id: 'documents', label: 'Documents', icon: FileCheck }, + { id: 'bikes', label: 'Bikes', icon: BikeIcon }, + { id: 'rent', label: 'Rent & Account', icon: CreditCard }, + { id: 'messages', label: 'Messages', icon: MessageSquare }, + { id: 'notes', label: 'Notes', icon: ClipboardList }, + { id: 'activity', label: 'Activity', icon: Activity }, + { id: 'damage', label: 'Damage', icon: AlertOctagon }, ]; const statusColors: Record = { @@ -138,114 +639,248 @@ const statusColors: Record = { pending: 'bg-amber-100 text-amber-700', inactive: 'bg-slate-100 text-slate-500', blocked: 'bg-red-100 text-red-700', + completed: 'bg-green-100 text-green-700', + cancelled: 'bg-red-100 text-red-700', + resolved: 'bg-green-100 text-green-700', }; -const statusLabels: Record = { - active: 'Active', - pending: 'Pending', - inactive: 'Inactive', - blocked: 'Blocked', +const severityColors: Record = { + Low: 'bg-blue-100 text-blue-700', + Medium: 'bg-amber-100 text-amber-700', + High: 'bg-orange-100 text-orange-700', + Critical: 'bg-red-100 text-red-700', }; -const kycColors: Record = { - verified: 'bg-green-100 text-green-700', - pending: 'bg-amber-100 text-amber-700', - rejected: 'bg-red-100 text-red-700', +const activityTypeColors: Record = { + login: 'bg-blue-100 text-blue-600', + rental: 'bg-green-100 text-green-600', + payment: 'bg-purple-100 text-purple-600', + kyc: 'bg-cyan-100 text-cyan-600', + document: 'bg-amber-100 text-amber-600', + penalty: 'bg-red-100 text-red-600', + bike: 'bg-indigo-100 text-indigo-600', + message: 'bg-pink-100 text-pink-600', }; -const membershipColors: Record = { - free: 'bg-slate-100 text-slate-600', - basic: 'bg-blue-100 text-blue-700', - premium: 'bg-purple-100 text-purple-700', - vip: 'bg-amber-100 text-amber-700', -}; +const damageTypes = [ + 'Battery Damage', + 'Body Damage', + 'Tire Damage', + 'Engine Damage', + 'Accident', + 'Theft', + 'Other', +]; -interface PageProps { - params: Promise<{ id: string }>; +const severityLevels = ['Low', 'Medium', 'High', 'Critical']; + +const smsTemplates = [ + { id: 'rent_due', label: 'Monthly rent due', message: 'Monthly rent due. Please pay ৳{amount} within 3 days.' }, + { id: 'contract_end', label: 'Contract ending', message: 'Your bike rental contract ends on {date}. Please renew or return.' }, + { id: 'kyc_approved', label: 'KYC approved', message: 'Congratulations! Your KYC has been approved. You can now rent bikes.' }, + { id: 'penalty', label: 'Penalty warning', message: 'Warning: Your account has a penalty of ৳{amount}. Please clear it to avoid service suspension.' }, + { id: 'docs_upload', label: 'Document request', message: 'Please upload your documents for verification. Missing documents: {docs}' }, + { id: 'battery_reminder', label: 'Battery swap reminder', message: 'Your battery is running low. Please swap at nearest station.' }, +]; + +function AddressCard({ title, address, icon: Icon, bgColor }: { title: string; address: any; icon: any; bgColor: string }) { + return ( +
+
+ +

{title}

+
+
+ {address.houseFlat && } + {address.floor && } + {address.road && } + {address.block && } + {address.area && } + {address.thana && } + {address.district && } + {address.division && } + {address.postalCode && } + {address.landmark && } +
+
+ ); } -export default function BikerDetailPage({ params }: PageProps) { - const resolvedParams = use(params); +function InfoRow({ label, value }: { label: string; value: string }) { + return value ? ( +
+ {label} + {value} +
+ ) : null; +} + +function ProfileField({ label, value, editing, onChange }: { label: string; value: string; editing?: boolean; onChange?: (v: string) => void }) { + if (editing) { + return ( +
+ {label} + onChange?.(e.target.value)} + className="w-40 px-2 py-1.5 border border-slate-200 rounded text-xs text-slate-800 bg-white" + /> +
+ ); + } + return ( +
+ {label} + {value} +
+ ); +} + +function SectionCard({ title, icon: Icon, children, headerBg = 'bg-slate-50', editKey, editingSection, setEditingSection, onEdit, editForm, setEditForm, biker }: { title: string; icon: any; children: React.ReactNode; headerBg?: string; editKey?: string; editingSection?: string | null; setEditingSection?: (s: string | null) => void; onEdit?: () => void; editForm?: any; setEditForm?: any; biker?: any }) { + return ( +
+
+
+ +

{title}

+
+ {editKey && setEditingSection ? ( + editingSection !== editKey ? ( + + ) : ( +
+ + +
+ ) + ) : null} +
+
+ {children} +
+
+ ); +} + +function TimelineItem({ stage, status, timestamp, adminName, notes, isLast }: { stage: string; status: string; timestamp: string; adminName: string; notes?: string; isLast: boolean }) { + return ( +
+
+
+ {status === 'completed' ? : } +
+ {!isLast &&
} +
+
+

{stage}

+

{timestamp} • {adminName}

+ {notes &&

{notes}

} +
+
+ ); +} + +export default function BikerDetailPage() { const router = useRouter(); const [activeTab, setActiveTab] = useState('personal'); + const [showMessageModal, setShowMessageModal] = useState(false); + const [showDamageModal, setShowDamageModal] = useState(false); + const [showDocumentUpload, setShowDocumentUpload] = useState(false); + const [messageTab, setMessageTab] = useState<'sms' | 'push' | 'template'>('sms'); + const [messageText, setMessageText] = useState(''); + const [selectedTemplate, setSelectedTemplate] = useState(''); + const [newNote, setNewNote] = useState(''); + const [damageForm, setDamageForm] = useState({ + bike: '', + damageType: '', + description: '', + severity: '', + estimatedCost: '', + location: '', + images: [] as string[], + }); + const [rentPage, setRentPage] = useState(1); + const [transactionPage, setTransactionPage] = useState(1); + const [editingSection, setEditingSection] = useState(null); + const [editForm, setEditForm] = useState({}); - const biker = mockBikers.find(b => b.id === resolvedParams.id) || mockBikers[0]; + const biker = mockBiker; + const perPage = 10; - const tabs = [ - { id: 'personal', label: 'Personal', icon: User }, - { id: 'license', label: 'License & GPS', icon: Car }, - { id: 'documents', label: 'Documents', icon: FileText }, - { 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 paginatedRentHistory = [...biker.currentRental ? [biker.currentRental] : [], ...biker.previousRentals].slice((rentPage - 1) * perPage, rentPage * perPage); + const totalRentPages = Math.ceil((1 + biker.previousRentals.length) / perPage); - 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 paginatedTransactions = biker.transactions.slice((transactionPage - 1) * perPage, transactionPage * perPage); + const totalTransactionPages = Math.ceil(biker.transactions.length / perPage); - 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 }, - ]; + const handleSendMessage = () => { + if (!messageText.trim()) return; + toast.success(`Message sent to ${biker.kyc.phone}`); + setMessageText(''); + setShowMessageModal(false); + }; + + const handleAddNote = () => { + if (!newNote.trim()) return; + toast.success('Note added successfully'); + setNewNote(''); + }; + + const handleSendTemplate = (template: typeof smsTemplates[0]) => { + let message = template.message; + message = message.replace('{amount}', biker.currentRental?.monthlyRate?.toString() || '4500'); + message = message.replace('{date}', biker.currentRental?.contractEndDate || '2024-04-30'); + message = message.replace('{docs}', 'Driving License, NID'); + setMessageText(message); + setMessageTab('sms'); + }; + + const handleSubmitDamage = () => { + if (!damageForm.damageType || !damageForm.description) { + toast.error('Please fill all required fields'); + return; + } + toast.success('Damage report submitted successfully'); + setShowDamageModal(false); + setDamageForm({ bike: '', damageType: '', description: '', severity: '', estimatedCost: '', location: '', images: [] }); + }; + + const formatAddress = (addr: any) => { + return [addr.houseFlat, addr.floor, addr.road, addr.block, addr.area, addr.thana, addr.district, addr.division, addr.postalCode].filter(Boolean).join(', '); + }; return ( -
-
- - - - -
-

Biker Details

-

View and manage biker profile

-
-
- - -
-
+
+
- {biker.name.charAt(0)} + {biker.kyc.fullName.charAt(0)}
-

{biker.name}

+

{biker.kyc.fullName}

{biker.status === 'active' && } {biker.status === 'pending' && } {biker.status === 'blocked' && } - {statusLabels[biker.status] || biker.status} + {biker.status.charAt(0).toUpperCase() + biker.status.slice(1)}
-

ID: {biker.id} • {biker.location}

+

ID: {biker.id} • {biker.location}

- + {biker.kycStatus === 'verified' && } {biker.kycStatus === 'pending' && } KYC: {biker.kycStatus} - + {biker.membershipType} {biker.rating > 0 && ( @@ -258,16 +893,59 @@ export default function BikerDetailPage({ params }: PageProps) {
- +
+
-
+ {/* Stats Grid */} +
+
+
+ + Total Rentals +
+

{biker.totalRentals}

+
+
+
+ + Active Rentals +
+

{biker.activeRentals}

+
+
+
+ + Completed +
+

{biker.completedRentals}

+
+
+
+ + Cancelled +
+

{biker.cancelledRentals}

+
+
+ + {/* Tabs Container */} +
+
-
-
- {activeTab === 'personal' && ( -
-
-
-

- Personal Information -

-
- - - - - - - - -
-
-

Address

-

{biker.address}

-
-
-
-

- Emergency Contact -

-
- - - -
-
-
-
- )} - - {activeTab === 'license' && ( -
-
-

- Driving License -

-
- - - - - -
-
-
-

- GPS Tracking Device -

-
- - -
-
-
- )} - - {activeTab === 'documents' && ( -
-

- Uploaded Documents -

+
+ {activeTab === 'personal' && (
- {biker.documents.map((doc, idx) => ( -
-
-
-

{doc.type}

-

Number: {doc.number || 'N/A'}

+ {/* Application Details */} +
+
+

+ Application Details +

+ {editingSection !== 'application' ? ( + + ) : ( +
+ +
- - {doc.verified ? : } - {doc.verified ? 'Verified' : 'Pending'} - -
+ )}
- ))} -
-
- )} - - {activeTab === 'reviews' && ( -
-

- Biker Reviews -

-
- {[ - { rider: 'Tashrif', rating: 5, comment: 'Great service! Very polite and quick.', date: '2024-03-20', rideId: 'R001' }, - { rider: 'Mahir', rating: 4, comment: 'Good ride, arrived on time.', date: '2024-03-19', rideId: 'R002' }, - { rider: 'Raisa', rating: 5, comment: 'Excellent experience. Would recommend!', date: '2024-03-18', rideId: 'R003' }, - { rider: 'Anika', rating: 3, comment: 'Ride was okay but a bit slow.', date: '2024-03-17', rideId: 'R004' }, - { rider: 'Ovi', rating: 5, comment: 'Best biker ever! Helped with luggage.', date: '2024-03-16', rideId: 'R005' }, - ].map((review, idx) => ( -
-
-
-
- {review.rider.charAt(0)} +
+ {editingSection === 'application' ? ( + <> +
+ + setEditForm({ ...editForm, fullName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, phone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, alternatePhone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
-

{review.rider}

-

Ride: {review.rideId}

+ + setEditForm({ ...editForm, email: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, nid: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, dateOfBirth: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + setEditForm({ ...editForm, religion: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, nationality: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + ) : ( + <> +
+

Full Name

+

{biker.kyc.fullName}

+
+
+
+

Phone

+

{biker.kyc.phone}

+
+ {biker.kyc.alternatePhone && ( +
+

Alternate Phone

+

{biker.kyc.alternatePhone}

+
+ )} +
+
+

Email

+

{biker.kyc.email}

+
+
+
+

NID

+

{biker.kyc.nid}

+
+
+

Date of Birth

+

{biker.kyc.dateOfBirth}

+
+
+
+
+

Gender

+

{biker.kyc.gender}

+
+
+

Blood Group

+

{biker.kyc.bloodGroup}

+
+
+

Marital Status

+

{biker.kyc.maritalStatus}

+
+
+
+
+

Religion

+

{biker.kyc.religion}

+
+
+

Nationality

+

{biker.kyc.nationality}

+
+
+ + )} +
+
+ + {/* Nominee Details */} +
+
+

+ Nominee Details +

+ {editingSection !== 'nominee' ? ( + + ) : ( +
+ + +
+ )} +
+
+ {editingSection === 'nominee' ? ( + <> +
+
+ + setEditForm({ ...editForm, name: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, relationship: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, nid: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, phone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, email: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, sharePercentage: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + +
+
+
+ + setEditForm({ ...editForm, address: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+
+

Nominee Name

+

{biker.kyc.nominee.name}

+
+
+

Relationship

+

{biker.kyc.nominee.relationship}

+
+
+
+
+

NID

+

{biker.kyc.nominee.nid}

+
+
+

Phone

+

{biker.kyc.nominee.phone}

+
+
+
+

Email

+

{biker.kyc.nominee.email}

+
+
+
+

Share %

+

{biker.kyc.nominee.sharePercentage}%

+
+
+

Blood Group

+

{biker.kyc.nominee.bloodGroup}

+
+
+
+

Address

+

{biker.kyc.nominee.address}

+
+ + )} +
+
+ + {/* Employment Information */} +
+
+

+ Employment Information +

+ {editingSection !== 'employment' ? ( + + ) : ( +
+ + +
+ )} +
+
+ {editingSection === 'employment' ? ( + <> +
+ + +
+
+ + setEditForm({ ...editForm, companyName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, department: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, designation: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, monthlyIncome: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, yearsOfExperience: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, businessAddress: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+
+

Status

+

{biker.kyc.employment.status}

+
+
+

Monthly Income

+

{biker.kyc.employment.monthlyIncome}

+
+
+
+

Company / Business

+

{biker.kyc.employment.companyName}

+
+
+
+

Department

+

{biker.kyc.employment.department}

+
+
+

Designation

+

{biker.kyc.employment.designation}

+
+
+
+

Experience

+

{biker.kyc.employment.yearsOfExperience}

+
+
+

Business Address

+

{biker.kyc.employment.businessAddress}

+
+ + )} +
+
+ + {/* Emergency Contact */} +
+
+

+ Emergency Contact +

+ {editingSection !== 'emergency' ? ( + + ) : ( +
+ + +
+ )} +
+
+ {editingSection === 'emergency' ? ( + <> +
+
+ + setEditForm({ ...editForm, name: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, relationship: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, phone: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, email: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, address: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+
+

Contact Name

+

{biker.kyc.emergencyContact.name}

+
+
+

Relationship

+

{biker.kyc.emergencyContact.relationship}

+
+
+
+
+

Phone

+

{biker.kyc.emergencyContact.phone}

+
+
+

Email

+

{biker.kyc.emergencyContact.email}

+
+
+
+

Address

+

{biker.kyc.emergencyContact.address}

+
+ + )} +
+
+ + {/* Present Address */} +
+
+

+ Present Address +

+ {editingSection !== 'presentAddress' ? ( + + ) : ( +
+ + +
+ )} +
+
+ {editingSection === 'presentAddress' ? ( + <> +
+ + setEditForm({ ...editForm, addressLine1: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="House No, Road, Area" /> +
+
+ + setEditForm({ ...editForm, addressLine2: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Additional info" /> +
+
+
+ + +
+
+ + setEditForm({ ...editForm, district: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, thana: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, zip: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, landmark: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Near landmark" /> +
+
+ + setEditForm({ ...editForm, country: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+

Address Line 1

+

{biker.kyc.presentAddress.houseFlat}, {biker.kyc.presentAddress.road}

+
+
+

Address Line 2

+

{biker.kyc.presentAddress.block ? `${biker.kyc.presentAddress.block}, ` : ''}{biker.kyc.presentAddress.area}

+
+
+
+

Division

+

{biker.kyc.presentAddress.division}

+
+
+

District

+

{biker.kyc.presentAddress.district}

+
+
+
+
+

Thana / Upazila

+

{biker.kyc.presentAddress.thana}

+
+
+

Zip Code

+

{biker.kyc.presentAddress.postalCode}

+
+
+ {biker.kyc.presentAddress.landmark && ( +
+

Landmark

+

{biker.kyc.presentAddress.landmark}

+
+ )} +
+

Country

+

Bangladesh

+
+ + )} +
+
+ + {/* Permanent Address */} +
+
+

+ Permanent Address +

+
+ + {editingSection !== 'permanentAddress' ? ( + + ) : ( +
+ + +
+ )} +
+
+
+ {editingSection === 'permanentAddress' ? ( + <> +
+ + setEditForm({ ...editForm, addressLine1: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="House No, Road, Area" /> +
+
+ + setEditForm({ ...editForm, addressLine2: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Additional info" /> +
+
+
+ + +
+
+ + setEditForm({ ...editForm, district: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, thana: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, zip: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, landmark: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Near landmark" /> +
+
+ + setEditForm({ ...editForm, country: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+

Address Line 1

+

{biker.kyc.permanentAddress.houseFlat}, {biker.kyc.permanentAddress.road}

+
+
+

Address Line 2

+

{biker.kyc.permanentAddress.block ? `${biker.kyc.permanentAddress.block}, ` : ''}{biker.kyc.permanentAddress.area}

+
+
+
+

Division

+

{biker.kyc.permanentAddress.division}

+
+
+

District

+

{biker.kyc.permanentAddress.district}

+
+
+
+
+

Thana / Upazila

+

{biker.kyc.permanentAddress.thana || '-'}

+
+
+

Zip Code

+

{biker.kyc.permanentAddress.postalCode}

+
+
+
+

Landmark

+

{'-'}

+
+
+

Country

+

Bangladesh

+
+ {biker.kyc.isPermanentSame && ( +
+ + Same as Present Address +
+ )} + + )} +
+
+
+ )} + + {activeTab === 'license' && ( +
+ { setEditForm({ licenseNumber: biker.kyc.drivingLicense.number, licenseType: biker.kyc.drivingLicense.licenseType, licenseIssueDate: biker.kyc.drivingLicense.issueDate, licenseExpiryDate: biker.kyc.drivingLicense.expiryDate, issuingAuthority: biker.kyc.drivingLicense.issuingAuthority }); }} editForm={editForm} setEditForm={setEditForm} biker={biker}> +
+ {editingSection === 'drivingLicense' ? ( + <> +
+
+ + setEditForm({ ...editForm, licenseNumber: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + +
+
+
+
+ + setEditForm({ ...editForm, licenseIssueDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, licenseExpiryDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, issuingAuthority: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+
+

License Number

+

{biker.kyc.drivingLicense.number}

+
+
+

License Type

+

{biker.kyc.drivingLicense.licenseType}

+
+
+
+
+

Issue Date

+

{biker.kyc.drivingLicense.issueDate}

+
+
+

Expiry Date

+

{biker.kyc.drivingLicense.expiryDate}

+
+
+
+

Issuing Authority

+

{biker.kyc.drivingLicense.issuingAuthority}

+
+
+
+

Front Image

+ {biker.kyc.drivingLicense.frontImage ? ( + License Front + ) : ( +
+ +
+ )} +
+
+

Back Image

+ {biker.kyc.drivingLicense.backImage ? ( + License Back + ) : ( +
+ +
+ )} +
+
+ + )} +
+
+ + { setEditForm({ gpsDeviceId: biker.gpsDeviceId, gpsStatus: biker.gpsStatus, lastLocation: biker.lastLocation }); }} editForm={editForm} setEditForm={setEditForm} biker={biker}> +
+ {editingSection === 'gps' ? ( + <> +
+ + setEditForm({ ...editForm, gpsDeviceId: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + +
+
+ + setEditForm({ ...editForm, lastLocation: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+ + ) : ( + <> +
+
+

Device ID

+

{biker.gpsDeviceId}

+
+
+

Status

+ + + {biker.gpsStatus} + +
+
+
+
+
+

Last Location

+

{biker.lastLocation}

+
+ + + +
+
+ + )} +
+
+
+ )} + + {activeTab === 'documents' && ( +
+
+

Documents

+ +
+ +
+
+

National ID

+
+
+
+

NID Front

+ + Approved + +
+ {biker.kyc.nidFrontImage && ( + NID Front + )} +
+
+
+

NID Back

+ + Approved + +
+ {biker.kyc.nidBackImage && ( + NID Back + )}
- - {Array.from({ length: review.rating }).map((_, i) => ( - - ))} -
-

{review.comment}

-

{review.date}

-
- ))} -
-
- )} - {activeTab === 'rent' && ( -
-
-

- Daily Rent History -

-
- -
-
- -
-
-

Total Paid

-

৳{rentHistory.filter(r => r.status === 'paid').reduce((sum, r) => sum + r.amount, 0)}

-
-
-

Pending

-

৳{rentHistory.filter(r => r.status === 'pending').reduce((sum, r) => sum + r.amount, 0)}

-
-
-

Total Days

-

{rentHistory.length}

-
-
-

Current Plan

-

{biker.membershipType}

-
-
- -
- - - - - - - - - - - - - {rentHistory.map(rent => ( - - - - - - - - - ))} - -
DateBike IDPlanAmountStatusActions
{rent.date}{rent.bikeId}{rent.plan}৳{rent.amount} - - {rent.status === 'paid' && } - {rent.status === 'pending' && } - {rent.status} - - - {rent.status === 'pending' ? ( - - ) : ( - - +
+

Driving License

+
+
+
+

License Front

+ {biker.kyc.drivingLicense.frontImage ? ( + + Uploaded + + ) : ( + Missing + )} +
+ {biker.kyc.drivingLicense.frontImage && ( + License Front )} -
-
-
- )} - - {activeTab === 'bike' && ( -
-
-

- Rented Bikes Details -

-
- -
-
- -
-
-
- -
-
-

{biker.currentBike || 'No Bike Assigned'}

-

Plate: {biker.bikePlate || 'N/A'}

-
-
-
- -
-
-

Mileage

-
-
- - -
-
- -

{biker.lastRideAt || 'N/A'}

-
-
- -

{biker.totalDistance.toLocaleString()} km

-
-
-
- -
-

Battery Health

-
-
- -
- - % +
+
+
+

License Back

+ {biker.kyc.drivingLicense.backImage ? ( + + Uploaded + + ) : ( + Missing + )} +
+ {biker.kyc.drivingLicense.backImage && ( + License Back + )} +
+
- - - Good - -
-
- -

85 km

+

Nominee Documents

+
+
+

Nominee Photo

+ {biker.kyc.nominee.photo ? ( + Nominee + ) : ( +
+ +
+ )} +
+
+

Nominee NID Front

+ {biker.kyc.nominee.nidFront ? ( + NID Front + ) : ( +
+ +
+ )} +
+
+

Nominee NID Back

+ {biker.kyc.nominee.nidBack ? ( + NID Back + ) : ( +
+ +
+ )} +
+
-
-
+ -
-

Bike Images

-
-
- - Front + +
+ {biker.kyc.kycTimeline.map((item, idx) => ( + + ))}
-
- - Back -
-
- - Left Side -
-
- - Right Side -
-
- +
+ )} -
-

Notes

- -
+ {activeTab === 'bikes' && ( +
+ {/* Current Bike */} + +
+ {/* Bike info row */} +
+
+ +
+
+

{biker.bikes.current.model}

+

{biker.bikes.current.brand} • {biker.bikes.current.year} • {biker.bikes.current.color}

+

{biker.bikes.current.plate}

+
+
+ + Active + +
+
-
-

- Rented Bikes History -

-
- - - - - - - - - - - - - - - {rentedBikesHistory.map(rental => ( - - - - - - - - - +
+
+ + setEditForm({ ...editForm, batPercent: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + +
+
+
+ + setEditForm({ ...editForm, batLocation: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+ + setEditForm({ ...editForm, batSwappedAt: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, batOdometer: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + ) : ( + <> +
+

Add New Battery

+ {biker.bikes.batteries.length > 0 && ( + + )} +
+
+
+ + setEditForm({ ...editForm, batId: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="BAT-DH-004" /> +
+
+ + setEditForm({ ...editForm, batName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Battery D" /> +
+
+
+
+ + setEditForm({ ...editForm, batPercent: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="0-100" /> +
+
+ + +
+
+
+ + setEditForm({ ...editForm, batLocation: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="Swap Station" /> +
+
+
+ + setEditForm({ ...editForm, batSwappedAt: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" placeholder="YYYY-MM-DD HH:MM" /> +
+
+ + setEditForm({ ...editForm, batOdometer: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + )} + + ) : ( + <> +
+ +
+ {biker.bikes.batteries.map((bat, idx) => ( +
+
+
+
50 ? 'bg-green-100' : bat.percent > 20 ? 'bg-amber-100' : 'bg-red-100'}`}> + 50 ? 'text-green-600' : bat.percent > 20 ? 'text-amber-600' : 'text-red-600'}`} /> +
+
+

{bat.name} ({bat.id})

+

{bat.location} • Swapped: {bat.swappedAt}

+
+
+
+ 50 ? 'text-green-600' : bat.percent > 20 ? 'text-amber-600' : 'text-red-600'}`}>{bat.percent}% + + {bat.status.charAt(0).toUpperCase() + bat.status.slice(1)} + + +
+
+
+ ))} + + )} + + + + {/* Previous Rentals */} + +
+ {biker.previousRentals.length > 0 ? ( + <> +
+ +
+ {biker.previousRentals.map((rental, idx) => ( +
+
+
+

{rental.evModel}

+

{rental.plate} • {rental.id}

+
+
+ + {rental.status} + + +
+
+ {editingSection === 'previousRentals' && editForm.editingIndex === idx ? ( + <> +
+
+ + setEditForm({ ...editForm, rentalEvModel: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, rentalPlate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + +
+
+ + +
+
+
+
+ + setEditForm({ ...editForm, rentalStartDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, rentalEndDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+
+
+ + setEditForm({ ...editForm, rentalTotalAmount: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, rentalPenalty: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + ) : ( +
+
+

Plan

+

{rental.planName}

+
+
+

Start

+

{rental.startDate}

+
+
+

End

+

{rental.endDate || '-'}

+
+
+

Total

+

৳{rental.totalAmount.toLocaleString()}{rental.penalty > 0 ? (+৳{rental.penalty}) : null}

+
+
+ )} +
+ ))} + + ) : ( +

No previous rentals

+ )} +
+
+ + )} + + {activeTab === 'rent' && ( +
+ {/* Financial Summary */} +
+
+

Total Paid

+

৳{biker.totalPaid.toLocaleString()}

+
+
+

Total Due

+

৳{biker.totalDue.toLocaleString()}

+
+
+

Pending

+

৳{biker.pendingAmount.toLocaleString()}

+
+
+

Penalties Paid

+

৳{biker.totalPenaltiesPaid.toLocaleString()}

+
+
+ + {/* Wallet */} + +
+

Balance

+

৳{biker.walletBalance.toLocaleString()}

+

Last updated: March 28, 2024

+
+
+ + {/* Payment History */} + +
+
BikePlatePlanStart DateEnd DateDaysTotal RentStatus
-
- + {/* Battery swap info */} +
+
50 ? 'bg-green-100' : biker.bikes.batteries[0]?.percent > 20 ? 'bg-amber-100' : 'bg-red-100'}`}> + 50 ? 'text-green-600' : biker.bikes.batteries[0]?.percent > 20 ? 'text-amber-600' : 'text-red-600'}`} /> +
+
+

Current Battery

+

50 ? 'text-green-600' : biker.bikes.batteries[0]?.percent > 20 ? 'text-amber-600' : 'text-red-600'}`}>{biker.bikes.batteries[0]?.percent}%

+
+
+

Battery ID

+

{biker.bikes.batteries[0]?.id}

+
+
+

Location

+

{biker.bikes.batteries[0]?.location}

+
+
+ + {/* Battery info grid */} +
+
+

Battery Capacity

+

{biker.bikes.current.batteryCapacity}

+
+
+

Max Range

+

{biker.bikes.current.maxRange}

+
+
+

Odometer

+

{biker.bikes.current.totalKmRun.toLocaleString()} km

+
+
+

VIN

+

{biker.bikes.current.vin}

+
+
+ +
+
+

Last Service

+

{biker.bikes.current.lastService}

+
+
+

Next Service

+

{biker.bikes.current.nextService}

+
+
+ + {/* Initial Condition Images */} + {biker.currentRental.initialConditionImages.length > 0 && ( +
+

Initial Condition Photos

+
+ {biker.currentRental.initialConditionImages.map((img, idx) => ( + {`Condition + ))} +
+
+ )} +
+ + + {/* Battery History */} + { setEditForm({}); }} editForm={editForm} setEditForm={setEditForm} biker={biker}> +
+ {editingSection === 'batteryHistory' ? ( + <> + {editForm.editingIndex !== undefined ? ( + <> +
+

Editing: {biker.bikes.batteries[editForm.editingIndex]?.name}

+ +
+
-

{rental.bikeName}

-

{rental.bikeId}

+ + setEditForm({ ...editForm, batName: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" /> +
+
+ + setEditForm({ ...editForm, batId: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-xs" />
-
{rental.plate}{rental.plan}{rental.startDate}{rental.endDate || '-'}{rental.totalDays}৳{rental.totalRent} - - {rental.status === 'active' && } - {rental.status === 'returned' && } - {rental.status === 'active' ? 'Active' : 'Returned'} - -
+ + + + + + + - ))} - -
DateTypeAmountMethodStatus
-
-
-
- )} - - {activeTab === 'stats' && ( -
-
- - - - -
-
- - 5 ? 'red' : ''} /> - - -
-
- )} - - {activeTab === 'account' && ( -
-
-
-

- Financial -

-
- - - - + + + {paginatedTransactions.map(txn => ( + + {txn.date} + {txn.type} + ৳{Math.abs(txn.amount).toLocaleString()} + {txn.method} + + {txn.status} + + + ))} + +
-
-
-

- Verification & Insurance -

-
- - - - -
-
-
-
- )} - - {activeTab === 'activity' && ( -
-

- Activity Log -

-
- {[ - { action: 'Profile Updated', date: '2024-03-21', icon: Edit }, - { action: 'Ride Completed', date: '2024-03-21', icon: BikeIcon }, - { action: 'Wallet Top Up - ৳2000', date: '2024-03-20', icon: Wallet }, - { action: 'KYC Verified', date: '2024-01-15', icon: CheckCircle }, - ].map((log, idx) => { - const Icon = log.icon; - return ( -
- -
-

{log.action}

-

{log.date}

-
+
+

Showing {((transactionPage - 1) * perPage) + 1}-{Math.min(transactionPage * perPage, biker.transactions.length)} of {biker.transactions.length}

+
+ +
- ); - })} +
+ + + {/* Rent History Table */} + +
+ + + + + + + + + + + + + + + {biker.currentRental && ( + + + + + + + + + + + )} + {biker.previousRentals.map(rental => ( + + + + + + + + + + + ))} + +
DateBikePlanDurationRentPenaltyTotalStatus
{biker.currentRental.startDate} +

{biker.currentRental.evModel}

+

{biker.currentRental.plate}

+
{biker.currentRental.planName}{biker.currentRental.subscriptionType}৳{(biker.currentRental.monthlyRate || biker.currentRental.weeklyRate || biker.currentRental.dailyRate).toLocaleString()}-৳{biker.currentRental.totalAmount.toLocaleString()} + Active +
{rental.startDate} +

{rental.evModel}

+

{rental.plate}

+
{rental.planName}{rental.endDate || '-'}৳{((rental.monthlyRate || rental.weeklyRate) || 0).toLocaleString()}{rental.penalty > 0 ? `৳${rental.penalty.toLocaleString()}` : '-'}৳{rental.totalAmount.toLocaleString()} + + {rental.status} + +
+
+
+

Showing page {rentPage} of {totalRentPages}

+
+ + +
+
+
+
-
- )} + )} + + {activeTab === 'activity' && ( +
+ +
+ {biker.activityLog.map(log => { + const Icon = log.icon; + return ( +
+
+ +
+
+

{log.action}

+
+ {log.timestamp} + {log.adminName && • {log.adminName}} +
+ {log.details &&

{log.details}

} +
+
+ ); + })} +
+
+
+ )} + + {activeTab === 'messages' && ( +
+ +
+ {biker.messageHistory.filter(m => m.channel === 'sms' || m.channel === 'in_app').map(msg => ( +
+
+
+ {msg.direction === 'sent' ? : } + {msg.direction === 'sent' ? 'Sent' : 'Received'} + ({msg.channel}) +
+ {msg.timestamp} +
+

{msg.message}

+ + {msg.status === 'Delivered' && } + {msg.status} + +
+ ))} +
+ +
+ + +
+ {biker.messageHistory.filter(m => m.channel === 'push').length > 0 ? ( + biker.messageHistory.filter(m => m.channel === 'push').map(msg => ( +
+
+ Push Notification + {msg.timestamp} +
+

{msg.message}

+ + {msg.status === 'Delivered' && } + {msg.status} + +
+ )) + ) : ( +

No push notifications sent

+ )} +
+
+
+ )} + + {activeTab === 'notes' && ( +
+ +
+ {biker.notes.length > 0 ? ( + biker.notes.map(note => ( +
+

{note.text}

+
+ {note.createdAt} + + {note.createdBy} +
+
+ )) + ) : ( +

No notes yet

+ )} +
+
+ setNewNote(e.target.value)} + placeholder="Add a note..." + className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" + /> + +
+
+
+ )} + + {activeTab === 'damage' && ( +
+ + + + + + {biker.damageReports.length > 0 ? ( +
+ {biker.damageReports.map(report => ( +
+
+
+

{report.damageType}

+

{report.date} • {report.rentalId}

+
+
+ + {report.severity} + + + {report.status === 'Resolved' && } + {report.status} + +
+
+

{report.description}

+
+
+

Bike

+

{report.bike}

+
+
+

Location

+

{report.location}

+
+
+

Estimated

+

৳{report.estimatedCost}

+
+
+

Actual

+

৳{report.actualCost || '-'}

+
+
+ {report.resolution && ( +
+

Resolution

+

{report.resolution}

+
+ )} + {report.images.length > 0 && ( +
+ {report.images.map((img, idx) => ( + {`Damage + ))} +
+ )} +
+ ))} +
+ ) : ( +

No damage reports

+ )} +
+ + +
+ {biker.damageReports.filter(r => r.images.length > 0).map(report => ( +
+

{report.date} - {report.damageType}

+
+ {report.images.map((img, idx) => ( + {`Damage + ))} +
+
+ ))} + {biker.damageReports.filter(r => r.images.length > 0).length === 0 && ( +

No damage photos

+ )} +
+
+
+ )} +
- {/*
-
- - + {showMessageModal && ( +
+
+
+

Send Message to {biker.kyc.fullName}

+ +
+
+
+ + + +
+
+
+ {messageTab === 'template' ? ( +
+

Select a template to use:

+ {smsTemplates.map(tmpl => ( + + ))} +
+ ) : ( +
+
+ +