import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { LogOut, Users, ShoppingBag, Trash2, Ban, UserCheck, RefreshCw, LifeBuoy } from 'lucide-react'; export function AdminDashboard() { const [activeTab, setActiveTab] = useState('users'); const [users, setUsers] = useState([]); const [listings, setListings] = useState([]); const [supportTickets, setSupportTickets] = useState([]); const [stats, setStats] = useState({ pendingSupport: 0 }); const [loading, setLoading] = useState(true); const [replyingTo, setReplyingTo] = useState(null); const [replyText, setReplyText] = useState(''); const navigate = useNavigate(); const token = sessionStorage.getItem('adminToken'); useEffect(() => { if (!token) { navigate('/admin'); return; } fetchData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeTab, token, navigate]); useEffect(() => { if (!token) return; import('../../socket').then(({ socket }) => { const handler = () => { if (activeTab === 'support') { fetchData(); // This also fetches stats } else { fetch(`/api/admin/stats`, { headers: { 'x-admin-key': token } }).then(r => r.json()).then(data => setStats(data)).catch(()=>{}); } }; socket.on('support-ticket-updated', handler); return () => socket.off('support-ticket-updated', handler); }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeTab, token]); const fetchData = async () => { setLoading(true); try { const statsRes = await fetch(`/api/admin/stats`, { headers: { 'x-admin-key': token } }); if (statsRes.status === 403) { sessionStorage.removeItem('adminToken'); navigate('/admin'); return; } if (statsRes.ok) { setStats(await statsRes.json()); } let endpoint = ''; if (activeTab === 'users') endpoint = '/api/admin/users'; else if (activeTab === 'listings') endpoint = '/api/admin/listings'; else if (activeTab === 'support') endpoint = '/api/admin/support'; const res = await fetch(`${endpoint}`, { headers: { 'x-admin-key': token } }); if (res.status === 403) { sessionStorage.removeItem('adminToken'); navigate('/admin'); return; } const data = await res.json(); if (activeTab === 'users') setUsers(data); else if (activeTab === 'listings') setListings(data); else if (activeTab === 'support') setSupportTickets(data); } catch (err) { console.error(err); } finally { setLoading(false); } }; const handleBlockUser = async (id, currentStatus) => { if (!window.confirm(`Are you sure you want to ${currentStatus ? 'unblock' : 'block'} this user?`)) return; try { const res = await fetch(`/api/admin/users/${id}/block`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'x-admin-key': token }, body: JSON.stringify({ isBlocked: !currentStatus }) }); if (res.ok) { setUsers(users.map(u => u.id === id ? { ...u, isBlocked: currentStatus ? 0 : 1 } : u)); } } catch (err) { console.error(err); } }; const handleDeleteUser = async (id) => { if (!window.confirm('CRITICAL WARING: Deleting a user will permanently wipe their profile and all active listings. Proceed?')) return; try { const res = await fetch(`/api/admin/users/${id}`, { method: 'DELETE', headers: { 'x-admin-key': token } }); if (res.ok) setUsers(users.filter(u => u.id !== id)); } catch (err) { console.error(err); } }; const handleDeleteListing = async (id) => { if (!window.confirm('Delete this listing permanently?')) return; try { const res = await fetch(`/api/admin/listings/${id}`, { method: 'DELETE', headers: { 'x-admin-key': token } }); if (res.ok) setListings(listings.filter(l => l.id !== id)); } catch (err) { console.error(err); } }; const handleResolveTicket = async (id) => { try { const res = await fetch(`/api/admin/support/${id}/resolve`, { method: 'PATCH', headers: { 'x-admin-key': token } }); if (res.ok) { setSupportTickets(supportTickets.map(t => t.id === id ? { ...t, isResolved: 1 } : t)); setStats(prev => ({ ...prev, pendingSupport: Math.max(0, prev.pendingSupport - 1) })); } } catch (err) { console.error(err); } }; const handleReplyTicket = async (id, reply) => { if (!reply || !reply.trim()) return; try { const res = await fetch(`/api/admin/support/${id}/reply`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'x-admin-key': token }, body: JSON.stringify({ reply }) }); if (res.ok) { setSupportTickets(supportTickets.map(t => t.id === id ? { ...t, adminReply: reply } : t)); setReplyingTo(null); setReplyText(''); } else { const errorData = await res.json(); alert(`Failed to send reply: ${errorData.error || 'Server error'}`); } } catch (err) { console.error(err); alert('Network error: Could not connect to support server.'); } }; if (!token) return null; return (
{/* Sidebar */}

Meri Mandi Admin

{/* Main Content Area */}

{activeTab === 'users' ? 'User Management' : activeTab === 'listings' ? 'Listings Moderation' : 'Support Inbox'}

{loading ? (
Loading data...
) : (
{activeTab === 'users' && ( {users.length === 0 ? : users.map(user => ( ))}
User Role Contact Status Actions
No users found.
Selfie {user.name} {user.role} {user.contact || 'N/A'} {user.isBlocked ? Blocked : Active }
)} {activeTab === 'listings' && ( {listings.length === 0 ? : listings.map(listing => ( ))}
Crop Details Seller Location Price Info Actions
No listings found.
{listing.images && listing.images.length > 0 ? ( Crop ) : (
)}
{listing.cropName}
Qty: {listing.quantity} • {listing.status === 'sold' ? Sold : Live}
{listing.sellerNameDisplay || listing.sellerName}
Phone: {listing.sellerContact || 'N/A'}
{(() => { try { const loc = JSON.parse(listing.location); return `${loc.lat.toFixed(4)}, ${loc.lng.toFixed(4)}`; } catch { return listing.location; } })()} {listing.price ? `₹${listing.price}` : 'Offer'}
)} {activeTab === 'support' && (
{supportTickets.length === 0 ?
No support tickets found.
: supportTickets.map(ticket => (
{ticket.selfiePath ? ( User ) : (
)}
{ticket.name || 'Guest Farmer'} {ticket.role || 'Unregistered'}
Contact: {ticket.contact || 'N/A'} • {new Date(ticket.timestamp).toLocaleString()}
{ticket.message} {ticket.adminReply && (
Admin Reply: {ticket.adminReply}
)}
{!ticket.isResolved && !ticket.adminReply && replyingTo !== ticket.id && ( )} {!ticket.isResolved ? ( ) : (
Resolved ✓
)}
{replyingTo === ticket.id && (