mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-12 23:01:05 +00:00
The namespace detail page hardcoded a limit of 20 skills with no pagination controls, so namespaces with more than 20 skills appeared truncated. Add page state, wire it into the search query, and render the shared Pagination component when total exceeds PAGE_SIZE. Also reset page to 0 when the namespace param changes. Closes #350
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { useNavigate, useParams } from '@tanstack/react-router'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { NamespaceHeader } from '@/features/namespace/namespace-header'
|
|
import { SkillCard } from '@/features/skill/skill-card'
|
|
import { SkeletonList } from '@/shared/components/skeleton-loader'
|
|
import { EmptyState } from '@/shared/components/empty-state'
|
|
import { Pagination } from '@/shared/components/pagination'
|
|
import { useSearchSkills } from '@/shared/hooks/use-skill-queries'
|
|
import { useNamespaceDetail } from '@/shared/hooks/use-namespace-queries'
|
|
|
|
const PAGE_SIZE = 20
|
|
|
|
/**
|
|
* Public namespace page showing namespace metadata and the skills currently discoverable inside it.
|
|
*/
|
|
export function NamespacePage() {
|
|
const { t } = useTranslation()
|
|
const navigate = useNavigate()
|
|
const { namespace } = useParams({ from: '/space/$namespace' })
|
|
const [page, setPage] = useState(0)
|
|
|
|
// Reset page when namespace changes
|
|
useEffect(() => {
|
|
setPage(0)
|
|
}, [namespace])
|
|
|
|
const { data: namespaceData, isLoading: isLoadingNamespace } = useNamespaceDetail(namespace)
|
|
const { data: skillsData, isLoading: isLoadingSkills } = useSearchSkills({
|
|
namespace,
|
|
page,
|
|
size: PAGE_SIZE,
|
|
})
|
|
|
|
const totalPages = skillsData ? Math.max(Math.ceil(skillsData.total / skillsData.size), 1) : 1
|
|
|
|
const handleSkillClick = (slug: string) => {
|
|
navigate({ to: `/space/${namespace}/${encodeURIComponent(slug)}` })
|
|
}
|
|
|
|
if (isLoadingNamespace) {
|
|
return (
|
|
<div className="space-y-6 animate-fade-up">
|
|
<div className="h-12 w-48 animate-shimmer rounded-lg" />
|
|
<div className="h-6 w-96 animate-shimmer rounded-md" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!namespaceData) {
|
|
return <EmptyState title={t('namespace.notFound')} />
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-8 animate-fade-up">
|
|
<NamespaceHeader namespace={namespaceData} />
|
|
|
|
<div className="space-y-6">
|
|
<h2 className="text-2xl font-bold font-heading">{t('namespace.skillList')}</h2>
|
|
{isLoadingSkills ? (
|
|
<SkeletonList count={6} />
|
|
) : skillsData && skillsData.items.length > 0 ? (
|
|
<>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
{skillsData.items.map((skill, idx) => (
|
|
<div key={skill.id} className={`animate-fade-up delay-${Math.min(idx + 1, 6)}`}>
|
|
<SkillCard
|
|
skill={skill}
|
|
onClick={() => handleSkillClick(skill.slug)}
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{skillsData.total > PAGE_SIZE ? (
|
|
<Pagination page={page} totalPages={totalPages} onPageChange={setPage} />
|
|
) : null}
|
|
</>
|
|
) : (
|
|
<EmptyState
|
|
title={t('namespace.emptyTitle')}
|
|
description={t('namespace.emptyDescription')}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|