fix(web): retain dashboard pagination on detail return

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
XiaoSeS 2026-09-03 10:27:51 +08:00
parent b62a487037
commit 5045901c9e
5 changed files with 66 additions and 26 deletions

View file

@ -393,6 +393,9 @@ const dashboardStarsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'dashboard/stars',
beforeLoad: requireAuth,
validateSearch: (search: Record<string, unknown>): { page?: number } => ({
page: typeof search.page === 'number' && search.page > 0 ? search.page : undefined,
}),
component: MyStarsPage,
})
@ -400,6 +403,9 @@ const dashboardSubscriptionsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'dashboard/subscriptions',
beforeLoad: requireAuth,
validateSearch: (search: Record<string, unknown>): { page?: number } => ({
page: typeof search.page === 'number' && search.page > 0 ? search.page : undefined,
}),
component: MySubscriptionsPage,
})

View file

@ -5,9 +5,21 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const navigate = vi.fn()
const { useMyStarsPage } = vi.hoisted(() => ({
useMyStarsPage: vi.fn(() => ({
data: {
items: [{ id: 1, namespace: 'team-a', slug: 'demo skill' }],
total: 1,
page: 0,
size: 12,
},
isLoading: false,
})),
}))
vi.mock('@tanstack/react-router', () => ({
useNavigate: () => navigate,
useSearch: () => ({ page: 2 }),
useLocation: () => ({
pathname: '/dashboard/stars',
searchStr: '?page=2',
@ -34,15 +46,7 @@ vi.mock('@/shared/components/pagination', () => ({
}))
vi.mock('@/shared/hooks/use-user-queries', () => ({
useMyStarsPage: () => ({
data: {
items: [{ id: 1, namespace: 'team-a', slug: 'demo skill' }],
total: 1,
page: 0,
size: 12,
},
isLoading: false,
}),
useMyStarsPage,
}))
vi.mock('@/shared/ui/card', () => ({
@ -72,4 +76,10 @@ describe('MyStarsPage', () => {
search: { returnTo: '/dashboard/stars?page=2#saved' },
})
})
it('uses the URL page as the query source', () => {
render(createElement(MyStarsPage))
expect(useMyStarsPage).toHaveBeenCalledWith({ page: 2, size: 12 })
})
})

View file

@ -1,5 +1,4 @@
import { useState } from 'react'
import { useLocation, useNavigate } from '@tanstack/react-router'
import { useLocation, useNavigate, useSearch } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import { SkillCard } from '@/features/skill/skill-card'
import { Pagination } from '@/shared/components/pagination'
@ -14,7 +13,8 @@ export function MyStarsPage() {
const { t } = useTranslation()
const navigate = useNavigate()
const location = useLocation()
const [page, setPage] = useState(0)
const search = useSearch({ from: '/dashboard/stars' })
const page = search.page ?? 0
const { data, isLoading } = useMyStarsPage({ page, size: PAGE_SIZE })
const skills = data?.items ?? []
const totalPages = data ? Math.max(Math.ceil(data.total / data.size), 1) : 1
@ -50,7 +50,14 @@ export function MyStarsPage() {
))}
</div>
{data && data.total > PAGE_SIZE ? (
<Pagination page={page} totalPages={totalPages} onPageChange={setPage} />
<Pagination
page={page}
totalPages={totalPages}
onPageChange={(nextPage) => navigate({
to: '/dashboard/stars',
search: { page: nextPage > 0 ? nextPage : undefined },
})}
/>
) : null}
</>
)}

View file

@ -5,9 +5,21 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const navigate = vi.fn()
const { useMySubscriptionsPage } = vi.hoisted(() => ({
useMySubscriptionsPage: vi.fn(() => ({
data: {
items: [{ id: 1, namespace: 'team-a', slug: 'demo-skill' }],
total: 1,
page: 0,
size: 12,
},
isLoading: false,
})),
}))
vi.mock('@tanstack/react-router', () => ({
useNavigate: () => navigate,
useSearch: () => ({ page: 1 }),
useLocation: () => ({
pathname: '/dashboard/subscriptions',
searchStr: '?page=1',
@ -29,15 +41,7 @@ vi.mock('@/features/skill/skill-card', () => ({
vi.mock('@/shared/components/pagination', () => ({ Pagination: () => null }))
vi.mock('@/shared/hooks/use-user-queries', () => ({
useMySubscriptionsPage: () => ({
data: {
items: [{ id: 1, namespace: 'team-a', slug: 'demo-skill' }],
total: 1,
page: 0,
size: 12,
},
isLoading: false,
}),
useMySubscriptionsPage,
}))
vi.mock('@/shared/ui/card', () => ({ Card: ({ children }: { children: unknown }) => children }))
vi.mock('@/shared/components/dashboard-page-header', () => ({ DashboardPageHeader: () => null }))
@ -57,4 +61,10 @@ describe('MySubscriptionsPage', () => {
search: { returnTo: '/dashboard/subscriptions?page=1' },
})
})
it('uses the URL page as the query source', () => {
render(createElement(MySubscriptionsPage))
expect(useMySubscriptionsPage).toHaveBeenCalledWith({ page: 1, size: 12 })
})
})

View file

@ -1,5 +1,4 @@
import { useState } from 'react'
import { useLocation, useNavigate } from '@tanstack/react-router'
import { useLocation, useNavigate, useSearch } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import { SkillCard } from '@/features/skill/skill-card'
import { Pagination } from '@/shared/components/pagination'
@ -14,7 +13,8 @@ export function MySubscriptionsPage() {
const { t } = useTranslation()
const navigate = useNavigate()
const location = useLocation()
const [page, setPage] = useState(0)
const search = useSearch({ from: '/dashboard/subscriptions' })
const page = search.page ?? 0
const { data, isLoading } = useMySubscriptionsPage({ page, size: PAGE_SIZE })
const skills = data?.items ?? []
const totalPages = data ? Math.max(Math.ceil(data.total / data.size), 1) : 1
@ -50,7 +50,14 @@ export function MySubscriptionsPage() {
))}
</div>
{data && data.total > PAGE_SIZE ? (
<Pagination page={page} totalPages={totalPages} onPageChange={setPage} />
<Pagination
page={page}
totalPages={totalPages}
onPageChange={(nextPage) => navigate({
to: '/dashboard/subscriptions',
search: { page: nextPage > 0 ? nextPage : undefined },
})}
/>
) : null}
</>
)}