feat: add swap station management module and settings configuration to admin dashboard
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Settings, Upload, Image, Globe, Mail, MessageSquare, Phone, MapPin, Link2, Clock, Save, FileText, Camera, Palette, Ruler, Sun, Moon, Monitor, Smartphone, Tablet, Package, Wrench, FileCheck, BadgeDollarSign, CreditCard, Plus, X, DollarSign } 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 } from 'lucide-react';
|
||||
|
||||
interface CompanySettings {
|
||||
name: string;
|
||||
@@ -138,6 +138,19 @@ interface CompanySettings {
|
||||
status: string;
|
||||
description: string;
|
||||
}[];
|
||||
swapStation: {
|
||||
id: string;
|
||||
name: string;
|
||||
batteryCount: number;
|
||||
swapPrice: number;
|
||||
monthlySubscription: number;
|
||||
dailySubscription: number;
|
||||
minBatteries: number;
|
||||
maxBatteries: number;
|
||||
profitSharePercent: number;
|
||||
status: string;
|
||||
description: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -495,12 +508,40 @@ const initialSettings: CompanySettings = {
|
||||
description: 'Investment plan for 5 bikes - medium scale investment',
|
||||
}
|
||||
],
|
||||
swapStation: [
|
||||
{
|
||||
id: 'ss_1',
|
||||
name: 'Basic Swap Station',
|
||||
batteryCount: 10,
|
||||
swapPrice: 50,
|
||||
monthlySubscription: 500,
|
||||
dailySubscription: 20,
|
||||
minBatteries: 1,
|
||||
maxBatteries: 5,
|
||||
profitSharePercent: 50,
|
||||
status: 'active',
|
||||
description: 'Basic swap station for small operators',
|
||||
},
|
||||
{
|
||||
id: 'ss_2',
|
||||
name: 'Premium Swap Station',
|
||||
batteryCount: 50,
|
||||
swapPrice: 45,
|
||||
monthlySubscription: 2000,
|
||||
dailySubscription: 80,
|
||||
minBatteries: 10,
|
||||
maxBatteries: 30,
|
||||
profitSharePercent: 55,
|
||||
status: 'active',
|
||||
description: 'Premium swap station for large operators',
|
||||
}
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default function CompanySettingsPage() {
|
||||
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans' | 'investment'>('general');
|
||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'rental' | 'plans' | 'investment' | 'swapstation'>('general');
|
||||
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rental'>('investor');
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [activePlanTab, setActivePlanTab] = useState<'singleRent' | 'rentToOwn' | 'shareEv'>('singleRent');
|
||||
@@ -553,6 +594,42 @@ export default function CompanySettingsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const [activeSwapTab, setActiveSwapTab] = useState(0);
|
||||
const [addSwapStationPlan, setAddSwapStationPlan] = useState(false);
|
||||
const [newSwapName, setNewSwapName] = useState('');
|
||||
const [newSwapStatus, setNewSwapStatus] = useState('active');
|
||||
const [newSwapBatteryCount, setNewSwapBatteryCount] = useState(10);
|
||||
const [newSwapPrice, setNewSwapPrice] = useState(50);
|
||||
const [newSwapMonthly, setNewSwapMonthly] = useState(500);
|
||||
const [newSwapDaily, setNewSwapDaily] = useState(20);
|
||||
const [newSwapMin, setNewSwapMin] = useState(1);
|
||||
const [newSwapMax, setNewSwapMax] = useState(5);
|
||||
const [newSwapProfit, setNewSwapProfit] = useState(50);
|
||||
const [newSwapDesc, setNewSwapDesc] = useState('');
|
||||
|
||||
const createSwapStationPlan = () => {
|
||||
if (newSwapName.trim() && typeof window !== 'undefined') {
|
||||
const newPlan = {
|
||||
id: 'ss_' + Date.now(),
|
||||
name: newSwapName,
|
||||
batteryCount: newSwapBatteryCount,
|
||||
swapPrice: newSwapPrice,
|
||||
monthlySubscription: newSwapMonthly,
|
||||
dailySubscription: newSwapDaily,
|
||||
minBatteries: newSwapMin,
|
||||
maxBatteries: newSwapMax,
|
||||
profitSharePercent: newSwapProfit,
|
||||
status: newSwapStatus,
|
||||
description: newSwapDesc
|
||||
};
|
||||
const updatedPlans = [...settings.plans.swapStation, newPlan];
|
||||
setSettings({ ...settings, plans: { ...settings.plans, swapStation: updatedPlans } });
|
||||
setActiveSwapTab(updatedPlans.length - 1);
|
||||
setAddSwapStationPlan(false);
|
||||
setNewSwapName('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 2000);
|
||||
@@ -569,6 +646,7 @@ export default function CompanySettingsPage() {
|
||||
{ id: 'rental', label: 'Rental Policy', icon: FileCheck },
|
||||
{ id: 'plans', label: 'Plan Selection', icon: Package },
|
||||
{ id: 'investment', label: 'Investment Plan', icon: DollarSign },
|
||||
{ id: 'swapstation', label: 'Swap Station Plan', icon: Zap },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -2062,6 +2140,158 @@ export default function CompanySettingsPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(activeTab as string) === 'swapstation' && (
|
||||
<div className="p-6 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold text-slate-800">Swap Station Plans</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between bg-blue-50 border border-blue-200 rounded-xl p-4">
|
||||
<div>
|
||||
<h4 className="font-semibold text-blue-800">Swap Station Plans ({settings.plans.swapStation.length})</h4>
|
||||
<p className="text-sm text-blue-600">Manage swap station plans for operators</p>
|
||||
</div>
|
||||
<button onClick={() => { setAddSwapStationPlan(true); setNewSwapName(''); }} className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
||||
<Plus className="w-4 h-4" /> New Plan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{addSwapStationPlan && (
|
||||
<div className="bg-white rounded-xl border border-blue-300 overflow-hidden">
|
||||
<div className="bg-blue-100 px-4 py-3 border-b border-blue-200 flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold text-blue-800">New Swap Station Plan</h4>
|
||||
<p className="text-sm text-blue-600 mt-1">Fill in the details below</p>
|
||||
</div>
|
||||
<button onClick={() => setAddSwapStationPlan(false)} className="text-blue-600 hover:text-blue-800">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid lg:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Plan Name</label>
|
||||
<input type="text" value={newSwapName} onChange={(e) => setNewSwapName(e.target.value)} placeholder="e.g., Standard Station" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Status</label>
|
||||
<select value={newSwapStatus} onChange={(e) => setNewSwapStatus(e.target.value)} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
||||
<option value="active">Active</option>
|
||||
<option value="paused">Paused</option>
|
||||
<option value="closed">Closed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Battery Count</label>
|
||||
<input type="number" value={newSwapBatteryCount} onChange={(e) => setNewSwapBatteryCount(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Swap Price (৳)</label>
|
||||
<input type="number" value={newSwapPrice} onChange={(e) => setNewSwapPrice(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
||||
<input type="number" value={newSwapMonthly} onChange={(e) => setNewSwapMonthly(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Daily Subscription (৳)</label>
|
||||
<input type="number" value={newSwapDaily} onChange={(e) => setNewSwapDaily(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Min Batteries</label>
|
||||
<input type="number" value={newSwapMin} onChange={(e) => setNewSwapMin(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Max Batteries</label>
|
||||
<input type="number" value={newSwapMax} onChange={(e) => setNewSwapMax(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Profit Share (%)</label>
|
||||
<input type="number" value={newSwapProfit} onChange={(e) => setNewSwapProfit(parseInt(e.target.value))} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="text-sm text-slate-600">Description</label>
|
||||
<textarea value={newSwapDesc} onChange={(e) => setNewSwapDesc(e.target.value)} placeholder="Enter plan description" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
||||
</div>
|
||||
<button onClick={createSwapStationPlan} className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium">Create Plan</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 border-b border-slate-200">
|
||||
{settings.plans.swapStation.map((plan, idx) => (
|
||||
<button key={idx} onClick={() => setActiveSwapTab(idx)} className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${activeSwapTab === idx ? 'border-blue-500 text-blue-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}> {plan.name}</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{settings.plans.swapStation.length > 0 && settings.plans.swapStation.map((plan, idx) => idx === activeSwapTab && (
|
||||
<div key={idx} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
||||
<div className="bg-blue-50 px-4 py-3 border-b border-blue-100 flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold text-blue-800">{plan.name}</h4>
|
||||
<p className="text-sm text-blue-600 mt-1">{plan.description}</p>
|
||||
</div>
|
||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${plan.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>{plan.status}</span>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="grid lg:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Plan Name</label>
|
||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Status</label>
|
||||
<select value={plan.status} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].status = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1">
|
||||
<option value="active">Active</option>
|
||||
<option value="paused">Paused</option>
|
||||
<option value="closed">Closed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Battery Count</label>
|
||||
<input type="number" value={plan.batteryCount} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].batteryCount = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Swap Price (৳)</label>
|
||||
<input type="number" value={plan.swapPrice} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].swapPrice = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Monthly Subscription (৳)</label>
|
||||
<input type="number" value={plan.monthlySubscription} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].monthlySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Daily Subscription (৳)</label>
|
||||
<input type="number" value={plan.dailySubscription} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].dailySubscription = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Min Batteries</label>
|
||||
<input type="number" value={plan.minBatteries} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].minBatteries = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Max Batteries</label>
|
||||
<input type="number" value={plan.maxBatteries} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].maxBatteries = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm text-slate-600">Profit Share (%)</label>
|
||||
<input type="number" value={plan.profitSharePercent} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].profitSharePercent = parseInt(e.target.value); setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="text-sm text-slate-600">Description</label>
|
||||
<textarea value={plan.description} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].description = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" rows={2} />
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end">
|
||||
<button onClick={handleSave} className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium flex items-center gap-2">
|
||||
<Save className="w-4 h-4" /> Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user