feat: add withdraw category to notifications and improve UI sidebar layout

This commit is contained in:
sazzadulalambd
2026-05-19 21:02:57 +06:00
parent bcb319ca71
commit 3933141140

View File

@@ -11,7 +11,7 @@ import toast from 'react-hot-toast';
interface Notification { interface Notification {
id: string; id: string;
category: 'kyc' | 'rental' | 'maintenance' | 'swap_station' | 'investor' | 'system'; category: 'kyc' | 'rental' | 'maintenance' | 'swap_station' | 'investor' | 'system' | 'withdraw';
priority: 'low' | 'medium' | 'high' | 'critical'; priority: 'low' | 'medium' | 'high' | 'critical';
title: string; title: string;
message: string; message: string;
@@ -51,7 +51,7 @@ const mockUsersList = [
const initialNotifications: Notification[] = [ const initialNotifications: Notification[] = [
{ {
id: 'notif-inv-002', id: 'notif-inv-002',
category: 'investor', category: 'withdraw',
priority: 'high', priority: 'high',
title: 'New Withdrawal Request', title: 'New Withdrawal Request',
message: 'Investor Md. Hasan Mahmud (inv1) submitted a withdrawal request of ৳15,000 to bank account Islami Bank Bangladesh Ltd.', message: 'Investor Md. Hasan Mahmud (inv1) submitted a withdrawal request of ৳15,000 to bank account Islami Bank Bangladesh Ltd.',
@@ -232,15 +232,15 @@ export default function AdminNotificationsPage() {
const [notifications, setNotifications] = useState<Notification[]>([]); const [notifications, setNotifications] = useState<Notification[]>([]);
const [broadcasts, setBroadcasts] = useState<Broadcast[]>([]); const [broadcasts, setBroadcasts] = useState<Broadcast[]>([]);
const [selectedTemplateId, setSelectedTemplateId] = useState<string>(''); const [selectedTemplateId, setSelectedTemplateId] = useState<string>('');
// Tabs: 'inbox' (System Events) vs 'outbox' (Admin Messaging logs) // Tabs: 'inbox' (System Events) vs 'outbox' (Admin Messaging logs)
const [activeTab, setActiveTab] = useState<'inbox' | 'outbox'>('inbox'); const [activeTab, setActiveTab] = useState<'inbox' | 'outbox'>('inbox');
// Filters // Filters
const [filter, setFilter] = useState<string>('all'); const [filter, setFilter] = useState<string>('all');
const [categoryFilter, setCategoryFilter] = useState<string>('all'); const [categoryFilter, setCategoryFilter] = useState<string>('all');
const [searchQuery, setSearchQuery] = useState<string>(''); const [searchQuery, setSearchQuery] = useState<string>('');
// Dialog/Modals State // Dialog/Modals State
const [selectedNotif, setSelectedNotif] = useState<Notification | null>(null); const [selectedNotif, setSelectedNotif] = useState<Notification | null>(null);
const [selectedBroadcast, setSelectedBroadcast] = useState<Broadcast | null>(null); const [selectedBroadcast, setSelectedBroadcast] = useState<Broadcast | null>(null);
@@ -627,7 +627,7 @@ export default function AdminNotificationsPage() {
> >
<Send className="w-4 h-4" /> Compose Broadcast <Send className="w-4 h-4" /> Compose Broadcast
</button> </button>
<button <button
onClick={() => setShowSimulatorDrawer(true)} onClick={() => setShowSimulatorDrawer(true)}
className="flex items-center justify-center gap-2 py-2.5 px-4 bg-slate-900 hover:bg-slate-800 text-white rounded-xl text-sm font-semibold transition-all cursor-pointer grow sm:grow-0" className="flex items-center justify-center gap-2 py-2.5 px-4 bg-slate-900 hover:bg-slate-800 text-white rounded-xl text-sm font-semibold transition-all cursor-pointer grow sm:grow-0"
@@ -660,9 +660,8 @@ export default function AdminNotificationsPage() {
<div className="flex border-b border-slate-200"> <div className="flex border-b border-slate-200">
<button <button
onClick={() => { setActiveTab('inbox'); setFilter('all'); }} onClick={() => { setActiveTab('inbox'); setFilter('all'); }}
className={`py-3 px-6 text-sm font-bold border-b-2 transition-all flex items-center gap-2 ${ className={`py-3 px-6 text-sm font-bold border-b-2 transition-all flex items-center gap-2 ${activeTab === 'inbox' ? 'border-accent text-accent' : 'border-transparent text-slate-500 hover:text-slate-800'
activeTab === 'inbox' ? 'border-accent text-accent' : 'border-transparent text-slate-500 hover:text-slate-800' }`}
}`}
> >
<Bell className="w-4 h-4" /> Alert Inbox ({notifications.length}) <Bell className="w-4 h-4" /> Alert Inbox ({notifications.length})
{unreadCount > 0 && ( {unreadCount > 0 && (
@@ -671,9 +670,8 @@ export default function AdminNotificationsPage() {
</button> </button>
<button <button
onClick={() => { setActiveTab('outbox'); setFilter('all'); }} onClick={() => { setActiveTab('outbox'); setFilter('all'); }}
className={`py-3 px-6 text-sm font-bold border-b-2 transition-all flex items-center gap-2 ${ className={`py-3 px-6 text-sm font-bold border-b-2 transition-all flex items-center gap-2 ${activeTab === 'outbox' ? 'border-accent text-accent' : 'border-transparent text-slate-500 hover:text-slate-800'
activeTab === 'outbox' ? 'border-accent text-accent' : 'border-transparent text-slate-500 hover:text-slate-800' }`}
}`}
> >
<Send className="w-4 h-4" /> Broadcast History ({broadcasts.length}) <Send className="w-4 h-4" /> Broadcast History ({broadcasts.length})
</button> </button>
@@ -723,12 +721,12 @@ export default function AdminNotificationsPage() {
{/* Main Grid Layout */} {/* Main Grid Layout */}
<div className="grid lg:grid-cols-4 gap-6 items-start"> <div className="grid lg:grid-cols-4 gap-6 items-start">
{/* Desktop Sidebar Filters: Kept for Desktop views */} {/* Desktop Sidebar Filters: Kept for Desktop views */}
<div className="hidden lg:block lg:col-span-1 space-y-4"> <div className="hidden lg:block lg:col-span-1 space-y-4 sticky top-6">
<div className="bg-white p-4 rounded-xl border border-slate-100 shadow-sm space-y-4"> <div className="bg-white p-4 rounded-xl border border-slate-100 shadow-sm space-y-4">
<h3 className="font-bold text-slate-700 flex items-center gap-2"><Filter className="w-4 h-4 text-accent" /> Filter Inbox</h3> <h3 className="font-bold text-slate-700 flex items-center gap-2"><Filter className="w-4 h-4 text-accent" /> Filter Inbox</h3>
{activeTab === 'inbox' && ( {activeTab === 'inbox' && (
<div className="space-y-1"> <div className="space-y-1">
{[ {[
@@ -738,9 +736,8 @@ export default function AdminNotificationsPage() {
<button <button
key={f.id} key={f.id}
onClick={() => setFilter(f.id)} onClick={() => setFilter(f.id)}
className={`w-full flex items-center justify-between 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 ${filter === f.id ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50'
filter === f.id ? 'bg-accent text-white' : 'text-slate-600 hover:bg-slate-50' }`}
}`}
> >
{f.label} {f.label}
{f.id === 'unread' && unreadCount > 0 && ( {f.id === 'unread' && unreadCount > 0 && (
@@ -764,23 +761,22 @@ export default function AdminNotificationsPage() {
{ id: 'maintenance', label: 'Vehicle Service' }, { id: 'maintenance', label: 'Vehicle Service' },
{ id: 'swap_station', label: 'Cabinet Network' }, { id: 'swap_station', label: 'Cabinet Network' },
{ id: 'investor', label: 'Investor Ledger' }, { id: 'investor', label: 'Investor Ledger' },
{ id: 'withdraw', label: 'Withdrawal Requests' },
].map(cat => { ].map(cat => {
const catCount = cat.id === 'all' const catCount = cat.id === 'all'
? notifications.length ? notifications.length
: notifications.filter(n => n.category === cat.id).length; : notifications.filter(n => n.category === cat.id).length;
return ( return (
<button <button
key={cat.id} key={cat.id}
onClick={() => setCategoryFilter(cat.id)} onClick={() => setCategoryFilter(cat.id)}
className={`w-full flex items-center justify-between 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'
categoryFilter === cat.id ? 'bg-slate-100 text-slate-800' : 'text-slate-600 hover:bg-slate-50' }`}
}`}
> >
<span>{cat.label}</span> <span>{cat.label}</span>
{catCount > 0 && ( {catCount > 0 && (
<span className={`px-2 py-0.5 rounded-full text-xs font-bold ${ <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'
categoryFilter === cat.id ? 'bg-accent text-white' : 'bg-slate-100 text-slate-600 border border-slate-200' }`}>
}`}>
{catCount} {catCount}
</span> </span>
)} )}
@@ -801,7 +797,7 @@ export default function AdminNotificationsPage() {
{/* Dynamic Main Listing Section */} {/* Dynamic Main Listing Section */}
<div className="lg:col-span-3 space-y-4"> <div className="lg:col-span-3 space-y-4">
{/* Responsive scrollable Category Pills on Mobile/Tablet */} {/* Responsive scrollable Category Pills on Mobile/Tablet */}
{activeTab === 'inbox' && ( {activeTab === 'inbox' && (
<div className="flex gap-2 overflow-x-auto pb-1 lg:hidden -mx-4 px-4 scrollbar-none"> <div className="flex gap-2 overflow-x-auto pb-1 lg:hidden -mx-4 px-4 scrollbar-none">
@@ -812,25 +808,24 @@ export default function AdminNotificationsPage() {
{ id: 'maintenance', label: 'Vehicle Service' }, { id: 'maintenance', label: 'Vehicle Service' },
{ id: 'swap_station', label: 'Cabinet Network' }, { id: 'swap_station', label: 'Cabinet Network' },
{ id: 'investor', label: 'Investor Ledger' }, { id: 'investor', label: 'Investor Ledger' },
{ id: 'withdraw', label: 'Withdrawal Requests' },
].map(cat => { ].map(cat => {
const catCount = cat.id === 'all' const catCount = cat.id === 'all'
? notifications.length ? notifications.length
: notifications.filter(n => n.category === cat.id).length; : notifications.filter(n => n.category === cat.id).length;
return ( return (
<button <button
key={cat.id} key={cat.id}
onClick={() => setCategoryFilter(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 flex items-center gap-1.5 ${ 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
categoryFilter === cat.id
? 'bg-accent border-accent text-white shadow-sm' ? 'bg-accent border-accent text-white shadow-sm'
: 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
}`} }`}
> >
<span>{cat.label}</span> <span>{cat.label}</span>
{catCount > 0 && ( {catCount > 0 && (
<span className={`px-1.5 py-0.5 rounded-full text-[10px] font-bold ${ <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'
categoryFilter === cat.id ? 'bg-white text-accent' : 'bg-slate-100 text-slate-500' }`}>
}`}>
{catCount} {catCount}
</span> </span>
)} )}
@@ -854,7 +849,7 @@ export default function AdminNotificationsPage() {
{/* List items Container */} {/* List items Container */}
<div className="bg-white rounded-xl border border-slate-100 shadow-sm overflow-hidden divide-y divide-slate-100"> <div className="bg-white rounded-xl border border-slate-100 shadow-sm overflow-hidden divide-y divide-slate-100">
{/* ALERT INBOX TAB VIEW */} {/* ALERT INBOX TAB VIEW */}
{activeTab === 'inbox' && ( {activeTab === 'inbox' && (
filteredNotifs.length === 0 ? ( filteredNotifs.length === 0 ? (
@@ -867,9 +862,8 @@ export default function AdminNotificationsPage() {
filteredNotifs.map(notif => ( filteredNotifs.map(notif => (
<div <div
key={notif.id} key={notif.id}
className={`p-4 flex gap-4 transition-colors relative hover:bg-slate-50/50 ${ className={`p-4 flex gap-4 transition-colors relative hover:bg-slate-50/50 ${!notif.read ? 'bg-accent-light/10 border-l-4 border-accent' : ''
!notif.read ? 'bg-accent-light/10 border-l-4 border-accent' : '' }`}
}`}
> >
<div className="flex-1 space-y-1.5 min-w-0 cursor-pointer" onClick={() => setSelectedNotif(notif)}> <div className="flex-1 space-y-1.5 min-w-0 cursor-pointer" onClick={() => setSelectedNotif(notif)}>
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
@@ -886,7 +880,7 @@ export default function AdminNotificationsPage() {
</h4> </h4>
<p className="text-xs text-slate-600 leading-relaxed truncate">{notif.message}</p> <p className="text-xs text-slate-600 leading-relaxed truncate">{notif.message}</p>
</div> </div>
<div className="flex items-center gap-0.5 sm:gap-1 self-center shrink-0"> <div className="flex items-center gap-0.5 sm:gap-1 self-center shrink-0">
<button <button
onClick={() => toggleReadStatus(notif.id)} onClick={() => toggleReadStatus(notif.id)}
@@ -927,7 +921,7 @@ export default function AdminNotificationsPage() {
<span className="inline-flex items-center gap-1 px-2.5 py-1 bg-accent/5 border border-accent/20 rounded-full text-xs font-bold text-accent"> <span className="inline-flex items-center gap-1 px-2.5 py-1 bg-accent/5 border border-accent/20 rounded-full text-xs font-bold text-accent">
<Send className="w-3 h-3" /> Broadcast Log <Send className="w-3 h-3" /> Broadcast Log
</span> </span>
{/* Selected Channels Icons */} {/* Selected Channels Icons */}
<div className="flex items-center gap-1.5 text-slate-400"> <div className="flex items-center gap-1.5 text-slate-400">
{broad.channels.includes('email') && <span title="Email dispatched"><Mail className="w-3.5 h-3.5 text-blue-500" /></span>} {broad.channels.includes('email') && <span title="Email dispatched"><Mail className="w-3.5 h-3.5 text-blue-500" /></span>}
@@ -936,9 +930,8 @@ export default function AdminNotificationsPage() {
</div> </div>
{/* Status tag */} {/* Status tag */}
<span className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider ${ <span className={`px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider ${broad.status === 'scheduled' ? 'bg-amber-100 text-amber-800' : 'bg-green-100 text-green-800'
broad.status === 'scheduled' ? 'bg-amber-100 text-amber-800' : 'bg-green-100 text-green-800' }`}>
}`}>
{broad.status} {broad.status}
</span> </span>
@@ -949,13 +942,13 @@ export default function AdminNotificationsPage() {
<h4 className="text-sm font-bold text-slate-800">{broad.title}</h4> <h4 className="text-sm font-bold text-slate-800">{broad.title}</h4>
<p className="text-xs text-slate-600 truncate">{broad.message}</p> <p className="text-xs text-slate-600 truncate">{broad.message}</p>
<div className="flex items-center gap-4 text-[10px] text-slate-400 font-medium"> <div className="flex items-center gap-4 text-[10px] text-slate-400 font-medium">
<span>Target: <strong className="text-slate-600 font-bold">{broad.targetValue}</strong></span> <span>Target: <strong className="text-slate-600 font-bold">{broad.targetValue}</strong></span>
<span>Delivered: <strong className="text-slate-600 font-bold">{broad.recipientCount} accounts</strong></span> <span>Delivered: <strong className="text-slate-600 font-bold">{broad.recipientCount} accounts</strong></span>
</div> </div>
</div> </div>
<div className="flex items-center shrink-0"> <div className="flex items-center shrink-0">
<button <button
onClick={() => deleteBroadcast(broad.id)} onClick={() => deleteBroadcast(broad.id)}
@@ -977,7 +970,7 @@ export default function AdminNotificationsPage() {
{showComposeModal && ( {showComposeModal && (
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 overflow-y-auto"> <div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 overflow-y-auto">
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-xl overflow-hidden border border-slate-100 flex flex-col my-8"> <div className="bg-white rounded-2xl shadow-2xl w-full max-w-xl overflow-hidden border border-slate-100 flex flex-col my-8">
{/* Modal Header */} {/* Modal Header */}
<div className="p-4 bg-gradient-to-r from-accent to-accent-dark text-white flex items-center justify-between"> <div className="p-4 bg-gradient-to-r from-accent to-accent-dark text-white flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -994,7 +987,7 @@ export default function AdminNotificationsPage() {
{/* Modal Form */} {/* Modal Form */}
<form onSubmit={handleSendBroadcast} className="p-6 space-y-5 overflow-y-auto max-h-[75vh]"> <form onSubmit={handleSendBroadcast} className="p-6 space-y-5 overflow-y-auto max-h-[75vh]">
{/* Channel Selector Checkboxes */} {/* Channel Selector Checkboxes */}
<div className="space-y-2"> <div className="space-y-2">
<label className="text-xs font-bold text-slate-700 uppercase tracking-wider block">1. Select Dispatch Channel(s)</label> <label className="text-xs font-bold text-slate-700 uppercase tracking-wider block">1. Select Dispatch Channel(s)</label>
@@ -1010,9 +1003,8 @@ export default function AdminNotificationsPage() {
key={ch.id} key={ch.id}
type="button" type="button"
onClick={() => setComposeChannels(prev => ({ ...prev, [ch.id]: !enabled }))} onClick={() => setComposeChannels(prev => ({ ...prev, [ch.id]: !enabled }))}
className={`flex flex-col items-center justify-center p-3 rounded-xl border-2 transition-all cursor-pointer text-center ${ className={`flex flex-col items-center justify-center p-3 rounded-xl border-2 transition-all cursor-pointer text-center ${enabled ? 'border-accent bg-accent-light/10 text-accent font-bold scale-[1.02]' : 'border-slate-200 text-slate-500 bg-white hover:border-slate-300'
enabled ? 'border-accent bg-accent-light/10 text-accent font-bold scale-[1.02]' : 'border-slate-200 text-slate-500 bg-white hover:border-slate-300' }`}
}`}
> >
<ch.icon className={`w-5 h-5 mb-1.5 ${enabled ? 'text-accent' : 'text-slate-400'}`} /> <ch.icon className={`w-5 h-5 mb-1.5 ${enabled ? 'text-accent' : 'text-slate-400'}`} />
<span className="text-[10px] tracking-tight">{ch.label}</span> <span className="text-[10px] tracking-tight">{ch.label}</span>
@@ -1035,9 +1027,8 @@ export default function AdminNotificationsPage() {
key={t.id} key={t.id}
type="button" type="button"
onClick={() => setComposeTargetType(t.id as any)} onClick={() => setComposeTargetType(t.id as any)}
className={`py-2 px-3 text-xs font-semibold rounded-lg border text-center transition-all cursor-pointer ${ className={`py-2 px-3 text-xs font-semibold rounded-lg border text-center transition-all cursor-pointer ${composeTargetType === t.id ? 'bg-slate-900 border-slate-900 text-white' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50'
composeTargetType === t.id ? 'bg-slate-900 border-slate-900 text-white' : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50' }`}
}`}
> >
{t.label} {t.label}
</button> </button>
@@ -1146,9 +1137,8 @@ export default function AdminNotificationsPage() {
isSelected ? prev.filter(id => id !== usr.id) : [...prev, usr.id] isSelected ? prev.filter(id => id !== usr.id) : [...prev, usr.id]
); );
}} }}
className={`flex items-center justify-between p-2 text-xs cursor-pointer hover:bg-slate-50 transition-colors ${ className={`flex items-center justify-between p-2 text-xs cursor-pointer hover:bg-slate-50 transition-colors ${isSelected ? 'bg-accent/5' : ''
isSelected ? 'bg-accent/5' : '' }`}
}`}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-6 h-6 rounded-full bg-slate-100 flex items-center justify-center font-bold text-slate-600 text-[10px]"> <div className="w-6 h-6 rounded-full bg-slate-100 flex items-center justify-center font-bold text-slate-600 text-[10px]">
@@ -1162,7 +1152,7 @@ export default function AdminNotificationsPage() {
<input <input
type="checkbox" type="checkbox"
checked={isSelected} checked={isSelected}
onChange={() => {}} // toggled by parent div onClick onChange={() => { }} // toggled by parent div onClick
className="w-3.5 h-3.5 accent-accent" className="w-3.5 h-3.5 accent-accent"
/> />
</div> </div>
@@ -1251,7 +1241,7 @@ export default function AdminNotificationsPage() {
/> />
<span className="text-xs font-bold text-slate-700 uppercase tracking-wider">Schedule for later</span> <span className="text-xs font-bold text-slate-700 uppercase tracking-wider">Schedule for later</span>
</label> </label>
{composeIsScheduled && ( {composeIsScheduled && (
<div className="grid grid-cols-2 gap-3 p-3 bg-slate-50 border border-slate-200 rounded-xl animate-fadeIn"> <div className="grid grid-cols-2 gap-3 p-3 bg-slate-50 border border-slate-200 rounded-xl animate-fadeIn">
<div> <div>
@@ -1331,7 +1321,7 @@ export default function AdminNotificationsPage() {
<X className="w-5 h-5" /> <X className="w-5 h-5" />
</button> </button>
</div> </div>
<div className="p-6 overflow-y-auto space-y-6 flex-1"> <div className="p-6 overflow-y-auto space-y-6 flex-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{getCategoryBadge(selectedNotif.category)} {getCategoryBadge(selectedNotif.category)}
@@ -1408,9 +1398,9 @@ export default function AdminNotificationsPage() {
<X className="w-5 h-5" /> <X className="w-5 h-5" />
</button> </button>
</div> </div>
<div className="p-6 overflow-y-auto space-y-6 flex-1"> <div className="p-6 overflow-y-auto space-y-6 flex-1">
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
<span className="inline-flex items-center gap-1 px-2.5 py-1 bg-accent/5 border border-accent/20 rounded-full text-xs font-bold text-accent"> <span className="inline-flex items-center gap-1 px-2.5 py-1 bg-accent/5 border border-accent/20 rounded-full text-xs font-bold text-accent">
<Send className="w-3 h-3" /> Dispatched Announcement <Send className="w-3 h-3" /> Dispatched Announcement
@@ -1481,7 +1471,7 @@ export default function AdminNotificationsPage() {
</button> </button>
</div> </div>
<p className="text-xs text-slate-400">Simulate real-time external events to verify database pipelines, toaster notifications, and dashboard stats.</p> <p className="text-xs text-slate-400">Simulate real-time external events to verify database pipelines, toaster notifications, and dashboard stats.</p>
<div className="space-y-3 pt-2"> <div className="space-y-3 pt-2">
{[ {[
{ id: 'kyc', label: 'Simulate Biker KYC Upload', desc: 'Adds walker-in application event', color: 'text-blue-400' }, { id: 'kyc', label: 'Simulate Biker KYC Upload', desc: 'Adds walker-in application event', color: 'text-blue-400' },