UNPKG

ajinkya-mhetre-mern

Version:

A MERN starter with frontend and backend folders

253 lines (230 loc) 11.3 kB
import React, { useEffect, useState } from 'react'; import { useAuth } from '../../contexts/AuthContext'; import Stats from '../../components/Admin/Stats'; const baseUrl = import.meta.env.VITE_API_URL; function Farmers() { const { token } = useAuth(); const [farmers, setFarmers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [actionLoading, setActionLoading] = useState({}); useEffect(() => { fetchPendingFarmers(); }, []); const fetchPendingFarmers = async () => { try { setLoading(true); const response = await fetch(`${baseUrl}/admin/pending-farmers`, { headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, }); const data = await response.json(); if (data.success) { setFarmers(data.farmers); setError(''); } else { setError('Failed to fetch pending farmers.'); } } catch (err) { setError('Error fetching pending farmers.'); console.error('Error:', err); } finally { setLoading(false); } }; const handleFarmerAction = async (farmerId, action) => { try { setActionLoading(prev => ({ ...prev, [farmerId]: action })); const response = await fetch(`${baseUrl}/admin/verify-farmer/${farmerId}`, { method: 'PUT', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ action }), }); const data = await response.json(); if (data.success || response.ok) { // Remove the farmer from the list after successful action setFarmers(prev => prev.filter(farmer => farmer._id !== farmerId)); // Show success message (you can replace this with a toast notification) alert(`Farmer ${action === 'approve' ? 'approved' : 'rejected'} successfully!`); } else { setError(`Failed to ${action} farmer.`); } } catch (err) { setError(`Error ${action}ing farmer.`); console.error('Error:', err); } finally { setActionLoading(prev => ({ ...prev, [farmerId]: null })); } }; const formatDate = (dateString) => { return new Date(dateString).toLocaleDateString('en-IN', { year: 'numeric', month: 'short', day: 'numeric', }); }; if (loading) { return ( <div className="w-full p-4"> <Stats /> <div className="mt-6 bg-white rounded-lg shadow"> <div className="p-6 text-center"> <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div> <p className="mt-2 text-gray-600">Loading pending farmers...</p> </div> </div> </div> ); } return ( <div className="w-full p-4"> <Stats /> <div className="mt-6"> <div className="bg-white rounded-lg shadow overflow-hidden"> <div className="px-6 py-4 border-b border-gray-200"> <div className="flex justify-between items-center"> <h2 className="text-xl font-semibold text-gray-800">Pending Farmers</h2> <div className="flex items-center space-x-2"> <span className="bg-yellow-100 text-yellow-800 px-3 py-1 rounded-full text-sm font-medium"> {farmers.length} Pending </span> <button onClick={fetchPendingFarmers} className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md text-sm font-medium transition-colors" > Refresh </button> </div> </div> </div> {error && ( <div className="px-6 py-4 bg-red-50 border-l-4 border-red-400"> <p className="text-red-700">{error}</p> </div> )} {farmers.length === 0 ? ( <div className="px-6 py-12 text-center"> <div className="text-gray-400 text-6xl mb-4">🌾</div> <h3 className="text-lg font-medium text-gray-900 mb-2">No Pending Farmers</h3> <p className="text-gray-500">All farmer applications have been processed.</p> </div> ) : ( <div className="overflow-x-auto"> <table className="min-w-full divide-y divide-gray-200"> <thead className="bg-gray-50"> <tr> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Farmer Details </th> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Contact Info </th> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Farm Details </th> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Location </th> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Applied Date </th> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> Actions </th> </tr> </thead> <tbody className="bg-white divide-y divide-gray-200"> {farmers.map((farmer) => ( <tr key={farmer._id} className="hover:bg-gray-50 transition-colors"> <td className="px-6 py-4 whitespace-nowrap"> <div className="flex items-center"> <div className="flex-shrink-0 h-12 w-12"> <div className="h-12 w-12 rounded-full bg-green-100 flex items-center justify-center"> <span className="text-green-600 font-semibold text-lg"> {farmer.name.charAt(0).toUpperCase()} </span> </div> </div> <div className="ml-4"> <div className="text-sm font-medium text-gray-900">{farmer.name}</div> <div className="text-sm text-gray-500">ID: {farmer._id.slice(-8)}</div> </div> </div> </td> <td className="px-6 py-4 whitespace-nowrap"> <div className="text-sm text-gray-900">{farmer.email}</div> <div className="text-sm text-gray-500">{farmer.phone}</div> </td> <td className="px-6 py-4"> <div className="text-sm font-medium text-gray-900">{farmer.farmDetails.farmName}</div> <div className="text-sm text-gray-500">Size: {farmer.farmDetails.farmSize}</div> {farmer.farmDetails.certifications.length > 0 && ( <div className="mt-1 flex flex-wrap gap-1"> {farmer.farmDetails.certifications.map((cert, index) => ( <span key={index} className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800" > {cert} </span> ))} </div> )} </td> <td className="px-6 py-4 whitespace-nowrap"> <div className="text-sm text-gray-900">{farmer.address.city}, {farmer.address.state}</div> <div className="text-sm text-gray-500">{farmer.address.street}</div> <div className="text-sm text-gray-500">{farmer.address.zipCode}</div> </td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> {formatDate(farmer.createdAt)} </td> <td className="px-6 py-4 whitespace-nowrap text-sm font-medium space-y-2"> <div className="flex space-x-2"> <button onClick={() => handleFarmerAction(farmer._id, 'approve')} disabled={actionLoading[farmer._id]} className="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors" > {actionLoading[farmer._id] === 'approve' ? ( <> <div className="animate-spin rounded-full h-3 w-3 border-b-2 border-white mr-1"></div> Approving... </> ) : ( '✓ Approve' )} </button> <button onClick={() => handleFarmerAction(farmer._id, 'reject')} disabled={actionLoading[farmer._id]} className="inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors" > {actionLoading[farmer._id] === 'reject' ? ( <> <div className="animate-spin rounded-full h-3 w-3 border-b-2 border-white mr-1"></div> Rejecting... </> ) : ( '✗ Reject' )} </button> </div> </td> </tr> ))} </tbody> </table> </div> )} </div> </div> </div> ); } export default Farmers;