2026-04-26 14:56:12 +06:00
|
|
|
'use client';
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
import { useState, useEffect } from 'react';
|
2026-04-26 14:56:12 +06:00
|
|
|
import Link from 'next/link';
|
2026-05-05 02:57:02 +06:00
|
|
|
import { useRouter } from 'next/navigation';
|
2026-05-05 00:29:47 +06:00
|
|
|
import {
|
|
|
|
|
Shield, Search, Filter, Check, X, Clock, User, Phone, MapPin, FileText,
|
|
|
|
|
Eye, Download, Send, MessageSquare, AlertCircle, DollarSign, Bike,
|
2026-04-26 14:56:12 +06:00
|
|
|
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';
|
2026-05-05 02:44:32 +06:00
|
|
|
import toast from 'react-hot-toast';
|
2026-04-26 14:56:12 +06:00
|
|
|
|
|
|
|
|
export type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral';
|
2026-05-05 01:34:10 +06:00
|
|
|
export type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general';
|
2026-05-05 02:44:32 +06:00
|
|
|
export type RiderPlan = 'single_rent' | 'rent_to_own' | 'share_ev';
|
2026-04-26 14:56:12 +06:00
|
|
|
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;
|
2026-05-05 00:29:47 +06:00
|
|
|
monthlyEarning: number;
|
2026-04-26 14:56:12 +06:00
|
|
|
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[];
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
riderPlan?: RiderPlan;
|
|
|
|
|
nomineeDetails?: NomineeDetails;
|
|
|
|
|
employmentInfo?: EmploymentInfo;
|
|
|
|
|
riskCheck?: RiskCheck;
|
|
|
|
|
agreement?: Agreement;
|
|
|
|
|
evAllocation?: EVAllocation;
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
securityDeposit?: number;
|
|
|
|
|
advancePayment?: number;
|
|
|
|
|
paymentMethod?: 'bank' | 'wallet' | 'cash';
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
bikeRequested?: string;
|
|
|
|
|
scheduleDate?: string;
|
|
|
|
|
appointmentDate?: string;
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
notes: string[];
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: { id: string; message: string; sentAt: string; sentBy: string }[];
|
2026-04-26 14:56:12 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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' },
|
|
|
|
|
],
|
2026-05-05 02:48:57 +06:00
|
|
|
riderPlan: 'single_rent',
|
2026-05-05 00:29:47 +06:00
|
|
|
employmentInfo: { company: 'Foodpanda', monthlyEarning: 2500, whyEV: 'Low maintenance, good for delivery', experience: '3 years bike riding' },
|
2026-04-26 14:56:12 +06:00
|
|
|
notes: ['Downloaded app and applied through mobile'],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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' },
|
|
|
|
|
],
|
2026-05-05 02:37:38 +06:00
|
|
|
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' },
|
2026-04-26 14:56:12 +06:00
|
|
|
],
|
|
|
|
|
notes: ['Walked in at Gulshan office - referred by current biker'],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'REQ003',
|
|
|
|
|
applicationSource: 'web',
|
|
|
|
|
name: 'Jamal Store',
|
|
|
|
|
phone: '01712345680',
|
|
|
|
|
email: 'jamal@shop.com',
|
2026-05-05 01:34:10 +06:00
|
|
|
type: 'swapstation',
|
2026-04-26 14:56:12 +06:00
|
|
|
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'],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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' },
|
2026-05-05 00:29:47 +06:00
|
|
|
employmentInfo: { company: 'Pathao', monthlyEarning: 3000, whyEV: 'Want to own EV eventually', experience: '5 years motorcycle experience' },
|
2026-04-26 14:56:12 +06:00
|
|
|
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'],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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' },
|
|
|
|
|
],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [
|
|
|
|
|
{ id: 'sms3', message: 'Your NID document is not clear. Please re-upload.', sentAt: '2024-03-10', sentBy: 'admin' },
|
2026-04-26 14:56:12 +06:00
|
|
|
],
|
|
|
|
|
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' },
|
|
|
|
|
],
|
2026-05-05 02:48:57 +06:00
|
|
|
riderPlan: 'single_rent',
|
2026-04-26 14:56:12 +06:00
|
|
|
nomineeDetails: { name: 'Rashid', phone: '01798765432', relationship: 'Brother', nid: '9876543210987' },
|
2026-05-05 00:29:47 +06:00
|
|
|
employmentInfo: { company: ' ssl', monthlyEarning: 2000, whyEV: 'Better income than fuel bike', experience: '2 years' },
|
2026-04-26 14:56:12 +06:00
|
|
|
notes: [],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
},
|
|
|
|
|
];
|
2026-04-22 01:02:45 +06:00
|
|
|
|
|
|
|
|
const statusColors: Record<string, string> = {
|
|
|
|
|
pending: 'bg-amber-100 text-amber-700',
|
2026-04-26 14:56:12 +06:00
|
|
|
documents_needed: 'bg-orange-100 text-orange-700',
|
|
|
|
|
under_review: 'bg-blue-100 text-blue-700',
|
|
|
|
|
risk_check: 'bg-purple-100 text-purple-700',
|
2026-04-22 01:02:45 +06:00
|
|
|
approved: 'bg-green-100 text-green-700',
|
|
|
|
|
rejected: 'bg-red-100 text-red-700',
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
const statusLabels: Record<string, string> = {
|
|
|
|
|
pending: 'Pending',
|
|
|
|
|
documents_needed: 'Documents Needed',
|
|
|
|
|
under_review: 'Under Review',
|
|
|
|
|
risk_check: 'Risk Check',
|
|
|
|
|
approved: 'Approved',
|
|
|
|
|
rejected: 'Rejected',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const stageLabels: Record<string, string> = {
|
|
|
|
|
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<string, string> = {
|
|
|
|
|
app: 'Mobile App',
|
|
|
|
|
web: 'Website',
|
|
|
|
|
walkin: 'Walk-in',
|
|
|
|
|
referral: 'Referral',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const sourceColors: Record<string, string> = {
|
|
|
|
|
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<string, string> = {
|
|
|
|
|
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<string, any> = {
|
|
|
|
|
biker: Bike,
|
|
|
|
|
investor: DollarSign,
|
2026-05-05 01:34:10 +06:00
|
|
|
swapstation: Store,
|
2026-04-26 14:56:12 +06:00
|
|
|
merchant: Users,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const typeColors: Record<string, string> = {
|
|
|
|
|
biker: 'bg-blue-100 text-blue-700',
|
|
|
|
|
investor: 'bg-purple-100 text-purple-700',
|
2026-05-05 01:34:10 +06:00
|
|
|
swapstation: 'bg-green-100 text-green-700',
|
2026-04-26 14:56:12 +06:00
|
|
|
merchant: 'bg-orange-100 text-orange-700',
|
|
|
|
|
general: 'bg-slate-100 text-slate-700',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const sourceIcons: Record<string, any> = {
|
|
|
|
|
app: Phone,
|
|
|
|
|
web: Globe,
|
|
|
|
|
walkin: User,
|
|
|
|
|
referral: Users,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function RequestsPage() {
|
2026-05-05 02:57:02 +06:00
|
|
|
const router = useRouter();
|
2026-05-05 01:34:10 +06:00
|
|
|
const [activeTab, setActiveTab] = useState<'all' | 'biker' | 'investor' | 'swapstation' | 'merchant'>('all');
|
2026-04-26 14:56:12 +06:00
|
|
|
const [requests, setRequests] = useState<Request[]>(mockRequests);
|
|
|
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
|
|
|
const [statusFilter, setStatusFilter] = useState('all');
|
2026-05-05 02:19:38 +06:00
|
|
|
const [hubFilter, setHubFilter] = useState('all');
|
2026-04-26 14:56:12 +06:00
|
|
|
const [selectedRequest, setSelectedRequest] = useState<Request | null>(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<string | null>(null);
|
|
|
|
|
|
|
|
|
|
const handleApproveDocument = (docId: string) => {
|
|
|
|
|
if (!selectedRequest) return;
|
|
|
|
|
const updatedRequests = requests.map(req => {
|
|
|
|
|
if (req.id === selectedRequest.id) {
|
|
|
|
|
return {
|
|
|
|
|
...req,
|
2026-05-05 00:29:47 +06:00
|
|
|
requiredDocuments: req.requiredDocuments.map(doc =>
|
2026-04-26 14:56:12 +06:00
|
|
|
doc.id === docId ? { ...doc, status: 'approved' as const } : doc
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return req;
|
|
|
|
|
});
|
|
|
|
|
setRequests(updatedRequests);
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.success('Document approved!');
|
2026-04-26 14:56:12 +06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRejectDocument = (docId: string, reason: string) => {
|
|
|
|
|
if (!selectedRequest || !reason.trim()) {
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.error('Please enter a reason for rejection');
|
2026-04-26 14:56:12 +06:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const updatedRequests = requests.map(req => {
|
|
|
|
|
if (req.id === selectedRequest.id) {
|
|
|
|
|
return {
|
|
|
|
|
...req,
|
2026-05-05 00:29:47 +06:00
|
|
|
requiredDocuments: req.requiredDocuments.map(doc =>
|
2026-04-26 14:56:12 +06:00
|
|
|
doc.id === docId ? { ...doc, status: 'rejected' as const, rejectedReason: reason } : doc
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return req;
|
|
|
|
|
});
|
|
|
|
|
setRequests(updatedRequests);
|
|
|
|
|
setRejectReason('');
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.error('Document rejected!');
|
2026-04-26 14:56:12 +06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
2026-05-05 02:19:38 +06:00
|
|
|
const matchesHub = hubFilter === 'all' || req.sourceDetails?.toLowerCase().includes(hubFilter.toLowerCase());
|
|
|
|
|
return matchesTab && matchesSearch && matchesStatus && matchesHub;
|
2026-04-26 14:56:12 +06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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,
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [
|
|
|
|
|
...req.smsHistory,
|
|
|
|
|
{ id: `sms-${Date.now()}`, message: messageText, sentAt: new Date().toISOString(), sentBy: 'admin' }
|
2026-04-26 14:56:12 +06:00
|
|
|
],
|
|
|
|
|
notes: [...req.notes, messageText]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return req;
|
|
|
|
|
});
|
|
|
|
|
setRequests(updatedRequests);
|
|
|
|
|
setShowMessageModal(false);
|
|
|
|
|
setMessageText('');
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.success(`SMS sent to ${selectedRequest.phone}`);
|
2026-04-26 14:56:12 +06:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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';
|
2026-05-05 00:29:47 +06:00
|
|
|
successMessage = selectedRequest.type === 'biker'
|
2026-04-26 14:56:12 +06:00
|
|
|
? `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.`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
const updatedRequests = requests.map(req =>
|
2026-04-26 14:56:12 +06:00
|
|
|
req.id === selectedRequest.id ? { ...req, status: newStatus } : req
|
|
|
|
|
);
|
|
|
|
|
setRequests(updatedRequests);
|
|
|
|
|
setShowActionModal(false);
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.success(successMessage);
|
2026-04-26 14:56:12 +06:00
|
|
|
};
|
2026-04-22 01:02:45 +06:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="p-4 lg:p-6">
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-6">
|
|
|
|
|
<div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<h1 className="text-2xl lg:text-3xl font-extrabold text-slate-800">Requests & Verification</h1>
|
|
|
|
|
<p className="text-sm text-slate-500 mt-1">Manage biker, investor, and shop requests</p>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setShowNewApplicationModal(true)}
|
|
|
|
|
className="py-2 px-4 bg-accent text-white rounded-lg text-sm font-medium hover:bg-accent-dark flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Plus className="w-4 h-4" /> New Application
|
|
|
|
|
</button>
|
2026-04-22 01:02:45 +06:00
|
|
|
<button className="py-2 px-4 border border-slate-200 rounded-lg text-sm font-medium text-slate-600 hover:bg-slate-50 flex items-center gap-2">
|
|
|
|
|
<Download className="w-4 h-4" /> Export
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
2026-04-22 01:02:45 +06:00
|
|
|
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="w-12 h-12 rounded-xl bg-amber-50 flex items-center justify-center">
|
|
|
|
|
<Clock className="w-6 h-6 text-amber-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<p className="text-2xl font-extrabold text-slate-800">{pending}</p>
|
|
|
|
|
<p className="text-sm text-slate-500">Pending</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="w-12 h-12 rounded-xl bg-blue-50 flex items-center justify-center">
|
|
|
|
|
<Eye className="w-6 h-6 text-blue-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-2xl font-extrabold text-slate-800">{underReview}</p>
|
|
|
|
|
<p className="text-sm text-slate-500">Under Review</p>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="w-12 h-12 rounded-xl bg-green-50 flex items-center justify-center">
|
|
|
|
|
<Check className="w-6 h-6 text-green-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<p className="text-2xl font-extrabold text-slate-800">{approved}</p>
|
2026-04-22 01:02:45 +06:00
|
|
|
<p className="text-sm text-slate-500">Approved</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
|
|
|
|
|
<div className="flex items-center gap-3">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="w-12 h-12 rounded-xl bg-slate-100 flex items-center justify-center">
|
|
|
|
|
<Users className="w-6 h-6 text-slate-600" />
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<p className="text-2xl font-extrabold text-slate-800">{requests.length}</p>
|
|
|
|
|
<p className="text-sm text-slate-500">Total Requests</p>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="bg-white rounded-xl shadow-sm border border-slate-100 mb-6">
|
2026-04-22 01:02:45 +06:00
|
|
|
<div className="p-4 border-b border-slate-100">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="flex flex-col lg:flex-row lg:items-center gap-4">
|
|
|
|
|
<div className="flex items-center gap-2 flex-wrap">
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setActiveTab('all')}
|
|
|
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium ${activeTab === 'all' ? 'bg-slate-800 text-white' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
|
|
|
|
>
|
|
|
|
|
All
|
|
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setActiveTab('biker')}
|
|
|
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium flex items-center gap-1 ${activeTab === 'biker' ? 'bg-slate-800 text-white' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
|
|
|
|
>
|
|
|
|
|
<Bike className="w-4 h-4" /> Bikers
|
|
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setActiveTab('investor')}
|
|
|
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium flex items-center gap-1 ${activeTab === 'investor' ? 'bg-slate-800 text-white' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
|
|
|
|
>
|
|
|
|
|
<DollarSign className="w-4 h-4" /> Investors
|
|
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-05-05 01:34:10 +06:00
|
|
|
onClick={() => setActiveTab('swapstation')}
|
|
|
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium flex items-center gap-1 ${activeTab === 'swapstation' ? 'bg-slate-800 text-white' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
2026-05-05 01:34:10 +06:00
|
|
|
<Store className="w-4 h-4" /> Swap Stations
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setActiveTab('merchant')}
|
|
|
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium flex items-center gap-1 ${activeTab === 'merchant' ? 'bg-slate-800 text-white' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'}`}
|
|
|
|
|
>
|
|
|
|
|
<Users className="w-4 h-4" /> Merchants
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Search by name, phone, email..."
|
|
|
|
|
value={searchQuery}
|
|
|
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
|
|
|
className="w-full lg:w-64 pl-9 pr-4 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<select
|
|
|
|
|
value={statusFilter}
|
|
|
|
|
onChange={(e) => setStatusFilter(e.target.value)}
|
|
|
|
|
className="py-2 px-3 border border-slate-200 rounded-lg text-sm font-medium text-slate-600"
|
|
|
|
|
>
|
|
|
|
|
<option value="all">All Status</option>
|
|
|
|
|
<option value="pending">Pending</option>
|
|
|
|
|
<option value="documents_needed">Documents Needed</option>
|
|
|
|
|
<option value="under_review">Under Review</option>
|
|
|
|
|
<option value="risk_check">Risk Check</option>
|
|
|
|
|
<option value="approved">Approved</option>
|
|
|
|
|
<option value="rejected">Rejected</option>
|
|
|
|
|
</select>
|
2026-05-05 02:19:38 +06:00
|
|
|
<select
|
|
|
|
|
value={hubFilter}
|
|
|
|
|
onChange={(e) => setHubFilter(e.target.value)}
|
|
|
|
|
className="py-2 px-3 border border-slate-200 rounded-lg text-sm font-medium text-slate-600"
|
|
|
|
|
>
|
|
|
|
|
<option value="all">All Hubs</option>
|
|
|
|
|
<option value="Gulshan">Gulshan Hub</option>
|
|
|
|
|
<option value="Banani">Banani Hub</option>
|
|
|
|
|
<option value="Dhanmondi">Dhanmondi Hub</option>
|
|
|
|
|
<option value="Mirpur">Mirpur Hub</option>
|
|
|
|
|
<option value="Uttara">Uttara Hub</option>
|
|
|
|
|
</select>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="divide-y divide-slate-50">
|
2026-04-26 14:56:12 +06:00
|
|
|
{filteredRequests.map(request => {
|
|
|
|
|
const TypeIcon = typeIcons[request.type];
|
|
|
|
|
return (
|
|
|
|
|
<Link key={request.id} href={`/admin/kyc/${request.id}`} className="block p-5 hover:bg-slate-50 transition-colors">
|
|
|
|
|
<div className="flex flex-col lg:flex-row lg:items-start gap-4">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<div className="w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center">
|
|
|
|
|
<User className="w-6 h-6 text-slate-600" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<p className="font-semibold text-slate-800">{request.name}</p>
|
|
|
|
|
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full ${typeColors[request.type]}`}>
|
|
|
|
|
<TypeIcon className="w-3 h-3" /> {request.type}
|
|
|
|
|
</span>
|
|
|
|
|
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full ${sourceColors[request.applicationSource]}`}>
|
|
|
|
|
<Phone className="w-3 h-3" /> {sourceLabels[request.applicationSource]}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-sm text-slate-500 flex items-center gap-2">
|
|
|
|
|
<Phone className="w-3 h-3" /> {request.phone}
|
|
|
|
|
<span className="text-slate-300">|</span>
|
|
|
|
|
<MapPin className="w-3 h-3" /> {request.location}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="flex-1">
|
|
|
|
|
<div className="flex flex-wrap gap-4 text-sm text-slate-600">
|
|
|
|
|
<p className="flex items-center gap-1">
|
|
|
|
|
<Clock className="w-3 h-3" /> {request.submittedAt}
|
|
|
|
|
</p>
|
|
|
|
|
<p className="flex items-center gap-1">
|
|
|
|
|
<FileText className="w-3 h-3" /> Docs: {request.requiredDocuments.filter(d => d.status === 'uploaded' || d.status === 'approved').length}/{request.requiredDocuments.length}
|
|
|
|
|
</p>
|
|
|
|
|
{request.type === 'biker' && request.verificationStage && (
|
|
|
|
|
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2 py-0.5 rounded-full bg-cyan-100 text-cyan-700`}>
|
|
|
|
|
<Check className="w-3 h-3" /> {stageLabels[request.verificationStage]}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
2026-05-05 02:37:38 +06:00
|
|
|
{request.smsHistory.length > 0 && (
|
2026-04-26 14:56:12 +06:00
|
|
|
<p className="flex items-center gap-1 text-blue-600">
|
2026-05-05 02:37:38 +06:00
|
|
|
<MessageSquare className="w-3 h-3" /> {request.smsHistory.length} msg
|
2026-04-26 14:56:12 +06:00
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${statusColors[request.status]}`}>
|
|
|
|
|
{request.status === 'pending' && <Clock className="w-3 h-3" />}
|
|
|
|
|
{request.status === 'documents_needed' && <AlertCircle className="w-3 h-3" />}
|
|
|
|
|
{request.status === 'under_review' && <Eye className="w-3 h-3" />}
|
|
|
|
|
{request.status === 'approved' && <Check className="w-3 h-3" />}
|
|
|
|
|
{request.status === 'rejected' && <X className="w-3 h-3" />}
|
|
|
|
|
{statusLabels[request.status]}
|
|
|
|
|
</span>
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
{request.status !== 'approved' && request.status !== 'rejected' && (
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
{request.type === 'biker' && request.status === 'under_review' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => handleAction(request, 'approve')}
|
|
|
|
|
className="py-1.5 px-3 bg-green-600 text-white text-xs font-semibold rounded-lg hover:bg-green-700 flex items-center gap-1"
|
|
|
|
|
>
|
|
|
|
|
<Check className="w-3 h-3" /> Make Biker
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{request.type === 'investor' && request.status === 'under_review' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => handleAction(request, 'approve')}
|
|
|
|
|
className="py-1.5 px-3 bg-purple-600 text-white text-xs font-semibold rounded-lg hover:bg-purple-700 flex items-center gap-1"
|
|
|
|
|
>
|
|
|
|
|
<DollarSign className="w-3 h-3" /> Make Investor
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-05-05 01:34:10 +06:00
|
|
|
{request.type === 'swapstation' && request.status === 'under_review' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => handleAction(request, 'approve')}
|
|
|
|
|
className="py-1.5 px-3 bg-green-600 text-white text-xs font-semibold rounded-lg hover:bg-green-700 flex items-center gap-1"
|
|
|
|
|
>
|
|
|
|
|
<Store className="w-3 h-3" /> Approve Shop
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-05-05 02:57:02 +06:00
|
|
|
onClick={(e) => { e.preventDefault(); router.push(`/admin/kyc/${request.id}`); }}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="py-1.5 px-3 border border-slate-200 text-slate-600 text-xs font-semibold rounded-lg hover:bg-slate-50 flex items-center gap-1"
|
|
|
|
|
>
|
|
|
|
|
<Eye className="w-3 h-3" /> Details
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Link>
|
|
|
|
|
);
|
|
|
|
|
})}
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
{filteredRequests.length === 0 && (
|
|
|
|
|
<div className="p-12 text-center">
|
|
|
|
|
<Users className="w-12 h-12 text-slate-300 mx-auto mb-4" />
|
|
|
|
|
<p className="text-slate-500">No requests found</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{showMessageModal && selectedRequest && (
|
|
|
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
|
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-lg">
|
|
|
|
|
<div className="p-4 border-b border-slate-100 flex items-center justify-between">
|
2026-05-05 02:37:38 +06:00
|
|
|
<h3 className="font-semibold text-slate-800">SMS to {selectedRequest.phone}</h3>
|
2026-04-26 14:56:12 +06:00
|
|
|
<button onClick={() => setShowMessageModal(false)} className="p-1 hover:bg-slate-100 rounded">
|
|
|
|
|
<X className="w-5 h-5 text-slate-400" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4 space-y-4">
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg">
|
|
|
|
|
<p className="text-sm text-slate-600">Phone: {selectedRequest.phone}</p>
|
|
|
|
|
<p className="text-sm text-slate-600">Type: {selectedRequest.type}</p>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-sm font-medium text-slate-600 mb-2 block">Quick Messages</label>
|
|
|
|
|
<div className="flex flex-wrap gap-2">
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-05-05 02:32:51 +06:00
|
|
|
onClick={() => setMessageText('Please upload your required documents to proceed with your KYC application.')}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="px-3 py-1.5 bg-slate-100 text-slate-600 text-xs rounded-lg hover:bg-slate-200"
|
|
|
|
|
>
|
2026-05-05 02:32:51 +06:00
|
|
|
Documents Required
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-05-05 02:32:51 +06:00
|
|
|
onClick={() => setMessageText('Your KYC application is now under review. We will notify you once the process is completed.')}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="px-3 py-1.5 bg-slate-100 text-slate-600 text-xs rounded-lg hover:bg-slate-200"
|
|
|
|
|
>
|
2026-05-05 02:32:51 +06:00
|
|
|
Under Review
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-05-05 02:32:51 +06:00
|
|
|
onClick={() => setMessageText('Your application is pending final approval. You will be notified once approved.')}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="px-3 py-1.5 bg-slate-100 text-slate-600 text-xs rounded-lg hover:bg-slate-200"
|
|
|
|
|
>
|
2026-05-05 02:32:51 +06:00
|
|
|
Pending Approval
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm font-medium text-slate-600 mb-2 block">Message</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={messageText}
|
|
|
|
|
onChange={(e) => setMessageText(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
rows={4}
|
|
|
|
|
placeholder="Type your message..."
|
|
|
|
|
></textarea>
|
|
|
|
|
<p className="text-xs text-slate-400 mt-1">This message will be sent via SMS to {selectedRequest.phone}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4 border-t border-slate-100 flex justify-end gap-2">
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setShowMessageModal(false)}
|
|
|
|
|
className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={handleSendMessage}
|
|
|
|
|
disabled={!messageText.trim()}
|
|
|
|
|
className="px-4 py-2 bg-accent text-white rounded-lg text-sm hover:bg-accent-dark disabled:opacity-50 flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Send className="w-4 h-4" /> Send Message
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{showDetailsModal && selectedRequest && (
|
|
|
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
|
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden">
|
|
|
|
|
<div className="p-4 border-b border-slate-100 flex items-center justify-between">
|
|
|
|
|
<h3 className="font-semibold text-slate-800">Request Details - {selectedRequest.id}</h3>
|
|
|
|
|
<button onClick={() => setShowDetailsModal(false)} className="p-1 hover:bg-slate-100 rounded">
|
|
|
|
|
<X className="w-5 h-5 text-slate-400" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4 overflow-y-auto max-h-[70vh]">
|
|
|
|
|
<div className="grid grid-cols-2 gap-4 mb-6">
|
|
|
|
|
<div className="bg-slate-50 p-4 rounded-lg">
|
|
|
|
|
<p className="text-xs text-slate-500">Name</p>
|
|
|
|
|
<p className="font-semibold text-slate-800">{selectedRequest.name}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-slate-50 p-4 rounded-lg">
|
|
|
|
|
<p className="text-xs text-slate-500">Phone</p>
|
|
|
|
|
<p className="font-semibold text-slate-800">{selectedRequest.phone}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-slate-50 p-4 rounded-lg">
|
|
|
|
|
<p className="text-xs text-slate-500">Email</p>
|
|
|
|
|
<p className="font-semibold text-slate-800">{selectedRequest.email}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="bg-slate-50 p-4 rounded-lg">
|
|
|
|
|
<p className="text-xs text-slate-500">Location</p>
|
|
|
|
|
<p className="font-semibold text-slate-800">{selectedRequest.location}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{selectedRequest.type === 'biker' && (
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<h4 className="font-semibold text-slate-700 mb-3 flex items-center gap-2">
|
|
|
|
|
<Bike className="w-5 h-5 text-accent" /> Bike Details
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="bg-slate-50 p-4 rounded-lg">
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs text-slate-500 block mb-1">Bike Model</label>
|
2026-05-05 00:29:47 +06:00
|
|
|
<select
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
value={selectedRequest.bikeRequested || ''}
|
|
|
|
|
onChange={(e) => {
|
2026-05-05 00:29:47 +06:00
|
|
|
const updated = requests.map(r =>
|
|
|
|
|
r.id === selectedRequest.id
|
2026-04-26 14:56:12 +06:00
|
|
|
? { ...r, bikeRequested: e.target.value }
|
|
|
|
|
: r
|
|
|
|
|
);
|
|
|
|
|
setRequests(updated);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Bike Model</option>
|
|
|
|
|
<option value="AIMA Lightning">AIMA Lightning</option>
|
|
|
|
|
<option value="Yadea DT3">Yadea DT3</option>
|
|
|
|
|
<option value="Etron ET50">Etron ET50</option>
|
|
|
|
|
<option value="AIMA i3">AIMA i3</option>
|
|
|
|
|
<option value="TVS iQube">TVS iQube</option>
|
|
|
|
|
<option value="Benling Aura">Benling Aura</option>
|
|
|
|
|
<option value="Okinawa Ridge">Okinawa Ridge</option>
|
|
|
|
|
<option value="Hero Optima">Hero Optima</option>
|
|
|
|
|
<option value="Ampere Magnus">Ampere Magnus</option>
|
|
|
|
|
<option value="Other">Other</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs text-slate-500 block mb-1">Brand</label>
|
2026-05-05 00:29:47 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Enter details"
|
|
|
|
|
value={selectedRequest.evAllocation?.bikeModel || ''}
|
|
|
|
|
onChange={(e) => {
|
2026-05-05 00:29:47 +06:00
|
|
|
const updated = requests.map(r =>
|
|
|
|
|
r.id === selectedRequest.id
|
2026-04-26 14:56:12 +06:00
|
|
|
? { ...r, evAllocation: { ...r.evAllocation!, bikeModel: e.target.value } }
|
|
|
|
|
: r
|
|
|
|
|
);
|
|
|
|
|
setRequests(updated);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<h4 className="font-semibold text-slate-700 mb-3 flex items-center gap-2">
|
|
|
|
|
<FileText className="w-5 h-5 text-accent" /> Documents
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{selectedRequest.requiredDocuments.map((doc, idx) => (
|
|
|
|
|
<div key={idx} className="flex items-center justify-between p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{doc.status === 'pending' ? (
|
|
|
|
|
<Upload className="w-4 h-4 text-amber-400" />
|
|
|
|
|
) : doc.status === 'uploaded' ? (
|
|
|
|
|
<FileText className="w-4 h-4 text-blue-400" />
|
|
|
|
|
) : doc.status === 'approved' ? (
|
|
|
|
|
<CheckCircle className="w-4 h-4 text-green-500" />
|
|
|
|
|
) : (
|
|
|
|
|
<XCircle className="w-4 h-4 text-red-500" />
|
|
|
|
|
)}
|
|
|
|
|
<div>
|
|
|
|
|
<span className="text-sm text-slate-600">{doc.name}</span>
|
|
|
|
|
{doc.uploadedAt && <span className="text-xs text-slate-400 ml-2">{doc.uploadedAt}</span>}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{doc.status === 'pending' && (
|
|
|
|
|
<span className="text-xs text-amber-600 flex items-center gap-1">
|
|
|
|
|
<Upload className="w-3 h-3" /> Waiting for upload
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{(doc.status === 'uploaded' || doc.status === 'approved') && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => handleApproveDocument(doc.id)}
|
|
|
|
|
className="p-1.5 bg-green-100 text-green-600 rounded-lg hover:bg-green-200"
|
|
|
|
|
title="Approve"
|
|
|
|
|
>
|
|
|
|
|
<CheckCircle className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{(doc.status === 'uploaded' || doc.status === 'approved') && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => {
|
|
|
|
|
const reason = prompt('Enter rejection reason:');
|
|
|
|
|
if (reason) handleRejectDocument(doc.id, reason);
|
|
|
|
|
}}
|
|
|
|
|
className="p-1.5 bg-red-100 text-red-600 rounded-lg hover:bg-red-200"
|
|
|
|
|
title="Reject"
|
|
|
|
|
>
|
|
|
|
|
<XCircle className="w-4 h-4" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{doc.status === 'rejected' && (
|
|
|
|
|
<span className="inline-flex items-center gap-1 text-xs font-medium px-2 py-1 rounded-full bg-red-100 text-red-700">
|
|
|
|
|
<XCircle className="w-3 h-3" /> Rejected
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{doc.status === 'approved' && (
|
|
|
|
|
<span className="inline-flex items-center gap-1 text-xs font-medium px-2 py-1 rounded-full bg-green-100 text-green-700">
|
|
|
|
|
<CheckCircle className="w-3 h-3" /> Approved
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{doc.status === 'rejected' && doc.rejectedReason && (
|
|
|
|
|
<div className="text-xs text-red-600 mt-1">Reason: {doc.rejectedReason}</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{selectedRequest.type === 'biker' && selectedRequest.bikeRequested && (
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<h4 className="font-semibold text-slate-700 mb-3 flex items-center gap-2">
|
|
|
|
|
<Camera className="w-5 h-5 text-accent" /> Bike Images (When Taking Bike)
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
{['Front', 'Back', 'Left Side', 'Right Side'].map((view) => (
|
|
|
|
|
<div key={view} className="aspect-square bg-slate-100 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:bg-slate-200 transition-colors">
|
|
|
|
|
<Camera className="w-8 h-8 text-slate-400 mb-2" />
|
|
|
|
|
<span className="text-xs text-slate-500">{view}</span>
|
|
|
|
|
<span className="text-xs text-amber-500 mt-1">Upload</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<button className="mt-3 px-4 py-2 border border-dashed border-slate-300 rounded-lg text-sm text-slate-500 hover:border-accent hover:text-accent w-full">
|
|
|
|
|
+ Add More Images
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 02:37:38 +06:00
|
|
|
{selectedRequest.smsHistory.length > 0 && (
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="mb-6">
|
2026-05-05 02:37:38 +06:00
|
|
|
<h4 className="font-semibold text-slate-700 mb-3">SMS History</h4>
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="space-y-2">
|
2026-05-05 02:37:38 +06:00
|
|
|
{selectedRequest.smsHistory.slice().reverse().map((msg) => (
|
|
|
|
|
<div key={msg.id} className={`p-3 rounded-lg ${msg.sentBy === 'admin' ? 'bg-blue-50' : 'bg-slate-50'}`}>
|
2026-04-26 14:56:12 +06:00
|
|
|
<div className="flex items-center justify-between mb-1">
|
2026-05-05 02:37:38 +06:00
|
|
|
<span className={`text-xs font-medium ${msg.sentBy === 'admin' ? 'text-blue-600' : 'text-slate-600'}`}>
|
|
|
|
|
{msg.sentBy === 'admin' ? 'Admin' : 'User'}
|
2026-04-26 14:56:12 +06:00
|
|
|
</span>
|
2026-05-05 02:37:38 +06:00
|
|
|
<span className="text-xs text-slate-400">{msg.sentAt}</span>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
|
|
|
|
<p className="text-sm text-slate-700">{msg.message}</p>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{selectedRequest.notes.length > 0 && (
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<h4 className="font-semibold text-slate-700 mb-3">Notes</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{selectedRequest.notes.map((note, idx) => (
|
|
|
|
|
<div key={idx} className="p-3 bg-amber-50 rounded-lg">
|
|
|
|
|
<p className="text-sm text-slate-700">{note}</p>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4 border-t border-slate-100 flex justify-end gap-2">
|
|
|
|
|
{selectedRequest.status !== 'approved' && selectedRequest.status !== 'rejected' && (
|
|
|
|
|
<>
|
|
|
|
|
{selectedRequest.type === 'biker' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => { setShowDetailsModal(false); handleAction(selectedRequest, 'approve'); }}
|
|
|
|
|
className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Check className="w-4 h-4" /> Make Biker
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{selectedRequest.type === 'investor' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => { setShowDetailsModal(false); handleAction(selectedRequest, 'approve'); }}
|
|
|
|
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700 flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<DollarSign className="w-4 h-4" /> Make Investor
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-05-05 01:34:10 +06:00
|
|
|
{selectedRequest.type === 'swapstation' && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => { setShowDetailsModal(false); handleAction(selectedRequest, 'approve'); }}
|
|
|
|
|
className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Store className="w-4 h-4" /> Approve Shop
|
|
|
|
|
</button>
|
2026-04-22 01:02:45 +06:00
|
|
|
)}
|
2026-04-26 14:56:12 +06:00
|
|
|
</>
|
|
|
|
|
)}
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setShowDetailsModal(false)}
|
|
|
|
|
className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50"
|
|
|
|
|
>
|
|
|
|
|
Close
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{showActionModal && selectedRequest && (
|
|
|
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
|
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
|
|
|
|
|
<div className="p-4 border-b border-slate-100">
|
|
|
|
|
<h3 className="font-semibold text-slate-800">
|
|
|
|
|
{actionType === 'approve' ? 'Approve Request' : actionType === 'reject' ? 'Reject Request' : 'Request Documents'}
|
|
|
|
|
</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<p className="text-sm text-slate-600 mb-4">
|
|
|
|
|
{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.`}
|
|
|
|
|
</p>
|
|
|
|
|
<div className="bg-slate-50 p-3 rounded-lg">
|
|
|
|
|
<p className="text-sm"><strong>Name:</strong> {selectedRequest.name}</p>
|
|
|
|
|
<p className="text-sm"><strong>Phone:</strong> {selectedRequest.phone}</p>
|
|
|
|
|
<p className="text-sm"><strong>Type:</strong> {selectedRequest.type}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4 border-t border-slate-100 flex justify-end gap-2">
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={() => setShowActionModal(false)}
|
|
|
|
|
className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button
|
2026-04-26 14:56:12 +06:00
|
|
|
onClick={confirmAction}
|
2026-05-05 00:29:47 +06:00
|
|
|
className={`px-4 py-2 text-white rounded-lg text-sm flex items-center gap-2 ${actionType === 'approve' ? 'bg-green-600 hover:bg-green-700' :
|
2026-04-26 14:56:12 +06:00
|
|
|
actionType === 'reject' ? 'bg-red-600 hover:bg-red-700' :
|
2026-05-05 00:29:47 +06:00
|
|
|
'bg-amber-600 hover:bg-amber-700'
|
|
|
|
|
}`}
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
|
|
|
|
{actionType === 'approve' && <><Check className="w-4 h-4" /> Confirm Approve</>}
|
|
|
|
|
{actionType === 'reject' && <><X className="w-4 h-4" /> Confirm Reject</>}
|
|
|
|
|
{actionType === 'documents' && <><Send className="w-4 h-4" /> Send Request</>}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<NewApplicationModal
|
|
|
|
|
isOpen={showNewApplicationModal}
|
|
|
|
|
onClose={() => 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: [],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
};
|
|
|
|
|
setRequests([newRequest, ...requests]);
|
|
|
|
|
setShowNewApplicationModal(false);
|
2026-05-05 02:44:32 +06:00
|
|
|
toast.success('Application created successfully!');
|
2026-04-26 14:56:12 +06:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function NewApplicationModal({ isOpen, onClose, onSave }: { isOpen: boolean; onClose: () => void; onSave: (data: Partial<Request>) => void }) {
|
2026-05-05 00:29:47 +06:00
|
|
|
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;
|
2026-05-05 01:21:37 +06:00
|
|
|
investmentPlan: { planName: string; planType: string; bikes: number; amount: number; monthlyReturn: number; expectedROI: number };
|
2026-05-05 01:34:10 +06:00
|
|
|
swapStationPlan: { planType: string; cabinets: number; amount: number; batteries: number; monthlyRent: number };
|
2026-05-05 01:47:27 +06:00
|
|
|
merchantPlan: { companyCategory: string; bikersRequested: number; amount: number; monthlyBudget: number; requiredArea: string };
|
2026-05-05 00:29:47 +06:00
|
|
|
requiredDocuments: Document[];
|
|
|
|
|
}>({
|
2026-04-26 14:56:12 +06:00
|
|
|
applicationSource: 'walkin',
|
|
|
|
|
sourceDetails: '',
|
2026-05-05 00:29:47 +06:00
|
|
|
type: 'biker',
|
|
|
|
|
status: 'pending',
|
|
|
|
|
hubId: '',
|
2026-04-26 14:56:12 +06:00
|
|
|
name: '',
|
|
|
|
|
phone: '',
|
2026-05-05 00:29:47 +06:00
|
|
|
altPhone: '',
|
2026-04-26 14:56:12 +06:00
|
|
|
email: '',
|
2026-05-05 00:29:47 +06:00
|
|
|
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' },
|
2026-05-05 00:34:33 +06:00
|
|
|
membershipType: 'basic',
|
2026-05-05 00:29:47 +06:00
|
|
|
referralCode: '',
|
|
|
|
|
referredBy: '',
|
|
|
|
|
employmentInfo: { company: '', monthlyEarning: 0, whyEV: '', experience: '' },
|
|
|
|
|
nomineeDetails: { name: '', phone: '', relationship: '', nid: '', dob: '', email: '', address: '', percentage: 100 },
|
2026-04-26 14:56:12 +06:00
|
|
|
riderPlan: 'daily_rent',
|
2026-05-05 00:29:47 +06:00
|
|
|
evModel: '',
|
|
|
|
|
evCondition: 'new',
|
|
|
|
|
scheduleDate: '',
|
2026-05-05 01:21:37 +06:00
|
|
|
investmentPlan: { planName: '', planType: '', bikes: 1, amount: 0, monthlyReturn: 0, expectedROI: 0 },
|
2026-05-05 01:34:10 +06:00
|
|
|
swapStationPlan: { planType: '', cabinets: 8, amount: 0, batteries: 0, monthlyRent: 0 },
|
2026-05-05 01:47:27 +06:00
|
|
|
merchantPlan: { companyCategory: '', bikersRequested: 0, amount: 0, monthlyBudget: 0, requiredArea: '' },
|
2026-05-05 00:29:47 +06:00
|
|
|
requiredDocuments: [],
|
2026-04-26 14:56:12 +06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [step, setStep] = useState(1);
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
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;
|
|
|
|
|
});
|
2026-04-26 14:56:12 +06:00
|
|
|
};
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
const updateDocStatus = (docId: string, status: 'pending' | 'uploaded' | 'approved' | 'rejected') => {
|
2026-04-26 14:56:12 +06:00
|
|
|
setFormData(prev => ({
|
|
|
|
|
...prev,
|
2026-05-05 00:29:47 +06:00
|
|
|
requiredDocuments: prev.requiredDocuments.map(d =>
|
|
|
|
|
d.id === docId ? { ...d, status, uploadedAt: status === 'uploaded' ? new Date().toISOString().split('T')[0] : d.uploadedAt } : d
|
|
|
|
|
),
|
2026-04-26 14:56:12 +06:00
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
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<string, Document[]> = {
|
|
|
|
|
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' },
|
|
|
|
|
],
|
2026-05-05 01:34:10 +06:00
|
|
|
swapstation: [
|
2026-05-05 00:29:47 +06:00
|
|
|
{ id: 'doc1', name: 'Trade License', status: 'pending' },
|
|
|
|
|
{ id: 'doc2', name: 'Owner NID', status: 'pending' },
|
2026-05-05 01:34:10 +06:00
|
|
|
{ id: 'doc3', name: 'Station Photos', status: 'pending' },
|
|
|
|
|
{ id: 'doc4', name: 'Electricity Connection Bill', status: 'pending' },
|
|
|
|
|
{ id: 'doc5', name: 'Station Layout/Blueprint', status: 'pending' },
|
2026-05-05 00:29:47 +06:00
|
|
|
],
|
|
|
|
|
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: [],
|
2026-05-05 02:37:38 +06:00
|
|
|
smsHistory: [],
|
2026-05-05 00:29:47 +06:00
|
|
|
};
|
|
|
|
|
onSave(newRequest);
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
if (!isOpen) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-2 sm:p-4">
|
|
|
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-4xl max-h-[95vh] overflow-hidden flex flex-col">
|
|
|
|
|
<div className="p-4 border-b border-slate-100 flex items-center justify-between flex-shrink-0">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<h3 className="font-semibold text-slate-800 text-lg">New KYC Application</h3>
|
|
|
|
|
<p className="text-xs text-slate-500">Step {step} of 5 - {step === 1 ? 'Basic Info' : step === 2 ? 'Employment' : step === 3 ? 'Nominee' : step === 4 ? 'Plan Selection' : 'Documents'}</p>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<button onClick={onClose} className="p-1.5 hover:bg-slate-100 rounded-lg">
|
2026-04-26 14:56:12 +06:00
|
|
|
<X className="w-5 h-5 text-slate-400" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="px-4 py-3 bg-slate-50 border-b border-slate-100 flex-shrink-0">
|
|
|
|
|
<div className="flex items-center gap-2">
|
2026-04-26 14:56:12 +06:00
|
|
|
{[
|
2026-05-05 00:29:47 +06:00
|
|
|
{ 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) => (
|
|
|
|
|
<div key={s.n} className={`flex-1 flex items-center gap-2 ${idx > 0 ? 'justify-start pl-2' : ''}`}>
|
|
|
|
|
<div className={`w-7 h-7 rounded-full flex items-center justify-center text-xs font-semibold ${step >= s.n ? 'bg-accent text-white' : 'bg-slate-200 text-slate-500'
|
|
|
|
|
}`}>
|
|
|
|
|
{step > s.n ? <Check className="w-4 h-4" /> : <s.icon className="w-3.5 h-3.5" />}
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`text-xs hidden sm:inline ${step >= s.n ? 'text-slate-700' : 'text-slate-400'}`}>{s.l}</span>
|
|
|
|
|
{idx < 4 && <div className={`flex-1 h-0.5 rounded-full ${step > s.n ? 'bg-accent' : 'bg-slate-200'}`} />}
|
|
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="flex-1 overflow-y-auto p-4 sm:p-6">
|
2026-04-26 14:56:12 +06:00
|
|
|
{step === 1 && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-blue-50/80 p-4 rounded-xl border border-blue-100">
|
|
|
|
|
<h4 className="font-semibold text-blue-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Shield className="w-4 h-4" /> Application Details
|
2026-04-26 14:56:12 +06:00
|
|
|
</h4>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Application Source</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<select
|
|
|
|
|
value={formData.applicationSource}
|
|
|
|
|
onChange={(e) => updateField('applicationSource', e.target.value)}
|
2026-05-05 00:29:47 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
2026-05-05 00:29:47 +06:00
|
|
|
<option value="walkin">Walk-in</option>
|
|
|
|
|
<option value="app">Mobile App</option>
|
|
|
|
|
<option value="web">Website</option>
|
2026-04-26 14:56:12 +06:00
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">KYC Type</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<select
|
|
|
|
|
value={formData.type}
|
|
|
|
|
onChange={(e) => updateField('type', e.target.value)}
|
2026-05-05 00:29:47 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
|
|
|
|
<option value="biker">Biker (Rider)</option>
|
|
|
|
|
<option value="investor">Investor</option>
|
2026-05-05 01:34:10 +06:00
|
|
|
<option value="swapstation">Swap Station</option>
|
2026-04-26 14:56:12 +06:00
|
|
|
<option value="merchant">Merchant</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Status</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.status}
|
|
|
|
|
onChange={(e) => updateField('status', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="pending">Pending</option>
|
|
|
|
|
<option value="active">Active</option>
|
|
|
|
|
<option value="inactive">Inactive</option>
|
|
|
|
|
<option value="blocked">Blocked</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Hub</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.hubId}
|
|
|
|
|
onChange={(e) => updateField('hubId', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Hub</option>
|
|
|
|
|
<option value="hub1">Gulshan Hub</option>
|
|
|
|
|
<option value="hub2">Banani Hub</option>
|
|
|
|
|
<option value="hub3">Dhanmondi Hub</option>
|
|
|
|
|
<option value="hub4">Mirpur Hub</option>
|
|
|
|
|
<option value="hub5">Uttara Hub</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Membership Type</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.membershipType}
|
|
|
|
|
onChange={(e) => updateField('membershipType', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="basic">Basic</option>
|
|
|
|
|
<option value="premium">Premium</option>
|
|
|
|
|
<option value="vip">VIP</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Referral Code</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.referralCode}
|
|
|
|
|
onChange={(e) => updateField('referralCode', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Optional"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="bg-indigo-50/80 p-4 rounded-xl border border-indigo-100">
|
|
|
|
|
<h4 className="font-semibold text-indigo-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<User className="w-4 h-4" /> Personal Information
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
|
|
|
<div className="sm:col-span-2 lg:col-span-1">
|
2026-04-26 14:56:12 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Full Name *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.name}
|
|
|
|
|
onChange={(e) => updateField('name', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Enter full name"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Phone Number *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.phone}
|
|
|
|
|
onChange={(e) => updateField('phone', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="01XXXXXXXXX"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Alternate Phone</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.altPhone}
|
|
|
|
|
onChange={(e) => updateField('altPhone', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Optional"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={formData.email}
|
|
|
|
|
onChange={(e) => updateField('email', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="email@example.com"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Date of Birth</label>
|
|
|
|
|
<input
|
|
|
|
|
type="date"
|
|
|
|
|
value={formData.dateOfBirth}
|
|
|
|
|
onChange={(e) => updateField('dateOfBirth', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Gender</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.gender}
|
|
|
|
|
onChange={(e) => updateField('gender', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select</option>
|
|
|
|
|
<option value="male">Male</option>
|
|
|
|
|
<option value="female">Female</option>
|
|
|
|
|
<option value="other">Other</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Blood Group</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.bloodGroup}
|
|
|
|
|
onChange={(e) => updateField('bloodGroup', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select</option>
|
|
|
|
|
<option value="A+">A+</option>
|
|
|
|
|
<option value="A-">A-</option>
|
|
|
|
|
<option value="B+">B+</option>
|
|
|
|
|
<option value="B-">B-</option>
|
|
|
|
|
<option value="AB+">AB+</option>
|
|
|
|
|
<option value="AB-">AB-</option>
|
|
|
|
|
<option value="O+">O+</option>
|
|
|
|
|
<option value="O-">O-</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">NID *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nid}
|
|
|
|
|
onChange={(e) => updateField('nid', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="NID number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Passport</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.passport}
|
|
|
|
|
onChange={(e) => updateField('passport', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Passport number (optional)"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="bg-emerald-50/80 p-4 rounded-xl border border-emerald-100">
|
|
|
|
|
<h4 className="font-semibold text-emerald-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<MapPin className="w-4 h-4" /> Present Address
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Address Line 1 *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.presentAddress.line1}
|
|
|
|
|
onChange={(e) => 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"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Address Line 2</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.presentAddress.line2}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.line2', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Additional info"
|
2026-04-26 14:56:12 +06:00
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Division *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.presentAddress.division}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.division', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Division</option>
|
|
|
|
|
<option value="Dhaka">Dhaka</option>
|
|
|
|
|
<option value="Chattogram">Chattogram</option>
|
|
|
|
|
<option value="Khulna">Khulna</option>
|
|
|
|
|
<option value="Barishal">Barishal</option>
|
|
|
|
|
<option value="Rajshahi">Rajshahi</option>
|
|
|
|
|
<option value="Rangpur">Rangpur</option>
|
|
|
|
|
<option value="Mymensingh">Mymensingh</option>
|
|
|
|
|
<option value="Sylhet">Sylhet</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">District *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.presentAddress.district}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.district', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="District"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Zip Code *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.presentAddress.zip}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.zip', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Zip"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Country *</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.presentAddress.country}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.country', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Country"
|
2026-04-26 14:56:12 +06:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="bg-red-50/80 p-4 rounded-xl border border-red-100">
|
|
|
|
|
<h4 className="font-semibold text-red-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<MapPinned className="w-4 h-4" /> Permanent Address
|
2026-04-26 14:56:12 +06:00
|
|
|
</h4>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="flex items-center justify-between mb-3">
|
|
|
|
|
<label className="flex items-center gap-2 cursor-pointer">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={formData.permanentAddress.sameAsPresent}
|
|
|
|
|
onChange={(e) => updateField('permanentAddress.sameAsPresent', e.target.checked)}
|
|
|
|
|
className="w-4 h-4 text-accent rounded"
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-xs text-slate-600">Same as Present Address</span>
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
{!formData.permanentAddress.sameAsPresent && (
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Address Line 1 *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.permanentAddress.line1}
|
|
|
|
|
onChange={(e) => 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}
|
|
|
|
|
/>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Address Line 2</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.permanentAddress.line2}
|
|
|
|
|
onChange={(e) => 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}
|
|
|
|
|
/>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Division *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.permanentAddress.division}
|
|
|
|
|
onChange={(e) => updateField('permanentAddress.division', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white" disabled={formData.permanentAddress.sameAsPresent}>
|
|
|
|
|
<option value="">Select Division</option>
|
|
|
|
|
<option value="Dhaka">Dhaka</option>
|
|
|
|
|
<option value="Chattogram">Chattogram</option>
|
|
|
|
|
<option value="Khulna">Khulna</option>
|
|
|
|
|
<option value="Barishal">Barishal</option>
|
|
|
|
|
<option value="Rajshahi">Rajshahi</option>
|
|
|
|
|
<option value="Rangpur">Rangpur</option>
|
|
|
|
|
<option value="Mymensingh">Mymensingh</option>
|
|
|
|
|
<option value="Sylhet">Sylhet</option>
|
|
|
|
|
</select>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">District *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.permanentAddress.district}
|
|
|
|
|
onChange={(e) => updateField('permanentAddress.district', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white" disabled={formData.permanentAddress.sameAsPresent}>
|
|
|
|
|
<option value="">Select District</option>
|
|
|
|
|
<option value="Dhaka">Dhaka</option>
|
|
|
|
|
<option value="Gazipur">Gazipur</option>
|
|
|
|
|
<option value="Narayanganj">Narayanganj</option>
|
|
|
|
|
<option value="Chattogram">Chattogram</option>
|
|
|
|
|
<option value="Cumilla">Cumilla</option>
|
|
|
|
|
<option value="Bogura">Bogura</option>
|
|
|
|
|
<option value="Rajshahi">Rajshahi</option>
|
|
|
|
|
<option value="Khulna">Khulna</option>
|
|
|
|
|
<option value="Sylhet">Sylhet</option>
|
|
|
|
|
<option value="Barishal">Barishal</option>
|
|
|
|
|
<option value="Rangpur">Rangpur</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Zip Code *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.permanentAddress.zip}
|
|
|
|
|
onChange={(e) => 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}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Country *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.permanentAddress.country}
|
|
|
|
|
onChange={(e) => 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}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 01:21:37 +06:00
|
|
|
{formData.type === 'biker' && (
|
2026-05-05 02:44:32 +06:00
|
|
|
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
|
|
|
|
|
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Key className="w-4 h-4" /> Driving License
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">License Number *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.drivingLicense.number}
|
|
|
|
|
onChange={(e) => updateField('drivingLicense.number', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="License number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Class *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.drivingLicense.class}
|
|
|
|
|
onChange={(e) => updateField('drivingLicense.class', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Class</option>
|
|
|
|
|
<option value="C">Class C</option>
|
|
|
|
|
<option value="D">Class D</option>
|
|
|
|
|
<option value="E">Class E</option>
|
|
|
|
|
<option value="F">Class F</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Status *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.drivingLicense.status}
|
|
|
|
|
onChange={(e) => updateField('drivingLicense.status', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="not_applied">Not Applied</option>
|
|
|
|
|
<option value="applied">Applied</option>
|
|
|
|
|
<option value="issued">Issued</option>
|
|
|
|
|
<option value="expired">Expired</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Issue Date *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="date"
|
|
|
|
|
value={formData.drivingLicense.issueDate}
|
|
|
|
|
onChange={(e) => updateField('drivingLicense.issueDate', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Expiry Date *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="date"
|
|
|
|
|
value={formData.drivingLicense.expiryDate}
|
|
|
|
|
onChange={(e) => updateField('drivingLicense.expiryDate', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
2026-05-05 01:21:37 +06:00
|
|
|
)}
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
{step === 2 && formData.type === 'biker' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-purple-50/80 p-4 rounded-xl border border-purple-100">
|
|
|
|
|
<h4 className="font-semibold text-purple-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Briefcase className="w-4 h-4" /> Employment Information
|
2026-04-26 14:56:12 +06:00
|
|
|
</h4>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Current Company</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.employmentInfo.company}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.company', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="e.g., Foodpanda, ssl, Pathao"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Monthly Earning (Tk)</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="number"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.employmentInfo.monthlyEarning}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.monthlyEarning', Number(e.target.value))}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Occupation</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.occupation}
|
|
|
|
|
onChange={(e) => updateField('occupation', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Your occupation"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
2026-04-26 14:56:12 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Why want EV?</label>
|
|
|
|
|
<textarea
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.employmentInfo.whyEV}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.whyEV', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
rows={3}
|
2026-04-26 14:56:12 +06:00
|
|
|
placeholder="Reason for choosing EV..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="sm:col-span-2">
|
2026-04-26 14:56:12 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Driving Experience</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.employmentInfo.experience}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.experience', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="e.g., 3 years motorcycle experience"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 01:21:37 +06:00
|
|
|
{step === 2 && formData.type === 'investor' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-purple-50/80 p-4 rounded-xl border border-purple-100">
|
|
|
|
|
<h4 className="font-semibold text-purple-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Briefcase className="w-4 h-4" /> Investment Information
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company/Business Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.employmentInfo.company}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.company', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Your business name"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Monthly Income (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.employmentInfo.monthlyEarning}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.monthlyEarning', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Investment Source</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.employmentInfo.whyEV}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.whyEV', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="e.g., Savings, Business profit, Inheritance"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Profession/Occupation</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.occupation}
|
|
|
|
|
onChange={(e) => updateField('occupation', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Your profession"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 01:34:10 +06:00
|
|
|
{(step === 2 && (formData.type === 'swapstation' || formData.type === 'merchant')) && (
|
2026-05-05 01:21:37 +06:00
|
|
|
<div className="space-y-5">
|
2026-05-05 01:34:10 +06:00
|
|
|
{formData.type === 'swapstation' ? (
|
|
|
|
|
<div className="bg-purple-50/80 p-4 rounded-xl border border-purple-100">
|
|
|
|
|
<h4 className="font-semibold text-purple-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Briefcase className="w-4 h-4" /> Station Business Information
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Station Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.employmentInfo.company}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.company', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Your station name"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Station Phone</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.phone}
|
|
|
|
|
onChange={(e) => updateField('phone', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="01XXXXXXXXX"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Station Address</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.presentAddress.line1}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.line1', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Station address"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Trade License Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nid}
|
|
|
|
|
onChange={(e) => updateField('nid', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Trade license number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">TIN Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.passport}
|
|
|
|
|
onChange={(e) => updateField('passport', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="TIN number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Monthly Rent/Income (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.employmentInfo.monthlyEarning}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.monthlyEarning', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Station Location (Google Map Link)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.employmentInfo.whyEV}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.whyEV', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://maps.google.com/..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Business Description</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={formData.employmentInfo.experience}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.experience', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
rows={3}
|
|
|
|
|
placeholder="Describe your swap station business..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 01:21:37 +06:00
|
|
|
</div>
|
2026-05-05 01:34:10 +06:00
|
|
|
</div>
|
2026-05-05 01:47:27 +06:00
|
|
|
) : null}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{(step === 2 && formData.type === 'merchant') && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-purple-50/80 p-4 rounded-xl border border-purple-100">
|
|
|
|
|
<h4 className="font-semibold text-purple-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Briefcase className="w-4 h-4" /> Company Information (Merchant)
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.employmentInfo.company}
|
|
|
|
|
onChange={(e) => 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, Uber, Pathao"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Phone</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.phone}
|
|
|
|
|
onChange={(e) => updateField('phone', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="01XXXXXXXXX"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={formData.email}
|
|
|
|
|
onChange={(e) => updateField('email', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="company@email.com"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Trade License Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nid}
|
|
|
|
|
onChange={(e) => updateField('nid', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Trade license number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">TIN Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.passport}
|
|
|
|
|
onChange={(e) => updateField('passport', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="TIN number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Monthly Revenue (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.employmentInfo.monthlyEarning}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.monthlyEarning', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Address</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.presentAddress.line1}
|
|
|
|
|
onChange={(e) => updateField('presentAddress.line1', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Company headquarters address"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Delivery Platform</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.employmentInfo.whyEV}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.whyEV', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Platform</option>
|
|
|
|
|
<option value="Foodpanda">Foodpanda</option>
|
|
|
|
|
<option value="Uber">Uber</option>
|
|
|
|
|
<option value="Pathao">Pathao</option>
|
|
|
|
|
<option value="SSL Commercial">SSL Commercial</option>
|
|
|
|
|
<option value="Others">Others</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Description</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={formData.employmentInfo.experience}
|
|
|
|
|
onChange={(e) => updateField('employmentInfo.experience', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
rows={3}
|
|
|
|
|
placeholder="Describe your company business..."
|
|
|
|
|
/>
|
2026-05-05 01:21:37 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 01:47:27 +06:00
|
|
|
</div>
|
2026-05-05 01:21:37 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 01:47:27 +06:00
|
|
|
{(step === 3 && (formData.type === 'biker' || formData.type === 'investor' || formData.type === 'swapstation' || formData.type === 'merchant')) && (
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-pink-50/80 p-4 rounded-xl border border-pink-100">
|
|
|
|
|
<h4 className="font-semibold text-pink-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Users className="w-4 h-4" /> Nominee Details
|
2026-04-26 14:56:12 +06:00
|
|
|
</h4>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee Name *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nomineeDetails.name}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.name', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Nominee name"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Relationship *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.nomineeDetails.relationship}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.relationship', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
2026-05-05 00:29:47 +06:00
|
|
|
<option value="">Select</option>
|
|
|
|
|
<option value="Spouse">Spouse</option>
|
|
|
|
|
<option value="Father">Father</option>
|
|
|
|
|
<option value="Mother">Mother</option>
|
|
|
|
|
<option value="Brother">Brother</option>
|
|
|
|
|
<option value="Sister">Sister</option>
|
|
|
|
|
<option value="Office">Office</option>
|
2026-04-26 14:56:12 +06:00
|
|
|
<option value="Other">Other</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee Phone *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nomineeDetails.phone}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.phone', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Phone number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee NID *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nomineeDetails.nid}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.nid', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="NID number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee Date of Birth</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="date"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.nomineeDetails.dob}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.dob', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={formData.nomineeDetails.email}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.email', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="email@example.com"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:40:34 +06:00
|
|
|
<div className="sm:col-span-3">
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Nominee Address *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.nomineeDetails.address}
|
|
|
|
|
onChange={(e) => updateField('nomineeDetails.address', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Address"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:40:34 +06:00
|
|
|
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="bg-red-50/80 p-4 rounded-xl border border-red-100 mt-4">
|
|
|
|
|
<h4 className="font-semibold text-red-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Phone className="w-4 h-4" /> Emergency Contact
|
2026-04-26 14:56:12 +06:00
|
|
|
</h4>
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Contact Name *</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.emergencyContact.name}
|
|
|
|
|
onChange={(e) => updateField('emergencyContact.name', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Contact name"
|
2026-04-26 14:56:12 +06:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Phone *</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.emergencyContact.phone}
|
|
|
|
|
onChange={(e) => updateField('emergencyContact.phone', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Phone number"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Relationship *</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.emergencyContact.relation}
|
|
|
|
|
onChange={(e) => updateField('emergencyContact.relation', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
|
|
|
|
<option value="">Select</option>
|
|
|
|
|
<option value="Spouse">Spouse</option>
|
|
|
|
|
<option value="Father">Father</option>
|
|
|
|
|
<option value="Mother">Mother</option>
|
|
|
|
|
<option value="Brother">Brother</option>
|
|
|
|
|
<option value="Sister">Sister</option>
|
|
|
|
|
<option value="Friend">Friend</option>
|
|
|
|
|
<option value="Other">Other</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={formData.emergencyContact.email}
|
|
|
|
|
onChange={(e) => updateField('emergencyContact.email', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="email@example.com"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2 lg:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Address *</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
|
|
|
|
type="text"
|
2026-05-05 00:29:47 +06:00
|
|
|
value={formData.emergencyContact.address}
|
|
|
|
|
onChange={(e) => updateField('emergencyContact.address', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
2026-05-05 00:29:47 +06:00
|
|
|
placeholder="Address"
|
2026-04-26 14:56:12 +06:00
|
|
|
/>
|
|
|
|
|
</div>
|
2026-05-05 00:29:47 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{step === 4 && formData.type === 'biker' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
|
|
|
|
|
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Calendar className="w-4 h-4" /> Plan Selection
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
|
|
|
|
{[
|
2026-05-05 02:44:32 +06:00
|
|
|
{ v: 'single_rent', l: 'Single Rent', d: 'Single person rental plan', icon: Bike },
|
2026-05-05 00:29:47 +06:00
|
|
|
{ v: 'rent_to_own', l: 'Rent to Own', d: 'Own after X months', icon: Key },
|
|
|
|
|
{ v: 'share_ev', l: 'Share EV', d: 'Shared bike', icon: Users },
|
|
|
|
|
].map(plan => (
|
|
|
|
|
<button
|
|
|
|
|
key={plan.v}
|
|
|
|
|
onClick={() => updateField('riderPlan', plan.v)}
|
|
|
|
|
className={`p-4 rounded-lg border text-left transition-all ${formData.riderPlan === plan.v
|
|
|
|
|
? 'border-accent bg-accent/10 ring-1 ring-accent'
|
|
|
|
|
: 'border-slate-200 bg-white hover:border-accent'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<plan.icon className={`w-5 h-5 mb-2 ${formData.riderPlan === plan.v ? 'text-accent' : 'text-slate-400'}`} />
|
|
|
|
|
<p className="text-sm font-semibold text-slate-800">{plan.l}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{plan.d}</p>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Requested EV Model</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.evModel}
|
|
|
|
|
onChange={(e) => updateField('evModel', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Model</option>
|
|
|
|
|
<option value="AIMA Lightning">AIMA Lightning</option>
|
|
|
|
|
<option value="Yadea DT3">Yadea DT3</option>
|
|
|
|
|
<option value="Etron ET50">Etron ET50</option>
|
|
|
|
|
<option value="AIMA i3">AIMA i3</option>
|
|
|
|
|
<option value="TVS iQube">TVS iQube</option>
|
|
|
|
|
<option value="Benling Aura">Benling Aura</option>
|
|
|
|
|
<option value="Okinawa Ridge">Okinawa Ridge</option>
|
|
|
|
|
<option value="Hero Optima">Hero Optima</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">EV Condition</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.evCondition}
|
|
|
|
|
onChange={(e) => updateField('evCondition', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="new">New</option>
|
|
|
|
|
<option value="old">Old</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
<div>
|
2026-05-05 00:29:47 +06:00
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Schedule Date</label>
|
2026-04-26 14:56:12 +06:00
|
|
|
<input
|
2026-05-05 00:29:47 +06:00
|
|
|
type="date"
|
|
|
|
|
value={formData.scheduleDate}
|
|
|
|
|
onChange={(e) => updateField('scheduleDate', e.target.value)}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-26 14:56:12 +06:00
|
|
|
)}
|
2026-05-05 00:29:47 +06:00
|
|
|
|
2026-05-05 01:21:37 +06:00
|
|
|
{step === 4 && formData.type === 'investor' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
|
|
|
|
|
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Wallet className="w-4 h-4" /> Investment Plan
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
|
|
|
|
{[
|
|
|
|
|
{ v: '1', l: '1 Bike', d: 'Invest in 1 bike', icon: Bike },
|
|
|
|
|
{ v: '5', l: '5 Bikes', d: 'Invest in 5 bikes', icon: Bike },
|
|
|
|
|
{ v: '10', l: '10 Bikes', d: 'Invest in 10 bikes', icon: Bike },
|
|
|
|
|
].map(plan => (
|
|
|
|
|
<button
|
|
|
|
|
key={plan.v}
|
|
|
|
|
onClick={() => updateField('investmentPlan.bikes', Number(plan.v))}
|
|
|
|
|
className={`p-4 rounded-lg border text-left transition-all ${formData.investmentPlan.bikes === Number(plan.v)
|
|
|
|
|
? 'border-accent bg-accent/10 ring-1 ring-accent'
|
|
|
|
|
: 'border-slate-200 bg-white hover:border-accent'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<plan.icon className={`w-5 h-5 mb-2 ${formData.investmentPlan.bikes === Number(plan.v) ? 'text-accent' : 'text-slate-400'}`} />
|
|
|
|
|
<p className="text-sm font-semibold text-slate-800">{plan.l}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{plan.d}</p>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Plan Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.investmentPlan.planName}
|
|
|
|
|
onChange={(e) => updateField('investmentPlan.planName', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="Plan name"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Plan Type</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.investmentPlan.planType}
|
|
|
|
|
onChange={(e) => updateField('investmentPlan.planType', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Type</option>
|
|
|
|
|
<option value="Bronze">Bronze</option>
|
|
|
|
|
<option value="Silver">Silver</option>
|
|
|
|
|
<option value="Gold">Gold</option>
|
|
|
|
|
<option value="Platinum">Platinum</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Investment Amount (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.investmentPlan.amount}
|
|
|
|
|
onChange={(e) => updateField('investmentPlan.amount', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Expected Monthly Return (%)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.investmentPlan.monthlyReturn}
|
|
|
|
|
onChange={(e) => updateField('investmentPlan.monthlyReturn', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="sm:col-span-2">
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Expected ROI (%)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.investmentPlan.expectedROI}
|
|
|
|
|
onChange={(e) => updateField('investmentPlan.expectedROI', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 01:34:10 +06:00
|
|
|
{step === 4 && formData.type === 'swapstation' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
|
|
|
|
|
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<BatteryCharging className="w-4 h-4" /> Swap Station Plan
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
|
|
|
|
{[
|
|
|
|
|
{ v: 8, l: '8 Cabinets', d: '8 battery swap capacity' },
|
|
|
|
|
{ v: 12, l: '12 Cabinets', d: '12 battery swap capacity' },
|
|
|
|
|
{ v: 20, l: '20 Cabinets', d: '20 battery swap capacity' },
|
|
|
|
|
].map(plan => (
|
|
|
|
|
<button
|
|
|
|
|
key={plan.v}
|
|
|
|
|
onClick={() => updateField('swapStationPlan.cabinets', plan.v)}
|
|
|
|
|
className={`p-4 rounded-lg border text-left transition-all ${formData.swapStationPlan.cabinets === plan.v
|
|
|
|
|
? 'border-accent bg-accent/10 ring-1 ring-accent'
|
|
|
|
|
: 'border-slate-200 bg-white hover:border-accent'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<BatteryCharging className={`w-5 h-5 mb-2 ${formData.swapStationPlan.cabinets === plan.v ? 'text-accent' : 'text-slate-400'}`} />
|
|
|
|
|
<p className="text-sm font-semibold text-slate-800">{plan.l}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{plan.d}</p>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Plan Type</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.swapStationPlan.planType}
|
|
|
|
|
onChange={(e) => updateField('swapStationPlan.planType', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Type</option>
|
|
|
|
|
<option value="Basic">Basic</option>
|
|
|
|
|
<option value="Standard">Standard</option>
|
|
|
|
|
<option value="Premium">Premium</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Number of Batteries</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.swapStationPlan.batteries}
|
|
|
|
|
onChange={(e) => updateField('swapStationPlan.batteries', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Investment Amount (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.swapStationPlan.amount}
|
|
|
|
|
onChange={(e) => updateField('swapStationPlan.amount', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Expected Monthly Rent (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.swapStationPlan.monthlyRent}
|
|
|
|
|
onChange={(e) => updateField('swapStationPlan.monthlyRent', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 01:47:27 +06:00
|
|
|
{step === 4 && formData.type === 'merchant' && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
|
|
|
|
|
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<Users className="w-4 h-4" /> Rider Request Plan
|
|
|
|
|
</h4>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
|
|
|
|
|
{[
|
|
|
|
|
{ v: 5, l: '5 Riders', d: 'Request for 5 bikers' },
|
|
|
|
|
{ v: 10, l: '10 Riders', d: 'Request for 10 bikers' },
|
|
|
|
|
{ v: 20, l: '20 Riders', d: 'Request for 20 bikers' },
|
|
|
|
|
{ v: 50, l: '50 Riders', d: 'Request for 50 bikers' },
|
|
|
|
|
{ v: 100, l: '100 Riders', d: 'Request for 100 bikers' },
|
|
|
|
|
].map(plan => (
|
|
|
|
|
<button
|
|
|
|
|
key={plan.v}
|
|
|
|
|
onClick={() => updateField('merchantPlan.bikersRequested', plan.v)}
|
|
|
|
|
className={`p-4 rounded-lg border text-left transition-all ${formData.merchantPlan.bikersRequested === plan.v
|
|
|
|
|
? 'border-accent bg-accent/10 ring-1 ring-accent'
|
|
|
|
|
: 'border-slate-200 bg-white hover:border-accent'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<Users className={`w-5 h-5 mb-2 ${formData.merchantPlan.bikersRequested === plan.v ? 'text-accent' : 'text-slate-400'}`} />
|
|
|
|
|
<p className="text-sm font-semibold text-slate-800">{plan.l}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{plan.d}</p>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Company Category</label>
|
|
|
|
|
<select
|
|
|
|
|
value={formData.merchantPlan.companyCategory}
|
|
|
|
|
onChange={(e) => updateField('merchantPlan.companyCategory', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Category</option>
|
|
|
|
|
<option value="Food Delivery">Food Delivery</option>
|
|
|
|
|
<option value="Parcel Delivery">Parcel Delivery</option>
|
|
|
|
|
<option value="E-commerce">E-commerce</option>
|
|
|
|
|
<option value="Logistics">Logistics</option>
|
|
|
|
|
<option value="Quick Commerce">Quick Commerce</option>
|
|
|
|
|
<option value="Others">Others</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Required Area/Zone</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={formData.merchantPlan.requiredArea}
|
|
|
|
|
onChange={(e) => updateField('merchantPlan.requiredArea', e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="e.g., Dhaka, Chattogram"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Investment Amount (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.merchantPlan.amount}
|
|
|
|
|
onChange={(e) => updateField('merchantPlan.amount', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-xs font-medium text-slate-600 mb-1 block">Monthly Budget (Tk)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={formData.merchantPlan.monthlyBudget}
|
|
|
|
|
onChange={(e) => updateField('merchantPlan.monthlyBudget', Number(e.target.value))}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
{step === 5 && (
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="bg-green-50/80 p-4 rounded-xl border border-green-100">
|
|
|
|
|
<h4 className="font-semibold text-green-900 mb-3 flex items-center gap-2 text-sm">
|
|
|
|
|
<FileText className="w-4 h-4" /> Required Documents ({formData.type})
|
|
|
|
|
</h4>
|
|
|
|
|
<p className="text-xs text-slate-600 mb-4">Documents required for {formData.type} KYC application</p>
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
|
|
|
{formData.requiredDocuments.map((doc) => (
|
|
|
|
|
<div key={doc.id} className="flex items-center justify-between p-3 bg-white rounded-lg border border-green-100">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
{doc.status === 'pending' ? (
|
|
|
|
|
<Upload className="w-4 h-4 text-amber-400" />
|
|
|
|
|
) : doc.status === 'uploaded' ? (
|
|
|
|
|
<FileText className="w-4 h-4 text-blue-400" />
|
|
|
|
|
) : doc.status === 'approved' ? (
|
|
|
|
|
<CheckCircle className="w-4 h-4 text-green-500" />
|
|
|
|
|
) : (
|
|
|
|
|
<XCircle className="w-4 h-4 text-red-500" />
|
|
|
|
|
)}
|
|
|
|
|
<span className="text-sm text-slate-700">{doc.name}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<span className="text-xs text-slate-500 flex items-center gap-1">
|
|
|
|
|
{doc.status === 'pending' ? (
|
|
|
|
|
<span className="text-amber-600">Pending</span>
|
|
|
|
|
) : doc.status === 'uploaded' ? (
|
|
|
|
|
<span className="text-blue-600">Uploaded</span>
|
|
|
|
|
) : doc.status === 'approved' ? (
|
|
|
|
|
<span className="text-green-600">Approved</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-red-600">Rejected</span>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 14:56:12 +06:00
|
|
|
</div>
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="p-4 border-t border-slate-100 flex justify-between flex-shrink-0 bg-slate-50">
|
2026-04-26 14:56:12 +06:00
|
|
|
<button
|
|
|
|
|
onClick={() => setStep(Math.max(1, step - 1))}
|
2026-05-05 00:29:47 +06:00
|
|
|
className={`px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-100 flex items-center gap-2 ${step === 1 ? 'invisible' : ''}`}
|
2026-04-26 14:56:12 +06:00
|
|
|
>
|
2026-05-05 00:29:47 +06:00
|
|
|
<ChevronDown className="w-4 h-4 rotate-90" /> Previous
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
2026-05-06 18:26:27 +06:00
|
|
|
<div className="flex gap-2 ">
|
2026-04-26 14:56:12 +06:00
|
|
|
{step < 5 ? (
|
2026-05-06 18:26:27 +06:00
|
|
|
<button
|
|
|
|
|
onClick={onClose}
|
|
|
|
|
className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-100 z-10"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
) : (
|
|
|
|
|
<
|
|
|
|
|
>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{step < 5 ? (
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setStep(step + 1)}
|
|
|
|
|
className="px-4 py-2 bg-accent text-white rounded-lg text-sm hover:bg-accent-dark flex items-center gap-2"
|
|
|
|
|
>
|
2026-05-05 00:29:47 +06:00
|
|
|
Next <ChevronDown className="w-4 h-4 -rotate-90" />
|
2026-04-26 14:56:12 +06:00
|
|
|
</button>
|
|
|
|
|
) : (
|
|
|
|
|
<button
|
2026-05-05 00:29:47 +06:00
|
|
|
onClick={handleSave}
|
2026-04-26 14:56:12 +06:00
|
|
|
className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Check className="w-4 h-4" /> Create Application
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-04-22 01:02:45 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-04-26 14:56:12 +06:00
|
|
|
}
|