mirror of
https://gitlab.massbug.com/massbug/judge4c.git
synced 2025-07-04 09:41:34 +00:00
feat(dashboard): refactor layout and remove unused components, integrate new sidebar and breadcrumb structure
This commit is contained in:
parent
e41b1e4590
commit
dc42896efe
@ -1,26 +1,49 @@
|
|||||||
import { Navbar } from "@/components/navbar";
|
import {
|
||||||
import { Sidebar } from "@/components/sidebar";
|
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 {
|
interface DashboardLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DashboardLayout = ({ children }: DashboardLayoutProps) => {
|
export default function DashboardLayout({ children }: DashboardLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen">
|
<SidebarProvider>
|
||||||
<div className="flex w-full h-full">
|
<AppSidebar />
|
||||||
<div className="fixed left-0 top-0 hidden lg:block lg:w-[264px] h-full overflow-y-auto">
|
<SidebarInset>
|
||||||
<Sidebar />
|
<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>
|
<div className="flex items-center gap-2 px-4">
|
||||||
<div className="lg:pl-[264px] w-full">
|
<SidebarTrigger className="-ml-1" />
|
||||||
<div className="mx-auto max-w-screen-2xl h-full">
|
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||||
<Navbar />
|
<Breadcrumb>
|
||||||
<main className="h-full py-8 px-6 flex flex-col">{children}</main>
|
<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>
|
</header>
|
||||||
</div>
|
<div className="flex flex-1 flex-col p-4 pt-0">{children}</div>
|
||||||
</div>
|
</SidebarInset>
|
||||||
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default DashboardLayout;
|
|
||||||
|
@ -2,7 +2,7 @@ import { redirect } from "next/navigation";
|
|||||||
import { getCurrent } from "@/features/auth/actions";
|
import { getCurrent } from "@/features/auth/actions";
|
||||||
import { CreateWorkspaceForm } from "@/features/workspaces/components/create-workspace-form";
|
import { CreateWorkspaceForm } from "@/features/workspaces/components/create-workspace-form";
|
||||||
|
|
||||||
export default async function HomePage() {
|
export default async function DashboardPage() {
|
||||||
const user = await getCurrent();
|
const user = await getCurrent();
|
||||||
|
|
||||||
if (!user) redirect("/sign-in");
|
if (!user) redirect("/sign-in");
|
||||||
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user