'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { Shield, Search, Filter, Check, X, Clock, User, Phone, MapPin, FileText, Eye, Download, Send, MessageSquare, AlertCircle, DollarSign, Bike, Store, Users, ChevronDown, ChevronUp, Bell, MoreHorizontal, Image as ImageIcon, Upload, CheckCircle, XCircle, Camera, AlertTriangle, Edit, Globe, Wallet, Calendar, CreditCard, FileSignature, MapPinned, Key, BatteryCharging, Briefcase, Plus } from 'lucide-react'; export type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral'; export type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general'; export type RiderPlan = 'daily_rent' | 'weekly_rent' | 'monthly_rent' | 'rent_to_own' | 'share_ev'; export type VerificationStage = 'application' | 'document_collection' | 'risk_check' | 'plan_selection' | 'payment' | 'agreement' | 'allocated' | 'active'; interface NomineeDetails { name: string; phone: string; relationship: string; nid: string; } interface EmploymentInfo { company: string; monthlyEarning: number; whyEV: string; experience: string; } interface RiskCheck { nidVerified: boolean; nomineeNidVerified: boolean; deliveryPlatformStatus: 'active' | 'inactive' | 'suspended'; paymentReliability: 'good' | 'fair' | 'poor'; notes: string; checkedAt?: string; checkedBy?: string; } interface Agreement { dailyRentObligation: number; latePenalty: number; damageTerms: string; vehicleRules: string; signedAt?: string; signedBy?: string; } interface EVAllocation { evId?: string; bikeModel?: string; batteryId?: string; hubLocation?: string; assignedAt?: string; gpsActivated: boolean; checklist?: string[]; } interface Document { id: string; name: string; status: 'pending' | 'uploaded' | 'approved' | 'rejected'; imageUrl?: string; rejectedReason?: string; uploadedAt?: string; } interface Request { id: string; applicationSource: ApplicationSource; sourceDetails?: string; name: string; phone: string; email: string; type: KYCType; status: 'pending' | 'documents_needed' | 'under_review' | 'risk_check' | 'approved' | 'rejected'; verificationStage: VerificationStage; submittedAt: string; location: string; address: string; requiredDocuments: Document[]; riderPlan?: RiderPlan; nomineeDetails?: NomineeDetails; employmentInfo?: EmploymentInfo; riskCheck?: RiskCheck; agreement?: Agreement; evAllocation?: EVAllocation; securityDeposit?: number; advancePayment?: number; paymentMethod?: 'bank' | 'wallet' | 'cash'; bikeRequested?: string; scheduleDate?: string; appointmentDate?: string; notes: string[]; smsHistory: { id: string; message: string; sentAt: string; sentBy: string }[]; } const mockRequests: Request[] = [ { id: 'REQ001', applicationSource: 'app', name: 'Rahim Ahmed', phone: '01712345678', email: 'rahim@email.com', type: 'biker', status: 'pending', verificationStage: 'application', submittedAt: '2024-03-20', location: 'Gulshan, Dhaka', address: 'House 12, Road 5, Gulshan 1', requiredDocuments: [ { id: 'd1', name: 'NID Front', status: 'uploaded', uploadedAt: '2024-03-20' }, { id: 'd2', name: 'NID Back', status: 'uploaded', uploadedAt: '2024-03-20' }, { id: 'd3', name: 'Driving License', status: 'pending' }, { id: 'd4', name: 'Profile Photo', status: 'uploaded', uploadedAt: '2024-03-20' }, ], riderPlan: 'daily_rent', employmentInfo: { company: 'Foodpanda', monthlyEarning: 2500, whyEV: 'Low maintenance, good for delivery', experience: '3 years bike riding' }, notes: ['Downloaded app and applied through mobile'], smsHistory: [], }, { id: 'REQ002', applicationSource: 'walkin', sourceDetails: 'Gulshan Hub', name: 'Karim Hasan', phone: '01712345679', email: 'karim@email.com', type: 'investor', status: 'documents_needed', verificationStage: 'document_collection', submittedAt: '2024-03-19', location: 'Banani, Dhaka', address: 'Flat 3B, House 22, Banani', requiredDocuments: [ { id: 'd5', name: 'NID', status: 'uploaded', uploadedAt: '2024-03-19' }, { id: 'd6', name: 'TIN Certificate', status: 'pending' }, { id: 'd7', name: 'Bank Statement', status: 'pending' }, ], smsHistory: [ { id: 'sms1', message: 'Please upload your TIN certificate and latest bank statement', sentAt: '2024-03-19', sentBy: 'admin' }, { id: 'sms2', message: 'I will upload them today', sentAt: '2024-03-19', sentBy: 'user' }, ], notes: ['Walked in at Gulshan office - referred by current biker'], }, { id: 'REQ003', applicationSource: 'web', name: 'Jamal Store', phone: '01712345680', email: 'jamal@shop.com', type: 'swapstation', status: 'under_review', verificationStage: 'plan_selection', submittedAt: '2024-03-18', location: 'Mirpur, Dhaka', address: 'Shop 45, Mirpur Market', requiredDocuments: [ { id: 'd8', name: 'Trade License', status: 'uploaded', uploadedAt: '2024-03-18' }, { id: 'd9', name: 'NID', status: 'uploaded', uploadedAt: '2024-03-18' }, { id: 'd10', name: 'Shop Photos', status: 'uploaded', uploadedAt: '2024-03-18' }, ], notes: ['Applied through website'], smsHistory: [], }, { id: 'REQ004', applicationSource: 'app', name: 'Sofiq Rahman', phone: '01712345681', email: 'sofiq@email.com', type: 'biker', status: 'approved', verificationStage: 'active', submittedAt: '2024-03-15', location: 'Dhanmondi, Dhaka', address: 'Road 8, Dhanmondi', requiredDocuments: [ { id: 'd11', name: 'NID', status: 'approved', uploadedAt: '2024-03-15' }, { id: 'd12', name: 'Driving License', status: 'approved', uploadedAt: '2024-03-15' }, { id: 'd13', name: 'Profile Photo', status: 'approved', uploadedAt: '2024-03-15' }, ], riderPlan: 'rent_to_own', nomineeDetails: { name: 'Fatema Begum', phone: '01712345699', relationship: 'Wife', nid: '1234567890123' }, employmentInfo: { company: 'Pathao', monthlyEarning: 3000, whyEV: 'Want to own EV eventually', experience: '5 years motorcycle experience' }, riskCheck: { nidVerified: true, nomineeNidVerified: true, deliveryPlatformStatus: 'active', paymentReliability: 'good', notes: 'Verified - clean record', checkedAt: '2024-03-15', checkedBy: 'Admin' }, agreement: { dailyRentObligation: 500, latePenalty: 100, damageTerms: 'Standard terms', vehicleRules: 'Follow JAIBEN guidelines', signedAt: '2024-03-15', signedBy: 'Sofiq Rahman' }, evAllocation: { evId: 'EV-004', bikeModel: 'AIMA Lightning', batteryId: 'BAT-044', hubLocation: 'Dhanmondi Hub', assignedAt: '2024-03-15', gpsActivated: true, checklist: ['Front light working', 'Brakes OK', 'Horn working', 'Tire pressure OK'] }, securityDeposit: 10000, advancePayment: 500, paymentMethod: 'bank', bikeRequested: 'AIMA Lightning', scheduleDate: '2024-03-15', appointmentDate: '2024-03-15', notes: ['Approved and active - EV allocated'], smsHistory: [], }, { id: 'REQ005', applicationSource: 'referral', sourceDetails: 'Rahim (BIKER-001)', name: 'Mizanur Investment', phone: '01712345682', email: 'mizan@invest.com', type: 'investor', status: 'rejected', verificationStage: 'application', submittedAt: '2024-03-10', location: 'Uttara, Dhaka', address: 'Sector 4, Uttara', requiredDocuments: [ { id: 'd14', name: 'NID', status: 'rejected', rejectedReason: 'Image is blurry and unreadable', uploadedAt: '2024-03-10' }, { id: 'd15', name: 'TIN Certificate', status: 'uploaded', uploadedAt: '2024-03-10' }, { id: 'd16', name: 'Bank Statement', status: 'uploaded', uploadedAt: '2024-03-10' }, ], smsHistory: [ { id: 'sms3', message: 'Your NID document is not clear. Please re-upload.', sentAt: '2024-03-10', sentBy: 'admin' }, ], notes: ['NID was unclear/blurry'], }, { id: 'REQ006', applicationSource: 'web', name: 'Ripon Mia', phone: '01712345683', email: 'ripon@email.com', type: 'biker', status: 'under_review', verificationStage: 'risk_check', submittedAt: '2024-03-21', location: 'Mohakhali, Dhaka', address: 'House 8, Mohakhali', requiredDocuments: [ { id: 'd17', name: 'NID', status: 'uploaded', uploadedAt: '2024-03-21' }, { id: 'd18', name: 'Driving License', status: 'uploaded', uploadedAt: '2024-03-21' }, { id: 'd19', name: 'Profile Photo', status: 'uploaded', uploadedAt: '2024-03-21' }, ], riderPlan: 'monthly_rent', nomineeDetails: { name: 'Rashid', phone: '01798765432', relationship: 'Brother', nid: '9876543210987' }, employmentInfo: { company: ' ssl', monthlyEarning: 2000, whyEV: 'Better income than fuel bike', experience: '2 years' }, notes: [], smsHistory: [], }, ]; const statusColors: Record = { pending: 'bg-amber-100 text-amber-700', documents_needed: 'bg-orange-100 text-orange-700', under_review: 'bg-blue-100 text-blue-700', risk_check: 'bg-purple-100 text-purple-700', approved: 'bg-green-100 text-green-700', rejected: 'bg-red-100 text-red-700', }; const statusLabels: Record = { pending: 'Pending', documents_needed: 'Documents Needed', under_review: 'Under Review', risk_check: 'Risk Check', approved: 'Approved', rejected: 'Rejected', }; const stageLabels: Record = { application: 'Application', document_collection: 'Documents', risk_check: 'Risk Check', plan_selection: 'Plan Selection', payment: 'Payment', agreement: 'Agreement', allocated: 'EV Allocated', active: 'Active', }; const sourceLabels: Record = { app: 'Mobile App', web: 'Website', walkin: 'Walk-in', referral: 'Referral', }; const sourceColors: Record = { app: 'bg-blue-100 text-blue-700', web: 'bg-green-100 text-green-700', walkin: 'bg-amber-100 text-amber-700', referral: 'bg-purple-100 text-purple-700', }; const planLabels: Record = { daily_rent: 'Daily Rent', weekly_rent: 'Weekly Rent', monthly_rent: 'Monthly Rent', rent_to_own: 'Rent-to-Own', share_ev: 'Share EV', }; const typeIcons: Record = { biker: Bike, investor: DollarSign, swapstation: Store, merchant: Users, }; const typeColors: Record = { biker: 'bg-blue-100 text-blue-700', investor: 'bg-purple-100 text-purple-700', swapstation: 'bg-green-100 text-green-700', merchant: 'bg-orange-100 text-orange-700', general: 'bg-slate-100 text-slate-700', }; const sourceIcons: Record = { app: Phone, web: Globe, walkin: User, referral: Users, }; export default function RequestsPage() { const [activeTab, setActiveTab] = useState<'all' | 'biker' | 'investor' | 'swapstation' | 'merchant'>('all'); const [requests, setRequests] = useState(mockRequests); const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [hubFilter, setHubFilter] = useState('all'); const [selectedRequest, setSelectedRequest] = useState(null); const [showMessageModal, setShowMessageModal] = useState(false); const [showDetailsModal, setShowDetailsModal] = useState(false); const [showNewApplicationModal, setShowNewApplicationModal] = useState(false); const [messageText, setMessageText] = useState(''); const [showActionModal, setShowActionModal] = useState(false); const [actionType, setActionType] = useState<'approve' | 'reject' | 'documents'>('approve'); const [rejectReason, setRejectReason] = useState(''); const [selectedDocId, setSelectedDocId] = useState(null); const handleApproveDocument = (docId: string) => { if (!selectedRequest) return; const updatedRequests = requests.map(req => { if (req.id === selectedRequest.id) { return { ...req, requiredDocuments: req.requiredDocuments.map(doc => doc.id === docId ? { ...doc, status: 'approved' as const } : doc ) }; } return req; }); setRequests(updatedRequests); alert('Document approved!'); }; const handleRejectDocument = (docId: string, reason: string) => { if (!selectedRequest || !reason.trim()) { alert('Please enter a reason for rejection'); return; } const updatedRequests = requests.map(req => { if (req.id === selectedRequest.id) { return { ...req, requiredDocuments: req.requiredDocuments.map(doc => doc.id === docId ? { ...doc, status: 'rejected' as const, rejectedReason: reason } : doc ) }; } return req; }); setRequests(updatedRequests); setRejectReason(''); alert('Document rejected!'); }; const filteredRequests = requests.filter(req => { const matchesTab = activeTab === 'all' || req.type === activeTab; const matchesSearch = req.name.toLowerCase().includes(searchQuery.toLowerCase()) || req.phone.includes(searchQuery) || req.email.toLowerCase().includes(searchQuery.toLowerCase()) || req.id.toLowerCase().includes(searchQuery.toLowerCase()); const matchesStatus = statusFilter === 'all' || req.status === statusFilter; const matchesHub = hubFilter === 'all' || req.sourceDetails?.toLowerCase().includes(hubFilter.toLowerCase()); return matchesTab && matchesSearch && matchesStatus && matchesHub; }); const pending = requests.filter(r => r.status === 'pending' || r.status === 'documents_needed').length; const underReview = requests.filter(r => r.status === 'under_review').length; const approved = requests.filter(r => r.status === 'approved').length; const handleSendMessage = () => { if (selectedRequest && messageText.trim()) { const updatedRequests = requests.map(req => { if (req.id === selectedRequest.id) { return { ...req, status: 'documents_needed' as const, smsHistory: [ ...req.smsHistory, { id: `sms-${Date.now()}`, message: messageText, sentAt: new Date().toISOString(), sentBy: 'admin' } ], notes: [...req.notes, messageText] }; } return req; }); setRequests(updatedRequests); setShowMessageModal(false); setMessageText(''); alert(`Message sent to ${selectedRequest.phone}: "${messageText}"`); } }; const handleAction = (request: Request, action: 'approve' | 'reject' | 'documents') => { setSelectedRequest(request); setActionType(action); setShowActionModal(true); }; const confirmAction = () => { if (!selectedRequest) return; let newStatus: Request['status'] = selectedRequest.status; let successMessage = ''; if (actionType === 'approve') { newStatus = 'approved'; successMessage = selectedRequest.type === 'biker' ? `Approved! SMS sent to ${selectedRequest.phone}: Welcome! Your biker request has been approved.` : `Approved! SMS sent to ${selectedRequest.phone}: Welcome! Your investor request has been approved.`; } else if (actionType === 'reject') { newStatus = 'rejected'; successMessage = `Rejected! SMS sent to ${selectedRequest.phone}: Sorry, your request has been rejected.`; } else if (actionType === 'documents') { newStatus = 'documents_needed'; successMessage = `Documents requested! SMS sent to ${selectedRequest.phone}: Please upload required documents.`; } const updatedRequests = requests.map(req => req.id === selectedRequest.id ? { ...req, status: newStatus } : req ); setRequests(updatedRequests); setShowActionModal(false); alert(successMessage); }; return (

