Merge pull request #122 from wen-templari/feat/server-side-pagination

feat: implement server-side pagination for repair admin
This commit is contained in:
clas 2025-11-12 21:46:45 +08:00 committed by GitHub
commit f767fce6ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 199 additions and 46 deletions

View file

@ -190,6 +190,7 @@ export default function App() {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const rowsPerPage = 10 const rowsPerPage = 10
const [totalCount, setTotalCount] = useState(0)
const [statusFilter, setStatusFilter] = useState<string[]>( const [statusFilter, setStatusFilter] = useState<string[]>(
UserEventStatus.filter(v => v.status !== EventStatus.cancelled).map(v => v.status), UserEventStatus.filter(v => v.status !== EventStatus.cancelled).map(v => v.status),
) )
@ -230,16 +231,25 @@ export default function App() {
const list = useAsyncList<PublicEvent>({ const list = useAsyncList<PublicEvent>({
async load() { async load() {
const { data } = await saturdayClient.GET("/events", { setIsLoading(true)
const offset = (page - 1) * rowsPerPage
const { data, response } = await saturdayClient.GET("/events", {
params: { params: {
query: { query: {
order: "DESC", order: "DESC",
offset: 0, offset: offset,
limit: 1000, limit: rowsPerPage,
status: statusFilter.length > 0 ? statusFilter : null,
}, },
}, },
}) })
// Extract total count from response headers
const totalCountHeader = response.headers.get("X-Total-Count")
if (totalCountHeader) {
setTotalCount(parseInt(totalCountHeader, 10))
}
setIsLoading(false) setIsLoading(false)
return { return {
@ -263,28 +273,24 @@ export default function App() {
}, },
}) })
const filteredList = useMemo(() => { // Items are now paginated and filtered by the server
if (statusFilter.length > 0) {
return list.items.filter(item => statusFilter.includes(item.status))
}
return list.items
}, [list, statusFilter])
const items = useMemo(() => { const items = useMemo(() => {
const start = (page - 1) * rowsPerPage return list.items
const end = start + rowsPerPage }, [list.items])
return filteredList.slice(start, end)
}, [filteredList, page, rowsPerPage])
const pages = useMemo(() => { const pages = useMemo(() => {
return Math.ceil(filteredList.length / rowsPerPage) return Math.ceil(totalCount / rowsPerPage)
}, [filteredList, rowsPerPage]) }, [totalCount, rowsPerPage])
useEffect(() => { useEffect(() => {
setPage(1) setPage(1)
list.reload()
}, [statusFilter]) }, [statusFilter])
useEffect(() => {
list.reload()
}, [page])
const columns: { const columns: {
key: string key: string
label: string label: string
@ -454,24 +460,26 @@ export default function App() {
<CheckboxPopover value={statusFilter} onValueChange={setStatusFilter} /> <CheckboxPopover value={statusFilter} onValueChange={setStatusFilter} />
</div> </div>
{isLoading <div className="min-h-[600px]">
? ( {isLoading
<div className="flex justify-center py-8"> ? (
<Spinner label="Loading..." /> <div className="flex justify-center py-8">
</div> <Spinner label="Loading..." />
) </div>
: ( )
<div className="flex flex-col gap-4"> : (
{items.map(event => ( <div className="flex flex-col gap-4">
<MobileEventCard key={event.eventId} event={event} /> {items.map(event => (
))} <MobileEventCard key={event.eventId} event={event} />
{items.length === 0 && ( ))}
<div className="text-center py-8 text-gray-500"> {items.length === 0 && (
<div className="text-center py-8 text-gray-500">
</div>
)} </div>
</div> )}
)} </div>
)}
</div>
{/* Mobile Pagination */} {/* Mobile Pagination */}
<div className="mt-6 flex justify-center"> <div className="mt-6 flex justify-center">

View file

@ -797,6 +797,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -842,6 +847,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -880,6 +890,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -918,6 +933,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -960,6 +980,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -992,6 +1017,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1025,6 +1055,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1055,7 +1090,7 @@ export interface operations {
* @example 50 * @example 50
*/ */
limit?: number limit?: number
status?: string status?: string[] | null
order?: string order?: string
} }
header?: never header?: never
@ -1067,6 +1102,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1087,16 +1127,6 @@ export interface operations {
"export-events-xlsx": { "export-events-xlsx": {
parameters: { parameters: {
query: { query: {
/**
* @description Offset
* @example 0
*/
offset?: number
/**
* @description Limit
* @example 50
*/
limit?: number
status?: string status?: string
order?: string order?: string
start_time: string start_time: string
@ -1143,6 +1173,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1181,6 +1216,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1219,6 +1259,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1251,6 +1296,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1287,6 +1337,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1323,6 +1378,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1359,6 +1419,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1404,6 +1469,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1442,6 +1512,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1480,6 +1555,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1518,6 +1598,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1560,6 +1645,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1602,6 +1692,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1633,6 +1728,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1673,6 +1773,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1709,6 +1814,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1752,6 +1862,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1787,6 +1902,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1829,6 +1949,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1871,6 +1996,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1912,6 +2042,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1951,6 +2086,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {
@ -1980,6 +2120,11 @@ export interface operations {
/** @description OK */ /** @description OK */
200: { 200: {
headers: { headers: {
"X-Limit"?: number | null
"X-Offset"?: number | null
"X-Page"?: number | null
"X-Total-Count"?: number | null
"X-Total-Pages"?: number | null
[name: string]: unknown [name: string]: unknown
} }
content: { content: {