feat: add functionality to create, edit, and delete company policy rules in admin settings

This commit is contained in:
sazzadulalambd
2026-05-06 01:04:32 +06:00
parent f319cc5bac
commit 2cdd18e255

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Settings, Upload, Image, Globe, Mail, MessageSquare, Phone, MapPin, Link2, Clock, Save, FileText, Camera, Palette, Ruler, Sun, Moon, Monitor, Smartphone, Tablet, Package, Wrench, FileCheck, BadgeDollarSign, CreditCard, Plus, X, DollarSign, Zap, Users, Check, Pencil } from 'lucide-react'; import { Settings, Upload, Image, Globe, Mail, MessageSquare, Phone, MapPin, Link2, Clock, Save, FileText, Camera, Palette, Ruler, Sun, Moon, Monitor, Smartphone, Tablet, Package, Wrench, FileCheck, BadgeDollarSign, CreditCard, Plus, X, DollarSign, Zap, Users, Check, Pencil, Trash2 } from 'lucide-react';
import RichTextEditor from '@/components/RichTextEditor'; import RichTextEditor from '@/components/RichTextEditor';
interface CompanySettings { interface CompanySettings {
@@ -679,6 +679,10 @@ export default function CompanySettingsPage() {
const [editingPolicy, setEditingPolicy] = useState<{tab: string; index: number} | null>(null); const [editingPolicy, setEditingPolicy] = useState<{tab: string; index: number} | null>(null);
const [editPolicyName, setEditPolicyName] = useState(''); const [editPolicyName, setEditPolicyName] = useState('');
const [editPolicyDesc, setEditPolicyDesc] = useState(''); const [editPolicyDesc, setEditPolicyDesc] = useState('');
const [editPolicyDescHtml, setEditPolicyDescHtml] = useState('');
const [showAddPolicy, setShowAddPolicy] = useState(false);
const [newPolicyName, setNewPolicyName] = useState('');
const [newPolicyDesc, setNewPolicyDesc] = useState('');
const [addInvestPlan, setAddInvestPlan] = useState(false); const [addInvestPlan, setAddInvestPlan] = useState(false);
const [newInvestName, setNewInvestName] = useState(''); const [newInvestName, setNewInvestName] = useState('');
const [newInvestTier, setNewInvestTier] = useState('Standard'); const [newInvestTier, setNewInvestTier] = useState('Standard');
@@ -802,6 +806,51 @@ setNewSwapName('');
setTimeout(() => setSaved(false), 2000); setTimeout(() => setSaved(false), 2000);
}; };
const addPolicyRule = (tab: 'investor' | 'merchant' | 'swapstation') => {
if (!newPolicyName.trim()) return;
const newRule = { name: newPolicyName, description: newPolicyDesc };
if (tab === 'investor') {
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, investor: { ...settings.companyPolicy.investor, rules: [...settings.companyPolicy.investor.rules, newRule] } } });
} else if (tab === 'merchant') {
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, merchant: { ...settings.companyPolicy.merchant, rules: [...settings.companyPolicy.merchant.rules, newRule] } } });
} else if (tab === 'swapstation') {
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, swapStation: { ...settings.companyPolicy.swapStation, rules: [...settings.companyPolicy.swapStation.rules, newRule] } } });
}
setNewPolicyName('');
setNewPolicyDesc('');
setShowAddPolicy(false);
};
const deletePolicyRule = (tab: 'investor' | 'merchant' | 'swapstation', index: number) => {
if (tab === 'investor') {
const newRules = settings.companyPolicy.investor.rules.filter((_, i) => i !== index);
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, investor: { ...settings.companyPolicy.investor, rules: newRules } } });
} else if (tab === 'merchant') {
const newRules = settings.companyPolicy.merchant.rules.filter((_, i) => i !== index);
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, merchant: { ...settings.companyPolicy.merchant, rules: newRules } } });
} else if (tab === 'swapstation') {
const newRules = settings.companyPolicy.swapStation.rules.filter((_, i) => i !== index);
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, swapStation: { ...settings.companyPolicy.swapStation, rules: newRules } } });
}
};
const updatePolicyRule = (tab: 'investor' | 'merchant' | 'swapstation', index: number) => {
if (tab === 'investor') {
const newRules = [...settings.companyPolicy.investor.rules];
newRules[index] = { name: editPolicyName, description: editPolicyDesc };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, investor: { ...settings.companyPolicy.investor, rules: newRules } } });
} else if (tab === 'merchant') {
const newRules = [...settings.companyPolicy.merchant.rules];
newRules[index] = { name: editPolicyName, description: editPolicyDesc };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, merchant: { ...settings.companyPolicy.merchant, rules: newRules } } });
} else if (tab === 'swapstation') {
const newRules = [...settings.companyPolicy.swapStation.rules];
newRules[index] = { name: editPolicyName, description: editPolicyDesc };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, swapStation: { ...settings.companyPolicy.swapStation, rules: newRules } } });
}
setEditingPolicy(null);
};
const tabs = [ const tabs = [
{ id: 'general', label: 'General', icon: Settings }, { id: 'general', label: 'General', icon: Settings },
{ id: 'branding', label: 'Branding', icon: Palette }, { id: 'branding', label: 'Branding', icon: Palette },
@@ -1842,10 +1891,23 @@ setNewSwapName('');
<div className="border-t border-slate-200 pt-4"> <div className="border-t border-slate-200 pt-4">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<label className="text-sm text-slate-600 font-medium">Policy List</label> <label className="text-sm text-slate-600 font-medium">Policy List</label>
<button onClick={() => setShowAddPolicy(true)} className="text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1">
<Plus className="w-3 h-3" /> Add Policy
</button>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{(settings.companyPolicy?.investor?.rules || []).map((policy, i) => ( {(settings.companyPolicy?.investor?.rules || []).map((policy, i) => (
<div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200"> <div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200">
{editingPolicy?.tab === 'investor' && editingPolicy?.index === i ? (
<div className="space-y-2">
<input type="text" value={editPolicyName} onChange={(e) => setEditPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" placeholder="Policy Name" />
<textarea value={editPolicyDesc} onChange={(e) => setEditPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => updatePolicyRule('investor', i)} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Save</button>
<button onClick={() => setEditingPolicy(null)} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
) : (
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -1853,10 +1915,29 @@ setNewSwapName('');
</div> </div>
<p className="text-xs text-slate-500 mt-1">{policy.description}</p> <p className="text-xs text-slate-500 mt-1">{policy.description}</p>
</div> </div>
<div className="flex items-center gap-1 ml-2">
<button onClick={() => { setEditingPolicy({ tab: 'investor', index: i }); setEditPolicyName(policy.name); setEditPolicyDesc(policy.description); }} className="p-1 text-slate-400 hover:text-blue-600">
<Pencil className="w-3.5 h-3.5" />
</button>
<button onClick={() => deletePolicyRule('investor', i)} className="p-1 text-slate-400 hover:text-red-600">
<Trash2 className="w-3.5 h-3.5" />
</button>
</div> </div>
</div> </div>
)}
</div>
))} ))}
</div> </div>
{showAddPolicy && (
<div className="mt-2 p-3 bg-white rounded-lg border border-blue-200">
<input type="text" value={newPolicyName} onChange={(e) => setNewPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" placeholder="Policy Name" />
<textarea value={newPolicyDesc} onChange={(e) => setNewPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => addPolicyRule('investor')} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Add</button>
<button onClick={() => { setShowAddPolicy(false); setNewPolicyName(''); setNewPolicyDesc(''); }} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
)}
</div> </div>
</div> </div>
)} )}
@@ -1877,10 +1958,23 @@ setNewSwapName('');
<div className="border-t border-slate-200 pt-4"> <div className="border-t border-slate-200 pt-4">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<label className="text-sm text-slate-600 font-medium">Policy List</label> <label className="text-sm text-slate-600 font-medium">Policy List</label>
<button onClick={() => setShowAddPolicy(true)} className="text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1">
<Plus className="w-3 h-3" /> Add Policy
</button>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{(settings.companyPolicy?.merchant?.rules || []).map((policy, i) => ( {(settings.companyPolicy?.merchant?.rules || []).map((policy, i) => (
<div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200"> <div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200">
{editingPolicy?.tab === 'merchant' && editingPolicy?.index === i ? (
<div className="space-y-2">
<input type="text" value={editPolicyName} onChange={(e) => setEditPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" placeholder="Policy Name" />
<textarea value={editPolicyDesc} onChange={(e) => setEditPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => updatePolicyRule('merchant', i)} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Save</button>
<button onClick={() => setEditingPolicy(null)} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
) : (
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -1888,10 +1982,29 @@ setNewSwapName('');
</div> </div>
<p className="text-xs text-slate-500 mt-1">{policy.description}</p> <p className="text-xs text-slate-500 mt-1">{policy.description}</p>
</div> </div>
<div className="flex items-center gap-1 ml-2">
<button onClick={() => { setEditingPolicy({ tab: 'merchant', index: i }); setEditPolicyName(policy.name); setEditPolicyDesc(policy.description); }} className="p-1 text-slate-400 hover:text-blue-600">
<Pencil className="w-3.5 h-3.5" />
</button>
<button onClick={() => deletePolicyRule('merchant', i)} className="p-1 text-slate-400 hover:text-red-600">
<Trash2 className="w-3.5 h-3.5" />
</button>
</div> </div>
</div> </div>
)}
</div>
))} ))}
</div> </div>
{showAddPolicy && (
<div className="mt-2 p-3 bg-white rounded-lg border border-blue-200">
<input type="text" value={newPolicyName} onChange={(e) => setNewPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" placeholder="Policy Name" />
<textarea value={newPolicyDesc} onChange={(e) => setNewPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => addPolicyRule('merchant')} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Add</button>
<button onClick={() => { setShowAddPolicy(false); setNewPolicyName(''); setNewPolicyDesc(''); }} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
)}
</div> </div>
</div> </div>
)} )}
@@ -1912,10 +2025,23 @@ setNewSwapName('');
<div className="border-t border-slate-200 pt-4"> <div className="border-t border-slate-200 pt-4">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<label className="text-sm text-slate-600 font-medium">Policy List</label> <label className="text-sm text-slate-600 font-medium">Policy List</label>
<button onClick={() => setShowAddPolicy(true)} className="text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1">
<Plus className="w-3 h-3" /> Add Policy
</button>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{(settings.companyPolicy?.swapStation?.rules || []).map((policy, i) => ( {(settings.companyPolicy?.swapStation?.rules || []).map((policy, i) => (
<div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200"> <div key={i} className="p-3 bg-slate-50 rounded-lg border border-slate-200">
{editingPolicy?.tab === 'swapstation' && editingPolicy?.index === i ? (
<div className="space-y-2">
<input type="text" value={editPolicyName} onChange={(e) => setEditPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" placeholder="Policy Name" />
<textarea value={editPolicyDesc} onChange={(e) => setEditPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => updatePolicyRule('swapstation', i)} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Save</button>
<button onClick={() => setEditingPolicy(null)} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
) : (
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -1923,10 +2049,29 @@ setNewSwapName('');
</div> </div>
<p className="text-xs text-slate-500 mt-1">{policy.description}</p> <p className="text-xs text-slate-500 mt-1">{policy.description}</p>
</div> </div>
<div className="flex items-center gap-1 ml-2">
<button onClick={() => { setEditingPolicy({ tab: 'swapstation', index: i }); setEditPolicyName(policy.name); setEditPolicyDesc(policy.description); }} className="p-1 text-slate-400 hover:text-blue-600">
<Pencil className="w-3.5 h-3.5" />
</button>
<button onClick={() => deletePolicyRule('swapstation', i)} className="p-1 text-slate-400 hover:text-red-600">
<Trash2 className="w-3.5 h-3.5" />
</button>
</div> </div>
</div> </div>
)}
</div>
))} ))}
</div> </div>
{showAddPolicy && (
<div className="mt-2 p-3 bg-white rounded-lg border border-blue-200">
<input type="text" value={newPolicyName} onChange={(e) => setNewPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" placeholder="Policy Name" />
<textarea value={newPolicyDesc} onChange={(e) => setNewPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => addPolicyRule('swapstation')} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Add</button>
<button onClick={() => { setShowAddPolicy(false); setNewPolicyName(''); setNewPolicyDesc(''); }} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
)}
</div> </div>
</div> </div>
)} )}
@@ -1936,15 +2081,8 @@ setNewSwapName('');
{(settings.companyPolicy?.rentalTypes || []).map((rtype, idx) => ( {(settings.companyPolicy?.rentalTypes || []).map((rtype, idx) => (
<div key={idx} className="border border-slate-200 rounded-lg overflow-hidden"> <div key={idx} className="border border-slate-200 rounded-lg overflow-hidden">
<div className="bg-slate-50 px-4 py-3 flex items-center justify-between"> <div className="bg-slate-50 px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-3">
<input type="checkbox" checked={rtype.enabled} onChange={(e) => {
const updated = [...(settings.companyPolicy?.rentalTypes || [])];
updated[idx] = { ...updated[idx], enabled: e.target.checked };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } });
}} className="w-4 h-4" />
<h4 className="font-medium text-slate-700">{rtype.name}</h4> <h4 className="font-medium text-slate-700">{rtype.name}</h4>
</div> </div>
</div>
<div className="p-4 space-y-3"> <div className="p-4 space-y-3">
<input type="text" value={rtype.title} onChange={(e) => { <input type="text" value={rtype.title} onChange={(e) => {
const updated = [...(settings.companyPolicy?.rentalTypes || [])]; const updated = [...(settings.companyPolicy?.rentalTypes || [])];
@@ -1956,6 +2094,73 @@ setNewSwapName('');
updated[idx] = { ...updated[idx], description: val }; updated[idx] = { ...updated[idx], description: val };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } }); setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } });
}} placeholder="Enter policy description..." minHeight={120} /> }} placeholder="Enter policy description..." minHeight={120} />
<div className="border-t border-slate-200 pt-4 mt-4">
<div className="flex items-center justify-between mb-2">
<label className="text-sm text-slate-600 font-medium">Policy List</label>
</div>
<div className="space-y-2">
{(rtype.rules || []).map((policy, policyIdx) => (
<div key={policyIdx} className="p-3 bg-slate-50 rounded-lg border border-slate-200">
{editingPolicy?.tab === 'rentalType' && editingPolicy?.index === policyIdx ? (
<div className="space-y-2">
<input type="text" value={editPolicyName} onChange={(e) => setEditPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" placeholder="Policy Name" />
<textarea value={editPolicyDesc} onChange={(e) => setEditPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => {
const updated = [...(settings.companyPolicy?.rentalTypes || [])];
updated[idx].rules[policyIdx] = { name: editPolicyName, description: editPolicyDesc };
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } });
setEditingPolicy(null);
}} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Save</button>
<button onClick={() => setEditingPolicy(null)} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
) : (
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-700">{policy.name}</span>
</div>
<p className="text-xs text-slate-500 mt-1">{policy.description}</p>
</div>
<div className="flex items-center gap-1 ml-2">
<button onClick={() => { setEditingPolicy({ tab: 'rentalType', index: policyIdx }); setEditPolicyName(policy.name); setEditPolicyDesc(policy.description); }} className="p-1 text-slate-400 hover:text-blue-600">
<Pencil className="w-3.5 h-3.5" />
</button>
<button onClick={() => {
const updated = [...(settings.companyPolicy?.rentalTypes || [])];
updated[idx].rules = updated[idx].rules?.filter((_, i) => i !== policyIdx);
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } });
}} className="p-1 text-slate-400 hover:text-red-600">
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
</div>
)}
</div>
))}
</div>
<button onClick={() => setEditingPolicy({ tab: 'rentalTypeAdd', index: idx })} className="mt-2 text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1">
<Plus className="w-3 h-3" /> Add Policy
</button>
{editingPolicy?.tab === 'rentalTypeAdd' && editingPolicy?.index === idx && (
<div className="mt-2 p-3 bg-white rounded-lg border border-blue-200">
<input type="text" value={newPolicyName} onChange={(e) => setNewPolicyName(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" placeholder="Policy Name" />
<textarea value={newPolicyDesc} onChange={(e) => setNewPolicyDesc(e.target.value)} className="w-full px-2 py-1.5 border border-slate-200 rounded text-sm mb-2" rows={2} placeholder="Description" />
<div className="flex gap-2">
<button onClick={() => {
const updated = [...(settings.companyPolicy?.rentalTypes || [])];
updated[idx].rules = [...(updated[idx].rules || []), { name: newPolicyName, description: newPolicyDesc }];
setSettings({ ...settings, companyPolicy: { ...settings.companyPolicy, rentalTypes: updated } });
setNewPolicyName('');
setNewPolicyDesc('');
setEditingPolicy(null);
}} className="px-2 py-1 bg-blue-600 text-white rounded text-xs">Add</button>
<button onClick={() => { setEditingPolicy(null); setNewPolicyName(''); setNewPolicyDesc(''); }} className="px-2 py-1 bg-slate-200 text-slate-600 rounded text-xs">Cancel</button>
</div>
</div>
)}
</div>
</div> </div>
</div> </div>
))} ))}