Requests & Verification

Manage biker, investor, and shop requests

{pending}

Pending

{underReview}

Under Review

{approved}

Approved

{requests.length}

Total Requests

setSearchQuery(e.target.value)} className="w-full lg:w-64 pl-9 pr-4 py-2 border border-slate-200 rounded-lg text-sm" />
{filteredRequests.map(request => { const TypeIcon = typeIcons[request.type]; return (

{request.name}

{request.type} {sourceLabels[request.applicationSource]}

{request.phone} | {request.location}

{request.submittedAt}

Docs: {request.requiredDocuments.filter(d => d.status === 'uploaded' || d.status === 'approved').length}/{request.requiredDocuments.length}

{request.type === 'biker' && request.verificationStage && ( {stageLabels[request.verificationStage]} )} {request.smsHistory.length > 0 && (

{request.smsHistory.length} msg

)}
{request.status === 'pending' && } {request.status === 'documents_needed' && } {request.status === 'under_review' && } {request.status === 'approved' && } {request.status === 'rejected' && } {statusLabels[request.status]} {request.status !== 'approved' && request.status !== 'rejected' && (
{request.type === 'biker' && request.status === 'under_review' && ( )} {request.type === 'investor' && request.status === 'under_review' && ( )} {request.type === 'swapstation' && request.status === 'under_review' && ( )}
)}
); })} {filteredRequests.length === 0 && (

No requests found

)}
{showMessageModal && selectedRequest && (

SMS to {selectedRequest.phone}

Phone: {selectedRequest.phone}

Type: {selectedRequest.type}

This message will be sent via SMS to {selectedRequest.phone}

)} {showDetailsModal && selectedRequest && (

Request Details - {selectedRequest.id}

Name

{selectedRequest.name}

Phone

{selectedRequest.phone}

Email

{selectedRequest.email}

Location

{selectedRequest.location}

{selectedRequest.type === 'biker' && (

Bike Details

{ const updated = requests.map(r => r.id === selectedRequest.id ? { ...r, evAllocation: { ...r.evAllocation!, bikeModel: e.target.value } } : r ); setRequests(updated); }} />
)}

Documents

{selectedRequest.requiredDocuments.map((doc, idx) => (
{doc.status === 'pending' ? ( ) : doc.status === 'uploaded' ? ( ) : doc.status === 'approved' ? ( ) : ( )}
{doc.name} {doc.uploadedAt && {doc.uploadedAt}}
{doc.status === 'pending' && ( Waiting for upload )} {(doc.status === 'uploaded' || doc.status === 'approved') && ( )} {(doc.status === 'uploaded' || doc.status === 'approved') && ( )} {doc.status === 'rejected' && ( Rejected )} {doc.status === 'approved' && ( Approved )} {doc.status === 'rejected' && doc.rejectedReason && (
Reason: {doc.rejectedReason}
)}
))}
{selectedRequest.type === 'biker' && selectedRequest.bikeRequested && (

Bike Images (When Taking Bike)

{['Front', 'Back', 'Left Side', 'Right Side'].map((view) => (
{view} Upload
))}
)} {selectedRequest.smsHistory.length > 0 && (

SMS History

{selectedRequest.smsHistory.slice().reverse().map((msg) => (
{msg.sentBy === 'admin' ? 'Admin' : 'User'} {msg.sentAt}

{msg.message}

))}
)} {selectedRequest.notes.length > 0 && (

Notes

{selectedRequest.notes.map((note, idx) => (

{note}

))}
)}
{selectedRequest.status !== 'approved' && selectedRequest.status !== 'rejected' && ( <> {selectedRequest.type === 'biker' && ( )} {selectedRequest.type === 'investor' && ( )} {selectedRequest.type === 'swapstation' && ( )} )}
)} {showActionModal && selectedRequest && (

{actionType === 'approve' ? 'Approve Request' : actionType === 'reject' ? 'Reject Request' : 'Request Documents'}

{actionType === 'approve' && `Are you sure you want to approve ${selectedRequest.name} as a ${selectedRequest.type}?`} {actionType === 'reject' && `Are you sure you want to reject ${selectedRequest.name}'s request?`} {actionType === 'documents' && `Send a message to ${selectedRequest.name} requesting required documents.`}

Name: {selectedRequest.name}

Phone: {selectedRequest.phone}

Type: {selectedRequest.type}

)} setShowNewApplicationModal(false)} onSave={(data) => { const newRequest: Request = { id: `REQ${String(Date.now()).slice(-6)}`, applicationSource: data.applicationSource || 'walkin', sourceDetails: data.sourceDetails || '', name: data.name || '', phone: data.phone || '', email: data.email || '', type: data.type || 'biker', location: data.location || '', address: data.address || '', status: 'pending', verificationStage: 'application', submittedAt: new Date().toISOString().split('T')[0], requiredDocuments: data.type === 'biker' ? [ { id: 'doc1', name: 'NID Front', status: 'pending' }, { id: 'doc2', name: 'NID Back', status: 'pending' }, { id: 'doc3', name: 'Driving License', status: 'pending' }, { id: 'doc4', name: 'Profile Photo', status: 'pending' }, ] : data.type === 'investor' ? [ { id: 'doc1', name: 'NID', status: 'pending' }, { id: 'doc2', name: 'TIN Certificate', status: 'pending' }, { id: 'doc3', name: 'Bank Statement', status: 'pending' }, ] : [ { id: 'doc1', name: 'NID', status: 'pending' }, { id: 'doc2', name: 'Trade License', status: 'pending' }, { id: 'doc3', name: 'Shop Photo', status: 'pending' }, ], riderPlan: data.riderPlan, employmentInfo: data.employmentInfo, nomineeDetails: data.nomineeDetails, securityDeposit: data.securityDeposit, advancePayment: data.advancePayment, paymentMethod: data.paymentMethod, bikeRequested: data.bikeRequested, scheduleDate: data.scheduleDate, notes: [], smsHistory: [], }; setRequests([newRequest, ...requests]); setShowNewApplicationModal(false); alert('Application created successfully!'); }} />
); } function NewApplicationModal({ isOpen, onClose, onSave }: { isOpen: boolean; onClose: () => void; onSave: (data: Partial) => void }) { const [formData, setFormData] = useState<{ applicationSource: string; sourceDetails: string; type: string; status: string; hubId: string; name: string; phone: string; altPhone: string; email: string; dateOfBirth: string; gender: string; bloodGroup: string; occupation: string; nid: string; passport: string; presentAddress: { line1: string; line2: string; division: string; district: string; zip: string; country: string }; permanentAddress: { sameAsPresent: boolean; line1: string; line2: string; division: string; district: string; zip: string; country: string }; emergencyContact: { name: string; phone: string; relation: string; email: string; address: string }; drivingLicense: { number: string; class: string; issueDate: string; expiryDate: string; status: string }; membershipType: string; referralCode: string; referredBy: string; employmentInfo: { company: string; monthlyEarning: number; whyEV: string; experience: string }; nomineeDetails: { name: string; phone: string; relationship: string; nid: string; dob: string; email: string; address: string; percentage: number }; riderPlan: string; evModel: string; evCondition: string; scheduleDate: string; investmentPlan: { planName: string; planType: string; bikes: number; amount: number; monthlyReturn: number; expectedROI: number }; swapStationPlan: { planType: string; cabinets: number; amount: number; batteries: number; monthlyRent: number }; merchantPlan: { companyCategory: string; bikersRequested: number; amount: number; monthlyBudget: number; requiredArea: string }; requiredDocuments: Document[]; }>({ applicationSource: 'walkin', sourceDetails: '', type: 'biker', status: 'pending', hubId: '', name: '', phone: '', altPhone: '', email: '', dateOfBirth: '', gender: '', bloodGroup: '', occupation: '', nid: '', passport: '', presentAddress: { line1: '', line2: '', division: '', district: '', zip: '', country: 'Bangladesh' }, permanentAddress: { sameAsPresent: false, line1: '', line2: '', division: '', district: '', zip: '', country: 'Bangladesh' }, emergencyContact: { name: '', phone: '', relation: '', email: '', address: '' }, drivingLicense: { number: '', class: '', issueDate: '', expiryDate: '', status: 'not_applied' }, membershipType: 'basic', referralCode: '', referredBy: '', employmentInfo: { company: '', monthlyEarning: 0, whyEV: '', experience: '' }, nomineeDetails: { name: '', phone: '', relationship: '', nid: '', dob: '', email: '', address: '', percentage: 100 }, riderPlan: 'daily_rent', evModel: '', evCondition: 'new', scheduleDate: '', investmentPlan: { planName: '', planType: '', bikes: 1, amount: 0, monthlyReturn: 0, expectedROI: 0 }, swapStationPlan: { planType: '', cabinets: 8, amount: 0, batteries: 0, monthlyRent: 0 }, merchantPlan: { companyCategory: '', bikersRequested: 0, amount: 0, monthlyBudget: 0, requiredArea: '' }, requiredDocuments: [], }); const [step, setStep] = useState(1); const updateField = (path: string, value: any) => { setFormData(prev => { const keys = path.split('.'); if (keys.length === 1) return { ...prev, [keys[0]]: value }; const updated = { ...prev }; let obj: any = updated; for (let i = 0; i < keys.length - 1; i++) { if (!obj[keys[i]]) obj[keys[i]] = {}; obj = obj[keys[i]]; } obj[keys[keys.length - 1]] = value; return updated; }); }; const updateDocStatus = (docId: string, status: 'pending' | 'uploaded' | 'approved' | 'rejected') => { setFormData(prev => ({ ...prev, requiredDocuments: prev.requiredDocuments.map(d => d.id === docId ? { ...d, status, uploadedAt: status === 'uploaded' ? new Date().toISOString().split('T')[0] : d.uploadedAt } : d ), })); }; useEffect(() => { if (isOpen && formData.type) { const docs = getDocumentsForType(formData.type as KYCType); setFormData(prev => ({ ...prev, requiredDocuments: docs })); } }, [isOpen, formData.type]); const getDocumentsForType = (kycType: string): Document[] => { const docs: Record = { biker: [ { id: 'doc1', name: 'NID Front', status: 'pending' }, { id: 'doc2', name: 'NID Back', status: 'pending' }, { id: 'doc3', name: 'Driving License', status: 'pending' }, { id: 'doc4', name: 'Profile Photo', status: 'pending' }, { id: 'doc5', name: 'Bank Account Card', status: 'pending' }, { id: 'doc6', name: 'Mobile Wallet Info', status: 'pending' }, ], investor: [ { id: 'doc1', name: 'NID', status: 'pending' }, { id: 'doc2', name: 'TIN Certificate', status: 'pending' }, { id: 'doc3', name: 'Bank Statement', status: 'pending' }, { id: 'doc4', name: 'Photo', status: 'pending' }, ], swapstation: [ { id: 'doc1', name: 'Trade License', status: 'pending' }, { id: 'doc2', name: 'Owner NID', status: 'pending' }, { id: 'doc3', name: 'Station Photos', status: 'pending' }, { id: 'doc4', name: 'Electricity Connection Bill', status: 'pending' }, { id: 'doc5', name: 'Station Layout/Blueprint', status: 'pending' }, ], merchant: [ { id: 'doc1', name: 'NID', status: 'pending' }, { id: 'doc2', name: 'Trade License', status: 'pending' }, { id: 'doc3', name: 'Shop/Store Photo', status: 'pending' }, ], }; return docs[kycType] || docs.biker; }; const handleSave = () => { const newRequest: Request = { id: `REQ${String(Date.now()).slice(-6)}`, applicationSource: formData.applicationSource as ApplicationSource, sourceDetails: formData.sourceDetails, name: formData.name, phone: formData.phone, email: formData.email, type: formData.type as KYCType, status: formData.status as Request['status'], verificationStage: 'application', submittedAt: new Date().toISOString().split('T')[0], location: `${formData.presentAddress.division}, ${formData.presentAddress.district}`, address: `${formData.presentAddress.line1}, ${formData.presentAddress.line2}, ${formData.presentAddress.district}, ${formData.presentAddress.zip}`, requiredDocuments: formData.requiredDocuments, riderPlan: formData.riderPlan as RiderPlan, employmentInfo: formData.employmentInfo, nomineeDetails: formData.nomineeDetails, notes: [], smsHistory: [], }; onSave(newRequest); }; if (!isOpen) return null; return (

New KYC Application

Step {step} of 5 - {step === 1 ? 'Basic Info' : step === 2 ? 'Employment' : step === 3 ? 'Nominee' : step === 4 ? 'Plan Selection' : 'Documents'}

{[ { n: 1, l: 'Basic', icon: User }, { n: 2, l: 'Employment', icon: Briefcase }, { n: 3, l: 'Nominee', icon: Users }, { n: 4, l: 'Plan', icon: Calendar }, { n: 5, l: 'Docs', icon: FileText }, ].map((s, idx) => (
0 ? 'justify-start pl-2' : ''}`}>
= s.n ? 'bg-accent text-white' : 'bg-slate-200 text-slate-500' }`}> {step > s.n ? : }
{idx < 4 &&
s.n ? 'bg-accent' : 'bg-slate-200'}`} />}
))}
{step === 1 && (

Application Details

updateField('referralCode', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Optional" />

Personal Information

updateField('name', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Enter full name" />
updateField('phone', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="01XXXXXXXXX" />
updateField('altPhone', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Optional" />
updateField('email', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="email@example.com" />
updateField('dateOfBirth', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
updateField('nid', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="NID number" />
updateField('passport', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Passport number (optional)" />

Present Address

updateField('presentAddress.line1', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="House No, Road, Area" />
updateField('presentAddress.line2', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Additional info" />
updateField('presentAddress.district', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="District" />
updateField('presentAddress.zip', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Zip" />
updateField('presentAddress.country', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Country" />

Permanent Address

{!formData.permanentAddress.sameAsPresent && (
updateField('permanentAddress.line1', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Permanent address" disabled={formData.permanentAddress.sameAsPresent} />
updateField('permanentAddress.line2', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Additional info" disabled={formData.permanentAddress.sameAsPresent} />
updateField('permanentAddress.zip', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Zip code" disabled={formData.permanentAddress.sameAsPresent} />
updateField('permanentAddress.country', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Bangladesh" defaultValue="Bangladesh" disabled={formData.permanentAddress.sameAsPresent} />
)}
{formData.type === 'biker' && (

Driving License

updateField('drivingLicense.number', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="License number" />
updateField('drivingLicense.issueDate', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
updateField('drivingLicense.expiryDate', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
)}
)} {step === 2 && formData.type === 'biker' && (

Employment Information

updateField('employmentInfo.company', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="e.g., Foodpanda, ssl, Pathao" />
updateField('employmentInfo.monthlyEarning', Number(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="0" />
updateField('occupation', e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Your occupation" />