frontend skip logic done

pull/35/head
ahmed.mujtaba 2026-08-31 12:54:24 +05:00
parent 17ddc09b00
commit 56f529afa1
2 changed files with 27 additions and 21 deletions

View File

@ -818,9 +818,7 @@ class Inbox_Messages(SQLModel, table=True):
async def get_inbox_messages( async def get_inbox_messages(
cls, session: AsyncSession, top: int | None, skip: int, search: str | None, isread: bool=True, application_status: Candidate_application_Status=Candidate_application_Status.CLOSED, assigned: bool | None=None, is_duplicate: bool | None=None, no_suggestions: bool | None=None, processing_state: str | None=None cls, session: AsyncSession, top: int | None, skip: int, search: str | None, isread: bool=True, application_status: Candidate_application_Status=Candidate_application_Status.CLOSED, assigned: bool | None=None, is_duplicate: bool | None=None, no_suggestions: bool | None=None, processing_state: str | None=None
): ):
# Page size is the caller's `top` (Inbox sends 10/25/50/100); `skip` is
# (page-1)*top so page 1 of 25 -> 0..24, page 2 -> 25..49. Newest first
# via created_at.
statement = cls._apply_filters( statement = cls._apply_filters(
select(cls).order_by(cls.created_at.desc(),cls.id.desc()), select(cls).order_by(cls.created_at.desc(),cls.id.desc()),
search, isread, application_status, assigned, is_duplicate, search, isread, application_status, assigned, is_duplicate,

View File

@ -1,8 +1,10 @@
/* ============================================================ /* ============================================================
Recruitment Inbox application tabs over GET /inbox/all-applications. Recruitment Inbox application tabs over GET /inbox/all-applications.
Page size defaults to 10. Pagination (skip/offset) is independent of the Page size defaults to 10. skip/offset is the window start and is not
limit control except that page 2 uses the current limit: skip = (page-1)*limit. recomputed when Per page changes: growing 10 to 30 on a window that
Total comes from a count endpoint called once when the page opens. started at row 11 requests skip=10, top=30 (rows 1140). Clicking a
page number realigns skip = (page-1)*limit. Total comes from a count
endpoint called once when the page opens.
============================================================ */ ============================================================ */
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@ -14,7 +16,7 @@ import EmailBody, { looksLikeHtml } from '../ui/EmailBody'
import OpenResumeButton from '../ui/OpenResumeButton' import OpenResumeButton from '../ui/OpenResumeButton'
import PageHeader from '../ui/PageHeader' import PageHeader from '../ui/PageHeader'
import { Tabs } from '../ui/Tabs' import { Tabs } from '../ui/Tabs'
import { DEFAULT_PAGE_SIZE, Pagination, pageAfterSizeChange, pageWindow } from '../ui/DataTable' import { DEFAULT_PAGE_SIZE, Pagination, pageWindow } from '../ui/DataTable'
import { Avatar, Badge, EmptyState, Icon, ScoreChip, SkeletonRows } from '../ui/primitives' import { Avatar, Badge, EmptyState, Icon, ScoreChip, SkeletonRows } from '../ui/primitives'
import { JobCard, PickRoleModal } from '../ui/SuggestedRoles' import { JobCard, PickRoleModal } from '../ui/SuggestedRoles'
import { useToast } from '../ui/Toast' import { useToast } from '../ui/Toast'
@ -690,7 +692,7 @@ export default function Inbox() {
const [channel, setChannel] = useState('email') const [channel, setChannel] = useState('email')
const [formSheet, setFormSheet] = useState(DEFAULT_FORM_SHEET) const [formSheet, setFormSheet] = useState(DEFAULT_FORM_SHEET)
const [tab, setTab] = useState('All Applications') const [tab, setTab] = useState('All Applications')
const [page, setPage] = useState(1) const [skip, setSkip] = useState(0)
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE) const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE)
const [selectedId, setSelectedId] = useState(null) const [selectedId, setSelectedId] = useState(null)
const [q, setQ] = useState('') const [q, setQ] = useState('')
@ -705,17 +707,17 @@ export default function Inbox() {
const listParams = useMemo(() => ({ const listParams = useMemo(() => ({
...tabFilter, ...tabFilter,
top: pageSize, top: pageSize,
skip: (page - 1) * pageSize, skip,
...(q.trim() ? { search: q.trim() } : {}), ...(q.trim() ? { search: q.trim() } : {}),
}), [tabFilter, page, pageSize, q]) }), [tabFilter, skip, pageSize, q])
const formParams = useMemo(() => ({ const formParams = useMemo(() => ({
sheet: formSheet || undefined, sheet: formSheet || undefined,
offset: (page - 1) * pageSize, offset: skip,
limit: pageSize, limit: pageSize,
...formTabFilter, ...formTabFilter,
...(q.trim() ? { search: q.trim() } : {}), ...(q.trim() ? { search: q.trim() } : {}),
}), [formSheet, page, pageSize, q, formTabFilter]) }), [formSheet, skip, pageSize, q, formTabFilter])
const applicationsQuery = useQuery({ const applicationsQuery = useQuery({
queryKey: qk.mailbox.applications(listParams), queryKey: qk.mailbox.applications(listParams),
@ -814,7 +816,14 @@ export default function Inbox() {
? (activeQuery.data?.total ?? 0) ? (activeQuery.data?.total ?? 0)
: (countsReady ? tabTotal : (poolTotal || (activeQuery.data?.total ?? 0))) : (countsReady ? tabTotal : (poolTotal || (activeQuery.data?.total ?? 0)))
const pages = Math.max(1, Math.ceil(total / pageSize)) const pages = Math.max(1, Math.ceil(total / pageSize))
const currentPage = Math.min(page, pages) const from = total ? skip + 1 : 0
const to = Math.min(skip + pageSize, total)
const currentPage = Math.min(Math.floor(skip / pageSize) + 1, pages)
useEffect(() => {
if (total <= 0 || skip < total) return
setSkip(Math.max(0, Math.floor((total - 1) / pageSize) * pageSize))
}, [total, pageSize, skip])
const list = inbox const list = inbox
@ -848,7 +857,7 @@ export default function Inbox() {
function switchChannel(next) { function switchChannel(next) {
if (next === channel) return if (next === channel) return
setChannel(next) setChannel(next)
setPage(1) setSkip(0)
setSelectedId(null) setSelectedId(null)
setQ('') setQ('')
// Unread is email-only; leave it behind when opening Sheet Forms. // Unread is email-only; leave it behind when opening Sheet Forms.
@ -987,7 +996,7 @@ export default function Inbox() {
value={tab} value={tab}
onChange={(t) => { onChange={(t) => {
setTab(t) setTab(t)
setPage(1) setSkip(0)
setSelectedId(null) setSelectedId(null)
selection.clear() selection.clear()
}} }}
@ -1019,7 +1028,7 @@ export default function Inbox() {
value={formSheet} value={formSheet}
onChange={(e) => { onChange={(e) => {
setFormSheet(e.target.value) setFormSheet(e.target.value)
setPage(1) setSkip(0)
setSelectedId(null) setSelectedId(null)
}} }}
style={{ width: '100%' }} style={{ width: '100%' }}
@ -1034,7 +1043,7 @@ export default function Inbox() {
<Icon name="search" /> <Icon name="search" />
<input <input
value={q} value={q}
onChange={(e) => { setQ(e.target.value); setPage(1) }} onChange={(e) => { setQ(e.target.value); setSkip(0) }}
placeholder={isForms ? 'Search name, email, position, city…' : 'Search applications…'} placeholder={isForms ? 'Search name, email, position, city…' : 'Search applications…'}
/> />
</div> </div>
@ -1102,18 +1111,17 @@ export default function Inbox() {
</div> </div>
{activeQuery.isSuccess && total > 0 && ( {activeQuery.isSuccess && total > 0 && (
<Pagination <Pagination
from={total ? (currentPage - 1) * pageSize + 1 : 0} from={from}
to={Math.min(currentPage * pageSize, total)} to={to}
total={total} total={total}
page={currentPage} page={currentPage}
pages={pages} pages={pages}
setPage={(p) => { setPage(p); setSelectedId(null); selection.clear() }} setPage={(p) => { setSkip((p - 1) * pageSize); setSelectedId(null); selection.clear() }}
pageButtons={pageWindow(currentPage, pages)} pageButtons={pageWindow(currentPage, pages)}
pageSize={pageSize} pageSize={pageSize}
pageSizeMax={PAGE_SIZE_MAX} pageSizeMax={PAGE_SIZE_MAX}
onPageSizeChange={(n) => { onPageSizeChange={(n) => {
setPageSize(n) setPageSize(n)
setPage((p) => pageAfterSizeChange(p, total, n))
setSelectedId(null) setSelectedId(null)
selection.clear() selection.clear()
}} }}