feat: add investment plan management section to company settings
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
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 } from 'lucide-react';
|
||||
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 } from 'lucide-react';
|
||||
|
||||
interface CompanySettings {
|
||||
name: string;
|
||||
@@ -120,6 +120,24 @@ interface CompanySettings {
|
||||
ficoSharePercent: number;
|
||||
description: string;
|
||||
}[];
|
||||
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;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -439,18 +457,101 @@ const initialSettings: CompanySettings = {
|
||||
description: 'Economy shared EV plan',
|
||||
}
|
||||
],
|
||||
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',
|
||||
}
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default function CompanySettingsPage() {
|
||||
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans'>('general');
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans' | 'investment'>('general');
|
||||
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rental'>('investor');
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [activePlanTab, setActivePlanTab] = useState<'singleRent' | 'rentToOwn' | 'shareEv'>('singleRent');
|
||||
const [addDocType, setAddDocType] = useState<'investor' | 'merchant' | 'swapstation' | 'rental' | null>(null);
|
||||
const [newDocName, setNewDocName] = useState('');
|
||||
const [newDocDesc, setNewDocDesc] = useState('');
|
||||
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('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
setSaved(true);
|
||||
@@ -467,6 +568,7 @@ export default function CompanySettingsPage() {
|
||||
{ id: 'parts', label: 'EV Parts', icon: Package },
|
||||
{ id: 'rental', label: 'Rental Policy', icon: FileCheck },
|
||||
{ id: 'plans', label: 'Plan Selection', icon: Package },
|
||||
{ id: 'investment', label: 'Investment Plan', icon: DollarSign },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -476,12 +578,12 @@ export default function CompanySettingsPage() {
|
||||
<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>
|
||||
<button
|
||||
{/* <button
|
||||
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
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
|
||||
{saved && (
|
||||
@@ -1772,6 +1874,194 @@ export default function CompanySettingsPage() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(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>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user