diff --git a/app/(app)/AppLayoutClient.tsx b/app/(app)/AppLayoutClient.tsx deleted file mode 100644 index 1d490fd..0000000 --- a/app/(app)/AppLayoutClient.tsx +++ /dev/null @@ -1,82 +0,0 @@ -'use client'; - -import React from 'react'; -import { usePathname } from 'next/navigation'; -import Link from 'next/link'; -import { ProtectedRoute, useAuth } from '@/lib/auth'; -import { LayoutDashboard, BarChart3, UserCircle } from 'lucide-react'; - -export default function AppLayoutClient({ - children, -}: { - children: React.ReactNode; -}) { - const { signOut, user } = useAuth(); - const pathname = usePathname(); - - const navigationItems = [ - { name: 'Dashboard', href: '/dashboard', icon: }, - { name: 'Analytics', href: '/analytics', icon: }, - { name: 'Account', href: '/account', icon: }, - ]; - - const handleSignOut = async () => { - await signOut(); - }; - - return ( - -
- {/* 侧边栏导航 */} -
-
- - ShortURL Analytics - -
- -
- - {/* 主内容区域 */} -
- {/* 顶部导航栏 */} -
-
-
- - {user?.email} - - -
-
-
- - {/* 页面内容 */} -
- {children} -
-
-
-
- ); -} \ No newline at end of file diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index 9f533c0..dc2a298 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -1,13 +1,10 @@ import '../globals.css'; import type { Metadata } from 'next'; -import { Inter } from 'next/font/google'; -import AppLayoutClient from './AppLayoutClient'; - -const inter = Inter({ subsets: ['latin'] }); +import { Sidebar } from '@/app/components/Sidebar'; export const metadata: Metadata = { title: 'ShortURL Analytics', - description: 'Analytics dashboard for ShortURL service', + description: 'Analytics for your shortened URLs', }; export default function AppLayout({ @@ -16,10 +13,16 @@ export default function AppLayout({ children: React.ReactNode; }) { return ( -
- - {children} - +
+ {/* 侧边栏 */} + + + {/* 主内容区域 */} +
+
+ {children} +
+
); } \ No newline at end of file diff --git a/app/components/Sidebar.tsx b/app/components/Sidebar.tsx new file mode 100644 index 0000000..f7526b0 --- /dev/null +++ b/app/components/Sidebar.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { + BarChartIcon, + HomeIcon, + PersonIcon, + ChevronLeftIcon, + ChevronRightIcon +} from '@radix-ui/react-icons'; + +interface NavItemProps { + href: string; + label: string; + icon: React.ReactNode; + isCollapsed: boolean; + isActive?: boolean; +} + +const NavItem = ({ href, label, icon, isCollapsed, isActive }: NavItemProps) => { + return ( + +
{icon}
+ {!isCollapsed && ( + + {label} + + )} + {isCollapsed && ( + {label} + )} + + ); +}; + +export function Sidebar() { + const [isCollapsed, setIsCollapsed] = useState(false); + const pathname = usePathname(); + + const toggleSidebar = () => { + setIsCollapsed(!isCollapsed); + }; + + const navigation = [ + { name: 'Dashboard', href: '/dashboard', icon: }, + { name: 'Analytics', href: '/analytics', icon: }, + { name: 'Account', href: '/account', icon: }, + ]; + + return ( +
+ {/* 顶部Logo和标题 */} +
+
+ S +
+ {!isCollapsed && ( + + ShortURL Analytics + + )} +
+ + {/* 导航菜单 */} +
+
    + {navigation.map((item) => ( +
  • + +
  • + ))} +
+
+ + {/* 底部折叠按钮 */} +
+ +
+
+ ); +} \ No newline at end of file diff --git a/package.json b/package.json index 68d2cb9..7ec33e5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@clickhouse/client": "^1.11.0", + "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-select": "^2.1.6", "@radix-ui/themes": "^3.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bb6ae0..0cbcb7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@clickhouse/client': specifier: ^1.11.0 version: 1.11.0 + '@radix-ui/react-icons': + specifier: ^1.3.2 + version: 1.3.2(react@19.0.0) '@radix-ui/react-popover': specifier: ^1.1.6 version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -657,6 +660,11 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + '@radix-ui/react-id@1.1.0': resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} peerDependencies: @@ -3531,6 +3539,10 @@ snapshots: '@types/react': 19.0.12 '@types/react-dom': 19.0.4(@types/react@19.0.12) + '@radix-ui/react-icons@1.3.2(react@19.0.0)': + dependencies: + react: 19.0.0 + '@radix-ui/react-id@1.1.0(@types/react@19.0.12)(react@19.0.0)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.12)(react@19.0.0)