feat: add plans configuration tab to admin settings page with CRUD support for rental plans
This commit is contained in:
@@ -80,6 +80,48 @@ interface CompanySettings {
|
||||
damagePenalty: { level: string; amount: number }[];
|
||||
rules: string[];
|
||||
};
|
||||
plans: {
|
||||
singleRent: {
|
||||
name: string;
|
||||
dailyRent: number;
|
||||
deposit: number;
|
||||
weeklySubscription: number;
|
||||
monthlySubscription: number;
|
||||
ficoSharePercent: number;
|
||||
jaibenKeepPercent: number;
|
||||
description: string;
|
||||
features: string[];
|
||||
}[];
|
||||
rentToOwn: {
|
||||
name: string;
|
||||
dailyRent: number;
|
||||
deposit: number;
|
||||
weeklySubscription: number;
|
||||
monthlySubscription: number;
|
||||
durationMonths: number;
|
||||
evPrice: number;
|
||||
totalPayment: number;
|
||||
profit: number;
|
||||
ficoRentSharePercent: number;
|
||||
ficoProfitSharePercent: number;
|
||||
description: string;
|
||||
features: string[];
|
||||
}[];
|
||||
shareEv: {
|
||||
name: string;
|
||||
dailyRentEach: number;
|
||||
totalDailyRent: number;
|
||||
depositEach: number;
|
||||
totalDeposit: number;
|
||||
weeklySubscriptionEach: number;
|
||||
totalWeeklySubscription: number;
|
||||
monthlySubscriptionEach: number;
|
||||
totalMonthlySubscription: number;
|
||||
ficoSharePercent: number;
|
||||
description: string;
|
||||
features: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
const initialSettings: CompanySettings = {
|
||||
@@ -273,12 +315,60 @@ const initialSettings: CompanySettings = {
|
||||
'Follow traffic rules',
|
||||
'Report accidents within 24 hours',
|
||||
]
|
||||
},
|
||||
plans: {
|
||||
singleRent: [
|
||||
{
|
||||
name: 'Single Rent',
|
||||
dailyRent: 300,
|
||||
deposit: 20000,
|
||||
weeklySubscription: 2100,
|
||||
monthlySubscription: 9000,
|
||||
ficoSharePercent: 45,
|
||||
jaibenKeepPercent: 55,
|
||||
description: 'Single person rental plan with deposit and weekly/monthly subscription',
|
||||
features: ['Deposit Money > 20,000TK', '1st Day Rent Advance', 'Weekly Subscription > 2,100TK', 'Monthly Subscription > 9,000TK', 'Auto Deduct from Wallet Balance', 'FICO gets 45% > 135TK', 'JAIBEN keeps 55% > 165TK']
|
||||
}
|
||||
],
|
||||
rentToOwn: [
|
||||
{
|
||||
name: 'Rent to Own',
|
||||
dailyRent: 250,
|
||||
deposit: 18000,
|
||||
weeklySubscription: 1750,
|
||||
monthlySubscription: 7000,
|
||||
durationMonths: 18,
|
||||
evPrice: 120000,
|
||||
totalPayment: 135000,
|
||||
profit: 15000,
|
||||
ficoRentSharePercent: 45,
|
||||
ficoProfitSharePercent: 45,
|
||||
description: 'Rent to own plan - get ownership after 18 months',
|
||||
features: ['Daily Rent = 250TK', 'Deposit Money > 18,000TK', 'Weekly Subscription > 1,750TK or 1,875TK', 'Monthly Subscription > 7,000TK or 7,500TK', 'Duration > 18 Months', 'In 18 months Total Pay > 1,35,000TK (EV Price 1,20,000TK)', 'Profit > 15,000TK', 'FICO gets 45% of the rent > 112 TK and Profit Share 45% > 6,750TK', 'Rider gets a brand new EV after 18 months']
|
||||
}
|
||||
],
|
||||
shareEv: [
|
||||
{
|
||||
name: 'Share an EV',
|
||||
dailyRentEach: 200,
|
||||
totalDailyRent: 400,
|
||||
depositEach: 15000,
|
||||
totalDeposit: 30000,
|
||||
weeklySubscriptionEach: 1400,
|
||||
totalWeeklySubscription: 2800,
|
||||
monthlySubscriptionEach: 5600,
|
||||
totalMonthlySubscription: 11200,
|
||||
ficoSharePercent: 45,
|
||||
description: 'Share EV between 2 riders - split earnings',
|
||||
features: ['Daily Rent = 200TK each (Total = 400TK)', 'Deposit Money > 15,000TK each (Total = 30,000TK)', 'Weekly Subscription > 1,400TK or 1,500TK each (Total = 2,800TK or 3,000TK)', 'Monthly Subscription > 5,600TK or 6,000TK each (Total = 11,200TK or 12,000TK)', 'FICO gets 45% of daily rent 90+90 > 180TK from two riders one EV', 'No ownership']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default function CompanySettingsPage() {
|
||||
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental'>('general');
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans'>('general');
|
||||
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rental'>('investor');
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [addDocType, setAddDocType] = useState<'investor' | 'merchant' | 'swapstation' | 'rental' | null>(null);
|
||||
@@ -296,10 +386,10 @@ const tabs = [
|
||||
{ id: 'social', label: 'Social Media', icon: Link2 },
|
||||
{ id: 'integration', label: 'Integrations', icon: Mail },
|
||||
{ id: 'landing', label: 'Landing Page', icon: Monitor },
|
||||
|
||||
{ id: 'kyc', label: 'KYC Documents', icon: Package },
|
||||
{ id: 'parts', label: 'EV Parts', icon: Package },
|
||||
{ id: 'rental', label: 'Rental Policy', icon: FileCheck },
|
||||
{ id: 'plans', label: 'Plan Selection', icon: Package },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -1379,8 +1469,200 @@ const tabs = [
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{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>
|
||||
<button onClick={handleSave} className="px-4 py-2 bg-accent text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
||||
<Save className="w-4 h-4" /> Save Changes
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{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">
|
||||
<h4 className="font-semibold text-blue-800">1. SINGLE RENT - ৳{plan.dailyRent}/day</h4>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid lg:grid-cols-2 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">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">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">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={plan.jaibenKeepPercent} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].jaibenKeepPercent = 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>
|
||||
<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>
|
||||
<div className="mt-4">
|
||||
<label className="text-sm text-slate-600">Features</label>
|
||||
<div className="space-y-2 mt-1">
|
||||
{plan.features.map((feature, fidx) => (
|
||||
<div key={fidx} className="flex items-center gap-2">
|
||||
<span className="text-sm text-slate-700">• {feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{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">
|
||||
<h4 className="font-semibold text-purple-800">2. RENT TO OWN - ৳{plan.dailyRent}/day</h4>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid lg:grid-cols-2 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">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">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">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>
|
||||
<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>
|
||||
<div className="mt-4">
|
||||
<label className="text-sm text-slate-600">Features</label>
|
||||
<div className="space-y-2 mt-1">
|
||||
{plan.features.map((feature, fidx) => (
|
||||
<div key={fidx} className="flex items-center gap-2">
|
||||
<span className="text-sm text-slate-700">• {feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{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">
|
||||
<h4 className="font-semibold text-green-800">3. SHARE AN EV - ৳{plan.dailyRentEach}/day each</h4>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid lg:grid-cols-2 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">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">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">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>
|
||||
<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>
|
||||
<div className="mt-4">
|
||||
<label className="text-sm text-slate-600">Features</label>
|
||||
<div className="space-y-2 mt-1">
|
||||
{plan.features.map((feature, fidx) => (
|
||||
<div key={fidx} className="flex items-center gap-2">
|
||||
<span className="text-sm text-slate-700">• {feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user