feat: 重设计 UI/UX — 展示/管理分离 + shadcn/ui + Admin 后台
- 引入 shadcn/ui(base-nova 风格,Tailwind v4,14 个组件) - 新增 Admin 后台路由架构:/admin(总览)、/admin/bookmarks(管理) - 重写首页为纯展示书签导航(BookmarkCard + CategoryGrid) - 新增 Admin 侧边栏导航(AdminSidebar + SidebarProvider) - 书签管理页:双栏布局 + Dialog 表单 + DnD 排序 + Toast 通知 - 修复 IconPicker overflow 裁切(改用 Dialog portal) - 修复嵌套 button hydration 错误(base-ui render prop) - 删除旧组件(CategorySection/BookmarkItem/IconPicker)和旧路由 - 所有新依赖归入 root catalog - 更新 AGENTS.md 文档(目录结构、shadcn 模式、render prop 规范)
This commit is contained in:
@@ -1,75 +1,43 @@
|
||||
import { createFileRoute, Link, useRouter } from '@tanstack/react-router'
|
||||
import * as icons from 'lucide-react'
|
||||
import { modules } from '@/modules/registry'
|
||||
import { authClient } from '@/server/auth/client'
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { createFileRoute, Link } from '@tanstack/react-router'
|
||||
import { Settings } from 'lucide-react'
|
||||
import { orpc } from '@/client/orpc'
|
||||
import { CategoryGrid } from '@/modules/bookmarks/components/CategoryGrid'
|
||||
import { GreetingHeader } from '@/modules/bookmarks/components/GreetingHeader'
|
||||
import { SearchBar } from '@/modules/bookmarks/components/SearchBar'
|
||||
|
||||
const iconComponents = icons as unknown as Record<string, typeof icons.Box>
|
||||
|
||||
export const Route = createFileRoute('/_protected' as never)({
|
||||
export const Route = createFileRoute('/_protected/' as never)({
|
||||
loader: async ({ context }: { context: { queryClient: QueryClient } }) => {
|
||||
await context.queryClient.ensureQueryData(orpc.bookmarks.category.list.queryOptions())
|
||||
},
|
||||
component: DashboardPage,
|
||||
})
|
||||
|
||||
function DashboardPage() {
|
||||
const router = useRouter()
|
||||
const { user } = Route.useRouteContext() as {
|
||||
user: {
|
||||
name: string
|
||||
email: string
|
||||
}
|
||||
}
|
||||
const enabledModules = modules.filter((mod) => mod.enabled)
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await authClient.signOut()
|
||||
router.navigate({ to: '/login' as never })
|
||||
}
|
||||
const { data: categories } = useSuspenseQuery(orpc.bookmarks.category.list.queryOptions())
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 px-4 py-12 sm:px-6">
|
||||
<div className="mx-auto max-w-4xl space-y-8">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-900">Kairos</h1>
|
||||
<p className="mt-1 text-slate-500">
|
||||
欢迎回来,{user.name} · {user.email}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSignOut}
|
||||
className="rounded-lg px-4 py-2 text-sm text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
|
||||
<div className="min-h-screen bg-stone-50/50 px-4 py-12 font-sans sm:px-6">
|
||||
<div className="mx-auto max-w-5xl space-y-12">
|
||||
<div className="flex items-center justify-between">
|
||||
<GreetingHeader />
|
||||
<Link
|
||||
to={'/admin' as never}
|
||||
className="rounded-full p-2.5 text-stone-400 transition-colors hover:bg-stone-100 hover:text-stone-600"
|
||||
title="管理后台"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
<Settings className="h-5 w-5" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{enabledModules.length === 0 ? (
|
||||
<div className="py-20 text-center text-slate-400">暂无可用模块</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{enabledModules.map((mod) => {
|
||||
const IconComponent = iconComponents[mod.icon] ?? icons.Box
|
||||
<div className="mx-auto max-w-2xl py-4">
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={mod.id}
|
||||
to={mod.route as never}
|
||||
className="group block rounded-2xl bg-white p-6 shadow-sm ring-1 ring-slate-100 transition-all hover:shadow-md hover:ring-slate-200"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-xl bg-indigo-50 transition-colors group-hover:bg-indigo-100">
|
||||
<IconComponent className="h-6 w-6 text-indigo-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-900">{mod.name}</h3>
|
||||
<p className="mt-0.5 text-sm text-slate-500">{mod.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<main>
|
||||
<CategoryGrid categories={categories} />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user