feat: add battery management functionality with assignment, registration, and investment tracking to investor dashboard
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Settings, Package, Palette, Link2, Mail, Monitor, FileCheck, DollarSign, Zap, Users, Plus, X, Save, Pencil, Trash2, FileText, Battery } from 'lucide-react';
|
import { Settings, Package, Palette, Link2, Mail, Monitor, FileCheck, DollarSign, Zap, Users, Plus, X, Save, Pencil, Trash2, FileText, Battery } from 'lucide-react';
|
||||||
import RichTextEditor from '@/components/RichTextEditor';
|
import RichTextEditor from '@/components/RichTextEditor';
|
||||||
import GeneralSettings from './components/GeneralSettings';
|
import GeneralSettings from './components/GeneralSettings';
|
||||||
@@ -871,6 +871,25 @@ const initialSettings: CompanySettings = {
|
|||||||
|
|
||||||
export default function CompanySettingsPage() {
|
export default function CompanySettingsPage() {
|
||||||
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
const [settings, setSettings] = useState<CompanySettings>(initialSettings);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const stored = localStorage.getItem('companySettings');
|
||||||
|
if (stored) {
|
||||||
|
try {
|
||||||
|
setSettings(JSON.parse(stored));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined' && settings !== initialSettings) {
|
||||||
|
localStorage.setItem('companySettings', JSON.stringify(settings));
|
||||||
|
}
|
||||||
|
}, [settings]);
|
||||||
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'companyPolicy' | 'plans' | 'investment' | 'batteryinvestment' | 'swapstation' | 'riderrequest' | 'templates'>('general');
|
const [activeTab, setActiveTab] = useState<'general' | 'branding' | 'social' | 'integration' | 'landing' | 'kyc' | 'parts' | 'companyPolicy' | 'plans' | 'investment' | 'batteryinvestment' | 'swapstation' | 'riderrequest' | 'templates'>('general');
|
||||||
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rentalType'>('investor');
|
const [activeMasterTab, setActiveMasterTab] = useState<'investor' | 'merchant' | 'swapstation' | 'rentalType'>('investor');
|
||||||
const [activeRentalTypeTab, setActiveRentalTypeTab] = useState<'single' | 'shared' | 'renttoown'>('single');
|
const [activeRentalTypeTab, setActiveRentalTypeTab] = useState<'single' | 'shared' | 'renttoown'>('single');
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Target, Plus, Zap, ChevronRight, ArrowRight, Edit, Trash2, Eye, TrendingUp, X, CreditCard } from 'lucide-react';
|
import { Target, Plus, Zap, ChevronRight, ArrowRight, Edit, Trash2, Eye, TrendingUp, X, CreditCard, Battery } from 'lucide-react';
|
||||||
import { investors } from '@/data/mockData';
|
import { investors } from '@/data/mockData';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import InvestorNotification from '@/components/InvestorNotification';
|
import InvestorNotification from '@/components/InvestorNotification';
|
||||||
@@ -12,14 +12,20 @@ export default function MyInvestmentsPage() {
|
|||||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||||
const [selectedTemplate, setSelectedTemplate] = useState<any>(null);
|
const [selectedTemplate, setSelectedTemplate] = useState<any>(null);
|
||||||
const [newInvestment, setNewInvestment] = useState({
|
const [newInvestment, setNewInvestment] = useState({
|
||||||
planName: '', planType: 'gold', totalInvestment: 0, initialPayment: 0, paymentType: 'full',
|
planName: '', planType: 'gold', assetType: 'bike', totalInvestment: 0, initialPayment: 0, paymentType: 'full',
|
||||||
startDate: '', endDate: '', paymentMethod: 'bank', transactionReference: '', notes: ''
|
startDate: '', endDate: '', paymentMethod: 'bank', transactionReference: '', notes: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const PLAN_TEMPLATES = [
|
const PLAN_TEMPLATES = [
|
||||||
{ id: '1bike', name: '1 Bike Plan', tier: 'Standard', evBasePrice: 200000, minQuantity: 1, duration: 12, maxInvestment: 1000000 },
|
{ id: '1bike', name: '1 Bike Plan', tier: 'Standard', evBasePrice: 200000, minQuantity: 1, duration: 12, maxInvestment: 1000000, type: 'bike' },
|
||||||
{ id: '5bike', name: '5 Bike Plan', tier: 'Premium', evBasePrice: 180000, minQuantity: 5, duration: 24, maxInvestment: 5000000 },
|
{ id: '5bike', name: '5 Bike Plan', tier: 'Premium', evBasePrice: 180000, minQuantity: 5, duration: 24, maxInvestment: 5000000, type: 'bike' },
|
||||||
{ id: '10bike', name: '10 Bike Plan', tier: 'Enterprise', evBasePrice: 170000, minQuantity: 10, duration: 36, maxInvestment: 10000000 },
|
{ id: '10bike', name: '10 Bike Plan', tier: 'Enterprise', evBasePrice: 170000, minQuantity: 10, duration: 36, maxInvestment: 10000000, type: 'bike' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const BATTERY_TEMPLATES = [
|
||||||
|
{ id: '1battery', name: '1 Battery Pack Plan', tier: 'Standard', evBasePrice: 45000, minQuantity: 1, duration: 12, maxInvestment: 500000, type: 'battery' },
|
||||||
|
{ id: '5battery', name: '5 Battery Pack Plan', tier: 'Premium', evBasePrice: 42000, minQuantity: 5, duration: 18, maxInvestment: 2000000, type: 'battery' },
|
||||||
|
{ id: '10battery', name: '10 Battery Pack Fleet', tier: 'Enterprise', evBasePrice: 40000, minQuantity: 10, duration: 24, maxInvestment: 5000000, type: 'battery' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const planConfig: Record<string, { bg: string; border: string; icon: string }> = {
|
const planConfig: Record<string, { bg: string; border: string; icon: string }> = {
|
||||||
@@ -44,7 +50,7 @@ export default function MyInvestmentsPage() {
|
|||||||
);
|
);
|
||||||
setShowCreateModal(false);
|
setShowCreateModal(false);
|
||||||
setSelectedTemplate(null);
|
setSelectedTemplate(null);
|
||||||
setNewInvestment({ planName: '', planType: 'gold', totalInvestment: 0, initialPayment: 0, paymentType: 'full', startDate: '', endDate: '', paymentMethod: 'bank', transactionReference: '', notes: '' });
|
setNewInvestment({ planName: '', planType: 'gold', assetType: 'bike', totalInvestment: 0, initialPayment: 0, paymentType: 'full', startDate: '', endDate: '', paymentMethod: 'bank', transactionReference: '', notes: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -85,7 +91,14 @@ export default function MyInvestmentsPage() {
|
|||||||
<div key={inv.id} className={`bg-white rounded-xl border ${style.border} overflow-hidden`}>
|
<div key={inv.id} className={`bg-white rounded-xl border ${style.border} overflow-hidden`}>
|
||||||
<div className={`${style.bg} p-4 flex items-center justify-between`}>
|
<div className={`${style.bg} p-4 flex items-center justify-between`}>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-slate-800">{inv.planName}</h4>
|
<h4 className="font-bold text-slate-800 flex items-center gap-1.5">
|
||||||
|
{inv.planName.toLowerCase().includes('battery') ? (
|
||||||
|
<Battery className="w-4 h-4 text-emerald-600 animate-pulse flex-shrink-0" />
|
||||||
|
) : (
|
||||||
|
<Zap className="w-4 h-4 text-investor flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
{inv.planName}
|
||||||
|
</h4>
|
||||||
<p className="text-sm text-slate-500 capitalize">{inv.planType} Plan</p>
|
<p className="text-sm text-slate-500 capitalize">{inv.planType} Plan</p>
|
||||||
</div>
|
</div>
|
||||||
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${inv.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-200 text-slate-600'}`}>
|
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${inv.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-slate-200 text-slate-600'}`}>
|
||||||
@@ -166,10 +179,41 @@ export default function MyInvestmentsPage() {
|
|||||||
<div className="p-5 overflow-y-auto flex-1 space-y-5">
|
<div className="p-5 overflow-y-auto flex-1 space-y-5">
|
||||||
{!selectedTemplate ? (
|
{!selectedTemplate ? (
|
||||||
<>
|
<>
|
||||||
|
{/* Category Selector */}
|
||||||
|
<div>
|
||||||
|
<label className="text-sm font-semibold text-slate-700 mb-2 block">Choose Investment Asset Type *</label>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setNewInvestment({ ...newInvestment, assetType: 'bike' });
|
||||||
|
}}
|
||||||
|
className={`py-3 px-4 rounded-xl border-2 flex items-center justify-center gap-2 font-bold text-sm transition-all ${
|
||||||
|
newInvestment.assetType === 'bike'
|
||||||
|
? 'bg-investor/10 border-investor text-investor shadow-sm'
|
||||||
|
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Zap className="w-4 h-4 text-investor animate-pulse" /> Bike Investment Plans
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setNewInvestment({ ...newInvestment, assetType: 'battery' });
|
||||||
|
}}
|
||||||
|
className={`py-3 px-4 rounded-xl border-2 flex items-center justify-center gap-2 font-bold text-sm transition-all ${
|
||||||
|
newInvestment.assetType === 'battery'
|
||||||
|
? 'bg-emerald-100 border-emerald-500 text-emerald-800 shadow-sm'
|
||||||
|
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Battery className="w-4 h-4 text-emerald-600 animate-pulse" /> Battery Investment Plans
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-slate-600 mb-2 block">Select Plan Template</label>
|
<label className="text-sm font-medium text-slate-600 mb-2 block">Select Plan Template</label>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||||
{PLAN_TEMPLATES.map(plan => (
|
{(newInvestment.assetType === 'battery' ? BATTERY_TEMPLATES : PLAN_TEMPLATES).map(plan => (
|
||||||
<button
|
<button
|
||||||
key={plan.id}
|
key={plan.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -182,10 +226,10 @@ export default function MyInvestmentsPage() {
|
|||||||
startDate: new Date().toISOString().split('T')[0],
|
startDate: new Date().toISOString().split('T')[0],
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="p-4 rounded-lg border-2 border-slate-200 hover:border-investor/50 text-left transition-all hover:bg-slate-50"
|
className={`p-4 rounded-lg border-2 text-left transition-all hover:bg-slate-50 ${newInvestment.assetType === 'battery' ? 'border-slate-200 hover:border-emerald-500' : 'border-slate-200 hover:border-investor/50'}`}
|
||||||
>
|
>
|
||||||
<p className="font-semibold text-slate-800">{plan.name}</p>
|
<p className="font-semibold text-slate-800">{plan.name}</p>
|
||||||
<p className="text-xs text-slate-500 mt-1">৳{plan.evBasePrice.toLocaleString()} × {plan.minQuantity} bikes</p>
|
<p className="text-xs text-slate-500 mt-1">৳{plan.evBasePrice.toLocaleString()} × {plan.minQuantity} {newInvestment.assetType}(s)</p>
|
||||||
<p className="text-sm text-slate-600 mt-1">Duration: {plan.duration} months</p>
|
<p className="text-sm text-slate-600 mt-1">Duration: {plan.duration} months</p>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@@ -228,11 +272,11 @@ export default function MyInvestmentsPage() {
|
|||||||
|
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-slate-600 mb-1 block">EV Base Price (৳)</label>
|
<label className="text-sm font-medium text-slate-600 mb-1 block">{selectedTemplate.type === 'battery' ? 'Battery Pack Cost (৳)' : 'EV Base Price (৳)'}</label>
|
||||||
<input type="number" value={selectedTemplate.evBasePrice} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
<input type="number" value={selectedTemplate.evBasePrice} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-slate-600 mb-1 block">Minimum Quantity</label>
|
<label className="text-sm font-medium text-slate-600 mb-1 block">Minimum Quantity ({selectedTemplate.type === 'battery' ? 'Packs' : 'Bikes'})</label>
|
||||||
<input type="number" value={selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
<input type="number" value={selectedTemplate.minQuantity} disabled className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-slate-50 cursor-not-allowed" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user