2026-05-10 17:17:37 +06:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react';
|
2026-05-21 11:40:03 +06:00
|
|
|
|
import { Plus, Save, Trash2, X, Gift } from 'lucide-react';
|
2026-05-10 17:17:37 +06:00
|
|
|
|
import { CompanySettings } from '../page';
|
|
|
|
|
|
|
|
|
|
|
|
interface PlanSelectionProps {
|
|
|
|
|
|
settings: CompanySettings;
|
|
|
|
|
|
setSettings: React.Dispatch<React.SetStateAction<CompanySettings>>;
|
|
|
|
|
|
activePlanTab: 'singleRent' | 'rentToOwn' | 'shareEv';
|
|
|
|
|
|
setActivePlanTab: (tab: 'singleRent' | 'rentToOwn' | 'shareEv') => void;
|
|
|
|
|
|
handleSave: () => void;
|
|
|
|
|
|
addNewPlan: (type: 'singleRent' | 'rentToOwn' | 'shareEv') => void;
|
2026-05-13 02:06:28 +06:00
|
|
|
|
isDirty?: boolean;
|
2026-05-10 17:17:37 +06:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-21 11:40:03 +06:00
|
|
|
|
// Reusable Free Service Conditions editor
|
|
|
|
|
|
function FreeServiceConditions({
|
|
|
|
|
|
conditions,
|
|
|
|
|
|
accentColor,
|
|
|
|
|
|
onChange,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
conditions: { months: number; freeServices: number }[];
|
|
|
|
|
|
accentColor: string;
|
|
|
|
|
|
onChange: (updated: { months: number; freeServices: number }[]) => void;
|
|
|
|
|
|
}) {
|
|
|
|
|
|
const addCondition = () => {
|
|
|
|
|
|
onChange([...conditions, { months: 3, freeServices: 1 }]);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const removeCondition = (i: number) => {
|
|
|
|
|
|
onChange(conditions.filter((_, idx) => idx !== i));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const updateCondition = (i: number, field: 'months' | 'freeServices', value: number) => {
|
|
|
|
|
|
const updated = conditions.map((c, idx) => idx === i ? { ...c, [field]: value } : c);
|
|
|
|
|
|
onChange(updated);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="bg-amber-50 border border-amber-100 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<Gift className="w-4 h-4 text-amber-600" />
|
|
|
|
|
|
<label className="text-xs font-semibold text-amber-700 uppercase tracking-wide">
|
|
|
|
|
|
Free Service Conditions
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<span className="text-[10px] text-amber-500 font-medium bg-amber-100 px-2 py-0.5 rounded-full">
|
|
|
|
|
|
e.g. "3 months → 2 free services"
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={addCondition}
|
|
|
|
|
|
className={`flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs font-semibold transition-all ${accentColor} text-white hover:opacity-90`}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Plus className="w-3 h-3" /> Add Condition
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{conditions.length === 0 && (
|
|
|
|
|
|
<p className="text-xs text-amber-400 italic text-center py-2">
|
|
|
|
|
|
No free service conditions set. Click "Add Condition" to add one.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{conditions.map((cond, i) => (
|
|
|
|
|
|
<div key={i} className="flex items-center gap-3 bg-white border border-amber-100 rounded-lg px-3 py-2 group">
|
|
|
|
|
|
{/* Month input */}
|
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
|
<label className="text-xs text-slate-500 font-medium shrink-0">Month:</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min={1}
|
|
|
|
|
|
max={999}
|
|
|
|
|
|
value={cond.months}
|
|
|
|
|
|
onChange={(e) => updateCondition(i, 'months', parseInt(e.target.value) || 1)}
|
|
|
|
|
|
className="w-16 px-2 py-1 border border-slate-200 rounded-md text-xs text-slate-800 text-center font-semibold focus:outline-none focus:ring-1 focus:ring-amber-400"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<span className="text-slate-300 text-sm">→</span>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Free services input */}
|
|
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
|
|
|
|
<label className="text-xs text-slate-500 font-medium shrink-0">Free Services:</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min={1}
|
|
|
|
|
|
max={99}
|
|
|
|
|
|
value={cond.freeServices}
|
|
|
|
|
|
onChange={(e) => updateCondition(i, 'freeServices', parseInt(e.target.value) || 1)}
|
|
|
|
|
|
className="w-16 px-2 py-1 border border-slate-200 rounded-md text-xs text-slate-800 text-center font-semibold focus:outline-none focus:ring-1 focus:ring-amber-400"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Preview badge */}
|
|
|
|
|
|
<span className="flex-1 text-[10px] font-bold text-amber-700 bg-amber-50 border border-amber-100 rounded-full px-2.5 py-1 text-center truncate">
|
|
|
|
|
|
{cond.months} {cond.months === 1 ? 'month' : 'months'} → {cond.freeServices} free service{cond.freeServices !== 1 ? 's' : ''} free
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Remove */}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={() => removeCondition(i)}
|
|
|
|
|
|
className="p-1 text-slate-300 hover:text-red-500 hover:bg-red-50 rounded-md transition-all opacity-0 group-hover:opacity-100"
|
|
|
|
|
|
title="Remove condition"
|
|
|
|
|
|
>
|
|
|
|
|
|
<X className="w-3.5 h-3.5" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-10 17:17:37 +06:00
|
|
|
|
export default function PlanSelection({
|
|
|
|
|
|
settings,
|
|
|
|
|
|
setSettings,
|
|
|
|
|
|
activePlanTab,
|
|
|
|
|
|
setActivePlanTab,
|
|
|
|
|
|
handleSave,
|
|
|
|
|
|
addNewPlan,
|
|
|
|
|
|
}: PlanSelectionProps) {
|
|
|
|
|
|
const [deleteModal, setDeleteModal] = useState<{ type: 'singleRent' | 'rentToOwn' | 'shareEv' | null; idx: number | null }>({ type: null, idx: null });
|
|
|
|
|
|
|
|
|
|
|
|
const handleDeletePlan = () => {
|
|
|
|
|
|
if (deleteModal.type !== null && deleteModal.idx !== null) {
|
|
|
|
|
|
const updated = settings.plans[deleteModal.type].filter((_, i) => i !== deleteModal.idx);
|
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, [deleteModal.type]: updated } });
|
|
|
|
|
|
setDeleteModal({ type: null, idx: null });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-10 17:52:21 +06:00
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<div className="p-6 space-y-6">
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<h3 className="text-lg font-semibold text-slate-800">Plan Selection</h3>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex gap-2 border-b border-slate-200">
|
|
|
|
|
|
<button onClick={() => setActivePlanTab('singleRent')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'singleRent' ? 'border-blue-500 text-blue-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>Single Rent</button>
|
|
|
|
|
|
<button onClick={() => setActivePlanTab('rentToOwn')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'rentToOwn' ? 'border-purple-500 text-purple-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>Rent to Own</button>
|
|
|
|
|
|
<button onClick={() => setActivePlanTab('shareEv')} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activePlanTab === 'shareEv' ? 'border-green-500 text-green-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>Share an EV</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{activePlanTab === 'singleRent' && (
|
|
|
|
|
|
<div className="space-y-6">
|
2026-05-13 02:06:28 +06:00
|
|
|
|
{settings.plans.singleRent.map((plan, idx) => (
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<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..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center gap-2 ml-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button onClick={() => setDeleteModal({ type: 'singleRent', idx })} 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 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 Condition</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 Condition" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<button type="button" onClick={() => { const el = document.getElementById(`ev-models-${idx}`); if (el) el.classList.toggle('hidden'); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white text-left flex items-center justify-between cursor-pointer hover:border-slate-300">
|
|
|
|
|
|
<span className={plan.evModels.length > 0 ? 'text-slate-800' : 'text-slate-400'}>
|
|
|
|
|
|
{plan.evModels.length > 0 ? `${plan.evModels.length} selected` : 'Select EV Models...'}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<svg className="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div id={`ev-models-${idx}`} className="hidden absolute z-50 w-full mt-1 bg-white border border-slate-200 rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
|
|
|
|
|
{['Etron ET50', 'Yadea DT3', 'AIMA Lightning', 'AIMA EM5', 'Yadea G5', 'TVS iQube', 'Bajaj Chetak', 'Hero Photon', 'Okinawa Praise', 'Ampere Magnus', 'Benling Aura', 'Lectrix LXS', 'Revolt RV400'].map(model => (
|
|
|
|
|
|
<label key={model} className="flex items-center gap-2 px-3 py-2 hover:bg-slate-50 cursor-pointer text-sm">
|
|
|
|
|
|
<input type="checkbox" checked={plan.evModels.includes(model)} onChange={() => { const updated = [...settings.plans.singleRent]; updated[idx].evModels = updated[idx].evModels.includes(model) ? updated[idx].evModels.filter(m => m !== model) : [...updated[idx].evModels, model]; setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="rounded border-slate-300 text-emerald-600" />
|
|
|
|
|
|
<span>{model}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</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">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Daily Rent</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.dailyRentPenalty1} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].dailyRentPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Weekly Subscription</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.weeklyPenalty1} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].weeklyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Monthly Subscription</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.monthlyPenalty1} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].monthlyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-lg p-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{plan.contractMonths.map(month => (
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{month} {month === 1 ? 'Month' : 'Months'}
|
|
|
|
|
|
<span className="ml-1 font-bold">×</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
|
<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 = ''; } } }} />
|
|
|
|
|
|
<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>
|
|
|
|
|
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
|
|
|
|
|
</div>
|
2026-05-21 11:40:03 +06:00
|
|
|
|
|
|
|
|
|
|
{/* Free Service Conditions */}
|
|
|
|
|
|
<FreeServiceConditions
|
|
|
|
|
|
conditions={plan.freeServiceConditions ?? []}
|
|
|
|
|
|
accentColor="bg-blue-600"
|
|
|
|
|
|
onChange={(updated) => {
|
|
|
|
|
|
const plans = [...settings.plans.singleRent];
|
|
|
|
|
|
plans[idx] = { ...plans[idx], freeServiceConditions: updated };
|
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, singleRent: plans } });
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-05-13 02:06:28 +06:00
|
|
|
|
<div>
|
|
|
|
|
|
<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" rows={2} placeholder="Enter plan description..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
2026-05-10 17:52:21 +06:00
|
|
|
|
<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>
|
2026-05-10 17:17:37 +06:00
|
|
|
|
</div>
|
2026-05-10 17:52:21 +06:00
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{activePlanTab === 'rentToOwn' && (
|
|
|
|
|
|
<div className="space-y-6">
|
2026-05-13 02:06:28 +06:00
|
|
|
|
{settings.plans.rentToOwn.map((plan, idx) => (
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<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..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center gap-2 ml-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button onClick={() => setDeleteModal({ type: 'rentToOwn', idx })} 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 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 Condition</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 Condition" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<button type="button" onClick={() => { const el = document.getElementById(`ev-models-rto-${idx}`); if (el) el.classList.toggle('hidden'); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white text-left flex items-center justify-between cursor-pointer hover:border-slate-300">
|
|
|
|
|
|
<span className={plan.evModels.length > 0 ? 'text-slate-800' : 'text-slate-400'}>
|
|
|
|
|
|
{plan.evModels.length > 0 ? `${plan.evModels.length} selected` : 'Select EV Models...'}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<svg className="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div id={`ev-models-rto-${idx}`} className="hidden absolute z-50 w-full mt-1 bg-white border border-slate-200 rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
|
|
|
|
|
{['Etron ET50', 'Yadea DT3', 'AIMA Lightning', 'AIMA EM5', 'Yadea G5', 'TVS iQube', 'Bajaj Chetak', 'Hero Photon', 'Okinawa Praise', 'Ampere Magnus', 'Benling Aura', 'Lectrix LXS', 'Revolt RV400'].map(model => (
|
|
|
|
|
|
<label key={model} className="flex items-center gap-2 px-3 py-2 hover:bg-slate-50 cursor-pointer text-sm">
|
|
|
|
|
|
<input type="checkbox" checked={plan.evModels.includes(model)} onChange={() => { const updated = [...settings.plans.rentToOwn]; updated[idx].evModels = updated[idx].evModels.includes(model) ? updated[idx].evModels.filter(m => m !== model) : [...updated[idx].evModels, model]; setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="rounded border-slate-300 text-emerald-600" />
|
|
|
|
|
|
<span>{model}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</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">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Daily Rent</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.dailyRentPenalty1} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].dailyRentPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Weekly Subscription</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.weeklyPenalty1} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].weeklyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Monthly Subscription</label>
|
|
|
|
|
|
<div className="grid grid-cols-4 gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.monthlyPenalty1} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].monthlyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-lg p-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{plan.contractMonths.map(month => (
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{month} {month === 1 ? 'Month' : 'Months'}
|
|
|
|
|
|
<span className="ml-1 font-bold">×</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
|
<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 = ''; } } }} />
|
|
|
|
|
|
<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>
|
|
|
|
|
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
|
|
|
|
|
</div>
|
2026-05-21 11:40:03 +06:00
|
|
|
|
|
|
|
|
|
|
{/* Free Service Conditions */}
|
|
|
|
|
|
<FreeServiceConditions
|
|
|
|
|
|
conditions={plan.freeServiceConditions ?? []}
|
|
|
|
|
|
accentColor="bg-purple-600"
|
|
|
|
|
|
onChange={(updated) => {
|
|
|
|
|
|
const plans = [...settings.plans.rentToOwn];
|
|
|
|
|
|
plans[idx] = { ...plans[idx], freeServiceConditions: updated };
|
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: plans } });
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-05-13 02:06:28 +06:00
|
|
|
|
<div>
|
|
|
|
|
|
<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" rows={2} placeholder="Enter plan description..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
2026-05-10 17:52:21 +06:00
|
|
|
|
<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>
|
2026-05-10 17:17:37 +06:00
|
|
|
|
</div>
|
2026-05-10 17:52:21 +06:00
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{activePlanTab === 'shareEv' && (
|
|
|
|
|
|
<div className="space-y-6">
|
2026-05-13 02:06:28 +06:00
|
|
|
|
{settings.plans.shareEv.map((plan, idx) => (
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<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..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center gap-2 ml-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
<Save className="w-3 h-3" /> Save
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button onClick={() => setDeleteModal({ type: 'shareEv', idx })} 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 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 Condition</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 Condition" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
|
|
|
|
|
<div className="relative">
|
|
|
|
|
|
<button type="button" onClick={() => { const el = document.getElementById(`ev-models-se-${idx}`); if (el) el.classList.toggle('hidden'); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white text-left flex items-center justify-between cursor-pointer hover:border-slate-300">
|
|
|
|
|
|
<span className={plan.evModels.length > 0 ? 'text-slate-800' : 'text-slate-400'}>
|
|
|
|
|
|
{plan.evModels.length > 0 ? `${plan.evModels.length} selected` : 'Select EV Models...'}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<svg className="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /></svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div id={`ev-models-se-${idx}`} className="hidden absolute z-50 w-full mt-1 bg-white border border-slate-200 rounded-lg shadow-lg max-h-60 overflow-y-auto">
|
|
|
|
|
|
{['Etron ET50', 'Yadea DT3', 'AIMA Lightning', 'AIMA EM5', 'Yadea G5', 'TVS iQube', 'Bajaj Chetak', 'Hero Photon', 'Okinawa Praise', 'Ampere Magnus', 'Benling Aura', 'Lectrix LXS', 'Revolt RV400'].map(model => (
|
|
|
|
|
|
<label key={model} className="flex items-center gap-2 px-3 py-2 hover:bg-slate-50 cursor-pointer text-sm">
|
|
|
|
|
|
<input type="checkbox" checked={plan.evModels.includes(model)} onChange={() => { const updated = [...settings.plans.shareEv]; updated[idx].evModels = updated[idx].evModels.includes(model) ? updated[idx].evModels.filter(m => m !== model) : [...updated[idx].evModels, model]; setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="rounded border-slate-300 text-emerald-600" />
|
|
|
|
|
|
<span>{model}</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</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">
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.dailyRentPenalty1} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].dailyRentPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.weeklyPenalty1} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].weeklyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-xl p-4 space-y-3">
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">Base (৳)</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-2 py-1.5 border border-slate-200 rounded-lg text-sm" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="text-xs text-slate-500">1st Day Penalty (৳)</label>
|
|
|
|
|
|
<input type="number" value={plan.monthlyPenalty1} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].monthlyPenalty1 = 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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">2nd Day Penalty (৳)</label>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<label className="text-xs text-slate-500">3rd Day Penalty + 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" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-slate-50 rounded-lg p-3">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{plan.contractMonths.map(month => (
|
|
|
|
|
|
<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">
|
|
|
|
|
|
{month} {month === 1 ? 'Month' : 'Months'}
|
|
|
|
|
|
<span className="ml-1 font-bold">×</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
|
<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 = ''; } } }} />
|
|
|
|
|
|
<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>
|
|
|
|
|
|
{plan.contractMonths.length === 0 && <p className="text-xs text-slate-400 mt-2">No contract months selected.</p>}
|
|
|
|
|
|
</div>
|
2026-05-21 11:40:03 +06:00
|
|
|
|
|
|
|
|
|
|
{/* Free Service Conditions */}
|
|
|
|
|
|
<FreeServiceConditions
|
|
|
|
|
|
conditions={plan.freeServiceConditions ?? []}
|
|
|
|
|
|
accentColor="bg-green-600"
|
|
|
|
|
|
onChange={(updated) => {
|
|
|
|
|
|
const plans = [...settings.plans.shareEv];
|
|
|
|
|
|
plans[idx] = { ...plans[idx], freeServiceConditions: updated };
|
|
|
|
|
|
setSettings({ ...settings, plans: { ...settings.plans, shareEv: plans } });
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-05-13 02:06:28 +06:00
|
|
|
|
<div>
|
|
|
|
|
|
<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" rows={2} placeholder="Enter plan description..." />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
2026-05-10 17:52:21 +06:00
|
|
|
|
<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>
|
2026-05-10 17:17:37 +06:00
|
|
|
|
</div>
|
2026-05-10 17:52:21 +06:00
|
|
|
|
)}
|
2026-05-10 17:17:37 +06:00
|
|
|
|
</div>
|
2026-05-21 11:40:03 +06:00
|
|
|
|
|
|
|
|
|
|
{/* Delete Confirmation Modal */}
|
|
|
|
|
|
{deleteModal.type !== null && (
|
|
|
|
|
|
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
|
|
|
|
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 space-y-4">
|
|
|
|
|
|
<h3 className="text-lg font-bold text-slate-800">Delete Plan?</h3>
|
|
|
|
|
|
<p className="text-sm text-slate-500">This will permanently remove the plan. This action cannot be undone.</p>
|
|
|
|
|
|
<div className="flex items-center justify-end gap-2 pt-2 border-t border-slate-100">
|
|
|
|
|
|
<button onClick={() => setDeleteModal({ type: null, idx: null })} className="px-4 py-2 border border-slate-200 text-slate-500 rounded-lg text-sm font-medium hover:bg-slate-50">Cancel</button>
|
|
|
|
|
|
<button onClick={handleDeletePlan} className="px-4 py-2 bg-red-600 text-white rounded-lg text-sm font-medium hover:bg-red-700">Delete Plan</button>
|
2026-05-10 17:52:21 +06:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-05-10 17:17:37 +06:00
|
|
|
|
</>
|
|
|
|
|
|
);
|
2026-05-13 02:06:28 +06:00
|
|
|
|
}
|