Inbox: All page-size option to show every application unpaged
PageSizeField gains an opt-in All toggle beside the numeric input (sentinel value all; the input disables while active). Only the Recruitment Inbox opts in: with All active the fetch omits top/limit, which /inbox/all-applications and /sheet/form-data/fetch already read as unpaged (Query(None) -> no LIMIT), so the whole tab renders as one page for both the Email and Sheet Forms channels. Every other table keeps its existing paged-only control. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>pull/50/head
parent
ea43b02b99
commit
3f06fbeff1
|
|
@ -731,7 +731,8 @@ export default function Inbox() {
|
|||
const formTabFilter = FORM_TAB_FILTERS[tab] ?? {}
|
||||
const listParams = useMemo(() => ({
|
||||
...tabFilter,
|
||||
top: pageSize,
|
||||
// 'all' drops the param entirely — the endpoint reads a missing top as unpaged.
|
||||
top: pageSize === 'all' ? undefined : pageSize,
|
||||
skip,
|
||||
...(q.trim() ? { search: q.trim() } : {}),
|
||||
}), [tabFilter, skip, pageSize, q])
|
||||
|
|
@ -739,7 +740,7 @@ export default function Inbox() {
|
|||
const formParams = useMemo(() => ({
|
||||
sheet: formSheet || undefined,
|
||||
offset: skip,
|
||||
limit: pageSize,
|
||||
limit: pageSize === 'all' ? undefined : pageSize,
|
||||
...formTabFilter,
|
||||
...(q.trim() ? { search: q.trim() } : {}),
|
||||
}), [formSheet, skip, pageSize, q, formTabFilter])
|
||||
|
|
@ -840,15 +841,19 @@ export default function Inbox() {
|
|||
const total = q.trim()
|
||||
? (activeQuery.data?.total ?? 0)
|
||||
: (countsReady ? tabTotal : (poolTotal || (activeQuery.data?.total ?? 0)))
|
||||
const pages = Math.max(1, Math.ceil(total / pageSize))
|
||||
// 'all' = unpaged: the fetch omits top/limit (the endpoints treat a missing
|
||||
// page size as no LIMIT), so the whole tab is one page.
|
||||
const showAll = pageSize === 'all'
|
||||
const pages = showAll ? 1 : Math.max(1, Math.ceil(total / pageSize))
|
||||
const from = total ? skip + 1 : 0
|
||||
const to = Math.min(skip + pageSize, total)
|
||||
const currentPage = Math.min(Math.floor(skip / pageSize) + 1, pages)
|
||||
const to = showAll ? total : Math.min(skip + pageSize, total)
|
||||
const currentPage = showAll ? 1 : Math.min(Math.floor(skip / pageSize) + 1, pages)
|
||||
|
||||
useEffect(() => {
|
||||
if (showAll) { if (skip !== 0) setSkip(0); return }
|
||||
if (total <= 0 || skip < total) return
|
||||
setSkip(Math.max(0, Math.floor((total - 1) / pageSize) * pageSize))
|
||||
}, [total, pageSize, skip])
|
||||
}, [total, pageSize, skip, showAll])
|
||||
|
||||
const list = inbox
|
||||
|
||||
|
|
@ -1207,12 +1212,14 @@ export default function Inbox() {
|
|||
total={total}
|
||||
page={currentPage}
|
||||
pages={pages}
|
||||
setPage={(p) => { setSkip((p - 1) * pageSize); setSelectedId(null); selection.clear() }}
|
||||
setPage={(p) => { if (showAll) return; setSkip((p - 1) * pageSize); setSelectedId(null); selection.clear() }}
|
||||
pageButtons={pageWindow(currentPage, pages)}
|
||||
pageSize={pageSize}
|
||||
pageSizeMax={PAGE_SIZE_MAX}
|
||||
allowAll
|
||||
onPageSizeChange={(n) => {
|
||||
setPageSize(n)
|
||||
if (n === 'all') setSkip(0) // numeric sizes keep their page (clamp effect above)
|
||||
setSelectedId(null)
|
||||
selection.clear()
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -103,15 +103,20 @@ export function pageAfterSizeChange(currentPage, total, nextSize) {
|
|||
return Math.min(Math.max(1, currentPage || 1), pages)
|
||||
}
|
||||
|
||||
/** Local draft so typing "50" does not fire a GET for 5, then 50. */
|
||||
export function PageSizeField({ value, onChange, max = 100, label = 'Per page', id }) {
|
||||
const [draft, setDraft] = useState(String(value ?? DEFAULT_PAGE_SIZE))
|
||||
useEffect(() => setDraft(String(value ?? DEFAULT_PAGE_SIZE)), [value])
|
||||
/** Local draft so typing "50" does not fire a GET for 5, then 50.
|
||||
With `allowAll`, an "All" toggle sits beside the input: active it reports
|
||||
the sentinel 'all' (the caller drops its top/limit param — the endpoints
|
||||
treat a missing page size as unpaged); committing a number exits it. */
|
||||
export function PageSizeField({ value, onChange, max = 100, label = 'Per page', id, allowAll = false }) {
|
||||
const isAll = allowAll && value === 'all'
|
||||
const numeric = isAll ? DEFAULT_PAGE_SIZE : (value ?? DEFAULT_PAGE_SIZE)
|
||||
const [draft, setDraft] = useState(String(numeric))
|
||||
useEffect(() => setDraft(String(numeric)), [numeric])
|
||||
|
||||
function commit() {
|
||||
const next = clampPageSize(draft, max)
|
||||
setDraft(String(next))
|
||||
if (next !== value) onChange(next)
|
||||
if (isAll || next !== value) onChange(next)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -124,6 +129,7 @@ export function PageSizeField({ value, onChange, max = 100, label = 'Per page',
|
|||
min={1}
|
||||
max={max}
|
||||
value={draft}
|
||||
disabled={isAll}
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
onBlur={commit}
|
||||
onKeyDown={(e) => {
|
||||
|
|
@ -134,13 +140,27 @@ export function PageSizeField({ value, onChange, max = 100, label = 'Per page',
|
|||
}}
|
||||
aria-label={label}
|
||||
/>
|
||||
{allowAll && (
|
||||
<button
|
||||
type="button"
|
||||
className={`page-btn${isAll ? ' active' : ''}`}
|
||||
aria-pressed={isAll}
|
||||
title={isAll ? 'Back to paged view' : 'Show all rows'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
onChange(isAll ? clampPageSize(draft, max) : 'all')
|
||||
}}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
)}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
export function Pagination({
|
||||
from, to, total, page, pages, setPage, pageButtons,
|
||||
pageSize, onPageSizeChange, pageSizeMax = 100,
|
||||
pageSize, onPageSizeChange, pageSizeMax = 100, allowAll = false,
|
||||
}) {
|
||||
return (
|
||||
<div className="pagination">
|
||||
|
|
@ -154,6 +174,7 @@ export function Pagination({
|
|||
value={pageSize ?? DEFAULT_PAGE_SIZE}
|
||||
onChange={onPageSizeChange}
|
||||
max={pageSizeMax}
|
||||
allowAll={allowAll}
|
||||
/>
|
||||
<span className="page-size-total">of <b>{total}</b></span>
|
||||
</>
|
||||
|
|
|
|||
Loading…
Reference in New Issue