From 9cdd200bc202ebce2629845f5f44d5adb6b5035b Mon Sep 17 00:00:00 2001 From: xiose Date: Thu, 19 Mar 2026 18:54:33 +0800 Subject: [PATCH] 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 --- web/src/features/review/use-review-list.ts | 4 ++-- web/src/pages/dashboard/profile-review-table.tsx | 6 +----- web/src/pages/dashboard/reviews.tsx | 9 +++++---- web/src/shared/ui/tabs.tsx | 15 ++++++++++----- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/web/src/features/review/use-review-list.ts b/web/src/features/review/use-review-list.ts index 225e00ed..d3a7c4ff 100644 --- a/web/src/features/review/use-review-list.ts +++ b/web/src/features/review/use-review-list.ts @@ -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), }) } diff --git a/web/src/pages/dashboard/profile-review-table.tsx b/web/src/pages/dashboard/profile-review-table.tsx index f91f6603..1937a3ee 100644 --- a/web/src/pages/dashboard/profile-review-table.tsx +++ b/web/src/pages/dashboard/profile-review-table.tsx @@ -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 ( diff --git a/web/src/pages/dashboard/reviews.tsx b/web/src/pages/dashboard/reviews.tsx index 5bdca8d1..101e1724 100644 --- a/web/src/pages/dashboard/reviews.tsx +++ b/web/src/pages/dashboard/reviews.tsx @@ -36,6 +36,7 @@ export function ReviewsPage() { APPROVED: 0, REJECTED: 0, }) + const [activeStatus, setActiveStatus] = useState('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() { - + setActiveStatus(v as ReviewStatus)}> void @@ -15,13 +16,17 @@ interface TabsContextValue { const TabsContext = React.createContext(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 (