mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-07 02:57:51 +00:00
feat(dashboard): add back navigation for subpages
This commit is contained in:
parent
f3c3c2beeb
commit
13840754ad
12 changed files with 72 additions and 43 deletions
|
|
@ -172,6 +172,7 @@
|
|||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"subtitle": "Manage your account and API Tokens",
|
||||
"backToDashboard": "Back to Dashboard",
|
||||
"userInfo": "User Info",
|
||||
"userInfoDesc": "Your account details",
|
||||
"loginVia": "Logged in via {{provider}}",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@
|
|||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"subtitle": "管理你的账户和 API Tokens",
|
||||
"backToDashboard": "返回控制台",
|
||||
"userInfo": "用户信息",
|
||||
"userInfoDesc": "你的账户详情",
|
||||
"loginVia": "通过 {{provider}} 登录",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Button } from '@/shared/ui/button'
|
|||
import { Card } from '@/shared/ui/card'
|
||||
import { NamespaceBadge } from '@/shared/components/namespace-badge'
|
||||
import { EmptyState } from '@/shared/components/empty-state'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import { useMyNamespaces } from '@/shared/hooks/use-skill-queries'
|
||||
|
||||
export function MyNamespacesPage() {
|
||||
|
|
@ -37,13 +38,11 @@ export function MyNamespacesPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('myNamespaces.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('myNamespaces.subtitle')}</p>
|
||||
</div>
|
||||
<Button disabled>{t('myNamespaces.create')}</Button>
|
||||
</div>
|
||||
<DashboardPageHeader
|
||||
title={t('myNamespaces.title')}
|
||||
subtitle={t('myNamespaces.subtitle')}
|
||||
actions={<Button disabled>{t('myNamespaces.create')}</Button>}
|
||||
/>
|
||||
|
||||
{namespaces && namespaces.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import { Button } from '@/shared/ui/button'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { EmptyState } from '@/shared/components/empty-state'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import { useMySkills } from '@/shared/hooks/use-skill-queries'
|
||||
import { formatCompactCount } from '@/shared/lib/number-format'
|
||||
|
||||
|
|
@ -47,15 +48,15 @@ export function MySkillsPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('mySkills.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('mySkills.subtitle')}</p>
|
||||
</div>
|
||||
<Button size="lg" onClick={() => navigate({ to: '/dashboard/publish' })}>
|
||||
<DashboardPageHeader
|
||||
title={t('mySkills.title')}
|
||||
subtitle={t('mySkills.subtitle')}
|
||||
actions={(
|
||||
<Button size="lg" onClick={() => navigate({ to: '/dashboard/publish' })}>
|
||||
{t('mySkills.publishNew')}
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
{skills && skills.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import { NamespaceHeader } from '@/features/namespace/namespace-header'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import { useNamespaceDetail, useNamespaceMembers } from '@/shared/hooks/use-skill-queries'
|
||||
|
||||
export function NamespaceMembersPage() {
|
||||
|
|
@ -34,11 +35,14 @@ export function NamespaceMembersPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<DashboardPageHeader
|
||||
title={t('members.title')}
|
||||
subtitle={namespace ? `@${namespace.slug}` : undefined}
|
||||
/>
|
||||
<NamespaceHeader namespace={namespace} />
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold font-heading">{t('members.title')}</h2>
|
||||
<div className="flex items-center justify-end">
|
||||
<Button disabled>{t('members.addMember')}</Button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Card } from '@/shared/ui/card'
|
|||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/shared/ui/tabs'
|
||||
import { useNamespaceDetail } from '@/shared/hooks/use-skill-queries'
|
||||
import { useReviewList } from '@/features/review/use-review-list'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
|
||||
function ReviewListSection({ namespaceId }: { namespaceId?: number }) {
|
||||
const { t } = useTranslation()
|
||||
|
|
@ -56,12 +57,10 @@ export function NamespaceReviewsPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('nsReviews.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{namespace ? t('nsReviews.reviewsFor', { name: namespace.displayName }) : t('nsReviews.loadingNamespace')}
|
||||
</p>
|
||||
</div>
|
||||
<DashboardPageHeader
|
||||
title={t('nsReviews.title')}
|
||||
subtitle={namespace ? t('nsReviews.reviewsFor', { name: namespace.displayName }) : t('nsReviews.loadingNamespace')}
|
||||
/>
|
||||
<ReviewListSection namespaceId={namespace?.id} />
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Button } from '@/shared/ui/button'
|
|||
import { Card } from '@/shared/ui/card'
|
||||
import { Input } from '@/shared/ui/input'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/shared/ui/tabs'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
|
||||
function PromotionSection({ status }: { status: 'PENDING' | 'APPROVED' | 'REJECTED' }) {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
|
@ -70,10 +71,7 @@ export function PromotionsPage() {
|
|||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('promotions.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('promotions.subtitle')}</p>
|
||||
</div>
|
||||
<DashboardPageHeader title={t('promotions.title')} subtitle={t('promotions.subtitle')} />
|
||||
<Tabs defaultValue="PENDING">
|
||||
<TabsList>
|
||||
<TabsTrigger value="PENDING">{t('promotions.tabPending')}</TabsTrigger>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Select } from '@/shared/ui/select'
|
|||
import { Label } from '@/shared/ui/label'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { useMyNamespaces, usePublishSkill } from '@/shared/hooks/use-skill-queries'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import { toast } from '@/shared/lib/toast'
|
||||
import { ApiError } from '@/api/client'
|
||||
|
||||
|
|
@ -73,10 +74,7 @@ export function PublishPage() {
|
|||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('publish.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('publish.subtitle')}</p>
|
||||
</div>
|
||||
<DashboardPageHeader title={t('publish.title')} subtitle={t('publish.subtitle')} />
|
||||
|
||||
<Card className="p-4 bg-blue-500/5 border-blue-500/20">
|
||||
<div className="flex items-start gap-3">
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
TableRow,
|
||||
} from '@/shared/ui/table'
|
||||
import { useReviewList } from '@/features/review/use-review-list'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
|
||||
export function ReviewsPage() {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
|
@ -94,10 +95,7 @@ export function ReviewsPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('reviews.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('reviews.subtitle')}</p>
|
||||
</div>
|
||||
<DashboardPageHeader title={t('reviews.title')} subtitle={t('reviews.subtitle')} />
|
||||
|
||||
<Tabs defaultValue="PENDING">
|
||||
<TabsList>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||
import { SkillCard } from '@/features/skill/skill-card'
|
||||
import { useMyStars } from '@/shared/hooks/use-skill-queries'
|
||||
import { Card } from '@/shared/ui/card'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
|
||||
export function MyStarsPage() {
|
||||
const { t } = useTranslation()
|
||||
|
|
@ -21,10 +22,7 @@ export function MyStarsPage() {
|
|||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('stars.title')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('stars.subtitle')}</p>
|
||||
</div>
|
||||
<DashboardPageHeader title={t('stars.title')} subtitle={t('stars.subtitle')} />
|
||||
|
||||
{!skills || skills.length === 0 ? (
|
||||
<Card className="p-12 text-center text-muted-foreground">{t('stars.empty')}</Card>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { TokenList } from '@/features/token/token-list'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
|
||||
export function TokensPage() {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-up">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{t('tokens.pageTitle')}</h1>
|
||||
<p className="text-muted-foreground text-lg">{t('tokens.pageSubtitle')}</p>
|
||||
</div>
|
||||
<DashboardPageHeader
|
||||
title={t('tokens.pageTitle')}
|
||||
subtitle={t('tokens.pageSubtitle')}
|
||||
/>
|
||||
<TokenList />
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
31
web/src/shared/components/dashboard-page-header.tsx
Normal file
31
web/src/shared/components/dashboard-page-header.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { useNavigate } from '@tanstack/react-router'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
|
||||
interface DashboardPageHeaderProps {
|
||||
title: string
|
||||
subtitle?: string
|
||||
actions?: React.ReactNode
|
||||
}
|
||||
|
||||
export function DashboardPageHeader({ title, subtitle, actions }: DashboardPageHeaderProps) {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Button variant="ghost" className="px-0 text-muted-foreground hover:text-foreground" onClick={() => navigate({ to: '/dashboard' })}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
{t('dashboard.backToDashboard')}
|
||||
</Button>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold font-heading mb-2">{title}</h1>
|
||||
{subtitle ? <p className="text-muted-foreground text-lg">{subtitle}</p> : null}
|
||||
</div>
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue