Compare commits
4 Commits
20ce14ae68
...
0d7b684c77
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d7b684c77 | ||
|
|
70f97b374b | ||
|
|
93e1d289ca | ||
|
|
0924d84983 |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -93,8 +93,9 @@ interface CompanySettings {
|
|||||||
};
|
};
|
||||||
plans: {
|
plans: {
|
||||||
singleRent: {
|
singleRent: {
|
||||||
tier: 'Economy' | 'Standard' | 'Premium';
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
evModels: string[];
|
||||||
contractMonths: number[];
|
contractMonths: number[];
|
||||||
dailyRent: number;
|
dailyRent: number;
|
||||||
dailyRentPenalty1: number;
|
dailyRentPenalty1: number;
|
||||||
@@ -113,8 +114,9 @@ interface CompanySettings {
|
|||||||
description: string;
|
description: string;
|
||||||
}[];
|
}[];
|
||||||
rentToOwn: {
|
rentToOwn: {
|
||||||
tier: 'Economy' | 'Standard' | 'Premium';
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
evModels: string[];
|
||||||
contractMonths: number[];
|
contractMonths: number[];
|
||||||
dailyRent: number;
|
dailyRent: number;
|
||||||
dailyRentPenalty1: number;
|
dailyRentPenalty1: number;
|
||||||
@@ -138,8 +140,9 @@ interface CompanySettings {
|
|||||||
description: string;
|
description: string;
|
||||||
}[];
|
}[];
|
||||||
shareEv: {
|
shareEv: {
|
||||||
tier: 'Economy' | 'Standard' | 'Premium';
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
evModels: string[];
|
||||||
contractMonths: number[];
|
contractMonths: number[];
|
||||||
dailyRentEach: number;
|
dailyRentEach: number;
|
||||||
dailyRentPenalty1: number;
|
dailyRentPenalty1: number;
|
||||||
@@ -445,8 +448,9 @@ const initialSettings: CompanySettings = {
|
|||||||
plans: {
|
plans: {
|
||||||
singleRent: [
|
singleRent: [
|
||||||
{
|
{
|
||||||
tier: 'Premium',
|
id: 'sr-1',
|
||||||
name: 'Single Rent - Premium',
|
name: 'Single Rent - Premium',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRent: 400,
|
dailyRent: 400,
|
||||||
dailyRentPenalty1: 500,
|
dailyRentPenalty1: 500,
|
||||||
@@ -465,8 +469,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Premium single person rental plan with extra benefits',
|
description: 'Premium single person rental plan with extra benefits',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Standard',
|
id: 'sr-2',
|
||||||
name: 'Single Rent - Standard',
|
name: 'Single Rent - Standard',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRent: 300,
|
dailyRent: 300,
|
||||||
dailyRentPenalty1: 400,
|
dailyRentPenalty1: 400,
|
||||||
@@ -485,8 +490,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Standard single person rental plan',
|
description: 'Standard single person rental plan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Economy',
|
id: 'sr-3',
|
||||||
name: 'Single Rent - Economy',
|
name: 'Single Rent - Economy',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRent: 250,
|
dailyRent: 250,
|
||||||
dailyRentPenalty1: 300,
|
dailyRentPenalty1: 300,
|
||||||
@@ -507,8 +513,9 @@ const initialSettings: CompanySettings = {
|
|||||||
],
|
],
|
||||||
rentToOwn: [
|
rentToOwn: [
|
||||||
{
|
{
|
||||||
tier: 'Premium',
|
id: 'rto-1',
|
||||||
name: 'Rent to Own - Premium',
|
name: 'Rent to Own - Premium',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [12, 18, 24, 36],
|
contractMonths: [12, 18, 24, 36],
|
||||||
dailyRent: 350,
|
dailyRent: 350,
|
||||||
dailyRentPenalty1: 450,
|
dailyRentPenalty1: 450,
|
||||||
@@ -532,8 +539,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Premium rent to own plan with high-end EV',
|
description: 'Premium rent to own plan with high-end EV',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Standard',
|
id: 'rto-2',
|
||||||
name: 'Rent to Own - Standard',
|
name: 'Rent to Own - Standard',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [12, 18, 24, 36],
|
contractMonths: [12, 18, 24, 36],
|
||||||
dailyRent: 250,
|
dailyRent: 250,
|
||||||
dailyRentPenalty1: 350,
|
dailyRentPenalty1: 350,
|
||||||
@@ -557,8 +565,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Standard rent to own plan',
|
description: 'Standard rent to own plan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Economy',
|
id: 'rto-3',
|
||||||
name: 'Rent to Own - Economy',
|
name: 'Rent to Own - Economy',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [12, 18, 24, 36],
|
contractMonths: [12, 18, 24, 36],
|
||||||
dailyRent: 200,
|
dailyRent: 200,
|
||||||
dailyRentPenalty1: 250,
|
dailyRentPenalty1: 250,
|
||||||
@@ -584,8 +593,9 @@ const initialSettings: CompanySettings = {
|
|||||||
],
|
],
|
||||||
shareEv: [
|
shareEv: [
|
||||||
{
|
{
|
||||||
tier: 'Premium',
|
id: 'se-1',
|
||||||
name: 'Share an EV - Premium',
|
name: 'Share an EV - Premium',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRentEach: 300,
|
dailyRentEach: 300,
|
||||||
dailyRentPenalty1: 400,
|
dailyRentPenalty1: 400,
|
||||||
@@ -611,8 +621,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Premium shared EV with premium bikes',
|
description: 'Premium shared EV with premium bikes',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Standard',
|
id: 'se-2',
|
||||||
name: 'Share an EV - Standard',
|
name: 'Share an EV - Standard',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRentEach: 200,
|
dailyRentEach: 200,
|
||||||
dailyRentPenalty1: 250,
|
dailyRentPenalty1: 250,
|
||||||
@@ -638,8 +649,9 @@ const initialSettings: CompanySettings = {
|
|||||||
description: 'Standard shared EV plan',
|
description: 'Standard shared EV plan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 'Economy',
|
id: 'se-3',
|
||||||
name: 'Share an EV - Economy',
|
name: 'Share an EV - Economy',
|
||||||
|
evModels: [],
|
||||||
contractMonths: [1, 3, 6, 12],
|
contractMonths: [1, 3, 6, 12],
|
||||||
dailyRentEach: 150,
|
dailyRentEach: 150,
|
||||||
dailyRentPenalty1: 200,
|
dailyRentPenalty1: 200,
|
||||||
@@ -934,6 +946,90 @@ export default function CompanySettingsPage() {
|
|||||||
setTimeout(() => setSaved(false), 2000);
|
setTimeout(() => setSaved(false), 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addNewPlan = (type: 'singleRent' | 'rentToOwn' | 'shareEv') => {
|
||||||
|
const newId = `${type}-${Date.now()}`;
|
||||||
|
const defaultPlan = type === 'singleRent' ? {
|
||||||
|
id: newId,
|
||||||
|
name: '',
|
||||||
|
evModels: [],
|
||||||
|
contractMonths: [1, 3, 6],
|
||||||
|
dailyRent: 300,
|
||||||
|
dailyRentPenalty1: 400,
|
||||||
|
dailyRentPenalty2: 800,
|
||||||
|
dailyRentPenalty3: 4000,
|
||||||
|
deposit: 20000,
|
||||||
|
weeklySubscription: 2100,
|
||||||
|
weeklyPenalty1: 2600,
|
||||||
|
weeklyPenalty2: 5200,
|
||||||
|
weeklyPenalty3: 12000,
|
||||||
|
monthlySubscription: 9000,
|
||||||
|
monthlyPenalty1: 11000,
|
||||||
|
monthlyPenalty2: 22000,
|
||||||
|
monthlyPenalty3: 40000,
|
||||||
|
ficoSharePercent: 45,
|
||||||
|
description: '',
|
||||||
|
} : type === 'rentToOwn' ? {
|
||||||
|
id: newId,
|
||||||
|
name: '',
|
||||||
|
evModels: [],
|
||||||
|
contractMonths: [12, 18, 24],
|
||||||
|
dailyRent: 250,
|
||||||
|
dailyRentPenalty1: 350,
|
||||||
|
dailyRentPenalty2: 700,
|
||||||
|
dailyRentPenalty3: 3500,
|
||||||
|
deposit: 18000,
|
||||||
|
weeklySubscription: 1750,
|
||||||
|
weeklyPenalty1: 2200,
|
||||||
|
weeklyPenalty2: 4400,
|
||||||
|
weeklyPenalty3: 10000,
|
||||||
|
monthlySubscription: 7000,
|
||||||
|
monthlyPenalty1: 9000,
|
||||||
|
monthlyPenalty2: 18000,
|
||||||
|
monthlyPenalty3: 30000,
|
||||||
|
durationMonths: 18,
|
||||||
|
evPrice: 120000,
|
||||||
|
totalPayment: 135000,
|
||||||
|
profit: 15000,
|
||||||
|
ficoRentSharePercent: 45,
|
||||||
|
ficoProfitSharePercent: 45,
|
||||||
|
description: '',
|
||||||
|
} : {
|
||||||
|
id: newId,
|
||||||
|
name: '',
|
||||||
|
evModels: [],
|
||||||
|
contractMonths: [1, 3, 6],
|
||||||
|
dailyRentEach: 200,
|
||||||
|
dailyRentPenalty1: 250,
|
||||||
|
dailyRentPenalty2: 500,
|
||||||
|
dailyRentPenalty3: 2500,
|
||||||
|
totalDailyRent: 400,
|
||||||
|
depositEach: 15000,
|
||||||
|
depositPenalty1: 18000,
|
||||||
|
depositPenalty2: 25000,
|
||||||
|
depositPenalty3: 40000,
|
||||||
|
totalDeposit: 30000,
|
||||||
|
weeklySubscriptionEach: 1400,
|
||||||
|
weeklyPenalty1: 1800,
|
||||||
|
weeklyPenalty2: 3500,
|
||||||
|
weeklyPenalty3: 8000,
|
||||||
|
totalWeeklySubscription: 2800,
|
||||||
|
monthlySubscriptionEach: 5600,
|
||||||
|
monthlyPenalty1: 7000,
|
||||||
|
monthlyPenalty2: 14000,
|
||||||
|
monthlyPenalty3: 25000,
|
||||||
|
totalMonthlySubscription: 11200,
|
||||||
|
ficoSharePercent: 45,
|
||||||
|
description: '',
|
||||||
|
};
|
||||||
|
if (type === 'singleRent') {
|
||||||
|
setSettings({ ...settings, plans: { ...settings.plans, singleRent: [...settings.plans.singleRent, defaultPlan as typeof settings.plans.singleRent[number]] } });
|
||||||
|
} else if (type === 'rentToOwn') {
|
||||||
|
setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: [...settings.plans.rentToOwn, defaultPlan as typeof settings.plans.rentToOwn[number]] } });
|
||||||
|
} else {
|
||||||
|
setSettings({ ...settings, plans: { ...settings.plans, shareEv: [...settings.plans.shareEv, defaultPlan as typeof settings.plans.shareEv[number]] } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const addPolicyRule = (tab: 'investor' | 'merchant' | 'swapstation') => {
|
const addPolicyRule = (tab: 'investor' | 'merchant' | 'swapstation') => {
|
||||||
if (!newPolicyName.trim()) return;
|
if (!newPolicyName.trim()) return;
|
||||||
const newRule = { title: newPolicyName, description: newPolicyDesc || '<p></p>' };
|
const newRule = { title: newPolicyName, description: newPolicyDesc || '<p></p>' };
|
||||||
@@ -2347,19 +2443,52 @@ export default function CompanySettingsPage() {
|
|||||||
{activePlanTab === 'singleRent' && (
|
{activePlanTab === 'singleRent' && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{settings.plans.singleRent.map((plan, idx) => (
|
{settings.plans.singleRent.map((plan, idx) => (
|
||||||
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
<div key={plan.id} 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="bg-blue-50 px-4 py-3 border-b border-blue-100 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-blue-800">SINGLE RENT - {plan.tier} - ৳{plan.dailyRent}/day</h4>
|
<h4 className="font-semibold text-blue-800">{plan.name} - ৳{plan.dailyRent}/day</h4>
|
||||||
<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 bg-transparent text-sm text-blue-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
<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 bg-transparent text-sm text-blue-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
||||||
</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">
|
<div className="flex items-center gap-2 ml-3">
|
||||||
<Save className="w-3 h-3" /> Save
|
<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 hover:bg-blue-700">
|
||||||
</button>
|
<Save className="w-3 h-3" /> Save
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { if (confirm('Delete this plan?')) { const updated = settings.plans.singleRent.filter((_, i) => i !== idx); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); } }} className="px-2 py-1.5 bg-red-50 text-red-600 rounded-lg text-xs font-medium flex items-center gap-1 hover:bg-red-100">
|
||||||
|
<Trash2 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-5 space-y-5">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
||||||
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].name = 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" placeholder="Plan Name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
|
<select multiple value={plan.evModels} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].evModels = Array.from(e.target.selectedOptions, opt => opt.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-2 border border-slate-200 rounded-lg text-sm bg-white min-h-[70px] cursor-pointer">
|
||||||
|
<option value="Etron ET50">Etron ET50</option>
|
||||||
|
<option value="Yadea DT3">Yadea DT3</option>
|
||||||
|
<option value="AIMA Lightning">AIMA Lightning</option>
|
||||||
|
<option value="AIMA EM5">AIMA EM5</option>
|
||||||
|
<option value="Yadea G5">Yadea G5</option>
|
||||||
|
<option value="TVS iQube">TVS iQube</option>
|
||||||
|
<option value="Bajaj Chetak">Bajaj Chetak</option>
|
||||||
|
<option value="Hero Photon">Hero Photon</option>
|
||||||
|
<option value="Okinawa Praise">Okinawa Praise</option>
|
||||||
|
<option value="Ampere Magnus">Ampere Magnus</option>
|
||||||
|
<option value="Benling Aura">Benling Aura</option>
|
||||||
|
<option value="Lectrix LXS">Lectrix LXS</option>
|
||||||
|
<option value="Revolt RV400">Revolt RV400</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Daily Rent</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Daily Rent</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2374,13 +2503,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Weekly Subscription</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Weekly Subscription</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2395,13 +2524,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Monthly Subscription</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Monthly Subscription</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2416,97 +2545,89 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 className="bg-slate-50 rounded-lg p-3">
|
<div className="bg-slate-50 rounded-lg p-3">
|
||||||
<label className="text-sm font-semibold text-slate-700 block mb-2">Contract Duration (Months)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide block mb-2">Contract Duration (Months)</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{plan.contractMonths.map(month => (
|
{plan.contractMonths.map(month => (
|
||||||
<button
|
<button key={month} onClick={() => { const updated = [...settings.plans.singleRent]; updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="px-3 py-1.5 rounded-lg text-xs font-medium bg-blue-600 text-white hover:bg-red-500 transition-all flex items-center gap-1">
|
||||||
key={month}
|
|
||||||
onClick={() => {
|
|
||||||
const updated = [...settings.plans.singleRent];
|
|
||||||
updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } });
|
|
||||||
}}
|
|
||||||
className="px-3 py-1.5 rounded-lg text-xs font-medium bg-blue-600 text-white hover:bg-red-500 transition-all flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{month} {month === 1 ? 'Month' : 'Months'}
|
{month} {month === 1 ? 'Month' : 'Months'}
|
||||||
<span className="ml-1 font-bold">×</span>
|
<span className="ml-1 font-bold">×</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input type="number" min="1" placeholder="Add" className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs" onKeyDown={(e) => { if (e.key === 'Enter') { const val = parseInt((e.target as HTMLInputElement).value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.singleRent]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); (e.target as HTMLInputElement).value = ''; } } }} />
|
||||||
type="number"
|
<button onClick={(e) => { const input = (e.currentTarget.previousElementSibling as HTMLInputElement); const val = parseInt(input.value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.singleRent]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); input.value = ''; } }} className="px-2 py-1.5 bg-blue-600 text-white rounded-lg text-xs hover:bg-blue-700">+</button>
|
||||||
min="1"
|
|
||||||
placeholder="Add"
|
|
||||||
className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs"
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
const val = parseInt((e.target as HTMLInputElement).value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.singleRent];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } });
|
|
||||||
(e.target as HTMLInputElement).value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
const input = (e.currentTarget.previousElementSibling as HTMLInputElement);
|
|
||||||
const val = parseInt(input.value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.singleRent];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } });
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="px-2 py-1.5 bg-blue-600 text-white rounded-lg text-xs hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{plan.contractMonths.length === 0 && (
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
||||||
<p className="text-xs text-slate-400 mt-2">No contract months selected.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Description</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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} />
|
<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" rows={2} placeholder="Enter plan description..." />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<button onClick={() => addNewPlan('singleRent')} className="w-full py-3 border-2 border-dashed border-slate-300 rounded-xl text-slate-500 hover:border-blue-400 hover:text-blue-500 hover:bg-blue-50 transition-all flex items-center justify-center gap-2 text-sm font-medium">
|
||||||
|
<Plus className="w-4 h-4" /> Add New Plan
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activePlanTab === 'rentToOwn' && (
|
{activePlanTab === 'rentToOwn' && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{settings.plans.rentToOwn.map((plan, idx) => (
|
{settings.plans.rentToOwn.map((plan, idx) => (
|
||||||
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
<div key={plan.id} 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="bg-purple-50 px-4 py-3 border-b border-purple-100 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-purple-800">RENT TO OWN - {plan.tier} - ৳{plan.dailyRent}/day</h4>
|
<h4 className="font-semibold text-purple-800">{plan.name} - ৳{plan.dailyRent}/day</h4>
|
||||||
<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 bg-transparent text-sm text-purple-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
<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 bg-transparent text-sm text-purple-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
||||||
</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">
|
<div className="flex items-center gap-2 ml-3">
|
||||||
<Save className="w-3 h-3" /> Save
|
<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 hover:bg-purple-700">
|
||||||
</button>
|
<Save className="w-3 h-3" /> Save
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { if (confirm('Delete this plan?')) { const updated = settings.plans.rentToOwn.filter((_, i) => i !== idx); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); } }} className="px-2 py-1.5 bg-red-50 text-red-600 rounded-lg text-xs font-medium flex items-center gap-1 hover:bg-red-100">
|
||||||
|
<Trash2 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-5 space-y-5">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
||||||
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].name = 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" placeholder="Plan Name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
|
<select multiple value={plan.evModels} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].evModels = Array.from(e.target.selectedOptions, opt => opt.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-2 border border-slate-200 rounded-lg text-sm bg-white min-h-[70px] cursor-pointer">
|
||||||
|
<option value="Etron ET50">Etron ET50</option>
|
||||||
|
<option value="Yadea DT3">Yadea DT3</option>
|
||||||
|
<option value="AIMA Lightning">AIMA Lightning</option>
|
||||||
|
<option value="AIMA EM5">AIMA EM5</option>
|
||||||
|
<option value="Yadea G5">Yadea G5</option>
|
||||||
|
<option value="TVS iQube">TVS iQube</option>
|
||||||
|
<option value="Bajaj Chetak">Bajaj Chetak</option>
|
||||||
|
<option value="Hero Photon">Hero Photon</option>
|
||||||
|
<option value="Okinawa Praise">Okinawa Praise</option>
|
||||||
|
<option value="Ampere Magnus">Ampere Magnus</option>
|
||||||
|
<option value="Benling Aura">Benling Aura</option>
|
||||||
|
<option value="Lectrix LXS">Lectrix LXS</option>
|
||||||
|
<option value="Revolt RV400">Revolt RV400</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Daily Rent</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Daily Rent</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2521,13 +2642,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Weekly Subscription</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Weekly Subscription</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2542,13 +2663,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Monthly Subscription</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Monthly Subscription</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2563,98 +2684,89 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 className="bg-slate-50 rounded-lg p-3">
|
<div className="bg-slate-50 rounded-lg p-3">
|
||||||
<label className="text-sm font-semibold text-slate-700 block mb-2">Contract Duration (Months)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide block mb-2">Contract Duration (Months)</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{plan.contractMonths.map(month => (
|
{plan.contractMonths.map(month => (
|
||||||
<button
|
<button key={month} onClick={() => { const updated = [...settings.plans.rentToOwn]; updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="px-3 py-1.5 rounded-lg text-xs font-medium bg-purple-600 text-white hover:bg-red-500 transition-all flex items-center gap-1">
|
||||||
key={month}
|
{month} {month === 1 ? 'Month' : 'Months'}
|
||||||
onClick={() => {
|
|
||||||
const updated = [...settings.plans.rentToOwn];
|
|
||||||
updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } });
|
|
||||||
}}
|
|
||||||
className="px-3 py-1.5 rounded-lg text-xs font-medium bg-purple-600 text-white hover:bg-red-500 transition-all flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{month} Months
|
|
||||||
<span className="ml-1 font-bold">×</span>
|
<span className="ml-1 font-bold">×</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input type="number" min="1" placeholder="Add" className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs" onKeyDown={(e) => { if (e.key === 'Enter') { const val = parseInt((e.target as HTMLInputElement).value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.rentToOwn]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); (e.target as HTMLInputElement).value = ''; } } }} />
|
||||||
type="number"
|
<button onClick={(e) => { const input = (e.currentTarget.previousElementSibling as HTMLInputElement); const val = parseInt(input.value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.rentToOwn]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); input.value = ''; } }} className="px-2 py-1.5 bg-purple-600 text-white rounded-lg text-xs hover:bg-purple-700">+</button>
|
||||||
min="1"
|
|
||||||
placeholder="Add"
|
|
||||||
className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs"
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
const val = parseInt((e.target as HTMLInputElement).value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.rentToOwn];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } });
|
|
||||||
(e.target as HTMLInputElement).value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
const input = (e.currentTarget.previousElementSibling as HTMLInputElement);
|
|
||||||
const val = parseInt(input.value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.rentToOwn];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } });
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="px-2 py-1.5 bg-purple-600 text-white rounded-lg text-xs hover:bg-purple-700"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{plan.contractMonths.length === 0 && (
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
||||||
<p className="text-xs text-slate-400 mt-2">No contract months selected.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Description</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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} />
|
<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" rows={2} placeholder="Enter plan description..." />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<button onClick={() => addNewPlan('rentToOwn')} className="w-full py-3 border-2 border-dashed border-slate-300 rounded-xl text-slate-500 hover:border-purple-400 hover:text-purple-500 hover:bg-purple-50 transition-all flex items-center justify-center gap-2 text-sm font-medium">
|
||||||
|
<Plus className="w-4 h-4" /> Add New Plan
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activePlanTab === 'shareEv' && (
|
{activePlanTab === 'shareEv' && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{settings.plans.shareEv.map((plan, idx) => (
|
{settings.plans.shareEv.map((plan, idx) => (
|
||||||
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
<div key={plan.id} 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="bg-green-50 px-4 py-3 border-b border-green-100 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-green-800">SHARE AN EV - {plan.tier} - ৳{plan.dailyRentEach}/day each (Total: ৳{plan.totalDailyRent})</h4>
|
<h4 className="font-semibold text-green-800">{plan.name} - ৳{plan.dailyRentEach}/day each</h4>
|
||||||
<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 bg-transparent text-sm text-green-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
<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 bg-transparent text-sm text-green-600 mt-1 border-0 resize-none p-0 focus:ring-0" rows={1} placeholder="Plan description..." />
|
||||||
</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">
|
<div className="flex items-center gap-2 ml-3">
|
||||||
<Save className="w-3 h-3" /> Save
|
<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 hover:bg-green-700">
|
||||||
</button>
|
<Save className="w-3 h-3" /> Save
|
||||||
|
</button>
|
||||||
|
<button onClick={() => { if (confirm('Delete this plan?')) { const updated = settings.plans.shareEv.filter((_, i) => i !== idx); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); } }} className="px-2 py-1.5 bg-red-50 text-red-600 rounded-lg text-xs font-medium flex items-center gap-1 hover:bg-red-100">
|
||||||
|
<Trash2 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-5 space-y-5">
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
||||||
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].name = 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" placeholder="Plan Name" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
|
<select multiple value={plan.evModels} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].evModels = Array.from(e.target.selectedOptions, opt => opt.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-2 border border-slate-200 rounded-lg text-sm bg-white min-h-[70px] cursor-pointer">
|
||||||
|
<option value="Etron ET50">Etron ET50</option>
|
||||||
|
<option value="Yadea DT3">Yadea DT3</option>
|
||||||
|
<option value="AIMA Lightning">AIMA Lightning</option>
|
||||||
|
<option value="AIMA EM5">AIMA EM5</option>
|
||||||
|
<option value="Yadea G5">Yadea G5</option>
|
||||||
|
<option value="TVS iQube">TVS iQube</option>
|
||||||
|
<option value="Bajaj Chetak">Bajaj Chetak</option>
|
||||||
|
<option value="Hero Photon">Hero Photon</option>
|
||||||
|
<option value="Okinawa Praise">Okinawa Praise</option>
|
||||||
|
<option value="Ampere Magnus">Ampere Magnus</option>
|
||||||
|
<option value="Benling Aura">Benling Aura</option>
|
||||||
|
<option value="Lectrix LXS">Lectrix LXS</option>
|
||||||
|
<option value="Revolt RV400">Revolt RV400</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Daily Rent (Each)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Daily Rent (Each)</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2669,13 +2781,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.dailyRentPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Weekly Subscription (Each)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Weekly Subscription (Each)</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2690,13 +2802,13 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.weeklyPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
||||||
<label className="text-sm font-semibold text-slate-700">Monthly Subscription (Each)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Monthly Subscription (Each)</label>
|
||||||
<div className="grid grid-cols-4 gap-3">
|
<div className="grid grid-cols-4 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">Base (৳)</label>
|
<label className="text-xs text-slate-500">Base (৳)</label>
|
||||||
@@ -2711,81 +2823,37 @@ export default function CompanySettingsPage() {
|
|||||||
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty2} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlyPenalty2 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-500">3rd Day Penalty + Bike Lock (৳)</label>
|
<label className="text-xs text-slate-500">3rd Day Bike Lock (৳)</label>
|
||||||
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
<input type="number" value={plan.monthlyPenalty3} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlyPenalty3 = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
||||||
<label className="text-sm font-semibold text-slate-700">Deposit (Each)</label>
|
|
||||||
<div>
|
|
||||||
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bg-slate-50 rounded-lg p-3">
|
<div className="bg-slate-50 rounded-lg p-3">
|
||||||
<label className="text-sm font-semibold text-slate-700 block mb-2">Contract Duration (Months)</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide block mb-2">Contract Duration (Months)</label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{plan.contractMonths.map(month => (
|
{plan.contractMonths.map(month => (
|
||||||
<button
|
<button key={month} onClick={() => { const updated = [...settings.plans.shareEv]; updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="px-3 py-1.5 rounded-lg text-xs font-medium bg-green-600 text-white hover:bg-red-500 transition-all flex items-center gap-1">
|
||||||
key={month}
|
|
||||||
onClick={() => {
|
|
||||||
const updated = [...settings.plans.shareEv];
|
|
||||||
updated[idx].contractMonths = updated[idx].contractMonths.filter(m => m !== month);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } });
|
|
||||||
}}
|
|
||||||
className="px-3 py-1.5 rounded-lg text-xs font-medium bg-green-600 text-white hover:bg-red-500 transition-all flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{month} {month === 1 ? 'Month' : 'Months'}
|
{month} {month === 1 ? 'Month' : 'Months'}
|
||||||
<span className="ml-1 font-bold">×</span>
|
<span className="ml-1 font-bold">×</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input type="number" min="1" placeholder="Add" className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs" onKeyDown={(e) => { if (e.key === 'Enter') { const val = parseInt((e.target as HTMLInputElement).value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.shareEv]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); (e.target as HTMLInputElement).value = ''; } } }} />
|
||||||
type="number"
|
<button onClick={(e) => { const input = (e.currentTarget.previousElementSibling as HTMLInputElement); const val = parseInt(input.value); if (val > 0 && !plan.contractMonths.includes(val)) { const updated = [...settings.plans.shareEv]; updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b); setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); input.value = ''; } }} className="px-2 py-1.5 bg-green-600 text-white rounded-lg text-xs hover:bg-green-700">+</button>
|
||||||
min="1"
|
|
||||||
placeholder="Add"
|
|
||||||
className="w-20 px-2 py-1.5 border border-slate-200 rounded-lg text-xs"
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
const val = parseInt((e.target as HTMLInputElement).value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.shareEv];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } });
|
|
||||||
(e.target as HTMLInputElement).value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
const input = (e.currentTarget.previousElementSibling as HTMLInputElement);
|
|
||||||
const val = parseInt(input.value);
|
|
||||||
if (val > 0 && !plan.contractMonths.includes(val)) {
|
|
||||||
const updated = [...settings.plans.shareEv];
|
|
||||||
updated[idx].contractMonths = [...updated[idx].contractMonths, val].sort((a, b) => a - b);
|
|
||||||
setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } });
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="px-2 py-1.5 bg-green-600 text-white rounded-lg text-xs hover:bg-green-700"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{plan.contractMonths.length === 0 && (
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
||||||
<p className="text-xs text-slate-400 mt-2">No contract months selected.</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Description</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">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} />
|
<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" rows={2} placeholder="Enter plan description..." />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
<button onClick={() => addNewPlan('shareEv')} className="w-full py-3 border-2 border-dashed border-slate-300 rounded-xl text-slate-500 hover:border-green-400 hover:text-green-500 hover:bg-green-50 transition-all flex items-center justify-center gap-2 text-sm font-medium">
|
||||||
|
<Plus className="w-4 h-4" /> Add New Plan
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const ROLE_PERMISSIONS: Record<string, string[]> = {
|
const ROLE_PERMISSIONS: Record<string, string[]> = {
|
||||||
super_admin: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'kyc.doc_approve', 'kyc.doc_reject', 'kyc.make_valid_user', 'dashboard.view'],
|
super_admin: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'kyc.doc_approve', 'kyc.doc_reject', 'kyc.make_valid_user', 'dashboard.view', 'rental.view', 'rental.create', 'rental.accept', 'rental.reject', 'rental.cancel', 'rental.edit', 'rental.image_approve', 'rental.lock', 'rental.unlock'],
|
||||||
admin_manager: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'kyc.doc_approve', 'kyc.doc_reject', 'kyc.make_valid_user', 'dashboard.view'],
|
admin_manager: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'kyc.doc_approve', 'kyc.doc_reject', 'kyc.make_valid_user', 'dashboard.view', 'rental.view', 'rental.create', 'rental.accept', 'rental.reject', 'rental.cancel', 'rental.edit', 'rental.image_approve', 'rental.lock', 'rental.unlock'],
|
||||||
staff: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'dashboard.view'],
|
staff: ['kyc.request', 'kyc.view', 'kyc.doc_upload', 'dashboard.view', 'rental.view', 'rental.create'],
|
||||||
accountant: ['dashboard.view', 'accounting.view', 'accounting.create', 'accounting.edit', 'accounting.delete'],
|
accountant: ['dashboard.view', 'accounting.view', 'accounting.create', 'accounting.edit', 'accounting.delete'],
|
||||||
investor: ['dashboard.view', 'kyc.request', 'kyc.view'],
|
investor: ['dashboard.view', 'kyc.request', 'kyc.view'],
|
||||||
biker: ['dashboard.view', 'kyc.request', 'kyc.view', 'rentals.view', 'rentals.create'],
|
biker: ['dashboard.view', 'kyc.request', 'kyc.view', 'rentals.view', 'rentals.create'],
|
||||||
@@ -9,6 +9,15 @@ const ROLE_PERMISSIONS: Record<string, string[]> = {
|
|||||||
merchant: ['dashboard.view', 'kyc.request', 'kyc.view', 'merchants.view'],
|
merchant: ['dashboard.view', 'kyc.request', 'kyc.view', 'merchants.view'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const canRentalAccept = () => hasPermission('rental.accept');
|
||||||
|
export const canRentalReject = () => hasPermission('rental.reject');
|
||||||
|
export const canRentalCancel = () => hasPermission('rental.cancel');
|
||||||
|
export const canRentalEdit = () => hasPermission('rental.edit');
|
||||||
|
export const canRentalImageApprove = () => hasPermission('rental.image_approve');
|
||||||
|
export const canRentalLock = () => hasPermission('rental.lock');
|
||||||
|
export const canRentalUnlock = () => hasPermission('rental.unlock');
|
||||||
|
export const canRentalCreate = () => hasPermission('rental.create');
|
||||||
|
|
||||||
export const isAuthenticated = (): boolean => {
|
export const isAuthenticated = (): boolean => {
|
||||||
return typeof window !== 'undefined' && !!sessionStorage.getItem('authToken');
|
return typeof window !== 'undefined' && !!sessionStorage.getItem('authToken');
|
||||||
};
|
};
|
||||||
@@ -23,10 +32,9 @@ export const getUserName = (): string | null => {
|
|||||||
|
|
||||||
export const getUserPermissions = (): string[] => {
|
export const getUserPermissions = (): string[] => {
|
||||||
if (typeof window === 'undefined') return [];
|
if (typeof window === 'undefined') return [];
|
||||||
const stored = sessionStorage.getItem('userPermissions');
|
|
||||||
if (stored) return JSON.parse(stored);
|
|
||||||
const role = getUserRole();
|
const role = getUserRole();
|
||||||
return role ? (ROLE_PERMISSIONS[role] || []) : [];
|
if (role) return ROLE_PERMISSIONS[role] || [];
|
||||||
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hasPermission = (permission: string): boolean => {
|
export const hasPermission = (permission: string): boolean => {
|
||||||
|
|||||||
Reference in New Issue
Block a user