feat(dashboard): refactor layout and remove unused components, integrate new sidebar and breadcrumb structure

This commit is contained in:
ngc2207 2025-02-04 14:35:26 +08:00
parent e41b1e4590
commit dc42896efe
9 changed files with 42 additions and 288 deletions

View File

@ -1,26 +1,49 @@
import { Navbar } from "@/components/navbar";
import { Sidebar } from "@/components/sidebar";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { AppSidebar } from "@/components/app-sidebar";
import { Separator } from "@/components/ui/separator";
interface DashboardLayoutProps {
children: React.ReactNode;
}
const DashboardLayout = ({ children }: DashboardLayoutProps) => {
export default function DashboardLayout({ children }: DashboardLayoutProps) {
return (
<div className="min-h-screen">
<div className="flex w-full h-full">
<div className="fixed left-0 top-0 hidden lg:block lg:w-[264px] h-full overflow-y-auto">
<Sidebar />
</div>
<div className="lg:pl-[264px] w-full">
<div className="mx-auto max-w-screen-2xl h-full">
<Navbar />
<main className="h-full py-8 px-6 flex flex-col">{children}</main>
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</div>
</div>
</div>
</header>
<div className="flex flex-1 flex-col p-4 pt-0">{children}</div>
</SidebarInset>
</SidebarProvider>
);
};
export default DashboardLayout;
}

View File

@ -2,7 +2,7 @@ import { redirect } from "next/navigation";
import { getCurrent } from "@/features/auth/actions";
import { CreateWorkspaceForm } from "@/features/workspaces/components/create-workspace-form";
export default async function HomePage() {
export default async function DashboardPage() {
const user = await getCurrent();
if (!user) redirect("/sign-in");

View File

@ -1,49 +0,0 @@
import { AppSidebar } from "@/components/app-sidebar";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { Separator } from "@/components/ui/separator";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
interface DashboardLayoutProps {
children: React.ReactNode;
}
export default function DashboardLayout({ children }: DashboardLayoutProps) {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="flex flex-1 flex-col p-4 pt-0">{children}</div>
</SidebarInset>
</SidebarProvider>
);
}

View File

@ -1,15 +0,0 @@
import { redirect } from "next/navigation";
import { getCurrent } from "@/features/auth/actions";
import { CreateWorkspaceForm } from "@/features/workspaces/components/create-workspace-form";
export default async function DashboardPage() {
const user = await getCurrent();
if (!user) redirect("/sign-in");
return (
<div className="h-full p-4">
<CreateWorkspaceForm />
</div>
);
}

View File

@ -1,30 +0,0 @@
"use client";
import { Sidebar } from "./sidebar";
import { Button } from "./ui/button";
import { MenuIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { Sheet, SheetContent, SheetTrigger } from "./ui/sheet";
export const MobileSidebar = () => {
const [isOpen, setIsOpen] = useState(false);
const pathname = usePathname();
useEffect(() => {
setIsOpen(false);
}, [pathname]);
return (
<Sheet modal={false} open={isOpen} onOpenChange={setIsOpen}>
<SheetTrigger asChild>
<Button variant="secondary" className="lg:hidden">
<MenuIcon className="size-4" />
</Button>
</SheetTrigger>
<SheetContent side="left" className="p-0">
<Sidebar />
</SheetContent>
</Sheet>
);
};

View File

@ -1,17 +0,0 @@
import { UserButton } from "@/features/auth/components/user-button";
import { MobileSidebar } from "./mobile-sidebar";
export const Navbar = () => {
return (
<nav className="pt-4 px-6 flex items-center justify-between">
<div className="flex-col hidden lg:flex">
<h1 className="text-2xl font-semibold">Home</h1>
<p className="text-muted-foreground">
Monitor all of your projects and tasks here
</p>
</div>
<MobileSidebar />
<UserButton />
</nav>
);
};

View File

@ -1,61 +0,0 @@
import {
GoHome,
GoHomeFill,
GoCheckCircle,
GoCheckCircleFill,
} from "react-icons/go";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { SettingsIcon, UsersIcon } from "lucide-react";
const routes = [
{
label: "Home",
href: "",
icon: GoHome,
activeIcon: GoHomeFill,
},
{
label: "My Tasks",
href: "/tasks",
icon: GoCheckCircle,
activeIcon: GoCheckCircleFill,
},
{
label: "Settings",
href: "/settings",
icon: SettingsIcon,
activeIcon: SettingsIcon,
},
{
label: "Members",
href: "/members",
icon: UsersIcon,
activeIcon: UsersIcon,
},
];
export const Navigation = () => {
return (
<ul className="flex flex-col">
{routes.map((item) => {
const isActive = false;
const Icon = isActive ? item.activeIcon : item.icon;
return (
<Link key={item.href} href={item.href}>
<div
className={cn(
"flex items-center gap-2.5 p-2.5 rounded-md font-medium hover:text-primary transition",
isActive && "shadow-sm hover:opacity-100 text-primary"
)}
>
<Icon className="size-5" />
{item.label}
</div>
</Link>
);
})}
</ul>
);
};

View File

@ -1,23 +0,0 @@
import Link from "next/link";
import Image from "next/image";
import { Navigation } from "./navigation";
import { Separator } from "./ui/separator";
export const Sidebar = () => {
return (
<aside className="h-full p-4 w-full">
<Link href="/">
<Image
src="/logo.svg"
alt="logo"
width={100}
height={50}
style={{ width: "auto", height: "auto" }}
priority
/>
</Link>
<Separator className="my-4" />
<Navigation />
</aside>
);
};

View File

@ -1,74 +0,0 @@
"use client";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useLogout } from "../api/use-logout";
import { Loader, LogOut } from "lucide-react";
import { useCurrent } from "../api/use-current";
import { Separator } from "@/components/ui/separator";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
export const UserButton = () => {
const { mutate: logout } = useLogout();
const { data: user, isLoading } = useCurrent();
if (isLoading) {
return (
<div className="size-10 rounded-full flex items-center justify-center border">
<Loader className="size-4 animate-spin text-muted-foreground" />
</div>
);
}
if (!user) {
return null;
}
const { name, email } = user;
const avatarFallback = name
? name.charAt(0).toUpperCase()
: email.charAt(0).toUpperCase() ?? "U";
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger className="outline-none relative">
<Avatar className="size-10 hover:opacity-75 transition border">
<AvatarFallback className="font-medium flex items-center justify-center">
{avatarFallback}
</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
side="bottom"
className="w-60"
sideOffset={10}
>
<div className="flex flex-col items-center justify-center gap-2 px-2.5 py-4">
<Avatar className="size-[52px] hover:opacity-75 transition border">
<AvatarFallback className="text-xl font-medium flex items-center justify-center">
{avatarFallback}
</AvatarFallback>
</Avatar>
<div className="flex flex-col items-center justify-center">
<p className="text-sm font-medium">{name || "User"}</p>
<p className="text-xs">{email}</p>
</div>
</div>
<Separator className="mb-1" />
<DropdownMenuItem
onClick={() => logout()}
className="h-10 flex items-center justify-center font-medium cursor-pointer"
>
<LogOut className="size-4 mr-2" />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};