From 3ce981a06a6c571178597bb7400c33607590abd3 Mon Sep 17 00:00:00 2001 From: imbytecat Date: Mon, 30 Mar 2026 21:28:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Dashboard=20?= =?UTF-8?q?=E5=92=8C=E4=B9=A6=E7=AD=BE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/_protected/bookmarks/index.tsx | 92 +++++++++++++++++++ apps/server/src/routes/_protected/index.tsx | 76 +++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 apps/server/src/routes/_protected/bookmarks/index.tsx create mode 100644 apps/server/src/routes/_protected/index.tsx diff --git a/apps/server/src/routes/_protected/bookmarks/index.tsx b/apps/server/src/routes/_protected/bookmarks/index.tsx new file mode 100644 index 0000000..1ca7991 --- /dev/null +++ b/apps/server/src/routes/_protected/bookmarks/index.tsx @@ -0,0 +1,92 @@ +import type { QueryClient } from '@tanstack/react-query' +import { useMutation, useSuspenseQuery } from '@tanstack/react-query' +import { createFileRoute } from '@tanstack/react-router' +import { Plus, X } from 'lucide-react' +import { useState } from 'react' +import { orpc } from '@/client/orpc' +import { CategorySection } from '@/modules/bookmarks/components/CategorySection' +import { GreetingHeader } from '@/modules/bookmarks/components/GreetingHeader' +import { SearchBar } from '@/modules/bookmarks/components/SearchBar' + +export const Route = createFileRoute('/_protected/bookmarks/' as never)({ + component: BookmarksPage, + loader: async ({ context }: { context: { queryClient: QueryClient } }) => { + await context.queryClient.ensureQueryData(orpc.bookmarks.category.list.queryOptions()) + }, +}) + +function BookmarksPage() { + const categoriesQuery = useSuspenseQuery(orpc.bookmarks.category.list.queryOptions()) + const createCategory = useMutation(orpc.bookmarks.category.create.mutationOptions()) + const [showAddCategory, setShowAddCategory] = useState(false) + const [newCategoryName, setNewCategoryName] = useState('') + + const handleAddCategory = (e: React.FormEvent) => { + e.preventDefault() + if (newCategoryName.trim()) { + createCategory.mutate({ + name: newCategoryName.trim(), + orderId: categoriesQuery.data.length, + }) + setNewCategoryName('') + setShowAddCategory(false) + } + } + + return ( +
+
+ + + + +
+ {categoriesQuery.data.map((category) => ( + + ))} +
+ + {categoriesQuery.data.length === 0 && !showAddCategory && ( +
+

还没有任何分类

+

创建一个分类来开始添加书签

+
+ )} + + {showAddCategory ? ( +
+ setNewCategoryName(e.target.value)} + placeholder="分类名称" + className="flex-1 px-4 py-2.5 rounded-xl bg-white ring-1 ring-slate-200 outline-none focus:ring-2 focus:ring-indigo-500/50 text-sm" + /> + + +
+ ) : ( + + )} +
+
+ ) +} diff --git a/apps/server/src/routes/_protected/index.tsx b/apps/server/src/routes/_protected/index.tsx new file mode 100644 index 0000000..979f237 --- /dev/null +++ b/apps/server/src/routes/_protected/index.tsx @@ -0,0 +1,76 @@ +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' + +const iconComponents = icons as unknown as Record + +export const Route = createFileRoute('/_protected' as never)({ + 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 }) + } + + return ( +
+
+
+
+

Kairos

+

+ 欢迎回来,{user.name} · {user.email} +

+
+ +
+ + {enabledModules.length === 0 ? ( +
暂无可用模块
+ ) : ( +
+ {enabledModules.map((mod) => { + const IconComponent = iconComponents[mod.icon] ?? icons.Box + + return ( + +
+
+ +
+
+

{mod.name}

+

{mod.description}

+
+
+ + ) + })} +
+ )} +
+
+ ) +}