feat(dashboard): add back navigation for subpages

This commit is contained in:
yun-zhi-ztl 2026-03-15 13:44:07 +08:00
parent f3c3c2beeb
commit 13840754ad
12 changed files with 72 additions and 43 deletions

View file

@ -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}}",

View file

@ -172,6 +172,7 @@
"dashboard": {
"title": "Dashboard",
"subtitle": "管理你的账户和 API Tokens",
"backToDashboard": "返回控制台",
"userInfo": "用户信息",
"userInfoDesc": "你的账户详情",
"loginVia": "通过 {{provider}} 登录",

View file

@ -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">

View file

@ -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">

View file

@ -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>

View file

@ -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>
)

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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>

View file

@ -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>
)

View 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>
)
}