fix(promotion): clamp emptied queue pages

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
XiaoSeS 2026-08-31 11:58:11 +08:00
parent 817426c50f
commit fe8a0cb21f
2 changed files with 44 additions and 2 deletions

View file

@ -1,5 +1,5 @@
/** @vitest-environment jsdom */
import { cleanup, fireEvent, render, screen, within } from '@testing-library/react'
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { PromotionStatus, PromotionTask } from '@/api/types'
@ -246,6 +246,34 @@ describe('PromotionsPage', () => {
expect(screen.getByRole('button', { name: 'pagination:0/2' })).toBeTruthy()
})
it('returns to the last valid page when a mutation empties the current page', async () => {
const pending = createPromotion()
mocks.usePromotionList.mockImplementation((params: { status?: PromotionStatus; page?: number } = {}) => {
const page = params.page ?? 0
return {
data: {
items: params.status === 'PENDING' && page === 0 ? [pending] : [],
total: params.status === 'PENDING' ? 20 : 0,
page,
size: 20,
},
isLoading: false,
}
})
render(<PromotionsPage />)
mocks.paginationProps[0]?.onPageChange(1)
await waitFor(() => {
expect(mocks.usePromotionList).toHaveBeenLastCalledWith({
status: 'PENDING',
page: 0,
size: 20,
})
})
expect(screen.getByText('Knowledge Helper')).toBeTruthy()
})
it('renders approved history as a sortable table', () => {
render(<PromotionsPage />)

View file

@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useApprovePromotion, usePromotionList, useRejectPromotion } from '@/features/promotion/use-promotion-list'
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
@ -79,6 +79,18 @@ function PromotionPagination({ data, onPageChange }: { data: PromotionPage; onPa
return <Pagination page={data.page} totalPages={totalPages} onPageChange={onPageChange} />
}
function useClampPromotionPage(data: PromotionPage | undefined, page: number, onPageChange: (page: number) => void) {
useEffect(() => {
if (!data) {
return
}
const totalPages = data.size > 0 ? Math.ceil(data.total / data.size) : 0
if (page > 0 && page >= totalPages) {
onPageChange(Math.max(0, totalPages - 1))
}
}, [data, onPageChange, page])
}
function PendingPromotionCard({
item,
comment,
@ -141,6 +153,7 @@ function PendingPromotionList({ page, onPageChange }: { page: number; onPageChan
const approveMutation = useApprovePromotion()
const rejectMutation = useRejectPromotion()
const [commentById, setCommentById] = useState<Record<number, string>>({})
useClampPromotionPage(data, page, onPageChange)
if (isLoading) {
return <div className="h-32 animate-shimmer rounded-xl" />
@ -192,6 +205,7 @@ function PromotionHistoryTable({
})
const nextDirection = sortDirection === 'DESC' ? 'ASC' : 'DESC'
const sortLabel = nextDirection === 'ASC' ? t('promotions.sortReviewedTimeAsc') : t('promotions.sortReviewedTimeDesc')
useClampPromotionPage(data, page, onPageChange)
if (isLoading) {
return (