192 lines
7.4 KiB
JavaScript
192 lines
7.4 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
import { Link } from 'react-router-dom';
|
||
import StatusBadge from '../../components/StatusBadge';
|
||
import { API_BASE_URL } from '../../config';
|
||
import { useAuth } from '../../context/AuthContext';
|
||
|
||
const SellerList = () => {
|
||
const { token } = useAuth();
|
||
const [filter, setFilter] = useState('all');
|
||
const [searchTerm, setSearchTerm] = useState('');
|
||
const [sellers, setSellers] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState(null);
|
||
|
||
// Pagination states
|
||
const [currentPage, setCurrentPage] = useState(1);
|
||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||
|
||
useEffect(() => {
|
||
const fetchSellers = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const url = filter === 'all'
|
||
? `${API_BASE_URL}/sellers/`
|
||
: `${API_BASE_URL}/sellers/?status=${filter}`;
|
||
|
||
const res = await fetch(url, {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
});
|
||
if (!res.ok) throw new Error('Failed to fetch sellers');
|
||
const data = await res.json();
|
||
setSellers(data);
|
||
setCurrentPage(1); // Reset page to 1 when filter changes
|
||
} catch (err) {
|
||
setError(err.message);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
fetchSellers();
|
||
}, [filter, token]);
|
||
|
||
const filteredSellers = sellers.filter(seller => {
|
||
const matchesSearch = seller.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||
seller.email.toLowerCase().includes(searchTerm.toLowerCase());
|
||
return matchesSearch;
|
||
});
|
||
|
||
// Reset page when search term changes
|
||
useEffect(() => {
|
||
setCurrentPage(1);
|
||
}, [searchTerm]);
|
||
|
||
// Compute pagination details
|
||
const totalItems = filteredSellers.length;
|
||
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
||
const startIdx = totalItems === 0 ? 0 : (currentPage - 1) * itemsPerPage + 1;
|
||
const endIdx = Math.min(currentPage * itemsPerPage, totalItems);
|
||
const currentSellers = filteredSellers.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
|
||
|
||
return (
|
||
<div>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
|
||
<h1 className="page-title" style={{ margin: 0 }}>Seller Management</h1>
|
||
</div>
|
||
|
||
<div className="glass-card">
|
||
<div style={{ display: 'flex', gap: '16px', marginBottom: '20px', flexWrap: 'wrap' }}>
|
||
<input
|
||
type="text"
|
||
className="form-input"
|
||
style={{ flex: 1, minWidth: '200px' }}
|
||
placeholder="Search sellers by name or email..."
|
||
value={searchTerm}
|
||
onChange={(e) => setSearchTerm(e.target.value)}
|
||
/>
|
||
<div style={{ display: 'flex', gap: '8px' }}>
|
||
{['all', 'unverified', 'pending_approval', 'approved', 'rejected', 'suspended'].map((status) => (
|
||
<button
|
||
key={status}
|
||
onClick={() => setFilter(status)}
|
||
className={`btn ${filter === status ? 'btn-primary' : 'btn-secondary'}`}
|
||
style={{ fontSize: '13px', padding: '8px 16px', textTransform: 'capitalize' }}
|
||
>
|
||
{status.replace('_', ' ')}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{loading ? (
|
||
<div style={{ padding: '40px', textAlign: 'center' }}>Loading seller profiles...</div>
|
||
) : error ? (
|
||
<div style={{ padding: '20px', color: 'var(--color-danger)', textAlign: 'center' }}>Error: {error}</div>
|
||
) : (
|
||
<>
|
||
<div className="table-container">
|
||
<table className="custom-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Seller Name</th>
|
||
<th>Email</th>
|
||
<th>Status</th>
|
||
<th>Joined Date</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{currentSellers.map((seller) => (
|
||
<tr key={seller.id}>
|
||
<td style={{ fontWeight: '600' }}>{seller.name}</td>
|
||
<td>{seller.email}</td>
|
||
<td>
|
||
<StatusBadge status={seller.status} />
|
||
</td>
|
||
<td>{seller.joined_at ? new Date(seller.joined_at).toLocaleDateString() : 'N/A'}</td>
|
||
<td>
|
||
<Link to={`/sellers/${seller.id}`} className="btn btn-secondary" style={{ fontSize: '12px', padding: '6px 12px' }}>
|
||
👁️ View Details
|
||
</Link>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
{/* Pagination Controls */}
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: '20px', flexWrap: 'wrap', gap: '12px', borderTop: '1px solid var(--glass-border)', paddingTop: '16px' }}>
|
||
<div style={{ color: 'var(--text-secondary)', fontSize: '13px' }}>
|
||
Showing {startIdx} to {endIdx} of {totalItems} items
|
||
</div>
|
||
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||
<span style={{ fontSize: '13px', color: 'var(--text-secondary)' }}>Items per page:</span>
|
||
<select
|
||
value={itemsPerPage}
|
||
onChange={(e) => {
|
||
setItemsPerPage(Number(e.target.value));
|
||
setCurrentPage(1);
|
||
}}
|
||
style={{
|
||
background: 'var(--bg-tertiary)',
|
||
color: '#fff',
|
||
border: '1px solid var(--glass-border)',
|
||
borderRadius: '6px',
|
||
padding: '4px 8px',
|
||
fontSize: '13px',
|
||
outline: 'none'
|
||
}}
|
||
>
|
||
{[5, 10, 20, 50, 100].map(size => (
|
||
<option key={size} value={size}>{size}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||
<button
|
||
className="btn btn-secondary"
|
||
style={{ fontSize: '12px', padding: '6px 12px' }}
|
||
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
|
||
disabled={currentPage === 1}
|
||
>
|
||
Previous
|
||
</button>
|
||
<span style={{ fontSize: '13px', color: 'var(--text-secondary)' }}>
|
||
Page {currentPage} of {totalPages || 1}
|
||
</span>
|
||
<button
|
||
className="btn btn-secondary"
|
||
style={{ fontSize: '12px', padding: '6px 12px' }}
|
||
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
|
||
disabled={currentPage === totalPages || totalPages === 0}
|
||
>
|
||
Next
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default SellerList;
|