feat(admin): allow selecting items per page (10, 20, 50, 100, 500) on /products

This commit is contained in:
Your Name committed 2026-09-24 18:46:53 +07:00
1 parent a27743678c
commit b585277214
1 file changed
+94 -27
+94 -27
View File
@@ -42,7 +42,9 @@ export default function ProductsPage() {
// Pagination State
const page = searchParams.get('page') ? Number(searchParams.get('page')) : 1
const [limit] = useState(10) // Small limit for testing pagination UI easily
const validLimits = [10, 20, 50, 100, 500]
const limitParam = Number(searchParams.get('limit'))
const limit = validLimits.includes(limitParam) ? limitParam : 10
const [meta, setMeta] = useState<PaginationMeta | null>(null)
const fixUrl = (url: string) => {
@@ -265,7 +267,19 @@ export default function ProductsPage() {
})
}
// Fetch when page, category, or search changes
const handleLimitChange = (newLimit: number) => {
setSearchParams(prev => {
if (newLimit === 10) {
prev.delete('limit')
} else {
prev.set('limit', newLimit.toString())
}
prev.delete('page')
return prev
})
}
// Fetch when page, category, search, or limit changes
useEffect(() => {
// Only debounce if we have a search value to avoid rapid fetches while typing
const timeout = setTimeout(() => {
@@ -273,7 +287,7 @@ export default function ProductsPage() {
}, search ? 300 : 0)
return () => clearTimeout(timeout)
}, [page, activeCategory, search])
}, [page, activeCategory, search, limit])
// Reset to page 1 when filter changes
const handleCategoryChange = (catId: number | null) => {
@@ -493,48 +507,101 @@ export default function ProductsPage() {
</div>
{/* Pagination Controls */}
<div className="px-6 py-4 bg-white/[0.01] border-t border-white/5 flex items-center justify-between">
<div className="hidden sm:block">
<p className="text-xs text-slate-500">
<div className="px-6 py-4 bg-white/[0.01] border-t border-white/5 flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="flex flex-wrap items-center gap-4 text-xs text-slate-500">
<p>
Showing page <span className="text-white font-bold">{page}</span> of {meta?.lastPage || 1}
{meta?.total !== undefined ? ` (${meta.total} ${t('products.totalItems', 'รายการทั้งหมด')})` : ''}
</p>
<div className="flex items-center gap-2">
<span className="text-slate-400 font-medium">{t('products.perPage', 'แสดงต่อหน้า:')}</span>
<select
value={limit}
onChange={(e) => handleLimitChange(Number(e.target.value))}
className="bg-night-950 border border-white/10 rounded-lg px-2.5 py-1 text-xs font-semibold text-cyan-glow hover:border-cyan-glow/40 focus:outline-none focus:border-cyan-glow cursor-pointer transition-all shadow-inner"
>
{validLimits.map((val) => (
<option key={val} value={val} className="bg-night-900 text-white">
{val}
</option>
))}
</select>
</div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={page <= 1 || loading}
className="p-2 rounded-lg bg-night-950 border border-white/5 disabled:opacity-30 disabled:cursor-not-allowed hover:border-white/10 transition-all text-white"
className="p-2 rounded-lg bg-night-950 border border-white/5 disabled:opacity-30 disabled:cursor-not-allowed hover:border-white/10 transition-all text-white cursor-pointer"
title="Previous page"
>
<ChevronLeft className="w-4 h-4" />
</button>
{/* Page numbers (limited) */}
{/* Page numbers with sliding window */}
<div className="flex items-center gap-1">
{Array.from({ length: Math.min(5, meta?.lastPage || 0) }).map((_, i) => {
const pageNum = i + 1;
// Logic for showing a realistic range could go here, but 5 is enough for now
return (
<button
key={pageNum}
onClick={() => setPage(pageNum)}
className={`w-8 h-8 rounded-lg text-xs font-bold transition-all ${
page === pageNum
? 'bg-cyan-glow text-night-900 border border-cyan-glow shadow-[0_0_10px_rgba(102,252,241,0.2)]'
: 'hover:bg-white/5 text-slate-400'
}`}
>
{pageNum}
</button>
)
})}
{(meta?.lastPage || 0) > 5 && <span className="text-slate-600 px-1">...</span>}
{(() => {
const lastPage = meta?.lastPage || 1
const maxButtons = 5
let start = Math.max(1, page - 2)
let end = Math.min(lastPage, start + maxButtons - 1)
if (end - start < maxButtons - 1) {
start = Math.max(1, end - maxButtons + 1)
}
const pages = []
for (let i = start; i <= end; i++) {
pages.push(i)
}
return (
<>
{start > 1 && (
<>
<button
onClick={() => setPage(1)}
className="w-8 h-8 rounded-lg text-xs font-bold hover:bg-white/5 text-slate-400 transition-all cursor-pointer"
>
1
</button>
{start > 2 && <span className="text-slate-600 px-1 text-xs">...</span>}
</>
)}
{pages.map((pageNum) => (
<button
key={pageNum}
onClick={() => setPage(pageNum)}
className={`w-8 h-8 rounded-lg text-xs font-bold transition-all cursor-pointer ${
page === pageNum
? 'bg-cyan-glow text-night-900 border border-cyan-glow shadow-[0_0_10px_rgba(102,252,241,0.2)]'
: 'hover:bg-white/5 text-slate-400'
}`}
>
{pageNum}
</button>
))}
{end < lastPage && (
<>
{end < lastPage - 1 && <span className="text-slate-600 px-1 text-xs">...</span>}
<button
onClick={() => setPage(lastPage)}
className="w-8 h-8 rounded-lg text-xs font-bold hover:bg-white/5 text-slate-400 transition-all cursor-pointer"
>
{lastPage}
</button>
</>
)}
</>
)
})()}
</div>
<button
onClick={() => setPage(p => Math.min(meta?.lastPage || p, p + 1))}
disabled={page >= (meta?.lastPage || 1) || loading}
className="p-2 rounded-lg bg-night-950 border border-white/5 disabled:opacity-30 disabled:cursor-not-allowed hover:border-white/10 transition-all text-white"
className="p-2 rounded-lg bg-night-950 border border-white/5 disabled:opacity-30 disabled:cursor-not-allowed hover:border-white/10 transition-all text-white cursor-pointer"
title="Next page"
>
<ChevronRight className="w-4 h-4" />
</button>