refactor: rename FOCO to FICO model and enhance notification system with new templates, metadata, and category filtering.

This commit is contained in:
sazzadulalambd
2026-05-19 20:53:50 +06:00
parent 9442e64a86
commit bcb319ca71
3 changed files with 135 additions and 37 deletions

View File

@@ -49,6 +49,26 @@ const mockUsersList = [
];
const initialNotifications: Notification[] = [
{
id: 'notif-inv-002',
category: 'investor',
priority: 'high',
title: 'New Withdrawal Request',
message: 'Investor Md. Hasan Mahmud (inv1) submitted a withdrawal request of ৳15,000 to bank account Islami Bank Bangladesh Ltd.',
time: '2026-05-19T09:30:00Z',
read: false,
meta: {
link: '/admin/investors/inv1?tab=financial',
actionLabel: 'Review Withdrawal',
details: {
'Investor': 'Md. Hasan Mahmud (inv1)',
'Withdrawal Amount': '৳15,000',
'Method': 'Bank Transfer (Islami Bank)',
'Available Balance': '৳69,250',
'Status': 'Awaiting Verification'
}
}
},
{
id: 'notif-inv-001',
category: 'investor',
@@ -161,7 +181,7 @@ const initialNotifications: Notification[] = [
actionLabel: 'View Investment',
details: {
'Investor': 'Jamal Uddin',
'Plan': '5 Bike FOCO Plan',
'Plan': '5 Bike FICO Plan',
'Base EV Price': '৳180,000 per EV',
'Total Investment': '৳900,000',
'Monthly ROI': '50% share'
@@ -195,9 +215,23 @@ const initialBroadcasts: Broadcast[] = [
}
];
const messageTemplates = [
{ id: 'welcome', type: 'email', name: 'Welcome Email', subject: 'Welcome to JAIBEN Mobility - Your Journey Starts Here!', body: 'Dear {name},\n\nWelcome to JAIBEN Mobility!\n\nWe\'re thrilled to have you join our community of eco-friendly commuters.\n\nBest regards,\nJAIBEN Mobility Team' },
{ id: 'rental_confirmation', type: 'email', name: 'Rental Confirmation', subject: 'Rental Confirmed - Your EV is Ready!', body: 'Dear {name},\n\nYour rental has been confirmed!\n\nBooking Details:\n- Bike: {bike}\n- Plan: {plan}\n- Start Date: {start_date}\n\nEnjoy your ride!\n\nJAIBEN Mobility Team' },
{ id: 'payment_reminder', type: 'email', name: 'Payment Reminder', subject: 'Payment Due Soon - {amount}', body: 'Dear {name},\n\nThis is a friendly reminder that your payment of {amount} is due on {due_date}.\n\nJAIBEN Mobility Team' },
{ id: 'due_notice', type: 'email', name: 'Due Notice', subject: 'Payment Overdue - Action Required', body: 'Dear {name},\n\nYour payment for the rental of {bike} is overdue. Overdue Amount: {amount}.\n\nJAIBEN Mobility Team' },
{ id: 'kyc_verification', type: 'email', name: 'KYC Verification Status', subject: 'KYC Verification Approved', body: 'Dear {name},\n\nYour KYC verification has been Approved. You can now access all features and rent EVs!\n\nJAIBEN Mobility Team' },
{ id: 'damage_report', type: 'email', name: 'Vehicle Damage Report', subject: 'Vehicle Damage Report - {bike}', body: 'Dear {name},\n\nA damage report has been filed for your rented vehicle.\nEstimated Repair Cost: {repair_cost}.\n\nJAIBEN Mobility Team' },
{ id: 'otp', type: 'sms', name: 'OTP Code (SMS)', subject: 'OTP Code Pin', body: 'JAIBEN: Your OTP is {otp}. Valid for 10 mins. Do not share this code.' },
{ id: 'payment_due_sms', type: 'sms', name: 'Payment Due (SMS)', subject: 'Payment Due Alert', body: 'JAIBEN: Payment of {amount} due on {due_date} for your rental. Avoid late fees!' },
{ id: 'battery_low_sms', type: 'sms', name: 'Battery Low Warning (SMS)', subject: 'Battery Low Alert', body: 'JAIBEN: Your bike battery is low. Visit nearest swap station or charge point.' },
{ id: 'kyc_approved_sms', type: 'sms', name: 'KYC Approved (SMS)', subject: 'KYC Approved Alert', body: 'JAIBEN: Great news! Your KYC is verified. You can now rent EVs. Download app to start!' }
];
export default function AdminNotificationsPage() {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [broadcasts, setBroadcasts] = useState<Broadcast[]>([]);
const [selectedTemplateId, setSelectedTemplateId] = useState<string>('');
// Tabs: 'inbox' (System Events) vs 'outbox' (Admin Messaging logs)
const [activeTab, setActiveTab] = useState<'inbox' | 'outbox'>('inbox');
@@ -491,7 +525,7 @@ export default function AdminNotificationsPage() {
category: 'investor',
priority: 'medium',
title: 'Partial Payment Investment Received',
message: 'Investor Karim Hasan submitted 50% deposit for 1 Bike FOCO investment.',
message: 'Investor Karim Hasan submitted 50% deposit for 1 Bike FICO investment.',
time: nowStr,
read: false,
meta: {
@@ -730,17 +764,29 @@ export default function AdminNotificationsPage() {
{ id: 'maintenance', label: 'Vehicle Service' },
{ id: 'swap_station', label: 'Cabinet Network' },
{ id: 'investor', label: 'Investor Ledger' },
].map(cat => (
].map(cat => {
const catCount = cat.id === 'all'
? notifications.length
: notifications.filter(n => n.category === cat.id).length;
return (
<button
key={cat.id}
onClick={() => setCategoryFilter(cat.id)}
className={`w-full px-3 py-2 text-sm font-semibold rounded-lg text-left transition-colors cursor-pointer ${
className={`w-full flex items-center justify-between px-3 py-2 text-sm font-semibold rounded-lg text-left transition-colors cursor-pointer ${
categoryFilter === cat.id ? 'bg-slate-100 text-slate-800' : 'text-slate-600 hover:bg-slate-50'
}`}
>
{cat.label}
<span>{cat.label}</span>
{catCount > 0 && (
<span className={`px-2 py-0.5 rounded-full text-xs font-bold ${
categoryFilter === cat.id ? 'bg-accent text-white' : 'bg-slate-100 text-slate-600 border border-slate-200'
}`}>
{catCount}
</span>
)}
</button>
))}
);
})}
</div>
</div>
)}
@@ -760,25 +806,37 @@ export default function AdminNotificationsPage() {
{activeTab === 'inbox' && (
<div className="flex gap-2 overflow-x-auto pb-1 lg:hidden -mx-4 px-4 scrollbar-none">
{[
{ id: 'all', label: 'All Alert Categories' },
{ id: 'kyc', label: 'KYC verification' },
{ id: 'rental', label: 'Rentals' },
{ id: 'maintenance', label: 'Maintenance' },
{ id: 'swap_station', label: 'Swap Cabinets' },
{ id: 'investor', label: 'Investors' },
].map(cat => (
{ id: 'all', label: 'All Categories' },
{ id: 'kyc', label: 'KYC Verification' },
{ id: 'rental', label: 'Rentals & Fines' },
{ id: 'maintenance', label: 'Vehicle Service' },
{ id: 'swap_station', label: 'Cabinet Network' },
{ id: 'investor', label: 'Investor Ledger' },
].map(cat => {
const catCount = cat.id === 'all'
? notifications.length
: notifications.filter(n => n.category === cat.id).length;
return (
<button
key={cat.id}
onClick={() => setCategoryFilter(cat.id)}
className={`px-4 py-2 rounded-full text-xs font-bold border transition-colors shrink-0 whitespace-nowrap cursor-pointer ${
className={`px-4 py-2 rounded-full text-xs font-bold border transition-colors shrink-0 whitespace-nowrap cursor-pointer flex items-center gap-1.5 ${
categoryFilter === cat.id
? 'bg-accent border-accent text-white shadow-sm'
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
}`}
>
{cat.label}
<span>{cat.label}</span>
{catCount > 0 && (
<span className={`px-1.5 py-0.5 rounded-full text-[10px] font-bold ${
categoryFilter === cat.id ? 'bg-white text-accent' : 'bg-slate-100 text-slate-500'
}`}>
{catCount}
</span>
)}
</button>
))}
);
})}
</div>
)}
@@ -1115,6 +1173,46 @@ export default function AdminNotificationsPage() {
)}
</div>
{/* Template Selector Dropdown */}
<div className="space-y-1">
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider block">Message Template (Optional)</label>
<select
value={selectedTemplateId}
onChange={(e) => {
const templateId = e.target.value;
setSelectedTemplateId(templateId);
if (templateId) {
const template = messageTemplates.find(t => t.id === templateId);
if (template) {
setComposeTitle(template.subject);
setComposeMessage(template.body);
// Automatically check channels based on template type
setComposeChannels(prev => ({
...prev,
email: template.type === 'email',
sms: template.type === 'sms',
push: true // Keep push alert active by default
}));
}
} else {
setComposeTitle('');
setComposeMessage('');
}
}}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white focus:outline-none focus:ring-1 focus:ring-accent"
>
<option value="">-- Select Template --</option>
<option disabled className="font-semibold text-slate-400">Email Templates</option>
{messageTemplates.filter(t => t.type === 'email').map(t => (
<option key={t.id} value={t.id}>&nbsp;&nbsp;{t.name}</option>
))}
<option disabled className="font-semibold text-slate-400">SMS Templates</option>
{messageTemplates.filter(t => t.type === 'sms').map(t => (
<option key={t.id} value={t.id}>&nbsp;&nbsp;{t.name}</option>
))}
</select>
</div>
{/* Subject Title */}
<div className="space-y-1">
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider block">3. Message Header Title</label>

View File

@@ -11,7 +11,7 @@ const inter = Inter({
export const metadata: Metadata = {
title: "JAIBEN Mobility - EV Rental Platform",
description: "JAIBEN Mobility Ltd - EV Rental, Rent-to-Own, Share EV, FOCO Investor",
description: "JAIBEN Mobility Ltd - EV Rental, Rent-to-Own, Share EV, FICO Investor",
manifest: "/manifest.json",
appleWebApp: {
capable: true,

View File

@@ -77,7 +77,7 @@ export default function LandingPage() {
</h2>
<p className="text-slate-400 text-lg lg:text-xl max-w-2xl mx-auto">
Rent, Rent-to-Own, or Invest in EVs. Join Bangladesh&apos;s fastest growing
electric mobility ecosystem with FOCO model for investors.
electric mobility ecosystem with FICO model for investors.
</p>
</div>
@@ -106,7 +106,7 @@ export default function LandingPage() {
<Wallet className="w-6 h-6 text-green-500" />
</div>
<h3 className="text-xl font-bold text-white mb-1">Investor</h3>
<p className="text-slate-400 text-sm mb-3">FOCO model with guaranteed returns</p>
<p className="text-slate-400 text-sm mb-3">FICO model with guaranteed returns</p>
<span className="text-green-500 text-sm font-medium flex items-center gap-1">
Login as Investor <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</span>