mirror of
https://github.com/crocofied/CoreControl.git
synced 2025-12-17 15:36:50 +00:00
Sidebar Design Update
This commit is contained in:
parent
dec15c0ce0
commit
547212cd9e
@ -1,10 +1,25 @@
|
||||
import * as React from "react"
|
||||
"use client"
|
||||
|
||||
import type * as React from "react"
|
||||
import Image from "next/image"
|
||||
import { AppWindow, Settings, LayoutDashboardIcon, Briefcase, Server, Network, Activity } from "lucide-react"
|
||||
import { usePathname } from "next/navigation"
|
||||
import {
|
||||
AppWindow,
|
||||
Settings,
|
||||
LayoutDashboardIcon,
|
||||
Briefcase,
|
||||
Server,
|
||||
Network,
|
||||
Activity,
|
||||
LogOut,
|
||||
ChevronDown,
|
||||
} from "lucide-react"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
@ -13,12 +28,15 @@ import {
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
SidebarRail,
|
||||
SidebarFooter,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import Link from "next/link"
|
||||
import Cookies from "js-cookie"
|
||||
import { useRouter } from "next/navigation"
|
||||
import packageJson from "@/package.json"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
|
||||
|
||||
interface NavItem {
|
||||
title: string
|
||||
@ -33,7 +51,7 @@ const data: { navMain: NavItem[] } = {
|
||||
{
|
||||
title: "Dashboard",
|
||||
icon: LayoutDashboardIcon,
|
||||
url: "/dashboard"
|
||||
url: "/dashboard",
|
||||
},
|
||||
{
|
||||
title: "My Infrastructure",
|
||||
@ -72,23 +90,38 @@ const data: { navMain: NavItem[] } = {
|
||||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const logout = async () => {
|
||||
Cookies.remove('token')
|
||||
Cookies.remove("token")
|
||||
router.push("/")
|
||||
}
|
||||
|
||||
// Check if a path is active (exact match or starts with path for parent items)
|
||||
const isActive = (path: string) => {
|
||||
if (path === "#") return false
|
||||
return pathname === path || (path !== "/dashboard" && pathname?.startsWith(path))
|
||||
}
|
||||
|
||||
// Check if any child item is active
|
||||
const hasActiveChild = (items?: NavItem[]) => {
|
||||
if (!items) return false
|
||||
return items.some((item) => isActive(item.url))
|
||||
}
|
||||
|
||||
return (
|
||||
<Sidebar {...props}>
|
||||
<SidebarHeader>
|
||||
<SidebarHeader className="border-b border-sidebar-border/30 pb-2">
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton size="lg" asChild>
|
||||
<a href="https://github.com/crocofied/corecontrol">
|
||||
<Image src="/logo.png" width={48} height={48} alt="Logo"/>
|
||||
<SidebarMenuButton size="lg" asChild className="gap-3">
|
||||
<a href="https://github.com/crocofied/corecontrol" className="transition-all hover:opacity-80">
|
||||
<div className="flex items-center justify-center rounded-lg overflow-hidden bg-gradient-to-br from-teal-500 to-emerald-600 shadow-sm">
|
||||
<Image src="/logo.png" width={48} height={48} alt="CoreControl Logo" className="object-cover" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 leading-none">
|
||||
<span className="font-semibold">CoreControl</span>
|
||||
<span className="">v{packageJson.version}</span>
|
||||
<span className="font-semibold text-base">CoreControl</span>
|
||||
<span className="text-xs text-sidebar-foreground/70">v{packageJson.version}</span>
|
||||
</div>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
@ -96,44 +129,77 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
|
||||
<SidebarContent className="flex flex-col h-full">
|
||||
<SidebarContent className="flex flex-col h-full py-4">
|
||||
<SidebarGroup className="flex-grow">
|
||||
<SidebarMenu>
|
||||
{data.navMain.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild>
|
||||
<Link href={item.url} className="font-medium">
|
||||
{item.icon && <item.icon className="mr-2" />}
|
||||
{item.title}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
{item.items?.length && (
|
||||
<SidebarMenuSub>
|
||||
{item.items.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton
|
||||
asChild
|
||||
isActive={subItem.isActive ?? false}
|
||||
<SidebarGroupLabel className="text-xs font-medium text-sidebar-foreground/60 uppercase tracking-wider px-4 mb-2">
|
||||
Main Navigation
|
||||
</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{data.navMain.map((item) =>
|
||||
item.items?.length ? (
|
||||
<Collapsible key={item.title} defaultOpen={hasActiveChild(item.items)} className="group/collapsible">
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
className={cn(
|
||||
"font-medium transition-all",
|
||||
(hasActiveChild(item.items) || isActive(item.url)) &&
|
||||
"text-sidebar-accent-foreground bg-sidebar-accent/50",
|
||||
)}
|
||||
>
|
||||
<Link href={subItem.url}>
|
||||
{subItem.icon && <subItem.icon className="mr-2" />}
|
||||
{subItem.title}
|
||||
</Link>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
)}
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
{item.icon && <item.icon className="h-4 w-4" />}
|
||||
<span>{item.title}</span>
|
||||
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
{item.items.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton asChild isActive={isActive(subItem.url)} className="transition-all">
|
||||
<Link href={subItem.url} className="flex items-center">
|
||||
{subItem.icon && <subItem.icon className="h-3.5 w-3.5 mr-2" />}
|
||||
<span>{subItem.title}</span>
|
||||
</Link>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
) : (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className={cn(
|
||||
"font-medium transition-all",
|
||||
isActive(item.url) && "text-sidebar-accent-foreground bg-sidebar-accent/50",
|
||||
)}
|
||||
>
|
||||
<Link href={item.url}>
|
||||
{item.icon && <item.icon className="h-4 w-4" />}
|
||||
<span>{item.title}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
),
|
||||
)}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
|
||||
<div className="p-4">
|
||||
<Button variant="destructive" className="w-full" onClick={logout}>
|
||||
<SidebarFooter className="border-t border-sidebar-border/30 pt-4 mt-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start text-destructive hover:text-destructive hover:bg-destructive/10 border-none shadow-none"
|
||||
onClick={logout}
|
||||
>
|
||||
<LogOut className="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</div>
|
||||
</SidebarFooter>
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarRail />
|
||||
|
||||
33
components/ui/collapsible.tsx
Normal file
33
components/ui/collapsible.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
"use client"
|
||||
|
||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
||||
|
||||
function Collapsible({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
||||
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
||||
}
|
||||
|
||||
function CollapsibleTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
||||
return (
|
||||
<CollapsiblePrimitive.CollapsibleTrigger
|
||||
data-slot="collapsible-trigger"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CollapsibleContent({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
||||
return (
|
||||
<CollapsiblePrimitive.CollapsibleContent
|
||||
data-slot="collapsible-content"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "corecontrol",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "corecontrol",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.6.0",
|
||||
"@prisma/extension-accelerate": "^1.3.0",
|
||||
"@radix-ui/react-accordion": "^1.2.4",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||
"@radix-ui/react-checkbox": "^1.1.5",
|
||||
"@radix-ui/react-collapsible": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.7",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
||||
"@radix-ui/react-label": "^2.1.3",
|
||||
@ -4576,21 +4577,6 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "15.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.0.tgz",
|
||||
"integrity": "sha512-vHUQS4YVGJPmpjn7r5lEZuMhK5UQBNBRSB+iGDvJjaNk649pTIcRluDWNb9siunyLLiu/LDPHfvxBtNamyuLTw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"@radix-ui/react-accordion": "^1.2.4",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||
"@radix-ui/react-checkbox": "^1.1.5",
|
||||
"@radix-ui/react-collapsible": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.7",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
||||
"@radix-ui/react-label": "^2.1.3",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user