Merge pull request 'added range' (#49) from create_new_Range into main
Deploy to S3 / deploy (push) Successful in 35s
Details
Deploy to S3 / deploy (push) Successful in 35s
Details
Reviewed-on: #49pull/50/head^2
commit
e5d1a20dc6
|
|
@ -1,8 +1,9 @@
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Recruitment Inbox — application tabs over GET /inbox/all-applications.
|
Recruitment Inbox — application tabs over GET /inbox/all-applications.
|
||||||
Page size defaults to 10. skip/offset is the window start and is not
|
Page size defaults to 10 (dropdown: 10 / 50 / 100). skip/offset is the
|
||||||
recomputed when Per page changes: growing 10 to 30 on a window that
|
window start and is not recomputed when Per page changes: growing 10 to
|
||||||
started at row 11 requests skip=10, top=30 (rows 11–40). Clicking a
|
50 on a window that started at row 11 requests skip=10, top=50
|
||||||
|
(rows 11–60). Clicking a
|
||||||
page number realigns skip = (page-1)*limit. Total comes from a count
|
page number realigns skip = (page-1)*limit. Total comes from a count
|
||||||
endpoint called once when the page opens.
|
endpoint called once when the page opens.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
|
||||||
|
|
@ -690,13 +690,9 @@ table.data tbody tr:last-child td { border-bottom: none; }
|
||||||
.page-nums { display: flex; gap: 4px; align-items: center; min-width: 0; }
|
.page-nums { display: flex; gap: 4px; align-items: center; min-width: 0; }
|
||||||
.page-size { display: flex; align-items: center; gap: 8px; margin-right: 8px; flex-shrink: 0; }
|
.page-size { display: flex; align-items: center; gap: 8px; margin-right: 8px; flex-shrink: 0; }
|
||||||
.page-size-label { font-size: 13px; color: var(--text-2); white-space: nowrap; }
|
.page-size-label { font-size: 13px; color: var(--text-2); white-space: nowrap; }
|
||||||
.page-size-select { height: 34px; padding: 0 28px 0 10px; font-size: 13px; }
|
.page-size-select {
|
||||||
.page-size-input {
|
height: 34px; min-width: 72px; padding: 0 28px 0 10px; font-size: 13px; font-weight: 600;
|
||||||
width: 72px; height: 34px; padding: 0 8px; font-size: 13px; font-weight: 600;
|
|
||||||
text-align: center; border-radius: 8px; border: 1px solid var(--border-strong);
|
|
||||||
background: var(--bg-elev); color: inherit; outline: none;
|
|
||||||
}
|
}
|
||||||
.page-size-input:focus { border-color: var(--primary); box-shadow: var(--ring); }
|
|
||||||
.page-size-total { font-size: 13px; color: var(--text-2); margin-right: 8px; white-space: nowrap; }
|
.page-size-total { font-size: 13px; color: var(--text-2); margin-right: 8px; white-space: nowrap; }
|
||||||
.page-btn { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px; display: grid; place-items: center; font-size: 13px; font-weight: 600; color: var(--text-2); border: 1px solid transparent; }
|
.page-btn { min-width: 34px; height: 34px; padding: 0 8px; border-radius: 8px; display: grid; place-items: center; font-size: 13px; font-weight: 600; color: var(--text-2); border: 1px solid transparent; }
|
||||||
.page-btn:hover:not(:disabled) { background: var(--bg-sunken); }
|
.page-btn:hover:not(:disabled) { background: var(--bg-sunken); }
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,17 @@ import { EmptyState } from './primitives'
|
||||||
/** Matches the backend Query(10) default on list GET endpoints. */
|
/** Matches the backend Query(10) default on list GET endpoints. */
|
||||||
export const DEFAULT_PAGE_SIZE = 10
|
export const DEFAULT_PAGE_SIZE = 10
|
||||||
|
|
||||||
|
/** Fixed Per page choices — a dropdown, not a free-text box. */
|
||||||
|
export const PAGE_SIZE_OPTIONS = [10, 50, 100]
|
||||||
|
|
||||||
export function clampPageSize(value, max = 100) {
|
export function clampPageSize(value, max = 100) {
|
||||||
|
const allowed = PAGE_SIZE_OPTIONS.filter((n) => n <= max)
|
||||||
|
const fallback = allowed.includes(DEFAULT_PAGE_SIZE)
|
||||||
|
? DEFAULT_PAGE_SIZE
|
||||||
|
: (allowed[0] ?? DEFAULT_PAGE_SIZE)
|
||||||
const n = Number.parseInt(value, 10)
|
const n = Number.parseInt(value, 10)
|
||||||
if (!Number.isFinite(n) || n < 1) return DEFAULT_PAGE_SIZE
|
if (!Number.isFinite(n) || !allowed.includes(n)) return fallback
|
||||||
return Math.min(n, max)
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDataTable({ columns, rows, pageSize = DEFAULT_PAGE_SIZE }) {
|
export function useDataTable({ columns, rows, pageSize = DEFAULT_PAGE_SIZE }) {
|
||||||
|
|
@ -103,37 +110,25 @@ export function pageAfterSizeChange(currentPage, total, nextSize) {
|
||||||
return Math.min(Math.max(1, currentPage || 1), pages)
|
return Math.min(Math.max(1, currentPage || 1), pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Local draft so typing "50" does not fire a GET for 5, then 50. */
|
/** Per page dropdown: 10, 50, 100 (values above `max` are omitted). */
|
||||||
export function PageSizeField({ value, onChange, max = 100, label = 'Per page', id }) {
|
export function PageSizeField({ value, onChange, max = 100, label = 'Per page', id }) {
|
||||||
const [draft, setDraft] = useState(String(value ?? DEFAULT_PAGE_SIZE))
|
const options = PAGE_SIZE_OPTIONS.filter((n) => n <= max)
|
||||||
useEffect(() => setDraft(String(value ?? DEFAULT_PAGE_SIZE)), [value])
|
const selected = clampPageSize(value, max)
|
||||||
|
|
||||||
function commit() {
|
|
||||||
const next = clampPageSize(draft, max)
|
|
||||||
setDraft(String(next))
|
|
||||||
if (next !== value) onChange(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label className="page-size">
|
<label className="page-size">
|
||||||
<span className="page-size-label">{label}</span>
|
<span className="page-size-label">{label}</span>
|
||||||
<input
|
<select
|
||||||
id={id}
|
id={id}
|
||||||
className="page-size-input"
|
className="select page-size-select"
|
||||||
type="number"
|
value={selected}
|
||||||
min={1}
|
onChange={(e) => onChange(clampPageSize(e.target.value, max))}
|
||||||
max={max}
|
|
||||||
value={draft}
|
|
||||||
onChange={(e) => setDraft(e.target.value)}
|
|
||||||
onBlur={commit}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
e.preventDefault()
|
|
||||||
e.currentTarget.blur()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
/>
|
>
|
||||||
|
{options.map((n) => (
|
||||||
|
<option key={n} value={n}>{n}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue