feat: pagination in the SPA for the Sites and Authors lists

This commit is contained in:
Florian Sylvain committed 2026-10-19 04:00:47 +02:00
1 parent 500ad59458
commit 340a0ad461
34 files changed
+2833 -896

No files matched your search

+39 -1
View File
@@ -21,6 +21,20 @@ export interface Call {
body: Record<string, unknown> | undefined
}
export class PagedReply {
body: unknown
total: number
constructor(body: unknown, total: number) {
this.body = body
this.total = total
}
}
export function paged(body: unknown, total: number) {
return new PagedReply(body, total)
}
export type Responder = (call: Call) => unknown | Promise<unknown>
export interface Backend {
@@ -177,12 +191,25 @@ function pageOf(backend: Backend, query: URLSearchParams) {
}
}
function sitePageOf(backend: Backend, query: URLSearchParams) {
const q = (query.get('q') ?? '').toLowerCase()
const size = Number(query.get('size') ?? 25)
const page = Number(query.get('page') ?? 1)
const matching = backend.sites.filter((item) =>
`${item.name} ${item.key} ${item.base_url}`.toLowerCase().includes(q),
)
return new PagedReply(
structuredClone(matching.slice((page - 1) * size, page * size)),
matching.length,
)
}
export function defaultRoute(backend: Backend, call: Call): unknown {
const { method, path, query, body } = call
const siteMatch = /^\/v1\/site\/(\d+)(\/default)?$/.exec(path)
const authorMatch = /^\/v1\/author\/(\d+)$/.exec(path)
if (method === 'GET' && path === '/v1/site/') {
return structuredClone(backend.sites)
return query.has('page') ? sitePageOf(backend, query) : structuredClone(backend.sites)
}
if (method === 'POST' && path === '/v1/site/') {
if (backend.sites.some((site) => site.key === body?.key)) {
@@ -349,9 +376,20 @@ export async function mountDirectory(
backend.responders[`${call.method} ${pathname}`]
return responder ? responder(call) : defaultRoute(backend, call)
})
const requestPaged = vi.fn(async (path: string, init?: RequestInit) => {
const result = await request(path, init)
if (result instanceof PagedReply) {
return { body: result.body, total: result.total }
}
if (Array.isArray(result)) {
return { body: result, total: result.length }
}
return { body: result, total: (result as { total: number }).total }
})
vi.mocked(useApi).mockReturnValue({
getMe: vi.fn().mockResolvedValue({ verified: true, username: 'marta', id: 1 }),
request,
requestPaged,
} as unknown as ReturnType<typeof useApi>)
await router.push(url)
const component = view === 'sites' ? SitesView : AuthorsView