feat: add plans configuration tab to admin settings page with CRUD support for rental plans

This commit is contained in:
sazzadulalambd
2026-05-05 03:09:21 +06:00
parent 59e2c0f3cb
commit 9133fdb7ff

View File

@@ -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>
);
}