feat(web): lazy-load review tabs and adjust profile review pagination

- Skill review tabs only fetch data when activated (not all 3 on mount)
- Tabs component now supports controlled mode via value prop
- Profile review page size reduced from 20 to 10
- Profile review pagination bar always visible regardless of page count
This commit is contained in:
xiose 2026-03-19 18:54:33 +08:00
parent f6c72030b6
commit 9cdd200bc2
4 changed files with 18 additions and 16 deletions

View file

@ -17,10 +17,10 @@ async function getReviewList(status: string, namespaceId?: number, page = 0, siz
/**
* Exposes the review list query used by dashboard moderation views.
*/
export function useReviewList(status: string, namespaceId?: number, page = 0, size = 20) {
export function useReviewList(status: string, namespaceId?: number, page = 0, size = 20, enabled = true) {
return useQuery({
queryKey: ['reviews', status, namespaceId, page, size],
queryFn: () => getReviewList(status, namespaceId, page, size),
enabled: namespaceId === undefined || namespaceId > 0,
enabled: enabled && (namespaceId === undefined || namespaceId > 0),
})
}

View file

@ -47,7 +47,7 @@ type ReviewItem = {
reviewedAt: string | null
}
const PAGE_SIZE = 20
const PAGE_SIZE = 10
export function ProfileReviewTable() {
const { t, i18n } = useTranslation()
@ -163,10 +163,6 @@ export function ProfileReviewTable() {
}
function renderPagination(status: ReviewStatus, totalElements: number, totalPages: number) {
if (totalPages <= 1) {
return null
}
const currentPage = pages[status]
return (

View file

@ -36,6 +36,7 @@ export function ReviewsPage() {
APPROVED: 0,
REJECTED: 0,
})
const [activeStatus, setActiveStatus] = useState<ReviewStatus>('PENDING')
const isSkillAdmin = hasRole('SKILL_ADMIN') || hasRole('SUPER_ADMIN')
const isUserAdmin = hasRole('USER_ADMIN') || hasRole('SUPER_ADMIN')
@ -44,9 +45,9 @@ export function ReviewsPage() {
// Determine default top-level tab
const defaultType = isSkillAdmin ? 'skill' : 'profile'
const pendingQuery = useReviewList('PENDING', undefined, pages.PENDING, PAGE_SIZE)
const approvedQuery = useReviewList('APPROVED', undefined, pages.APPROVED, PAGE_SIZE)
const rejectedQuery = useReviewList('REJECTED', undefined, pages.REJECTED, PAGE_SIZE)
const pendingQuery = useReviewList('PENDING', undefined, pages.PENDING, PAGE_SIZE, activeStatus === 'PENDING')
const approvedQuery = useReviewList('APPROVED', undefined, pages.APPROVED, PAGE_SIZE, activeStatus === 'APPROVED')
const rejectedQuery = useReviewList('REJECTED', undefined, pages.REJECTED, PAGE_SIZE, activeStatus === 'REJECTED')
const formatDate = (dateString: string) => formatLocalDateTime(dateString, i18n.language)
@ -168,7 +169,7 @@ export function ReviewsPage() {
</div>
</CardHeader>
<CardContent className="space-y-6">
<Tabs defaultValue="PENDING">
<Tabs value={activeStatus} onValueChange={(v) => setActiveStatus(v as ReviewStatus)}>
<TabsList className="gap-4 rounded-xl border-b-0 bg-muted/70 p-1 shadow-none">
<TabsTrigger
value="PENDING"

View file

@ -2,7 +2,8 @@ import * as React from 'react'
import { cn } from '@/shared/lib/utils'
interface TabsProps {
defaultValue: string
defaultValue?: string
value?: string
children: React.ReactNode
className?: string
onValueChange?: (value: string) => void
@ -15,13 +16,17 @@ interface TabsContextValue {
const TabsContext = React.createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ defaultValue, children, className, onValueChange }: TabsProps) {
const [value, setValue] = React.useState(defaultValue)
export function Tabs({ defaultValue, value: controlledValue, children, className, onValueChange }: TabsProps) {
const [internalValue, setInternalValue] = React.useState(defaultValue ?? '')
const isControlled = controlledValue !== undefined
const value = isControlled ? controlledValue : internalValue
const handleValueChange = React.useCallback((nextValue: string) => {
setValue(nextValue)
if (!isControlled) {
setInternalValue(nextValue)
}
onValueChange?.(nextValue)
}, [onValueChange])
}, [isControlled, onValueChange])
return (
<TabsContext.Provider value={{ value, setValue: handleValueChange }}>