Skip to content

Commit

Permalink
feat: admin layout
Browse files Browse the repository at this point in the history
  • Loading branch information
liony823 committed Apr 17, 2024
1 parent ed5405a commit 9faf98d
Show file tree
Hide file tree
Showing 37 changed files with 752 additions and 1,038 deletions.
Binary file modified bun.lockb
Binary file not shown.
9 changes: 9 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
redirects: () => {
return [
{
source:"/admin",
destination:"/admin/dashboard",
permanent: true
}
]
},
images: {
remotePatterns: [
{
Expand Down
4 changes: 2 additions & 2 deletions src/app/(site)/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { GITHUB } from '~/config/constants';
import { usePathname } from 'next/navigation';
import { cn } from '~/lib/utils';
import { clamp } from '~/lib/math';
import { UserInfo } from '~/app/(site)/UserInfo';
import { ThemeSwitcher } from '~/app/(site)/ThemeSwitcher';
import { UserInfo } from '~/app/_components/UserInfo';
import { ThemeSwitcher } from '~/app/_components/ThemeSwitcher';

const fromScale = 1;
const toScale = 36 / 64;
Expand Down
10 changes: 2 additions & 8 deletions src/app/(site)/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function DesktopNav({ className }: { className?: string }) {
style={{ background }}
aria-hidden="true"
/>
<ul className="flex px-3 dark:text-zinc-200 text-zinc-800 text-sm font-medium ">
<ul className="flex px-3 dark:text-zinc-200 text-zinc-800 text-sm leading-6 font-medium ">
{Navs.map((nav) => (
<NavItem href={nav.link} key={nav.link}>
{nav.label}
Expand All @@ -106,13 +106,7 @@ function DesktopNav({ className }: { className?: string }) {
);
}

function NavItem({
href,
children
}: {
href: string;
children: ReactNode;
}) {
function NavItem({ href, children }: { href: string; children: ReactNode }) {
const isActive = usePathname() === href;
return (
<li>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import { useState } from 'react';
'use client';
import { HTMLAttributes, useState } from 'react';
import { useTheme } from 'next-themes';
import { Tooltip } from '~/components/ui';
import { AnimatePresence, motion } from 'framer-motion';
import { MoonIcon, SunIcon } from '~/assets';
import { cn } from '~/lib/utils';

export function ThemeSwitcher() {
export function ThemeSwitcher({
className,
side = 'top',
...rest
}: HTMLAttributes<HTMLButtonElement> & {
side?: 'top' | 'left' | 'bottom' | 'right';
}) {
const [tooltipOpen, setTooltipOpen] = useState(false);
const { theme, setTheme } = useTheme();

return (
<Tooltip.Provider delayDuration={400}>
<Tooltip.Provider delayDuration={300} disableHoverableContent>
<Tooltip.Root open={tooltipOpen} onOpenChange={setTooltipOpen}>
<Tooltip.Trigger asChild>
<button
className="group h-10 flex items-center rounded-full bg-gradient-to-b from-zinc-50/20 to-white/80 px-3 text-xl font-medium text-zinc-800 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur-md focus:outline-none focus-visible:ring-2 dark:from-zinc-900/30 dark:to-zinc-800/80 dark:text-zinc-200 dark:ring-white/10 dark:hover:ring-white/20 dark:focus-visible:ring-yellow-500/80"
className={cn(
'group h-10 flex items-center justify-center rounded-full bg-gradient-to-b from-zinc-50/20 to-white/80 px-3 text-xl font-medium text-zinc-800 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur-md focus:outline-none focus-visible:ring-2 dark:from-zinc-900/30 dark:to-zinc-800/80 dark:text-zinc-200 dark:ring-white/10 dark:hover:ring-white/20 dark:focus-visible:ring-yellow-500/80',
className
)}
type="button"
onClick={() => setTheme(theme !== 'dark' ? 'dark' : 'light')}
{...rest}
>
<AnimatePresence initial={false} mode="popLayout">
{theme === 'dark' ? (
Expand Down Expand Up @@ -44,7 +56,7 @@ export function ThemeSwitcher() {
<AnimatePresence>
{tooltipOpen && (
<Tooltip.Portal forceMount>
<Tooltip.Content asChild>
<Tooltip.Content side={side} asChild>
<motion.div
initial={{ opacity: 0, scale: 0.96 }}
animate={{ opacity: 1, scale: 1 }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import { useMemo, useState } from 'react';
import {
SignedIn,
Expand Down
24 changes: 24 additions & 0 deletions src/app/admin/_components/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { SheetNavbar } from '~/app/admin/_components/sheetNavbar';
import { HeaderBreadcrumb } from '~/app/admin/_components/headerBreadcrumb';
import { SearchIcon } from '~/assets';
import { Input } from '~/components/ui';
import { UserInfo } from '~/app/_components/UserInfo';
import * as React from 'react';

export function Header() {
return (
<header className="sticky top-0 z-10 h-14 border-b bg-background flex items-center gap-4 px-4 sm:sticky sm:px-6 sm:h-auto sm:border-transparent sm:bg-transparent transition-colors">
<SheetNavbar />
<HeaderBreadcrumb />
<div className="relative flex-1 ml-auto sm:grow-0">
<SearchIcon className="absolute text-muted-foreground left-2.5 top-2.5" />
<Input
type="search"
placeholder="Search..."
className="rounded-lg sm:w-[200px] md:w-[280px] lg:w-[320px] bg-background pl-8"
/>
</div>
<UserInfo />
</header>
);
}
29 changes: 29 additions & 0 deletions src/app/admin/_components/headerBreadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';
import { Breadcrumb } from '~/components/ui';
import { HomeIcon } from '~/assets';
import * as React from 'react';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';

export function HeaderBreadcrumb() {
const pathname = usePathname();
const [pageTitle, setPageTitle] = React.useState('');
useEffect(() => {
setPageTitle(document.title);
}, [pathname]);
return (
<Breadcrumb className="hidden sm:flex">
<Breadcrumb.List>
<Breadcrumb.Item>
<Breadcrumb.Link href="#">
<HomeIcon className="text-lg"></HomeIcon>
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Page>
<Breadcrumb.Link href={pathname}>{pageTitle}</Breadcrumb.Link>
</Breadcrumb.Page>
</Breadcrumb.List>
</Breadcrumb>
);
}
47 changes: 47 additions & 0 deletions src/app/admin/_components/sheetNavbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client';
import { Button, Sheet } from '~/components/ui';
import { PannelLeftIcon } from '~/assets';
import Link from 'next/link';
import Image from 'next/image';
import { GITHUB } from '~/config/constants';
import * as React from 'react';
import { CommonMenus, SettingMenu } from '~/app/admin/_components/sidebar';

export function SheetNavbar() {
return (
<Sheet>
<Sheet.Trigger asChild>
<Button variant="outline" size="icon" className="sm:hidden">
<PannelLeftIcon className="h-5 w-5" />
<span className="sr-only">Toggle Menu</span>
</Button>
</Sheet.Trigger>
<Sheet.Content side="left">
<nav className="grid gap-6 text-lg font-medium">
<Link href="#" className="mb-5">
<Image
className="rounded-full bg-zinc-100 object-cover dark:bg-zinc-800"
src={GITHUB.DEFAULLT_AVATAR}
alt={GITHUB.DEFAULLT_NAME}
width={64}
height={64}
priority
></Image>
</Link>
{[...CommonMenus, SettingMenu].map((menu) => (
<div key={menu.link} className="flex justify-between">
<Link
href={menu.link}
className="flex flex-1 items-center gap-4 px-2.5 text-muted-foreground hover:text-foreground"
>
{menu.icon}
{menu.label}
</Link>
{menu.suffix}
</div>
))}
</nav>
</Sheet.Content>
</Sheet>
);
}
101 changes: 101 additions & 0 deletions src/app/admin/_components/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use client';
import Link from 'next/link';
import Image from 'next/image';
import { GITHUB } from '~/config/constants';
import { Tooltip } from '~/components/ui';
import { ThemeSwitcher } from '~/app/_components/ThemeSwitcher';
import * as React from 'react';
import type { ReactNode } from 'react';
import {
DashboardIcon,
MailboxIcon,
NotebookIcon,
RSSIcon,
SettingIcon
} from '~/assets';
import { cn } from '~/lib/utils';
import { usePathname } from 'next/navigation';

export interface IMenu {
label: string;
icon: ReactNode;
link: string;
suffix?: ReactNode;
}

export const CommonMenus: IMenu[] = [
{ label: '数据看板', icon: <DashboardIcon />, link: '/admin/dashboard' },
{ label: '博客管理', icon: <RSSIcon />, link: '/admin/blog' },
{ label: '笔记管理', icon: <NotebookIcon />, link: '/admin/notebook' },
{ label: '留言管理', icon: <MailboxIcon />, link: '/admin/mailbox' }
];

export const SettingMenu: IMenu = {
label: '后台设置',
icon: <SettingIcon />,
link: '/admin/setting',
suffix: <ThemeSwitcher />
};

export function Sidebar() {
const pathname = usePathname();
return (
<aside className="fixed inset-y-0 left-0 w-14 bg-background border-r hidden flex-col sm:flex">
<nav className="flex flex-col items-center gap-4 px-2 sm:py-4">
<Link href="#">
<Image
className="rounded-full bg-zinc-100 object-cover dark:bg-zinc-800"
src={GITHUB.DEFAULLT_AVATAR}
alt={GITHUB.DEFAULLT_NAME}
width={32}
height={32}
priority
></Image>
</Link>
{CommonMenus.map((menu) => (
<Tooltip.Provider delayDuration={100} key={menu.link}>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Link
href={menu.link}
className={cn(
'w-9 h-9 md:w-8 md:h-8 text-xl transition-colors text-muted-foreground hover:text-foreground bg-transparent rounded-lg flex items-center justify-center',
pathname === menu.link && 'bg-accent text-foreground'
)}
>
{menu.icon}
<span className="sr-only">{menu.label}</span>
</Link>
</Tooltip.Trigger>
<Tooltip.Content custom={false} side="right">
{menu.label}
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
))}
</nav>
<nav className="mt-auto flex flex-col items-center justify-end gap-4 px-2 sm:py-4">
<Tooltip.Provider delayDuration={100}>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Link
href={SettingMenu.link}
className={cn(
'w-9 h-9 md:w-8 md:h-8 text-xl transition-colors text-muted-foreground hover:text-foreground bg-transparent rounded-lg flex items-center justify-center',
pathname === SettingMenu.link && 'bg-accent text-foreground'
)}
>
{SettingMenu.icon}
<span className="sr-only">{SettingMenu.label}</span>
</Link>
</Tooltip.Trigger>
<Tooltip.Content custom={false} side="right">
{SettingMenu.label}
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
<ThemeSwitcher side="right" className="h-8 w-8 px-0 text-base" />
</nav>
</aside>
);
}
8 changes: 8 additions & 0 deletions src/app/admin/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Metadata } from 'next';

export const metadata: Metadata = {
title: '博客管理'
};
export default function Page() {
return <div>Blog</div>;
}
9 changes: 9 additions & 0 deletions src/app/admin/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Metadata } from 'next';

export const metadata: Metadata = {
title: '数据看板'
};

export default function Page() {
return <div>Dashboard</div>;
}
Loading

0 comments on commit 9faf98d

Please sign in to comment.