From d07736618195c227828fb3a91db824bae967b278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Fri, 7 Mar 2025 17:01:32 +0400 Subject: [PATCH] nav skeletons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- apps/console/src/components/AppSidebar.tsx | 2 + .../src/components/AppSidebarShell.tsx | 83 +----------- .../src/components/AppSidebarSkeleton.tsx | 2 + apps/console/src/components/NavMain.tsx | 128 ++++++++++-------- .../src/components/NavMainSkeleton.tsx | 55 ++++++++ apps/console/src/components/NavSecondary.tsx | 51 +++---- apps/console/src/components/NavUser.tsx | 19 +-- .../src/components/NavUserSkeleton.tsx | 21 +-- .../src/components/OrganizationSwitcher.tsx | 57 ++++---- .../OrganizationSwitcherSkeleton.tsx | 12 +- 10 files changed, 187 insertions(+), 243 deletions(-) create mode 100644 apps/console/src/components/NavMainSkeleton.tsx diff --git a/apps/console/src/components/AppSidebar.tsx b/apps/console/src/components/AppSidebar.tsx index 7a190ad8c..0cddb0dd6 100644 --- a/apps/console/src/components/AppSidebar.tsx +++ b/apps/console/src/components/AppSidebar.tsx @@ -13,6 +13,7 @@ import type { AppSidebarQuery as AppSidebarQueryType } from "./__generated__/App import { OrganizationSwitcher } from "@/components/OrganizationSwitcher"; import { AppSidebarShell } from "./AppSidebarShell"; import { AppSidebarSkeleton } from "./AppSidebarSkeleton"; +import { NavMain } from "./NavMain"; const AppSidebarQuery = graphql` query AppSidebarQuery { @@ -37,6 +38,7 @@ function AppSidebarContent({ organizationSwitcher={ } + navMain={} navUser={} {...props} /> diff --git a/apps/console/src/components/AppSidebarShell.tsx b/apps/console/src/components/AppSidebarShell.tsx index 93ccf66d9..147acb47b 100644 --- a/apps/console/src/components/AppSidebarShell.tsx +++ b/apps/console/src/components/AppSidebarShell.tsx @@ -1,5 +1,3 @@ -import { useParams } from "react-router"; -import { NavMain } from "./NavMain"; import { NavSecondary } from "./NavSecondary"; import { Sidebar, @@ -7,101 +5,28 @@ import { SidebarFooter, SidebarHeader, } from "./ui/sidebar"; -import { - BookOpen, - Users, - ToyBrick, - LifeBuoy, - Send, - Settings, - Building, - FileText, -} from "lucide-react"; import { ReactNode } from "react"; interface AppSidebarShellProps extends React.ComponentProps { navUser: ReactNode; + navMain: ReactNode; organizationSwitcher: ReactNode; } export function AppSidebarShell({ navUser, + navMain, organizationSwitcher, ...props }: AppSidebarShellProps) { - const { organizationId } = useParams(); - const navItems = getNavItems(organizationId); - return ( {organizationSwitcher} - - + {navMain} + {navUser} ); } - -function getNavItems(organizationId?: string) { - // Always return the same structure, but with or without URLs depending on whether an organization is selected - return { - navMain: [ - { - title: "Frameworks", - url: organizationId - ? `/organizations/${organizationId}/frameworks` - : undefined, - icon: BookOpen, - }, - { - title: "Organizations", - icon: Building, - url: organizationId - ? `/organizations/${organizationId}/peoples` - : undefined, - items: organizationId - ? [ - { - title: "Peoples", - url: `/organizations/${organizationId}/peoples`, - icon: Users, - }, - { - title: "Vendors", - url: `/organizations/${organizationId}/vendors`, - icon: ToyBrick, - }, - ] - : [], - }, - { - title: "Policies", - url: organizationId - ? `/organizations/${organizationId}/policies` - : undefined, - icon: FileText, - }, - { - title: "Settings", - url: organizationId - ? `/organizations/${organizationId}/settings` - : undefined, - icon: Settings, - }, - ], - navSecondary: [ - { - title: "Support", - url: "#", - icon: LifeBuoy, - }, - { - title: "Feedback", - url: "#", - icon: Send, - }, - ], - }; -} diff --git a/apps/console/src/components/AppSidebarSkeleton.tsx b/apps/console/src/components/AppSidebarSkeleton.tsx index 26b590884..99657f387 100644 --- a/apps/console/src/components/AppSidebarSkeleton.tsx +++ b/apps/console/src/components/AppSidebarSkeleton.tsx @@ -1,4 +1,5 @@ import { AppSidebarShell } from "./AppSidebarShell"; +import { NavMainSkeleton } from "./NavMainSkeleton"; import { NavUserSkeleton } from "./NavUserSkeleton"; import { OrganizationSwitcherSkeleton } from "./OrganizationSwitcherSkeleton"; import { Sidebar } from "./ui/sidebar"; @@ -9,6 +10,7 @@ export function AppSidebarSkeleton( return ( } + navMain={} navUser={} {...props} /> diff --git a/apps/console/src/components/NavMain.tsx b/apps/console/src/components/NavMain.tsx index e08961cc1..8610681e8 100644 --- a/apps/console/src/components/NavMain.tsx +++ b/apps/console/src/components/NavMain.tsx @@ -19,71 +19,35 @@ import { SidebarMenuSubButton, SidebarMenuSubItem, } from "@/components/ui/sidebar"; +import { + BookOpen, + Building, + FileText, + Settings, + ToyBrick, + Users, +} from "lucide-react"; +import { NavMainSkeleton } from "./NavMainSkeleton"; -export function NavMain({ - items, -}: { - items: { +interface NavItem { + title: string; + url?: string; + icon: LucideIcon; + isActive?: boolean; + items?: { title: string; - url?: string; + url: string; icon: LucideIcon; - isActive?: boolean; - items?: { - title: string; - url: string; - }[]; }[]; -}) { +} + +export function NavMain() { const location = useLocation(); const { organizationId } = useParams(); + const items: NavItem[] = getNavItems(organizationId); - const noOrganizationSelected = organizationId === undefined; - - if (noOrganizationSelected) { - return ( - - -
- - - {/* First item - simple item */} - - -
-
- - - - {/* Second item - with dropdown */} - - -
-
-
-
-
- - - {[1, 2].map((i) => ( - - -
- - - ))} - - - - {/* Third item - simple item */} - - -
-
- - - - - ); + if (!organizationId) { + return ; } const isItemActive = (item: { url?: string; items?: { url: string }[] }) => { @@ -171,3 +135,51 @@ export function NavMain({ ); } + +function getNavItems(organizationId?: string): NavItem[] { + // Always return the same structure, but with or without URLs depending on whether an organization is selected + return [ + { + title: "Frameworks", + url: organizationId + ? `/organizations/${organizationId}/frameworks` + : undefined, + icon: BookOpen, + }, + { + title: "Organizations", + icon: Building, + url: organizationId + ? `/organizations/${organizationId}/peoples` + : undefined, + items: organizationId + ? [ + { + title: "Peoples", + url: `/organizations/${organizationId}/peoples`, + icon: Users, + }, + { + title: "Vendors", + url: `/organizations/${organizationId}/vendors`, + icon: ToyBrick, + }, + ] + : [], + }, + { + title: "Policies", + url: organizationId + ? `/organizations/${organizationId}/policies` + : undefined, + icon: FileText, + }, + { + title: "Settings", + url: organizationId + ? `/organizations/${organizationId}/settings` + : undefined, + icon: Settings, + }, + ]; +} diff --git a/apps/console/src/components/NavMainSkeleton.tsx b/apps/console/src/components/NavMainSkeleton.tsx new file mode 100644 index 000000000..c80b302f3 --- /dev/null +++ b/apps/console/src/components/NavMainSkeleton.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar"; + +export function NavMainSkeleton() { + return ( + + +
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
+ + + + + ); +} diff --git a/apps/console/src/components/NavSecondary.tsx b/apps/console/src/components/NavSecondary.tsx index bd25ba84c..a233b352e 100644 --- a/apps/console/src/components/NavSecondary.tsx +++ b/apps/console/src/components/NavSecondary.tsx @@ -1,7 +1,6 @@ import * as React from "react"; -import { type LucideIcon } from "lucide-react"; -import { useLocation } from "react-router"; +import { LifeBuoy, Send } from "lucide-react"; import { SidebarGroup, SidebarGroupContent, @@ -10,43 +9,27 @@ import { SidebarMenuItem, } from "@/components/ui/sidebar"; -export function NavSecondary({ - items, - ...props -}: { - items: { - title: string; - url: string; - icon: LucideIcon; - }[]; -} & React.ComponentPropsWithoutRef) { - const location = useLocation(); - const noOrganizationSelected = location.pathname === "/"; - - if (noOrganizationSelected) { - return ( - - - - {[1, 2].map((i) => ( - - -
-
- - - ))} - - - - ); - } +const navItems = [ + { + title: "Support", + url: "#", + icon: LifeBuoy, + }, + { + title: "Feedback", + url: "#", + icon: Send, + }, +]; +export function NavSecondary( + props: React.ComponentPropsWithoutRef +) { return ( - {items.map((item) => ( + {navItems.map((item) => ( { await logout(); navigate("/login"); }; - if (noOrganizationSelected) { - return ( - - - -
-
-
-
-
-
- - - - ); + if (!organizationId) { + return ; } return ( diff --git a/apps/console/src/components/NavUserSkeleton.tsx b/apps/console/src/components/NavUserSkeleton.tsx index 6a94b772d..de6fc349c 100644 --- a/apps/console/src/components/NavUserSkeleton.tsx +++ b/apps/console/src/components/NavUserSkeleton.tsx @@ -1,8 +1,5 @@ "use client"; -import { ChevronsUpDown } from "lucide-react"; - -import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { SidebarMenu, SidebarMenuButton, @@ -17,20 +14,14 @@ export function NavUserSkeleton() { isActive disabled size="lg" - className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" + className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground animate-pulse" > - - - -
-
-
-
-
-
-
+
+
+
+
- +
diff --git a/apps/console/src/components/OrganizationSwitcher.tsx b/apps/console/src/components/OrganizationSwitcher.tsx index 886c6cb48..1709fde0a 100644 --- a/apps/console/src/components/OrganizationSwitcher.tsx +++ b/apps/console/src/components/OrganizationSwitcher.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useEffect } from "react"; -import { ChevronsUpDown, Plus, Building } from "lucide-react"; +import { ChevronsUpDown, Plus } from "lucide-react"; import { graphql, useFragment } from "react-relay"; import { Link, useNavigate, useParams } from "react-router"; @@ -25,6 +25,7 @@ import { OrganizationSwitcher_organizations$data, } from "./__generated__/OrganizationSwitcher_organizations.graphql"; import { cn } from "@/lib/utils"; +import { OrganizationSwitcherSkeleton } from "./OrganizationSwitcherSkeleton"; export const organizationSwitcherFragment = graphql` fragment OrganizationSwitcher_organizations on User { @@ -95,20 +96,7 @@ export function OrganizationSwitcher({ }; if (!hasOrganizations) { - return ( - - - -
-
-
-
-
-
- - - - ); + return ; } return ( @@ -118,32 +106,33 @@ export function OrganizationSwitcher({ -
+
{currentOrganization ? ( ) : ( - +
)}
- - {currentOrganization - ? currentOrganization.name - : "Select Organization"} - - - {currentOrganization ? "Free" : "No organization selected"} - + {currentOrganization ? ( + <> + + {currentOrganization.name} + + + Free + + + ) : ( + + Select Organization + + )}
diff --git a/apps/console/src/components/OrganizationSwitcherSkeleton.tsx b/apps/console/src/components/OrganizationSwitcherSkeleton.tsx index 60efbebfd..3a06b5107 100644 --- a/apps/console/src/components/OrganizationSwitcherSkeleton.tsx +++ b/apps/console/src/components/OrganizationSwitcherSkeleton.tsx @@ -1,7 +1,5 @@ "use client"; -import { ChevronsUpDown } from "lucide-react"; - import { SidebarMenu, SidebarMenuButton, @@ -16,16 +14,16 @@ export function OrganizationSwitcherSkeleton() { size="lg" className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground gap-2.5" > -
-
+
+
-
+
-
+
- +