Add vendors listing with search, category filter, grid/list, pagination

This commit is contained in:
2026-04-23 21:14:02 +04:00
parent 8dd618ce88
commit 73a0d24c4f
5 changed files with 441 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
import { Link } from '@tanstack/react-router'
import { cn } from '~/lib/utils'
type Props = {
page: number
itemsPerPage: number
totalItems: number
limit?: number
buildSearch: (nextPage: number) => Record<string, unknown>
}
export function Pagination({ page, itemsPerPage, totalItems, limit = 3, buildSearch }: Props) {
const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage))
if (totalPages === 1) return null
const pages: number[] = []
for (let i = 0; i < totalPages; i++) {
if (i < limit || i >= totalPages - limit || (i >= page - limit && i < page + limit - 1)) {
pages.push(i + 1)
}
}
const prev = Math.max(1, page - 1)
const next = Math.min(totalPages, page + 1)
return (
<div className="my-6 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<Link
to="."
search={buildSearch(prev)}
className="inline-flex h-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground"
>
Previous
</Link>
<div className="hidden gap-1 md:flex">
{pages.map((p, idx) => (
<div key={p} className="flex items-center">
{idx > 0 && p - 1 !== pages[idx - 1] && <span className="mx-2 leading-10"></span>}
<Link
to="."
search={buildSearch(p)}
className={cn(
'inline-flex h-10 min-w-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground',
p === page && 'bg-accent font-semibold',
)}
>
{p}
</Link>
</div>
))}
</div>
<select
className="h-10 rounded-md border bg-transparent px-3 text-sm md:hidden"
value={page}
onChange={(e) => {
const target = Number(e.target.value)
const hash = Object.entries(buildSearch(target))
.map(([k, v]) => `${k}=${v}`)
.join('&')
window.location.search = `?${hash}`
}}
>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
<option key={p} value={p}>
{p}
</option>
))}
</select>
<Link
to="."
search={buildSearch(next)}
className="inline-flex h-10 items-center justify-center rounded-md border px-3 text-sm hover:bg-accent hover:text-accent-foreground"
>
Next
</Link>
</div>
)
}