2026-04-26 18:32:52 +06:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react';
|
2026-05-05 21:54:46 +06:00
|
|
|
import { Settings, Upload, Image, Globe, Mail, MessageSquare, Phone, MapPin, Link2, Clock, Save, FileText, Camera, Palette, Ruler, Sun, Moon, Monitor, Smartphone, Tablet, Package, Wrench, FileCheck, BadgeDollarSign, CreditCard, Plus, X, DollarSign, Zap } from 'lucide-react';
|
2026-04-26 18:32:52 +06:00
|
|
|
|
|
|
|
|
interface CompanySettings {
|
|
|
|
|
name: string;
|
|
|
|
|
shortName: string;
|
|
|
|
|
tagLine: string;
|
|
|
|
|
description: string;
|
|
|
|
|
email: string;
|
|
|
|
|
phone: string;
|
|
|
|
|
website: string;
|
|
|
|
|
address: string;
|
|
|
|
|
city: string;
|
|
|
|
|
country: string;
|
|
|
|
|
tin: string;
|
|
|
|
|
bin: string;
|
|
|
|
|
tradeLicense: string;
|
|
|
|
|
timezone: string;
|
|
|
|
|
currency: string;
|
|
|
|
|
language: string;
|
|
|
|
|
logo: string;
|
|
|
|
|
favicon: string;
|
|
|
|
|
primaryColor: string;
|
|
|
|
|
secondaryColor: string;
|
|
|
|
|
borderRadius: number;
|
|
|
|
|
socialLinks: {
|
|
|
|
|
facebook: string;
|
|
|
|
|
twitter: string;
|
|
|
|
|
instagram: string;
|
|
|
|
|
linkedin: string;
|
|
|
|
|
youtube: string;
|
|
|
|
|
};
|
|
|
|
|
smtp: {
|
|
|
|
|
host: string;
|
|
|
|
|
port: number;
|
|
|
|
|
user: string;
|
|
|
|
|
password: string;
|
|
|
|
|
fromEmail: string;
|
|
|
|
|
fromName: string;
|
|
|
|
|
encryption: 'ssl' | 'tls' | 'none';
|
|
|
|
|
};
|
|
|
|
|
sms: {
|
|
|
|
|
provider: string;
|
|
|
|
|
apiKey: string;
|
|
|
|
|
senderId: string;
|
|
|
|
|
url: string;
|
|
|
|
|
};
|
|
|
|
|
heroData: {
|
|
|
|
|
title: string;
|
|
|
|
|
subtitle: string;
|
|
|
|
|
ctaText: string;
|
|
|
|
|
ctaLink: string;
|
|
|
|
|
videoUrl: string;
|
|
|
|
|
backgroundImage: string;
|
|
|
|
|
};
|
|
|
|
|
masterData: {
|
|
|
|
|
kycDocuments: { name: string; required: boolean }[];
|
2026-05-05 00:29:47 +06:00
|
|
|
riderDocuments: { id: string; name: string; required: boolean; description: string }[];
|
2026-04-26 18:32:52 +06:00
|
|
|
subscriptionPlans: { name: string; price: number; duration: number; features: string[] }[];
|
2026-05-05 00:29:47 +06:00
|
|
|
rentalPlans: { id: string; name: string; type: string; description: string; price: number; duration: number }[];
|
2026-04-26 18:32:52 +06:00
|
|
|
investorDocuments: { id: string; name: string; required: boolean; description: string }[];
|
|
|
|
|
merchantDocuments: { id: string; name: string; required: boolean; description: string }[];
|
|
|
|
|
swapStationDocuments: { id: string; name: string; required: boolean; description: string }[];
|
|
|
|
|
rentalDocuments: {
|
|
|
|
|
type: 'single' | 'shared' | 'rent-to-own';
|
|
|
|
|
name: string;
|
|
|
|
|
documents: { id: string; name: string; required: boolean; description: string }[];
|
|
|
|
|
}[];
|
|
|
|
|
};
|
2026-05-05 00:29:47 +06:00
|
|
|
parts: { id: string; name: string; price?: number; minPrice?: number; maxPrice?: number; inStock: number }[];
|
2026-04-26 18:32:52 +06:00
|
|
|
serviceCenters: { id: string; name: string; address: string; phone: string; rating: number }[];
|
|
|
|
|
rentalPolicy: {
|
|
|
|
|
minAge: number;
|
|
|
|
|
requireLicense: boolean;
|
|
|
|
|
deposit: number;
|
|
|
|
|
lateFeePerHour: number;
|
|
|
|
|
cancellationFee: number;
|
|
|
|
|
damagePenalty: { level: string; amount: number }[];
|
|
|
|
|
rules: string[];
|
|
|
|
|
};
|
2026-05-05 03:09:21 +06:00
|
|
|
plans: {
|
|
|
|
|
singleRent: {
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Economy' | 'Standard' | 'Premium';
|
2026-05-05 03:09:21 +06:00
|
|
|
name: string;
|
|
|
|
|
dailyRent: number;
|
|
|
|
|
deposit: number;
|
|
|
|
|
weeklySubscription: number;
|
|
|
|
|
monthlySubscription: number;
|
|
|
|
|
ficoSharePercent: number;
|
|
|
|
|
description: string;
|
|
|
|
|
}[];
|
|
|
|
|
rentToOwn: {
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Economy' | 'Standard' | 'Premium';
|
2026-05-05 03:09:21 +06:00
|
|
|
name: string;
|
|
|
|
|
dailyRent: number;
|
|
|
|
|
deposit: number;
|
|
|
|
|
weeklySubscription: number;
|
|
|
|
|
monthlySubscription: number;
|
|
|
|
|
durationMonths: number;
|
|
|
|
|
evPrice: number;
|
|
|
|
|
totalPayment: number;
|
|
|
|
|
profit: number;
|
|
|
|
|
ficoRentSharePercent: number;
|
|
|
|
|
ficoProfitSharePercent: number;
|
|
|
|
|
description: string;
|
|
|
|
|
}[];
|
|
|
|
|
shareEv: {
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Economy' | 'Standard' | 'Premium';
|
2026-05-05 03:09:21 +06:00
|
|
|
name: string;
|
|
|
|
|
dailyRentEach: number;
|
|
|
|
|
totalDailyRent: number;
|
|
|
|
|
depositEach: number;
|
|
|
|
|
totalDeposit: number;
|
|
|
|
|
weeklySubscriptionEach: number;
|
|
|
|
|
totalWeeklySubscription: number;
|
|
|
|
|
monthlySubscriptionEach: number;
|
|
|
|
|
totalMonthlySubscription: number;
|
|
|
|
|
ficoSharePercent: number;
|
|
|
|
|
description: string;
|
|
|
|
|
}[];
|
2026-05-05 21:48:18 +06:00
|
|
|
investment: {
|
|
|
|
|
id: string;
|
|
|
|
|
tier: string;
|
|
|
|
|
name: string;
|
|
|
|
|
minInvestment: number;
|
|
|
|
|
maxInvestment: number;
|
|
|
|
|
monthlyReturnPercent: number;
|
|
|
|
|
durationMonths: number;
|
|
|
|
|
profitSharePercent: number;
|
|
|
|
|
lockInMonths: number;
|
|
|
|
|
totalReturnPercent: number;
|
|
|
|
|
earlyExitPenalty: number;
|
|
|
|
|
startDate: string;
|
|
|
|
|
endDate: string;
|
|
|
|
|
targetAmount: number;
|
|
|
|
|
status: string;
|
|
|
|
|
description: string;
|
|
|
|
|
}[];
|
2026-05-05 21:54:46 +06:00
|
|
|
swapStation: {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
batteryCount: number;
|
|
|
|
|
swapPrice: number;
|
|
|
|
|
monthlySubscription: number;
|
|
|
|
|
dailySubscription: number;
|
|
|
|
|
minBatteries: number;
|
|
|
|
|
maxBatteries: number;
|
|
|
|
|
profitSharePercent: number;
|
|
|
|
|
status: string;
|
|
|
|
|
description: string;
|
|
|
|
|
}[];
|
2026-05-05 03:09:21 +06:00
|
|
|
};
|
2026-04-26 18:32:52 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const initialSettings: CompanySettings = {
|
|
|
|
|
name: 'JAIBEN Mobility Ltd',
|
|
|
|
|
shortName: 'JAIBEN',
|
|
|
|
|
tagLine: 'Your Trusted EV Mobility Partner',
|
|
|
|
|
description: 'Leading electric vehicle rental and swap station network in Bangladesh',
|
|
|
|
|
email: 'info@jaiben.com',
|
|
|
|
|
phone: '+880 9611-222-333',
|
|
|
|
|
website: 'https://jaiben.com',
|
|
|
|
|
address: 'House 45, Road 13, Gulshan 1',
|
|
|
|
|
city: 'Dhaka',
|
|
|
|
|
country: 'Bangladesh',
|
|
|
|
|
tin: '123456789012',
|
|
|
|
|
bin: '987654321098',
|
|
|
|
|
tradeLicense: 'TRAD/2023/001234',
|
|
|
|
|
timezone: 'Asia/Dhaka',
|
|
|
|
|
currency: 'BDT',
|
|
|
|
|
language: 'en',
|
|
|
|
|
logo: '/logo.png',
|
|
|
|
|
favicon: '/favicon.ico',
|
|
|
|
|
primaryColor: '#10B981',
|
|
|
|
|
secondaryColor: '#3B82F6',
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
socialLinks: {
|
|
|
|
|
facebook: 'https://facebook.com/jaiben',
|
|
|
|
|
twitter: 'https://twitter.com/jaiben',
|
|
|
|
|
instagram: 'https://instagram.com/jaiben',
|
|
|
|
|
linkedin: 'https://linkedin.com/company/jaiben',
|
|
|
|
|
youtube: 'https://youtube.com/@jaiben'
|
|
|
|
|
},
|
|
|
|
|
smtp: {
|
|
|
|
|
host: 'smtp.mailserver.com',
|
|
|
|
|
port: 587,
|
|
|
|
|
user: 'noreply@jaiben.com',
|
|
|
|
|
password: '••••••••••••',
|
|
|
|
|
fromEmail: 'noreply@jaiben.com',
|
|
|
|
|
fromName: 'JAIBEN Mobility',
|
|
|
|
|
encryption: 'tls'
|
|
|
|
|
},
|
|
|
|
|
sms: {
|
|
|
|
|
provider: 'BulkSMS',
|
|
|
|
|
apiKey: '••••••••••••••••',
|
|
|
|
|
senderId: 'JAIBEN',
|
|
|
|
|
url: 'https://api.bulksmsbd.com'
|
|
|
|
|
},
|
|
|
|
|
heroData: {
|
|
|
|
|
title: 'Welcome to JAIBEN',
|
|
|
|
|
subtitle: 'Your trusted partner for electric vehicle mobility solutions across Bangladesh',
|
|
|
|
|
ctaText: 'Get Started',
|
|
|
|
|
ctaLink: '/rent',
|
|
|
|
|
videoUrl: '',
|
|
|
|
|
backgroundImage: '/hero-bg.jpg'
|
|
|
|
|
},
|
|
|
|
|
masterData: {
|
|
|
|
|
kycDocuments: [
|
|
|
|
|
{ name: 'NID Card', required: true },
|
|
|
|
|
{ name: 'Driving License', required: true },
|
|
|
|
|
{ name: 'Passport Photo', required: true },
|
|
|
|
|
{ name: 'Address Proof', required: false },
|
|
|
|
|
{ name: 'Business Trade License', required: false },
|
|
|
|
|
],
|
2026-05-05 00:29:47 +06:00
|
|
|
riderDocuments: [
|
|
|
|
|
{ id: 'rider_nid_front', name: 'NID (Front)', required: true, description: 'National ID card front side' },
|
|
|
|
|
{ id: 'rider_nid_back', name: 'NID (Back)', required: true, description: 'National ID card back side' },
|
|
|
|
|
{ id: 'rider_license', name: 'Driving License', required: true, description: 'Valid driving license' },
|
|
|
|
|
{ id: 'rider_photo', name: 'Profile Photo', required: true, description: 'Recent passport size photo' },
|
|
|
|
|
{ id: 'rider_bank_card', name: 'Bank Account Card', required: false, description: 'Bank account card/cheque' },
|
|
|
|
|
{ id: 'rider_wallet', name: 'Mobile Wallet Info', required: false, description: 'bKash/Rocket account details' },
|
|
|
|
|
],
|
2026-04-26 18:32:52 +06:00
|
|
|
subscriptionPlans: [
|
|
|
|
|
{ name: 'Daily', price: 500, duration: 1, features: ['24 hours access', 'Basic support'] },
|
|
|
|
|
{ name: 'Weekly', price: 3000, duration: 7, features: ['7 days access', 'Priority support', 'Free swap'] },
|
|
|
|
|
{ name: 'Monthly', price: 10000, duration: 30, features: ['30 days access', 'VIP support', 'Unlimited swap', 'Damage coverage'] },
|
|
|
|
|
{ name: 'Yearly', price: 80000, duration: 365, features: ['1 year access', 'Dedicated manager', 'Unlimited swap', 'Full coverage', 'Discounted rates'] },
|
|
|
|
|
],
|
2026-05-05 00:29:47 +06:00
|
|
|
rentalPlans: [
|
|
|
|
|
{ id: 'single_rent', name: 'Single Rent', type: 'single', description: 'Single person rental plan', price: 500, duration: 1 },
|
|
|
|
|
{ id: 'weekly_rent', name: 'Weekly Rent', type: 'weekly', description: '7 days rental plan', price: 3000, duration: 7 },
|
|
|
|
|
{ id: 'monthly_rent', name: 'Monthly Rent', type: 'monthly', description: '30 days rental plan', price: 10000, duration: 30 },
|
|
|
|
|
{ id: 'rent_to_own', name: 'Rent to Own', type: 'rent-to-own', description: 'Own after X months', price: 15000, duration: 365 },
|
|
|
|
|
{ id: 'share_ev', name: 'Share EV', type: 'shared', description: 'Shared bike', price: 2500, duration: 30 },
|
|
|
|
|
],
|
2026-04-26 18:32:52 +06:00
|
|
|
investorDocuments: [
|
|
|
|
|
{ id: 'inv_nid', name: 'NID Card (Front & Back)', required: true, description: 'National ID card copy' },
|
|
|
|
|
{ id: 'inv_photo', name: 'Passport Size Photo', required: true, description: 'Recent passport size photograph' },
|
|
|
|
|
{ id: 'inv_bank', name: 'Bank Account Info', required: true, description: 'Bank account details for earnings' },
|
|
|
|
|
{ id: 'inv_mobile', name: 'Mobile Banking', required: true, description: 'bKash/Rocket account details' },
|
|
|
|
|
{ id: 'inv_tin', name: 'TIN Certificate', required: false, description: 'Tax Identification Number' },
|
|
|
|
|
{ id: 'inv_tax', name: 'Tax Return', required: false, description: 'Recent tax return copy' },
|
|
|
|
|
{ id: 'inv_ec', name: 'Emergency Contact ID', required: false, description: 'Emergency contact NID copy' },
|
|
|
|
|
],
|
|
|
|
|
merchantDocuments: [
|
|
|
|
|
{ id: 'mer_trade', name: 'Trade License', required: true, description: 'Business trade license' },
|
|
|
|
|
{ id: 'mer_nid', name: 'Owner NID Card', required: true, description: 'Owner/proprietor NID card' },
|
|
|
|
|
{ id: 'mer_photo', name: 'Shop Photo', required: true, description: 'Photo of shop premises' },
|
|
|
|
|
{ id: 'mer_tin', name: 'TIN Certificate', required: true, description: 'Tax Identification Number' },
|
|
|
|
|
{ id: 'mer_bank', name: 'Bank Account Info', required: true, description: 'Business bank account' },
|
|
|
|
|
{ id: 'mer_agreement', name: 'Shop Agreement', required: false, description: 'Rent agreement or ownership' },
|
|
|
|
|
{ id: 'mer_fire', name: 'Fire License', required: false, description: 'Fire safety license' },
|
|
|
|
|
],
|
|
|
|
|
swapStationDocuments: [
|
|
|
|
|
{ id: 'ss_trade', name: 'Trade License', required: true, description: 'Business trade license' },
|
|
|
|
|
{ id: 'ss_owner_nid', name: 'Owner NID Card', required: true, description: 'Proprietor NID card' },
|
|
|
|
|
{ id: 'ss_photo', name: 'Station Photo', required: true, description: 'Photo of swap station' },
|
|
|
|
|
{ id: 'ss_tin', name: 'TIN Certificate', required: true, description: 'Tax Identification Number' },
|
|
|
|
|
{ id: 'ss_electric', name: 'Electricity Bill', required: true, description: 'Recent electricity bill' },
|
|
|
|
|
{ id: 'ss_agreement', name: 'Space Agreement', required: false, description: 'Rent/space agreement' },
|
|
|
|
|
{ id: 'ss_safety', name: 'Safety Certificate', required: false, description: 'Electrical safety certificate' },
|
|
|
|
|
],
|
|
|
|
|
rentalDocuments: [
|
|
|
|
|
{
|
|
|
|
|
type: 'single',
|
|
|
|
|
name: 'Rental (Single)',
|
|
|
|
|
documents: [
|
|
|
|
|
{ id: 'rent_nid', name: 'NID Card', required: true, description: 'Valid National ID' },
|
|
|
|
|
{ id: 'rent_license', name: 'Driving License', required: true, description: 'Valid driving license' },
|
|
|
|
|
{ id: 'rent_photo', name: 'Passport Photo', required: true, description: 'Recent photo' },
|
|
|
|
|
{ id: 'rent_deposit', name: 'Deposit Receipt', required: true, description: 'Security deposit proof' },
|
|
|
|
|
{ id: 'rent_ec', name: 'Emergency Contact', required: true, description: 'Emergency contact info' },
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'shared',
|
|
|
|
|
name: 'Rental (2 Person Shared)',
|
|
|
|
|
documents: [
|
|
|
|
|
{ id: 'rent_nid_1', name: 'Rider 1 - NID Card', required: true, description: 'Primary rider NID' },
|
|
|
|
|
{ id: 'rent_license_1', name: 'Rider 1 - Driving License', required: true, description: 'Primary rider license' },
|
|
|
|
|
{ id: 'rent_nid_2', name: 'Rider 2 - NID Card', required: true, description: 'Secondary rider NID' },
|
|
|
|
|
{ id: 'rent_license_2', name: 'Rider 2 - Driving License', required: true, description: 'Secondary rider license' },
|
|
|
|
|
{ id: 'rent_photo', name: 'Passport Photos (Both)', required: true, description: 'Photos of both riders' },
|
|
|
|
|
{ id: 'rent_deposit', name: 'Deposit Receipt', required: true, description: 'Security deposit proof' },
|
|
|
|
|
{ id: 'rent_agreement', name: 'Shared Agreement', required: true, description: 'Agreement between riders' },
|
|
|
|
|
{ id: 'rent_ec', name: 'Emergency Contacts', required: true, description: 'Emergency contacts for both' },
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: 'rent-to-own',
|
|
|
|
|
name: 'Rent-to-Own',
|
|
|
|
|
documents: [
|
|
|
|
|
{ id: 'rto_nid', name: 'NID Card', required: true, description: 'Valid National ID' },
|
|
|
|
|
{ id: 'rto_license', name: 'Driving License', required: true, description: 'Valid driving license' },
|
|
|
|
|
{ id: 'rto_photo', name: 'Passport Photo', required: true, description: 'Recent photo' },
|
|
|
|
|
{ id: 'rto_income', name: 'Income Proof', required: true, description: 'Salary/income certificate' },
|
|
|
|
|
{ id: 'rto_bank', name: 'Bank Statement', required: true, description: '6 months bank statement' },
|
|
|
|
|
{ id: 'rto_tin', name: 'TIN Certificate', required: false, description: 'Tax Identification Number' },
|
|
|
|
|
{ id: 'rto_guarantor', name: 'Guarantor Info', required: true, description: 'Guarantor details' },
|
|
|
|
|
{ id: 'rto_guarantor_nid', name: 'Guarantor NID', required: true, description: 'Guarantor NID copy' },
|
|
|
|
|
{ id: 'rto_agreement', name: 'RTO Agreement', required: true, description: 'Rent-to-own contract' },
|
|
|
|
|
{ id: 'rto_insurance', name: 'Insurance', required: false, description: 'Vehicle insurance' },
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
parts: [
|
|
|
|
|
{ id: 'PRT-001', name: 'Battery 60V', price: 15000, inStock: 25 },
|
2026-05-05 00:29:47 +06:00
|
|
|
{ id: 'PRT-002', name: 'Battery 48V', price: 12000, inStock: 30 },
|
|
|
|
|
{ id: 'PRT-003', name: 'Front Tire', price: 2500, inStock: 50 },
|
|
|
|
|
{ id: 'PRT-004', name: 'Rear Tire', price: 2500, inStock: 50 },
|
|
|
|
|
{ id: 'PRT-005', name: 'Brake Pad', price: 800, inStock: 100 },
|
|
|
|
|
{ id: 'PRT-006', name: 'Mirror', price: 350, inStock: 80 },
|
|
|
|
|
{ id: 'PRT-007', name: 'Controller', minPrice: 3500, maxPrice: 5000, inStock: 15 },
|
|
|
|
|
{ id: 'PRT-008', name: 'Motor', minPrice: 8000, maxPrice: 12000, inStock: 10 },
|
|
|
|
|
{ id: 'PRT-009', name: 'Charger', minPrice: 1500, maxPrice: 2500, inStock: 20 },
|
|
|
|
|
{ id: 'PRT-010', name: 'Display Meter', price: 1200, inStock: 25 },
|
|
|
|
|
{ id: 'PRT-011', name: 'Throttle', price: 800, inStock: 40 },
|
|
|
|
|
{ id: 'PRT-012', name: 'Brake Cable', price: 250, inStock: 60 },
|
|
|
|
|
{ id: 'PRT-013', name: 'Chain', price: 600, inStock: 35 },
|
|
|
|
|
{ id: 'PRT-014', name: 'Sprocket', price: 450, inStock: 45 },
|
|
|
|
|
{ id: 'PRT-015', name: 'Foot Peg', price: 300, inStock: 50 },
|
2026-04-26 18:32:52 +06:00
|
|
|
],
|
|
|
|
|
serviceCenters: [
|
|
|
|
|
{ id: 'SC-001', name: 'JAIBEN Service Center - Gulshan', address: 'House 45, Road 13, Gulshan 1, Dhaka', phone: '+8801712345670', rating: 4.8 },
|
|
|
|
|
{ id: 'SC-002', name: 'JAIBEN Service Center - Banani', address: 'Road 11, Banani, Dhaka', phone: '+8801712345671', rating: 4.5 },
|
|
|
|
|
],
|
|
|
|
|
rentalPolicy: {
|
|
|
|
|
minAge: 18,
|
|
|
|
|
requireLicense: true,
|
|
|
|
|
deposit: 5000,
|
|
|
|
|
lateFeePerHour: 200,
|
|
|
|
|
cancellationFee: 1000,
|
|
|
|
|
damagePenalty: [
|
|
|
|
|
{ level: 'Minor Scratch', amount: 500 },
|
|
|
|
|
{ level: 'Major Damage', amount: 5000 },
|
|
|
|
|
{ level: 'Total Loss', amount: 25000 },
|
|
|
|
|
],
|
|
|
|
|
rules: [
|
|
|
|
|
'No smoking in vehicles',
|
|
|
|
|
'Return with same fuel/charge level',
|
|
|
|
|
'No unauthorized drivers',
|
|
|
|
|
'Follow traffic rules',
|
|
|
|
|
'Report accidents within 24 hours',
|
|
|
|
|
]
|
2026-05-05 03:09:21 +06:00
|
|
|
},
|
|
|
|
|
plans: {
|
|
|
|
|
singleRent: [
|
|
|
|
|
{
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Premium',
|
|
|
|
|
name: 'Single Rent - Premium',
|
|
|
|
|
dailyRent: 400,
|
|
|
|
|
deposit: 25000,
|
|
|
|
|
weeklySubscription: 2800,
|
|
|
|
|
monthlySubscription: 12000,
|
|
|
|
|
ficoSharePercent: 50,
|
|
|
|
|
description: 'Premium single person rental plan with extra benefits',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Standard',
|
|
|
|
|
name: 'Single Rent - Standard',
|
2026-05-05 03:09:21 +06:00
|
|
|
dailyRent: 300,
|
|
|
|
|
deposit: 20000,
|
|
|
|
|
weeklySubscription: 2100,
|
|
|
|
|
monthlySubscription: 9000,
|
|
|
|
|
ficoSharePercent: 45,
|
2026-05-05 04:20:17 +06:00
|
|
|
description: 'Standard single person rental plan',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Economy',
|
|
|
|
|
name: 'Single Rent - Economy',
|
|
|
|
|
dailyRent: 250,
|
|
|
|
|
deposit: 15000,
|
|
|
|
|
weeklySubscription: 1750,
|
|
|
|
|
monthlySubscription: 7500,
|
|
|
|
|
ficoSharePercent: 40,
|
|
|
|
|
description: 'Economy single person rental plan',
|
2026-05-05 03:09:21 +06:00
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
rentToOwn: [
|
|
|
|
|
{
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Premium',
|
|
|
|
|
name: 'Rent to Own - Premium',
|
|
|
|
|
dailyRent: 350,
|
|
|
|
|
deposit: 25000,
|
|
|
|
|
weeklySubscription: 2450,
|
|
|
|
|
monthlySubscription: 10500,
|
|
|
|
|
durationMonths: 18,
|
|
|
|
|
evPrice: 150000,
|
|
|
|
|
totalPayment: 170000,
|
|
|
|
|
profit: 20000,
|
|
|
|
|
ficoRentSharePercent: 50,
|
|
|
|
|
ficoProfitSharePercent: 45,
|
|
|
|
|
description: 'Premium rent to own plan with high-end EV',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Standard',
|
|
|
|
|
name: 'Rent to Own - Standard',
|
2026-05-05 03:09:21 +06:00
|
|
|
dailyRent: 250,
|
|
|
|
|
deposit: 18000,
|
|
|
|
|
weeklySubscription: 1750,
|
|
|
|
|
monthlySubscription: 7000,
|
|
|
|
|
durationMonths: 18,
|
|
|
|
|
evPrice: 120000,
|
|
|
|
|
totalPayment: 135000,
|
|
|
|
|
profit: 15000,
|
|
|
|
|
ficoRentSharePercent: 45,
|
|
|
|
|
ficoProfitSharePercent: 45,
|
2026-05-05 04:20:17 +06:00
|
|
|
description: 'Standard rent to own plan',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Economy',
|
|
|
|
|
name: 'Rent to Own - Economy',
|
|
|
|
|
dailyRent: 200,
|
|
|
|
|
deposit: 15000,
|
|
|
|
|
weeklySubscription: 1400,
|
|
|
|
|
monthlySubscription: 6000,
|
|
|
|
|
durationMonths: 18,
|
|
|
|
|
evPrice: 100000,
|
|
|
|
|
totalPayment: 115000,
|
|
|
|
|
profit: 15000,
|
|
|
|
|
ficoRentSharePercent: 40,
|
|
|
|
|
ficoProfitSharePercent: 40,
|
|
|
|
|
description: 'Economy rent to own plan',
|
2026-05-05 03:09:21 +06:00
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
shareEv: [
|
|
|
|
|
{
|
2026-05-05 04:20:17 +06:00
|
|
|
tier: 'Premium',
|
|
|
|
|
name: 'Share an EV - Premium',
|
|
|
|
|
dailyRentEach: 300,
|
|
|
|
|
totalDailyRent: 600,
|
|
|
|
|
depositEach: 20000,
|
|
|
|
|
totalDeposit: 40000,
|
|
|
|
|
weeklySubscriptionEach: 2100,
|
|
|
|
|
totalWeeklySubscription: 4200,
|
|
|
|
|
monthlySubscriptionEach: 8400,
|
|
|
|
|
totalMonthlySubscription: 16800,
|
|
|
|
|
ficoSharePercent: 50,
|
|
|
|
|
description: 'Premium shared EV with premium bikes',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Standard',
|
|
|
|
|
name: 'Share an EV - Standard',
|
2026-05-05 03:09:21 +06:00
|
|
|
dailyRentEach: 200,
|
|
|
|
|
totalDailyRent: 400,
|
|
|
|
|
depositEach: 15000,
|
|
|
|
|
totalDeposit: 30000,
|
|
|
|
|
weeklySubscriptionEach: 1400,
|
|
|
|
|
totalWeeklySubscription: 2800,
|
|
|
|
|
monthlySubscriptionEach: 5600,
|
|
|
|
|
totalMonthlySubscription: 11200,
|
|
|
|
|
ficoSharePercent: 45,
|
2026-05-05 04:20:17 +06:00
|
|
|
description: 'Standard shared EV plan',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
tier: 'Economy',
|
|
|
|
|
name: 'Share an EV - Economy',
|
|
|
|
|
dailyRentEach: 150,
|
|
|
|
|
totalDailyRent: 300,
|
|
|
|
|
depositEach: 12000,
|
|
|
|
|
totalDeposit: 24000,
|
|
|
|
|
weeklySubscriptionEach: 1050,
|
|
|
|
|
totalWeeklySubscription: 2100,
|
|
|
|
|
monthlySubscriptionEach: 4200,
|
|
|
|
|
totalMonthlySubscription: 8400,
|
|
|
|
|
ficoSharePercent: 40,
|
|
|
|
|
description: 'Economy shared EV plan',
|
2026-05-05 03:09:21 +06:00
|
|
|
}
|
2026-05-05 04:20:17 +06:00
|
|
|
],
|
2026-05-05 21:48:18 +06:00
|
|
|
investment: [
|
|
|
|
|
{
|
|
|
|
|
id: 'inv_demo_1',
|
|
|
|
|
name: '1 Bike Plan',
|
|
|
|
|
tier: 'Economy',
|
|
|
|
|
minInvestment: 50000,
|
|
|
|
|
maxInvestment: 100000,
|
|
|
|
|
monthlyReturnPercent: 2,
|
|
|
|
|
durationMonths: 12,
|
|
|
|
|
profitSharePercent: 50,
|
|
|
|
|
lockInMonths: 3,
|
|
|
|
|
totalReturnPercent: 24,
|
|
|
|
|
earlyExitPenalty: 10,
|
|
|
|
|
startDate: '2026-01-01',
|
|
|
|
|
endDate: '2026-12-31',
|
|
|
|
|
targetAmount: 1000000,
|
|
|
|
|
status: 'active',
|
|
|
|
|
description: 'Investment plan for 1 bike - perfect for small investors',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'inv_demo_2',
|
|
|
|
|
name: '5 Bike Plan',
|
|
|
|
|
tier: 'Standard',
|
|
|
|
|
minInvestment: 150000,
|
|
|
|
|
maxInvestment: 500000,
|
|
|
|
|
monthlyReturnPercent: 2.5,
|
|
|
|
|
durationMonths: 24,
|
|
|
|
|
profitSharePercent: 50,
|
|
|
|
|
lockInMonths: 6,
|
|
|
|
|
totalReturnPercent: 60,
|
|
|
|
|
earlyExitPenalty: 10,
|
|
|
|
|
startDate: '2026-01-01',
|
|
|
|
|
endDate: '2026-12-31',
|
|
|
|
|
targetAmount: 5000000,
|
|
|
|
|
status: 'active',
|
|
|
|
|
description: 'Investment plan for 5 bikes - medium scale investment',
|
|
|
|
|
}
|
|
|
|
|
],
|
2026-05-05 21:54:46 +06:00
|
|
|
swapStation: [
|
|
|
|
|
{
|
|
|
|
|
id: 'ss_1',
|
|
|
|
|
name: 'Basic Swap Station',
|
|
|
|
|
batteryCount: 10,
|
|
|
|
|
swapPrice: 50,
|
|
|
|
|
monthlySubscription: 500,
|
|
|
|
|
dailySubscription: 20,
|
|
|
|
|
minBatteries: 1,
|
|
|
|
|
maxBatteries: 5,
|
|
|
|
|
profitSharePercent: 50,
|
|
|
|
|
status: 'active',
|
|
|
|
|
description: 'Basic swap station for small operators',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'ss_2',
|
|
|
|
|
name: 'Premium Swap Station',
|
|
|
|
|
batteryCount: 50,
|
|
|
|
|
swapPrice: 45,
|
|
|
|
|
monthlySubscription: 2000,
|
|
|
|
|
dailySubscription: 80,
|
|
|
|
|
minBatteries: 10,
|
|
|
|
|
maxBatteries: 30,
|
|
|
|
|
profitSharePercent: 55,
|
|
|
|
|
status: 'active',
|
|
|
|
|
description: 'Premium swap station for large operators',
|
|
|
|
|
}
|
|
|
|
|
],
|
2026-05-05 04:20:17 +06:00
|
|
|
},
|
2026-04-26 18:32:52 +06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function CompanySettingsPage() {
|
|
|
|
|
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
2026-05-05 21:54:46 +06:00
|
|
|
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans' | 'investment' | 'swapstation'>('general');
|
2026-04-26 18:41:54 +06:00
|
|
|
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rental'>('investor');
|
2026-04-26 18:32:52 +06:00
|
|
|
const [saved, setSaved] = useState(false);
|
2026-05-05 03:19:10 +06:00
|
|
|
const [activePlanTab, setActivePlanTab] = useState<'singleRent' | 'rentToOwn' | 'shareEv'>('singleRent');
|
2026-05-05 00:29:47 +06:00
|
|
|
const [addDocType, setAddDocType] = useState<'investor' | 'merchant' | 'swapstation' | 'rental' | null>(null);
|
|
|
|
|
const [newDocName, setNewDocName] = useState('');
|
|
|
|
|
const [newDocDesc, setNewDocDesc] = useState('');
|
2026-05-05 21:48:18 +06:00
|
|
|
const [activeInvestTab, setActiveInvestTab] = useState(0);
|
|
|
|
|
const [addInvestPlan, setAddInvestPlan] = useState(false);
|
|
|
|
|
const [newInvestName, setNewInvestName] = useState('');
|
|
|
|
|
const [newInvestTier, setNewInvestTier] = useState('Standard');
|
|
|
|
|
const [newInvestStatus, setNewInvestStatus] = useState('active');
|
|
|
|
|
const [newInvestTarget, setNewInvestTarget] = useState(1000000);
|
|
|
|
|
const [newInvestStart, setNewInvestStart] = useState('2026-01-01');
|
|
|
|
|
const [newInvestEnd, setNewInvestEnd] = useState('2026-12-31');
|
|
|
|
|
const [newInvestMin, setNewInvestMin] = useState(10000);
|
|
|
|
|
const [newInvestMax, setNewInvestMax] = useState(100000);
|
|
|
|
|
const [newInvestMonthly, setNewInvestMonthly] = useState(2);
|
|
|
|
|
const [newInvestDuration, setNewInvestDuration] = useState(12);
|
|
|
|
|
const [newInvestLock, setNewInvestLock] = useState(3);
|
|
|
|
|
const [newInvestTotal, setNewInvestTotal] = useState(24);
|
|
|
|
|
const [newInvestPenalty, setNewInvestPenalty] = useState(10);
|
|
|
|
|
const [newInvestDesc, setNewInvestDesc] = useState('');
|
|
|
|
|
|
|
|
|
|
const createInvestPlan = () => {
|
|
|
|
|
if (newInvestName.trim() && typeof window !== 'undefined') {
|
|
|
|
|
const ficoShare = settings.plans.singleRent[0]?.ficoSharePercent || 50;
|
|
|
|
|
const newPlan = {
|
|
|
|
|
id: 'inv_' + Date.now(),
|
|
|
|
|
name: newInvestName,
|
|
|
|
|
tier: newInvestTier,
|
|
|
|
|
minInvestment: newInvestMin,
|
|
|
|
|
maxInvestment: newInvestMax,
|
|
|
|
|
monthlyReturnPercent: newInvestMonthly,
|
|
|
|
|
durationMonths: newInvestDuration,
|
|
|
|
|
profitSharePercent: ficoShare,
|
|
|
|
|
lockInMonths: newInvestLock,
|
|
|
|
|
totalReturnPercent: newInvestTotal,
|
|
|
|
|
earlyExitPenalty: newInvestPenalty,
|
|
|
|
|
startDate: newInvestStart,
|
|
|
|
|
endDate: newInvestEnd,
|
|
|
|
|
targetAmount: newInvestTarget,
|
|
|
|
|
status: newInvestStatus,
|
|
|
|
|
description: newInvestDesc
|
|
|
|
|
};
|
|
|
|
|
const updatedPlans = [...settings.plans.investment, newPlan];
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, investment: updatedPlans } });
|
|
|
|
|
setActiveInvestTab(updatedPlans.length - 1);
|
|
|
|
|
setAddInvestPlan(false);
|
|
|
|
|
setNewInvestName('');
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-04-26 18:32:52 +06:00
|
|
|
|
2026-05-05 21:54:46 +06:00
|
|
|
const [activeSwapTab, setActiveSwapTab] = useState(0);
|
|
|
|
|
const [addSwapStationPlan, setAddSwapStationPlan] = useState(false);
|
|
|
|
|
const [newSwapName, setNewSwapName] = useState('');
|
|
|
|
|
const [newSwapStatus, setNewSwapStatus] = useState('active');
|
|
|
|
|
const [newSwapBatteryCount, setNewSwapBatteryCount] = useState(10);
|
|
|
|
|
const [newSwapPrice, setNewSwapPrice] = useState(50);
|
|
|
|
|
const [newSwapMonthly, setNewSwapMonthly] = useState(500);
|
|
|
|
|
const [newSwapDaily, setNewSwapDaily] = useState(20);
|
|
|
|
|
const [newSwapMin, setNewSwapMin] = useState(1);
|
|
|
|
|
const [newSwapMax, setNewSwapMax] = useState(5);
|
|
|
|
|
const [newSwapProfit, setNewSwapProfit] = useState(50);
|
|
|
|
|
const [newSwapDesc, setNewSwapDesc] = useState('');
|
|
|
|
|
|
|
|
|
|
const createSwapStationPlan = () => {
|
|
|
|
|
if (newSwapName.trim() && typeof window !== 'undefined') {
|
|
|
|
|
const newPlan = {
|
|
|
|
|
id: 'ss_' + Date.now(),
|
|
|
|
|
name: newSwapName,
|
|
|
|
|
batteryCount: newSwapBatteryCount,
|
|
|
|
|
swapPrice: newSwapPrice,
|
|
|
|
|
monthlySubscription: newSwapMonthly,
|
|
|
|
|
dailySubscription: newSwapDaily,
|
|
|
|
|
minBatteries: newSwapMin,
|
|
|
|
|
maxBatteries: newSwapMax,
|
|
|
|
|
profitSharePercent: newSwapProfit,
|
|
|
|
|
status: newSwapStatus,
|
|
|
|
|
description: newSwapDesc
|
|
|
|
|
};
|
|
|
|
|
const updatedPlans = [...settings.plans.swapStation, newPlan];
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, swapStation: updatedPlans } });
|
|
|
|
|
setActiveSwapTab(updatedPlans.length - 1);
|
|
|
|
|
setAddSwapStationPlan(false);
|
|
|
|
|
setNewSwapName('');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
const handleSave = () => {
|
|
|
|
|
setSaved(true);
|
|
|
|
|
setTimeout(() => setSaved(false), 2000);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-05 03:09:21 +06:00
|
|
|
const tabs = [
|
2026-04-26 18:32:52 +06:00
|
|
|
{ id: 'general', label: 'General', icon: Settings },
|
|
|
|
|
{ id: 'branding', label: 'Branding', icon: Palette },
|
|
|
|
|
{ id: 'social', label: 'Social Media', icon: Link2 },
|
|
|
|
|
{ id: 'integration', label: 'Integrations', icon: Mail },
|
|
|
|
|
{ id: 'landing', label: 'Landing Page', icon: Monitor },
|
2026-04-26 18:41:54 +06:00
|
|
|
{ id: 'kyc', label: 'KYC Documents', icon: Package },
|
2026-05-05 00:29:47 +06:00
|
|
|
{ id: 'parts', label: 'EV Parts', icon: Package },
|
2026-04-26 18:32:52 +06:00
|
|
|
{ id: 'rental', label: 'Rental Policy', icon: FileCheck },
|
2026-05-05 03:09:21 +06:00
|
|
|
{ id: 'plans', label: 'Plan Selection', icon: Package },
|
2026-05-05 21:48:18 +06:00
|
|
|
{ id: 'investment', label: 'Investment Plan', icon: DollarSign },
|
2026-05-05 21:54:46 +06:00
|
|
|
{ id: 'swapstation', label: 'Swap Station Plan', icon: Zap },
|
2026-04-26 18:32:52 +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>
|
|
|
|
|
<h1 className="text-2xl lg:text-3xl font-extrabold text-slate-800">Company Settings</h1>
|
|
|
|
|
<p className="text-sm text-slate-500 mt-1">Manage your company information and configurations</p>
|
|
|
|
|
</div>
|
2026-05-05 21:48:18 +06:00
|
|
|
{/* <button
|
2026-04-26 18:32:52 +06:00
|
|
|
onClick={handleSave}
|
|
|
|
|
className="py-2.5 px-4 bg-accent text-white rounded-lg font-semibold text-sm hover:bg-accent-dark transition-colors flex items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<Save className="w-4 h-4" /> Save Changes
|
2026-05-05 21:48:18 +06:00
|
|
|
</button> */}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{saved && (
|
|
|
|
|
<div className="mb-4 p-3 bg-green-100 text-green-700 rounded-lg text-sm">
|
|
|
|
|
Settings saved successfully!
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="grid lg:grid-cols-4 gap-6">
|
|
|
|
|
<div className="lg:col-span-1 bg-white rounded-xl shadow-sm border border-slate-100 p-2">
|
|
|
|
|
<nav className="space-y-1">
|
|
|
|
|
{tabs.map(tab => (
|
|
|
|
|
<button
|
|
|
|
|
key={tab.id}
|
|
|
|
|
onClick={() => setActiveTab(tab.id as typeof activeTab)}
|
2026-04-26 18:41:54 +06:00
|
|
|
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors ${activeTab === tab.id
|
|
|
|
|
? 'bg-accent text-white'
|
|
|
|
|
: 'text-slate-600 hover:bg-slate-50'
|
|
|
|
|
}`}
|
2026-04-26 18:32:52 +06:00
|
|
|
>
|
|
|
|
|
<tab.icon className="w-4 h-4" />
|
|
|
|
|
{tab.label}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="lg:col-span-3 bg-white rounded-xl shadow-sm border border-slate-100">
|
|
|
|
|
{activeTab === 'general' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">General Information</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Company Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.name}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, name: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Short Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.shortName}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, shortName: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Tagline</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.tagLine}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, tagLine: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Website</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.website}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, website: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={settings.description}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, description: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
rows={3}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={settings.email}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, email: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Phone</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.phone}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, phone: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Address</label>
|
|
|
|
|
<textarea
|
|
|
|
|
value={settings.address}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, address: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
rows={2}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">City</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.city}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, city: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Country</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.country}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, country: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Timezone</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.timezone}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, timezone: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
>
|
|
|
|
|
<option value="Asia/Dhaka">Asia/Dhaka (GMT+6)</option>
|
|
|
|
|
<option value="Asia/Kolkata">Asia/Kolkata (GMT+5:30)</option>
|
|
|
|
|
<option value="UTC">UTC (GMT+0)</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800 pt-4 border-t">Business Registration</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">TIN Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.tin}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, tin: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">BIN Number</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.bin}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, bin: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Trade License</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.tradeLicense}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, tradeLicense: e.target.value })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Documents</label>
|
|
|
|
|
<div className="mt-2 p-4 border-2 border-dashed border-slate-200 rounded-lg text-center hover:border-accent cursor-pointer">
|
|
|
|
|
<Upload className="w-8 h-8 mx-auto text-slate-400" />
|
|
|
|
|
<p className="text-sm text-slate-500 mt-1">Drop files here or click to upload</p>
|
|
|
|
|
<p className="text-xs text-slate-400">PDF, JPG, PNG up to 10MB</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'branding' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Branding & Design</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-6">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Company Logo</label>
|
|
|
|
|
<div className="mt-2 border-2 border-dashed border-slate-200 rounded-lg p-6 text-center hover:border-accent cursor-pointer">
|
|
|
|
|
<Image className="w-12 h-12 mx-auto text-slate-400" />
|
|
|
|
|
<p className="text-sm text-slate-500 mt-2">Upload Logo (PNG, SVG)</p>
|
|
|
|
|
<p className="text-xs text-slate-400">Recommended: 200x50px</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Favicon</label>
|
|
|
|
|
<div className="mt-2 border-2 border-dashed border-slate-200 rounded-lg p-6 text-center hover:border-accent cursor-pointer">
|
|
|
|
|
<Camera className="w-12 h-12 mx-auto text-slate-400" />
|
|
|
|
|
<p className="text-sm text-slate-500 mt-2">Upload Favicon</p>
|
|
|
|
|
<p className="text-xs text-slate-400">Recommended: 32x32px</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid lg:grid-cols-2 gap-6 pt-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Primary Color</label>
|
|
|
|
|
<div className="flex items-center gap-3 mt-2">
|
|
|
|
|
<input
|
|
|
|
|
type="color"
|
|
|
|
|
value={settings.primaryColor}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, primaryColor: e.target.value })}
|
|
|
|
|
className="w-12 h-12 rounded-lg cursor-pointer border-0"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.primaryColor}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, primaryColor: e.target.value })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Secondary Color</label>
|
|
|
|
|
<div className="flex items-center gap-3 mt-2">
|
|
|
|
|
<input
|
|
|
|
|
type="color"
|
|
|
|
|
value={settings.secondaryColor}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, secondaryColor: e.target.value })}
|
|
|
|
|
className="w-12 h-12 rounded-lg cursor-pointer border-0"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.secondaryColor}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, secondaryColor: e.target.value })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Border Radius</label>
|
|
|
|
|
<div className="flex items-center gap-4 mt-2">
|
|
|
|
|
<Ruler className="w-5 h-5 text-slate-400" />
|
|
|
|
|
<input
|
|
|
|
|
type="range"
|
|
|
|
|
min={0}
|
|
|
|
|
max={20}
|
|
|
|
|
value={settings.borderRadius}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, borderRadius: parseInt(e.target.value) })}
|
|
|
|
|
className="flex-1"
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-sm text-slate-600">{settings.borderRadius}px</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex gap-2 mt-4">
|
|
|
|
|
{[4, 8, 12, 16].map(radius => (
|
|
|
|
|
<button
|
|
|
|
|
key={radius}
|
|
|
|
|
onClick={() => setSettings({ ...settings, borderRadius: radius })}
|
|
|
|
|
className={`px-3 py-2 text-sm border ${settings.borderRadius === radius ? 'bg-accent text-white' : 'border-slate-200'}`}
|
|
|
|
|
style={{ borderRadius: radius }}
|
|
|
|
|
>
|
|
|
|
|
{radius}px
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Preview</label>
|
|
|
|
|
<div className="mt-2 p-6 bg-slate-100 rounded-lg flex items-center justify-center gap-4">
|
2026-04-26 18:41:54 +06:00
|
|
|
<div
|
2026-04-26 18:32:52 +06:00
|
|
|
className="px-4 py-2 text-white font-bold"
|
|
|
|
|
style={{ backgroundColor: settings.primaryColor, borderRadius: settings.borderRadius }}
|
|
|
|
|
>
|
|
|
|
|
Primary
|
|
|
|
|
</div>
|
2026-04-26 18:41:54 +06:00
|
|
|
<div
|
2026-04-26 18:32:52 +06:00
|
|
|
className="px-4 py-2 text-white font-bold"
|
|
|
|
|
style={{ backgroundColor: settings.secondaryColor, borderRadius: settings.borderRadius }}
|
|
|
|
|
>
|
|
|
|
|
Secondary
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'social' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Social Media Links</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="space-y-4">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="w-5 text-blue-600 font-bold text-sm">FB</span>
|
|
|
|
|
<label className="w-24 text-sm text-slate-600">Facebook</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.socialLinks.facebook}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, socialLinks: { ...settings.socialLinks, facebook: e.target.value } })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://facebook.com/yourpage"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="w-5 text-sky-500 font-bold text-sm">X</span>
|
|
|
|
|
<label className="w-24 text-sm text-slate-600">Twitter</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.socialLinks.twitter}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, socialLinks: { ...settings.socialLinks, twitter: e.target.value } })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://twitter.com/yourpage"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="w-5 text-pink-600 font-bold text-sm">IG</span>
|
|
|
|
|
<label className="w-24 text-sm text-slate-600">Instagram</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.socialLinks.instagram}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, socialLinks: { ...settings.socialLinks, instagram: e.target.value } })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://instagram.com/yourpage"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="w-5 text-blue-700 font-bold text-sm">IN</span>
|
|
|
|
|
<label className="w-24 text-sm text-slate-600">LinkedIn</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.socialLinks.linkedin}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, socialLinks: { ...settings.socialLinks, linkedin: e.target.value } })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://linkedin.com/company/yourpage"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<span className="w-5 text-red-600 font-bold text-sm">YT</span>
|
|
|
|
|
<label className="w-24 text-sm text-slate-600">YouTube</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.socialLinks.youtube}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, socialLinks: { ...settings.socialLinks, youtube: e.target.value } })}
|
|
|
|
|
className="flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
|
|
|
|
placeholder="https://youtube.com/@yourchannel"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'integration' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Email Integration (SMTP)</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">SMTP Host</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.smtp.host}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, host: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">SMTP Port</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={settings.smtp.port}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, port: parseInt(e.target.value) } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Username</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.smtp.user}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, user: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Password</label>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={settings.smtp.password}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, password: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">From Email</label>
|
|
|
|
|
<input
|
|
|
|
|
type="email"
|
|
|
|
|
value={settings.smtp.fromEmail}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, fromEmail: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">From Name</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.smtp.fromName}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, smtp: { ...settings.smtp, fromName: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Encryption</label>
|
|
|
|
|
<div className="flex gap-3 mt-2">
|
|
|
|
|
{(['ssl', 'tls', 'none'] as const).map(enc => (
|
|
|
|
|
<button
|
|
|
|
|
key={enc}
|
|
|
|
|
onClick={() => setSettings({ ...settings, smtp: { ...settings.smtp, encryption: enc } })}
|
2026-04-26 18:41:54 +06:00
|
|
|
className={`px-4 py-2 text-sm rounded-lg border ${settings.smtp.encryption === enc
|
|
|
|
|
? 'bg-accent text-white border-accent'
|
|
|
|
|
: 'border-slate-200 text-slate-600'
|
|
|
|
|
}`}
|
2026-04-26 18:32:52 +06:00
|
|
|
>
|
|
|
|
|
{enc.toUpperCase()}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800 pt-4 border-t">SMS Integration</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Provider</label>
|
|
|
|
|
<select
|
|
|
|
|
value={settings.sms.provider}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, sms: { ...settings.sms, provider: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
>
|
|
|
|
|
<option value="BulkSMS">BulkSMS BD</option>
|
|
|
|
|
<option value="Twilio">Twilio</option>
|
|
|
|
|
<option value="Msg91">Msg91</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Sender ID</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.sms.senderId}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, sms: { ...settings.sms, senderId: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="lg:col-span-2">
|
|
|
|
|
<label className="text-sm text-slate-600">API Key</label>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={settings.sms.apiKey}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, sms: { ...settings.sms, apiKey: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="lg:col-span-2">
|
|
|
|
|
<label className="text-sm text-slate-600">API URL</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.sms.url}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, sms: { ...settings.sms, url: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'landing' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Landing Page Hero Section</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Hero Title</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.heroData.title}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, heroData: { ...settings.heroData, title: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Subtitle</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.heroData.subtitle}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, heroData: { ...settings.heroData, subtitle: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">CTA Button Text</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.heroData.ctaText}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, heroData: { ...settings.heroData, ctaText: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">CTA Link</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.heroData.ctaLink}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, heroData: { ...settings.heroData, ctaLink: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Background Image</label>
|
|
|
|
|
<div className="mt-2 border-2 border-dashed border-slate-200 rounded-lg p-6 text-center hover:border-accent cursor-pointer">
|
|
|
|
|
<Image className="w-12 h-12 mx-auto text-slate-400" />
|
|
|
|
|
<p className="text-sm text-slate-500 mt-2">Upload Background Image</p>
|
|
|
|
|
<p className="text-xs text-slate-400">Recommended: 1920x1080px</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Video URL (Optional)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
value={settings.heroData.videoUrl}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, heroData: { ...settings.heroData, videoUrl: e.target.value } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
placeholder="https://youtube.com/watch?v=..."
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="pt-4 border-t">
|
|
|
|
|
<label className="text-sm text-slate-600">Preview</label>
|
|
|
|
|
<div className="mt-2 p-8 bg-gradient-to-br from-slate-800 to-slate-900 rounded-lg text-center">
|
|
|
|
|
<h4 className="text-2xl font-bold text-white">{settings.heroData.title}</h4>
|
|
|
|
|
<p className="text-slate-300 mt-2">{settings.heroData.subtitle}</p>
|
2026-04-26 18:41:54 +06:00
|
|
|
<button
|
2026-04-26 18:32:52 +06:00
|
|
|
className="mt-4 px-6 py-2 bg-accent text-white rounded-lg"
|
|
|
|
|
style={{ borderRadius: settings.borderRadius }}
|
|
|
|
|
>
|
|
|
|
|
{settings.heroData.ctaText}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-04-26 18:41:54 +06:00
|
|
|
{activeTab === 'kyc' && (
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="p-6 space-y-6">
|
2026-04-26 18:41:54 +06:00
|
|
|
<h3 className="text-lg font-semibold text-slate-800">KYC Documents</h3>
|
|
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="flex flex-wrap gap-2 border-b border-slate-200 pb-2">
|
|
|
|
|
{[
|
|
|
|
|
{ id: 'investor', label: 'Investor' },
|
|
|
|
|
{ id: 'merchant', label: 'Merchant' },
|
|
|
|
|
{ id: 'swapstation', label: 'Swap Station' },
|
|
|
|
|
{ id: 'rental', label: 'Rental Types' },
|
|
|
|
|
].map(tab => (
|
|
|
|
|
<button
|
|
|
|
|
key={tab.id}
|
|
|
|
|
onClick={() => setActiveMasterTab(tab.id as typeof activeMasterTab)}
|
2026-04-26 18:41:54 +06:00
|
|
|
className={`px-3 py-1.5 text-sm rounded-lg ${activeMasterTab === tab.id
|
2026-05-05 03:09:21 +06:00
|
|
|
? 'bg-accent text-white'
|
|
|
|
|
: 'text-slate-600 hover:bg-slate-50'
|
2026-04-26 18:41:54 +06:00
|
|
|
}`}
|
2026-04-26 18:32:52 +06:00
|
|
|
>
|
|
|
|
|
{tab.label}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{activeMasterTab === 'investor' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-medium text-slate-700 mb-3">Investor Documents</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{settings.masterData.investorDocuments.map((doc, i) => (
|
|
|
|
|
<div key={doc.id} className="flex items-center gap-3 p-3 border border-slate-200 rounded-lg">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={doc.required}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const updated = [...settings.masterData.investorDocuments];
|
|
|
|
|
updated[i].required = e.target.checked;
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, investorDocuments: updated } });
|
|
|
|
|
}}
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="text-sm font-medium">{doc.name}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{doc.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`text-xs px-2 py-0.5 rounded ${doc.required ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-500'}`}>
|
|
|
|
|
{doc.required ? 'Required' : 'Optional'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
<button
|
2026-05-05 00:29:47 +06:00
|
|
|
onClick={() => setAddDocType('investor')}
|
|
|
|
|
className="mt-4 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
|
|
|
|
+ Add Document
|
|
|
|
|
</button>
|
|
|
|
|
{addDocType === 'investor' && (
|
|
|
|
|
<div className="mt-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Document name"
|
|
|
|
|
value={newDocName}
|
|
|
|
|
onChange={(e) => setNewDocName(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Description"
|
|
|
|
|
value={newDocDesc}
|
|
|
|
|
onChange={(e) => setNewDocDesc(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (newDocName.trim()) {
|
|
|
|
|
const updated = [...settings.masterData.investorDocuments, {
|
|
|
|
|
id: `inv_${Date.now()}`,
|
|
|
|
|
name: newDocName,
|
|
|
|
|
required: false,
|
|
|
|
|
description: newDocDesc || ''
|
|
|
|
|
}];
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, investorDocuments: updated } });
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 bg-accent text-white text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Add
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 border border-slate-200 text-slate-600 text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeMasterTab === 'merchant' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-medium text-slate-700 mb-3">Merchant Documents</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{settings.masterData.merchantDocuments.map((doc, i) => (
|
|
|
|
|
<div key={doc.id} className="flex items-center gap-3 p-3 border border-slate-200 rounded-lg">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={doc.required}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const updated = [...settings.masterData.merchantDocuments];
|
|
|
|
|
updated[i].required = e.target.checked;
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, merchantDocuments: updated } });
|
|
|
|
|
}}
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="text-sm font-medium">{doc.name}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{doc.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`text-xs px-2 py-0.5 rounded ${doc.required ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-500'}`}>
|
|
|
|
|
{doc.required ? 'Required' : 'Optional'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
<button
|
2026-05-05 00:29:47 +06:00
|
|
|
onClick={() => setAddDocType('merchant')}
|
|
|
|
|
className="mt-4 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
|
|
|
|
+ Add Document
|
|
|
|
|
</button>
|
|
|
|
|
{addDocType === 'merchant' && (
|
|
|
|
|
<div className="mt-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Document name"
|
|
|
|
|
value={newDocName}
|
|
|
|
|
onChange={(e) => setNewDocName(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Description"
|
|
|
|
|
value={newDocDesc}
|
|
|
|
|
onChange={(e) => setNewDocDesc(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (newDocName.trim()) {
|
|
|
|
|
const updated = [...settings.masterData.merchantDocuments, {
|
|
|
|
|
id: `mer_${Date.now()}`,
|
|
|
|
|
name: newDocName,
|
|
|
|
|
required: false,
|
|
|
|
|
description: newDocDesc || ''
|
|
|
|
|
}];
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, merchantDocuments: updated } });
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 bg-accent text-white text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Add
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 border border-slate-200 text-slate-600 text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeMasterTab === 'swapstation' && (
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-medium text-slate-700 mb-3">Swap Station Documents</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{settings.masterData.swapStationDocuments.map((doc, i) => (
|
|
|
|
|
<div key={doc.id} className="flex items-center gap-3 p-3 border border-slate-200 rounded-lg">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={doc.required}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const updated = [...settings.masterData.swapStationDocuments];
|
|
|
|
|
updated[i].required = e.target.checked;
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, swapStationDocuments: updated } });
|
|
|
|
|
}}
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="text-sm font-medium">{doc.name}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{doc.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`text-xs px-2 py-0.5 rounded ${doc.required ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-500'}`}>
|
|
|
|
|
{doc.required ? 'Required' : 'Optional'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
<button
|
2026-05-05 00:29:47 +06:00
|
|
|
onClick={() => setAddDocType('swapstation')}
|
|
|
|
|
className="mt-4 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
|
|
|
|
+ Add Document
|
|
|
|
|
</button>
|
|
|
|
|
{addDocType === 'swapstation' && (
|
|
|
|
|
<div className="mt-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Document name"
|
|
|
|
|
value={newDocName}
|
|
|
|
|
onChange={(e) => setNewDocName(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Description"
|
|
|
|
|
value={newDocDesc}
|
|
|
|
|
onChange={(e) => setNewDocDesc(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (newDocName.trim()) {
|
|
|
|
|
const updated = [...settings.masterData.swapStationDocuments, {
|
|
|
|
|
id: `ss_${Date.now()}`,
|
|
|
|
|
name: newDocName,
|
|
|
|
|
required: false,
|
|
|
|
|
description: newDocDesc || ''
|
|
|
|
|
}];
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, swapStationDocuments: updated } });
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 bg-accent text-white text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Add
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 border border-slate-200 text-slate-600 text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeMasterTab === 'rental' && (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{settings.masterData.rentalDocuments.map((rental, ri) => (
|
|
|
|
|
<div key={rental.type} className="border border-slate-200 rounded-lg p-4">
|
|
|
|
|
<div className="flex items-center justify-between mb-3">
|
|
|
|
|
<h4 className="font-medium text-slate-700">{rental.name}</h4>
|
|
|
|
|
<span className="text-xs px-2 py-1 bg-blue-100 text-blue-700 rounded">{rental.documents.length} docs</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{rental.documents.map((doc, di) => (
|
|
|
|
|
<div key={doc.id} className="flex items-center gap-3 p-2 bg-slate-50 rounded-lg">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={doc.required}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const updated = [...settings.masterData.rentalDocuments];
|
|
|
|
|
updated[ri].documents[di].required = e.target.checked;
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, rentalDocuments: updated } });
|
|
|
|
|
}}
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="text-sm">{doc.name}</p>
|
|
|
|
|
<p className="text-xs text-slate-500">{doc.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`text-xs px-2 py-0.5 rounded ${doc.required ? 'bg-red-100 text-red-700' : 'bg-slate-200 text-slate-600'}`}>
|
|
|
|
|
{doc.required ? 'Required' : 'Optional'}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
<button
|
2026-05-05 00:29:47 +06:00
|
|
|
onClick={() => setAddDocType('rental')}
|
|
|
|
|
className="mt-3 text-sm text-accent hover:underline"
|
|
|
|
|
>
|
|
|
|
|
+ Add Document
|
|
|
|
|
</button>
|
|
|
|
|
{addDocType === 'rental' && (
|
|
|
|
|
<div className="mt-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Document name"
|
|
|
|
|
value={newDocName}
|
|
|
|
|
onChange={(e) => setNewDocName(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Description"
|
|
|
|
|
value={newDocDesc}
|
|
|
|
|
onChange={(e) => setNewDocDesc(e.target.value)}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mb-2"
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (newDocName.trim()) {
|
|
|
|
|
const updated = [...settings.masterData.rentalDocuments];
|
|
|
|
|
updated.forEach((rental) => {
|
|
|
|
|
rental.documents.push({
|
|
|
|
|
id: `rent_${Date.now()}`,
|
|
|
|
|
name: newDocName,
|
|
|
|
|
required: false,
|
|
|
|
|
description: newDocDesc || ''
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
setSettings({ ...settings, masterData: { ...settings.masterData, rentalDocuments: updated } });
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 bg-accent text-white text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Add to All
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setAddDocType(null);
|
|
|
|
|
setNewDocName('');
|
|
|
|
|
setNewDocDesc('');
|
|
|
|
|
}}
|
|
|
|
|
className="px-3 py-1.5 border border-slate-200 text-slate-600 text-sm rounded-lg"
|
|
|
|
|
>
|
|
|
|
|
Cancel
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
{activeTab === 'parts' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">EV Parts</h3>
|
2026-05-05 03:09:21 +06:00
|
|
|
|
2026-05-05 00:29:47 +06:00
|
|
|
<div className="overflow-x-auto">
|
|
|
|
|
<table className="w-full">
|
|
|
|
|
<thead className="bg-slate-50">
|
|
|
|
|
<tr>
|
|
|
|
|
<th className="px-3 py-2 text-left text-xs font-semibold">Part ID</th>
|
|
|
|
|
<th className="px-3 py-2 text-left text-xs font-semibold">Name</th>
|
|
|
|
|
<th className="px-3 py-2 text-left text-xs font-semibold">Price Range</th>
|
|
|
|
|
<th className="px-3 py-2 text-left text-xs font-semibold">In Stock</th>
|
|
|
|
|
<th className="px-3 py-2 text-left text-xs font-semibold">Type</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{settings.parts.map(part => (
|
|
|
|
|
<tr key={part.id} className="border-t">
|
|
|
|
|
<td className="px-3 py-2 text-sm">{part.id}</td>
|
|
|
|
|
<td className="px-3 py-2 text-sm font-medium">{part.name}</td>
|
|
|
|
|
<td className="px-3 py-2 text-sm">
|
|
|
|
|
{part.minPrice && part.maxPrice ? (
|
|
|
|
|
<span className="text-amber-600">৳{part.minPrice} - ৳{part.maxPrice}</span>
|
|
|
|
|
) : part.price ? (
|
|
|
|
|
<span>৳{part.price}</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-slate-400">-</span>
|
|
|
|
|
)}
|
|
|
|
|
</td>
|
|
|
|
|
<td className="px-3 py-2 text-sm">{part.inStock}</td>
|
|
|
|
|
<td className="px-3 py-2 text-sm">
|
|
|
|
|
{part.minPrice ? (
|
|
|
|
|
<span className="text-xs px-2 py-0.5 bg-amber-100 text-amber-700 rounded">Range</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-xs px-2 py-0.5 bg-green-100 text-green-700 rounded">Fixed</span>
|
|
|
|
|
)}
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
<button className="mt-4 text-sm text-accent hover:underline">+ Add Part</button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
{activeTab === 'rental' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Rental Policy</h3>
|
2026-04-26 18:41:54 +06:00
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
<div className="grid lg:grid-cols-2 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Minimum Age</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={settings.rentalPolicy.minAge}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, rentalPolicy: { ...settings.rentalPolicy, minAge: parseInt(e.target.value) } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Security Deposit (৳)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={settings.rentalPolicy.deposit}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, rentalPolicy: { ...settings.rentalPolicy, deposit: parseInt(e.target.value) } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Late Fee per Hour (৳)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={settings.rentalPolicy.lateFeePerHour}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, rentalPolicy: { ...settings.rentalPolicy, lateFeePerHour: parseInt(e.target.value) } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Cancellation Fee (৳)</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
value={settings.rentalPolicy.cancellationFee}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, rentalPolicy: { ...settings.rentalPolicy, cancellationFee: parseInt(e.target.value) } })}
|
|
|
|
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
|
|
|
|
checked={settings.rentalPolicy.requireLicense}
|
|
|
|
|
onChange={(e) => setSettings({ ...settings, rentalPolicy: { ...settings.rentalPolicy, requireLicense: e.target.checked } })}
|
|
|
|
|
className="w-4 h-4"
|
|
|
|
|
/>
|
|
|
|
|
<label className="text-sm text-slate-600">Require Driving License</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-medium text-slate-700 mb-3">Damage Penalties</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{settings.rentalPolicy.damagePenalty.map((penalty, i) => (
|
|
|
|
|
<div key={i} className="flex items-center gap-3 p-2 border border-slate-200 rounded-lg">
|
|
|
|
|
<span className="w-32 text-sm">{penalty.level}</span>
|
|
|
|
|
<span className="text-sm font-medium">৳{penalty.amount}</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-medium text-slate-700 mb-3">Rental Rules</h4>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{settings.rentalPolicy.rules.map((rule, i) => (
|
|
|
|
|
<div key={i} className="flex items-center gap-2 p-2 bg-slate-50 rounded-lg">
|
|
|
|
|
<span className="text-sm">{rule}</span>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-05-05 04:20:17 +06:00
|
|
|
{activeTab === 'plans' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Plan Selection</h3>
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
|
2026-05-05 04:20:17 +06:00
|
|
|
<div className="flex gap-2 border-b border-slate-200">
|
|
|
|
|
<button onClick={() => setActivePlanTab('singleRent')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'singleRent' ? 'border-blue-500 text-blue-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>1. Single Rent</button>
|
|
|
|
|
<button onClick={() => setActivePlanTab('rentToOwn')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'rentToOwn' ? 'border-purple-500 text-purple-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>2. Rent to Own</button>
|
|
|
|
|
<button onClick={() => setActivePlanTab('shareEv')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'shareEv' ? 'border-green-500 text-green-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>3. Share an EV</button>
|
|
|
|
|
</div>
|
2026-05-05 03:19:10 +06:00
|
|
|
|
2026-05-05 04:20:17 +06:00
|
|
|
{activePlanTab === 'singleRent' && (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{settings.plans.singleRent.map((plan, idx) => (
|
|
|
|
|
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
|
|
|
<div className="bg-blue-50 px-4 py-3 border-b border-blue-100 flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
{/* <span className="text-2xl">{plan.tier === 'Premium' ? '👑' : plan.tier === 'Standard' ? '⚖️' : '💰'}</span> */}
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-blue-800">SINGLE RENT - {plan.tier} - ৳{plan.dailyRent}/day</h4>
|
|
|
|
|
<p className="text-sm text-blue-600 mt-1">{plan.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={handleSave} className="px-3 py-1.5 bg-blue-600 text-white rounded-lg text-xs font-medium flex items-center gap-1">
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
</button>
|
2026-05-05 03:19:10 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Daily Rent (৳)</label>
|
|
|
|
|
<input type="number" value={plan.dailyRent} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Weekly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.weeklySubscription} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.monthlySubscription} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Deposit (৳)</label>
|
|
|
|
|
<input type="number" value={plan.deposit} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].deposit = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">FICO Share (%)</label>
|
|
|
|
|
<input type="number" value={plan.ficoSharePercent} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].ficoSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">JAIBEN Keep (%)</label>
|
|
|
|
|
<input type="number" value={100 - plan.ficoSharePercent} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1 bg-slate-100" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="text-slate-600">FICO + JAIBEN =</span>
|
|
|
|
|
<span className={`font-semibold ${plan.ficoSharePercent + (100 - plan.ficoSharePercent) === 100 ? 'text-green-600' : 'text-red-600'}`}>{plan.ficoSharePercent + (100 - plan.ficoSharePercent)}% (FICO: {plan.ficoSharePercent}% + JAIBEN: {100 - plan.ficoSharePercent}%)</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
))}
|
2026-05-05 03:19:10 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
)}
|
2026-05-05 03:09:21 +06:00
|
|
|
|
2026-05-05 04:20:17 +06:00
|
|
|
{activePlanTab === 'rentToOwn' && (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{settings.plans.rentToOwn.map((plan, idx) => (
|
|
|
|
|
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
|
|
|
<div className="bg-purple-50 px-4 py-3 border-b border-purple-100 flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
{/* <span className="text-2xl">{plan.tier === 'Premium' ? '👑' : plan.tier === 'Standard' ? '⚖️' : '💰'}</span> */}
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-purple-800">RENT TO OWN - {plan.tier} - ৳{plan.dailyRent}/day</h4>
|
|
|
|
|
<p className="text-sm text-purple-600 mt-1">{plan.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={handleSave} className="px-3 py-1.5 bg-purple-600 text-white rounded-lg text-xs font-medium flex items-center gap-1">
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
</button>
|
2026-05-05 03:37:09 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Daily Rent (৳)</label>
|
|
|
|
|
<input type="number" value={plan.dailyRent} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Weekly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.weeklySubscription} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.monthlySubscription} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Deposit (৳)</label>
|
|
|
|
|
<input type="number" value={plan.deposit} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].deposit = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Duration (Months)</label>
|
|
|
|
|
<input type="number" value={plan.durationMonths} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].durationMonths = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">EV Price (৳)</label>
|
|
|
|
|
<input type="number" value={plan.evPrice} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].evPrice = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Payment (৳)</label>
|
|
|
|
|
<input type="number" value={plan.totalPayment} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].totalPayment = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Profit (৳)</label>
|
|
|
|
|
<input type="number" value={plan.profit} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].profit = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">FICO Rent Share (%)</label>
|
|
|
|
|
<input type="number" value={plan.ficoRentSharePercent} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].ficoRentSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">FICO Profit Share (%)</label>
|
|
|
|
|
<input type="number" value={plan.ficoProfitSharePercent} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].ficoProfitSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">JAIBEN Keep (%)</label>
|
|
|
|
|
<input type="number" value={100 - plan.ficoRentSharePercent - plan.ficoProfitSharePercent} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1 bg-slate-100" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="text-slate-600">FICO + JAIBEN =</span>
|
|
|
|
|
<span className={`font-semibold ${plan.ficoRentSharePercent + plan.ficoProfitSharePercent + (100 - plan.ficoRentSharePercent - plan.ficoProfitSharePercent) === 100 ? 'text-green-600' : 'text-red-600'}`}>{plan.ficoRentSharePercent + plan.ficoProfitSharePercent + (100 - plan.ficoRentSharePercent - plan.ficoProfitSharePercent)}% (FICO Rent: {plan.ficoRentSharePercent}% + FICO Profit: {plan.ficoProfitSharePercent}% + JAIBEN: {100 - plan.ficoRentSharePercent - plan.ficoProfitSharePercent}%)</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
2026-05-05 03:19:10 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
))}
|
2026-05-05 03:19:10 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
)}
|
2026-05-05 03:09:21 +06:00
|
|
|
|
2026-05-05 04:20:17 +06:00
|
|
|
{activePlanTab === 'shareEv' && (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
{settings.plans.shareEv.map((plan, idx) => (
|
|
|
|
|
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
|
|
|
<div className="bg-green-50 px-4 py-3 border-b border-green-100 flex items-center justify-between">
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
{/* <span className="text-2xl">{plan.tier === 'Premium' ? '👑' : plan.tier === 'Standard' ? '⚖️' : '💰'}</span> */}
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-green-800">SHARE AN EV - {plan.tier} - ৳{plan.dailyRentEach}/day each (Total: ৳{plan.totalDailyRent})</h4>
|
|
|
|
|
<p className="text-sm text-green-600 mt-1">{plan.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={handleSave} className="px-3 py-1.5 bg-green-600 text-white rounded-lg text-xs font-medium flex items-center gap-1">
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
</button>
|
2026-05-05 03:37:09 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Daily Rent Each (৳)</label>
|
|
|
|
|
<input type="number" value={plan.dailyRentEach} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentEach = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Daily Rent (৳)</label>
|
|
|
|
|
<input type="number" value={plan.totalDailyRent} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].totalDailyRent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Weekly Subscription Each (৳)</label>
|
|
|
|
|
<input type="number" value={plan.weeklySubscriptionEach} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklySubscriptionEach = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Weekly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.totalWeeklySubscription} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].totalWeeklySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Subscription Each (৳)</label>
|
|
|
|
|
<input type="number" value={plan.monthlySubscriptionEach} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlySubscriptionEach = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Monthly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.totalMonthlySubscription} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].totalMonthlySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Deposit Each (৳)</label>
|
|
|
|
|
<input type="number" value={plan.depositEach} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].depositEach = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Deposit (৳)</label>
|
|
|
|
|
<input type="number" value={plan.totalDeposit} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].totalDeposit = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">FICO Share (%)</label>
|
|
|
|
|
<input type="number" value={plan.ficoSharePercent} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].ficoSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">JAIBEN Keep (%)</label>
|
|
|
|
|
<input type="number" value={100 - plan.ficoSharePercent} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1 bg-slate-100" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="text-slate-600">FICO + JAIBEN =</span>
|
|
|
|
|
<span className={`font-semibold ${plan.ficoSharePercent + (100 - plan.ficoSharePercent) === 100 ? 'text-green-600' : 'text-red-600'}`}>{plan.ficoSharePercent + (100 - plan.ficoSharePercent)}% (FICO: {plan.ficoSharePercent}% + JAIBEN: {100 - plan.ficoSharePercent}%)</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
))}
|
2026-05-05 03:19:10 +06:00
|
|
|
</div>
|
2026-05-05 04:20:17 +06:00
|
|
|
)}
|
2026-05-05 03:09:21 +06:00
|
|
|
</div>
|
|
|
|
|
)}
|
2026-05-05 21:48:18 +06:00
|
|
|
|
|
|
|
|
{(activeTab as string) === 'investment' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Investment Plans</h3>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-amber-800">Investment Plans ({settings.plans.investment.length})</h4>
|
|
|
|
|
<p className="text-sm text-amber-600">Manage investment plans for investors</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={() => { setAddInvestPlan(true); setNewInvestName(''); }} className="px-4 py-2 bg-amber-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
|
|
|
|
<Plus className="w-4 h-4" /> New Plan
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{addInvestPlan && (
|
|
|
|
|
<div className="bg-white rounded-xl border border-amber-300 overflow-hidden">
|
|
|
|
|
<div className="bg-amber-100 px-4 py-3 border-b border-amber-200 flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-amber-800">New Investment Plan</h4>
|
|
|
|
|
<p className="text-sm text-amber-600 mt-1">Fill in the details below</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={() => setAddInvestPlan(false)} className="text-amber-600 hover:text-amber-800">
|
|
|
|
|
<X className="w-5 h-5" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Plan Name</label>
|
|
|
|
|
<input type="text" value={newInvestName} onChange={(e) => setNewInvestName(e.target.value)} placeholder="e.g., 1 Bike Plan" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Status</label>
|
|
|
|
|
<select value={newInvestStatus} onChange={(e) => setNewInvestStatus(e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
|
|
|
|
<option value="active">Active</option>
|
|
|
|
|
<option value="paused">Paused</option>
|
|
|
|
|
<option value="closed">Closed</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Target Amount (৳)</label>
|
|
|
|
|
<input type="number" value={newInvestTarget} onChange={(e) => setNewInvestTarget(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Start Date</label>
|
|
|
|
|
<input type="date" value={newInvestStart} onChange={(e) => setNewInvestStart(e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">End Date</label>
|
|
|
|
|
<input type="date" value={newInvestEnd} onChange={(e) => setNewInvestEnd(e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Min Investment (৳)</label>
|
|
|
|
|
<input type="number" value={newInvestMin} onChange={(e) => setNewInvestMin(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Max Investment (৳)</label>
|
|
|
|
|
<input type="number" value={newInvestMax} onChange={(e) => setNewInvestMax(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Return (%)</label>
|
|
|
|
|
<input type="number" step="0.1" value={newInvestMonthly} onChange={(e) => setNewInvestMonthly(parseFloat(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Duration (Months)</label>
|
|
|
|
|
<input type="number" value={newInvestDuration} onChange={(e) => setNewInvestDuration(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Lock-in Period (Months)</label>
|
|
|
|
|
<input type="number" value={newInvestLock} onChange={(e) => setNewInvestLock(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Return (%)</label>
|
|
|
|
|
<input type="number" step="0.1" value={newInvestTotal} onChange={(e) => setNewInvestTotal(parseFloat(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Early Exit Penalty (%)</label>
|
|
|
|
|
<input type="number" value={newInvestPenalty} onChange={(e) => setNewInvestPenalty(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="text-slate-600">Profit from Plan Selection (FICO Share):</span>
|
|
|
|
|
<span className="font-semibold text-green-600">{settings.plans.singleRent[0]?.ficoSharePercent || 50}%</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={newInvestDesc} onChange={(e) => setNewInvestDesc(e.target.value)} placeholder="Enter plan description" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={createInvestPlan} className="mt-4 px-4 py-2 bg-amber-600 text-white rounded-lg text-sm font-medium">Create Plan</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="flex gap-2 border-b border-slate-200">
|
|
|
|
|
{settings.plans.investment.map((plan, idx) => (
|
|
|
|
|
<button key={idx} onClick={() => setActiveInvestTab(idx)} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activeInvestTab === idx ? 'border-amber-500 text-amber-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}> {plan.name}</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{settings.plans.investment.length > 0 && settings.plans.investment.map((plan, idx) => idx === activeInvestTab && (
|
|
|
|
|
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
|
|
|
<div className="bg-amber-50 px-4 py-3 border-b border-amber-100 flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-amber-800">{plan.name} - {plan.monthlyReturnPercent}% per month</h4>
|
|
|
|
|
<p className="text-sm text-amber-600 mt-1">{plan.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${plan.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{plan.status}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Plan Name</label>
|
|
|
|
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Status</label>
|
|
|
|
|
<select value={plan.status} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].status = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
|
|
|
|
<option value="active">Active</option>
|
|
|
|
|
<option value="paused">Paused</option>
|
|
|
|
|
<option value="closed">Closed</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Target Amount (৳)</label>
|
|
|
|
|
<input type="number" value={plan.targetAmount} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].targetAmount = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Start Date</label>
|
|
|
|
|
<input type="date" value={plan.startDate} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].startDate = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">End Date</label>
|
|
|
|
|
<input type="date" value={plan.endDate} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].endDate = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Min Investment (৳)</label>
|
|
|
|
|
<input type="number" value={plan.minInvestment} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].minInvestment = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Max Investment (৳)</label>
|
|
|
|
|
<input type="number" value={plan.maxInvestment} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].maxInvestment = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Return (%)</label>
|
|
|
|
|
<input type="number" step="0.1" value={plan.monthlyReturnPercent} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].monthlyReturnPercent = parseFloat(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Duration (Months)</label>
|
|
|
|
|
<input type="number" value={plan.durationMonths} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].durationMonths = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Lock-in Period (Months)</label>
|
|
|
|
|
<input type="number" value={plan.lockInMonths} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].lockInMonths = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Total Return (%)</label>
|
|
|
|
|
<input type="number" step="0.1" value={plan.totalReturnPercent} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].totalReturnPercent = parseFloat(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Early Exit Penalty (%)</label>
|
|
|
|
|
<input type="number" value={plan.earlyExitPenalty} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].earlyExitPenalty = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 p-3 bg-slate-50 rounded-lg">
|
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
|
|
|
<span className="text-slate-600">Profit from Plan Selection (FICO Share):</span>
|
|
|
|
|
<span className="font-semibold text-green-600">{settings.plans.singleRent[0]?.ficoSharePercent || 50}%</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 flex justify-end">
|
|
|
|
|
<button onClick={handleSave} className="px-4 py-2 bg-amber-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
|
|
|
|
<Save className="w-4 h-4" /> Save Changes
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-05-05 21:54:46 +06:00
|
|
|
|
|
|
|
|
{(activeTab as string) === 'swapstation' && (
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Swap Station Plans</h3>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between bg-blue-50 border border-blue-200 rounded-xl p-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-blue-800">Swap Station Plans ({settings.plans.swapStation.length})</h4>
|
|
|
|
|
<p className="text-sm text-blue-600">Manage swap station plans for operators</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={() => { setAddSwapStationPlan(true); setNewSwapName(''); }} className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
|
|
|
|
<Plus className="w-4 h-4" /> New Plan
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{addSwapStationPlan && (
|
|
|
|
|
<div className="bg-white rounded-xl border border-blue-300 overflow-hidden">
|
|
|
|
|
<div className="bg-blue-100 px-4 py-3 border-b border-blue-200 flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-blue-800">New Swap Station Plan</h4>
|
|
|
|
|
<p className="text-sm text-blue-600 mt-1">Fill in the details below</p>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={() => setAddSwapStationPlan(false)} className="text-blue-600 hover:text-blue-800">
|
|
|
|
|
<X className="w-5 h-5" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Plan Name</label>
|
|
|
|
|
<input type="text" value={newSwapName} onChange={(e) => setNewSwapName(e.target.value)} placeholder="e.g., Standard Station" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Status</label>
|
|
|
|
|
<select value={newSwapStatus} onChange={(e) => setNewSwapStatus(e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
|
|
|
|
<option value="active">Active</option>
|
|
|
|
|
<option value="paused">Paused</option>
|
|
|
|
|
<option value="closed">Closed</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Battery Count</label>
|
|
|
|
|
<input type="number" value={newSwapBatteryCount} onChange={(e) => setNewSwapBatteryCount(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Swap Price (৳)</label>
|
|
|
|
|
<input type="number" value={newSwapPrice} onChange={(e) => setNewSwapPrice(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={newSwapMonthly} onChange={(e) => setNewSwapMonthly(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Daily Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={newSwapDaily} onChange={(e) => setNewSwapDaily(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Min Batteries</label>
|
|
|
|
|
<input type="number" value={newSwapMin} onChange={(e) => setNewSwapMin(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Max Batteries</label>
|
|
|
|
|
<input type="number" value={newSwapMax} onChange={(e) => setNewSwapMax(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Profit Share (%)</label>
|
|
|
|
|
<input type="number" value={newSwapProfit} onChange={(e) => setNewSwapProfit(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={newSwapDesc} onChange={(e) => setNewSwapDesc(e.target.value)} placeholder="Enter plan description" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={createSwapStationPlan} className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium">Create Plan</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="flex gap-2 border-b border-slate-200">
|
|
|
|
|
{settings.plans.swapStation.map((plan, idx) => (
|
|
|
|
|
<button key={idx} onClick={() => setActiveSwapTab(idx)} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activeSwapTab === idx ? 'border-blue-500 text-blue-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}> {plan.name}</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{settings.plans.swapStation.length > 0 && settings.plans.swapStation.map((plan, idx) => idx === activeSwapTab && (
|
|
|
|
|
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
|
|
|
<div className="bg-blue-50 px-4 py-3 border-b border-blue-100 flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h4 className="font-semibold text-blue-800">{plan.name}</h4>
|
|
|
|
|
<p className="text-sm text-blue-600 mt-1">{plan.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${plan.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{plan.status}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<div className="grid lg:grid-cols-3 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Plan Name</label>
|
|
|
|
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Status</label>
|
|
|
|
|
<select value={plan.status} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].status = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
|
|
|
|
<option value="active">Active</option>
|
|
|
|
|
<option value="paused">Paused</option>
|
|
|
|
|
<option value="closed">Closed</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Battery Count</label>
|
|
|
|
|
<input type="number" value={plan.batteryCount} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].batteryCount = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Swap Price (৳)</label>
|
|
|
|
|
<input type="number" value={plan.swapPrice} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].swapPrice = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.monthlySubscription} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].monthlySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Daily Subscription (৳)</label>
|
|
|
|
|
<input type="number" value={plan.dailySubscription} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].dailySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Min Batteries</label>
|
|
|
|
|
<input type="number" value={plan.minBatteries} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].minBatteries = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Max Batteries</label>
|
|
|
|
|
<input type="number" value={plan.maxBatteries} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].maxBatteries = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label className="text-sm text-slate-600">Profit Share (%)</label>
|
|
|
|
|
<input type="number" value={plan.profitSharePercent} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].profitSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label className="text-sm text-slate-600">Description</label>
|
|
|
|
|
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-4 flex justify-end">
|
|
|
|
|
<button onClick={handleSave} className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
|
|
|
|
<Save className="w-4 h-4" /> Save Changes
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-05-05 03:09:21 +06:00
|
|
|
|
|
|
|
|
|
2026-04-26 18:32:52 +06:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|