diff --git a/src/components/layout/app-sidebar.tsx b/src/components/layout/app-sidebar.tsx index 8c78dac..8ccab57 100644 --- a/src/components/layout/app-sidebar.tsx +++ b/src/components/layout/app-sidebar.tsx @@ -1,6 +1,6 @@ 'use client' -import { Bell, Bookmark, CircleSlash2, LayoutGrid, Rows3, Settings2, SquareDot, Tag } from "lucide-react" +import { Bell, Bookmark, ChevronRight, CircleSlash2, LayoutGrid, Rows3, Settings2, SquareDot, Tag } from "lucide-react" import { Sidebar, SidebarContent, @@ -10,8 +10,11 @@ import { SidebarGroupLabel, SidebarHeader, SidebarMenu, + SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, + SidebarMenuSub, + SidebarRail, SidebarTrigger, } from "@/components/ui/sidebar" import { DarkModeToggle } from "./dark-mode-toggle" @@ -19,6 +22,8 @@ import Link from "next/link" import { useConfig } from "@/contexts/config" import { useAlerts } from "@/contexts/alerts" import { alertFilter, flattenAlerts } from "../alerts/utils" +import { ViewConfig } from "@/config/types" +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible" const items = [ { @@ -38,14 +43,29 @@ const items = [ }, ] +type CategorizedViews = { [key: string]: { handle: string, view: ViewConfig }[] } export function AppSidebar() { const { config } = useConfig() - const { views } = config + const { views, viewCategories } = config const { alerts } = useAlerts() const flatAlerts = flattenAlerts(alerts) + // Group views by category + const categorizedViews = Object.entries(views).reduce((acc: CategorizedViews, [handle, view]) => { + if (handle !== 'default') { + const category = view.category || '__uncategorized__'; + if (!acc[category]) { + acc[category] = []; + } + acc[category].push({ handle, view }); + } + return acc; + }, {}); + const uncategorizedViews = categorizedViews.__uncategorized__ || []; + delete categorizedViews.__uncategorized__; + return ( {/* @@ -80,30 +100,61 @@ export function AppSidebar() { - {Object.entries(views).map(([handle, view]) => ( - handle === 'default' ? null : ( - - - + {Object.entries(categorizedViews).map(([category, views]) => ( + + + + -
- {view.name || handle} - - {flatAlerts.filter(alertFilter(view.filters)).length} - -
- -
+ {viewCategories[category]?.name || category} + +
+ + + + {views.map(({ handle, view }) => ( + + + +
+ {view.name || handle} +
+ +
+ + {flatAlerts.filter(alertFilter(view.filters)).length} + +
+ ))} +
+
- )))} + + ))} + {uncategorizedViews.map(({ handle, view }) => ( + + + +
+ {view.name || handle} +
+ +
+ + {flatAlerts.filter(alertFilter(view.filters)).length} + +
+ ))}
+ - - + {/* - -
+ */} + + + ) } diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index a5c70e5..23b6cc1 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -690,7 +690,7 @@ const SidebarMenuSub = React.forwardRef< ref={ref} data-sidebar="menu-sub" className={cn( - "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", + "ml-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border pl-2.5 py-0.5", "group-data-[collapsible=icon]:hidden", className )} diff --git a/src/config/types.ts b/src/config/types.ts index 31406d8..10e3737 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -8,11 +8,14 @@ const ClusterSchema = z.object({ labels: z.record(z.string()), }) -export type ClusterConfig = z.infer +export const ViewCategorySchema = z.object({ + name: z.string(), +}) export const ViewsSchema = z.object({ name: z.string().optional().default(''), groupBy: z.string(), + category: z.string().optional().default(''), filters: z.array( z.object({ label: z.string(), @@ -23,11 +26,13 @@ export const ViewsSchema = z.object({ ), }) -export type ViewConfig = z.infer - export const ConfigSchema = z.object({ clusters: z.array(ClusterSchema), + viewCategories: z.record(ViewCategorySchema), views: z.record(ViewsSchema), }) +export type ClusterConfig = z.infer +export type ViewCategoryConfig = z.infer +export type ViewConfig = z.infer export type Config = z.infer