@@ -13,6 +13,7 @@ import type { AppSidebarQuery as AppSidebarQueryType } from "./__generated__/App
|
|||||||
import { OrganizationSwitcher } from "@/components/OrganizationSwitcher";
|
import { OrganizationSwitcher } from "@/components/OrganizationSwitcher";
|
||||||
import { AppSidebarShell } from "./AppSidebarShell";
|
import { AppSidebarShell } from "./AppSidebarShell";
|
||||||
import { AppSidebarSkeleton } from "./AppSidebarSkeleton";
|
import { AppSidebarSkeleton } from "./AppSidebarSkeleton";
|
||||||
|
import { NavMain } from "./NavMain";
|
||||||
|
|
||||||
const AppSidebarQuery = graphql`
|
const AppSidebarQuery = graphql`
|
||||||
query AppSidebarQuery {
|
query AppSidebarQuery {
|
||||||
@@ -37,6 +38,7 @@ function AppSidebarContent({
|
|||||||
organizationSwitcher={
|
organizationSwitcher={
|
||||||
<OrganizationSwitcher organizations={data.viewer} />
|
<OrganizationSwitcher organizations={data.viewer} />
|
||||||
}
|
}
|
||||||
|
navMain={<NavMain />}
|
||||||
navUser={<NavUser viewer={data.viewer} />}
|
navUser={<NavUser viewer={data.viewer} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { useParams } from "react-router";
|
|
||||||
import { NavMain } from "./NavMain";
|
|
||||||
import { NavSecondary } from "./NavSecondary";
|
import { NavSecondary } from "./NavSecondary";
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
@@ -7,101 +5,28 @@ import {
|
|||||||
SidebarFooter,
|
SidebarFooter,
|
||||||
SidebarHeader,
|
SidebarHeader,
|
||||||
} from "./ui/sidebar";
|
} from "./ui/sidebar";
|
||||||
import {
|
|
||||||
BookOpen,
|
|
||||||
Users,
|
|
||||||
ToyBrick,
|
|
||||||
LifeBuoy,
|
|
||||||
Send,
|
|
||||||
Settings,
|
|
||||||
Building,
|
|
||||||
FileText,
|
|
||||||
} from "lucide-react";
|
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
interface AppSidebarShellProps extends React.ComponentProps<typeof Sidebar> {
|
interface AppSidebarShellProps extends React.ComponentProps<typeof Sidebar> {
|
||||||
navUser: ReactNode;
|
navUser: ReactNode;
|
||||||
|
navMain: ReactNode;
|
||||||
organizationSwitcher: ReactNode;
|
organizationSwitcher: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppSidebarShell({
|
export function AppSidebarShell({
|
||||||
navUser,
|
navUser,
|
||||||
|
navMain,
|
||||||
organizationSwitcher,
|
organizationSwitcher,
|
||||||
...props
|
...props
|
||||||
}: AppSidebarShellProps) {
|
}: AppSidebarShellProps) {
|
||||||
const { organizationId } = useParams();
|
|
||||||
const navItems = getNavItems(organizationId);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar variant="inset" {...props}>
|
<Sidebar variant="inset" {...props}>
|
||||||
<SidebarHeader>{organizationSwitcher}</SidebarHeader>
|
<SidebarHeader>{organizationSwitcher}</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<NavMain items={navItems.navMain} />
|
{navMain}
|
||||||
<NavSecondary items={navItems.navSecondary} className="mt-auto" />
|
<NavSecondary className="mt-auto" />
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>{navUser}</SidebarFooter>
|
<SidebarFooter>{navUser}</SidebarFooter>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AppSidebarShell } from "./AppSidebarShell";
|
import { AppSidebarShell } from "./AppSidebarShell";
|
||||||
|
import { NavMainSkeleton } from "./NavMainSkeleton";
|
||||||
import { NavUserSkeleton } from "./NavUserSkeleton";
|
import { NavUserSkeleton } from "./NavUserSkeleton";
|
||||||
import { OrganizationSwitcherSkeleton } from "./OrganizationSwitcherSkeleton";
|
import { OrganizationSwitcherSkeleton } from "./OrganizationSwitcherSkeleton";
|
||||||
import { Sidebar } from "./ui/sidebar";
|
import { Sidebar } from "./ui/sidebar";
|
||||||
@@ -9,6 +10,7 @@ export function AppSidebarSkeleton(
|
|||||||
return (
|
return (
|
||||||
<AppSidebarShell
|
<AppSidebarShell
|
||||||
organizationSwitcher={<OrganizationSwitcherSkeleton />}
|
organizationSwitcher={<OrganizationSwitcherSkeleton />}
|
||||||
|
navMain={<NavMainSkeleton />}
|
||||||
navUser={<NavUserSkeleton />}
|
navUser={<NavUserSkeleton />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,71 +19,35 @@ import {
|
|||||||
SidebarMenuSubButton,
|
SidebarMenuSubButton,
|
||||||
SidebarMenuSubItem,
|
SidebarMenuSubItem,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import {
|
||||||
|
BookOpen,
|
||||||
|
Building,
|
||||||
|
FileText,
|
||||||
|
Settings,
|
||||||
|
ToyBrick,
|
||||||
|
Users,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { NavMainSkeleton } from "./NavMainSkeleton";
|
||||||
|
|
||||||
export function NavMain({
|
interface NavItem {
|
||||||
items,
|
title: string;
|
||||||
}: {
|
url?: string;
|
||||||
items: {
|
icon: LucideIcon;
|
||||||
|
isActive?: boolean;
|
||||||
|
items?: {
|
||||||
title: string;
|
title: string;
|
||||||
url?: string;
|
url: string;
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
isActive?: boolean;
|
|
||||||
items?: {
|
|
||||||
title: string;
|
|
||||||
url: string;
|
|
||||||
}[];
|
|
||||||
}[];
|
}[];
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
export function NavMain() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { organizationId } = useParams();
|
const { organizationId } = useParams();
|
||||||
|
const items: NavItem[] = getNavItems(organizationId);
|
||||||
|
|
||||||
const noOrganizationSelected = organizationId === undefined;
|
if (!organizationId) {
|
||||||
|
return <NavMainSkeleton />;
|
||||||
if (noOrganizationSelected) {
|
|
||||||
return (
|
|
||||||
<SidebarGroup>
|
|
||||||
<SidebarGroupLabel>
|
|
||||||
<div className="h-4 w-28 rounded-md bg-gray-200 animate-pulse" />
|
|
||||||
</SidebarGroupLabel>
|
|
||||||
<SidebarMenu className="space-y-1.5">
|
|
||||||
{/* First item - simple item */}
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton className="animate-pulse">
|
|
||||||
<div className="h-4 w-4 rounded-md bg-gray-200" />
|
|
||||||
<div className="h-4 w-32 rounded-md bg-gray-200 ml-2" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
|
|
||||||
{/* Second item - with dropdown */}
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton className="animate-pulse">
|
|
||||||
<div className="h-4 w-4 rounded-md bg-gray-200" />
|
|
||||||
<div className="h-4 w-28 rounded-md bg-gray-200 ml-2" />
|
|
||||||
<div className="ml-auto">
|
|
||||||
<div className="h-4 w-4 rounded-md bg-gray-200" />
|
|
||||||
</div>
|
|
||||||
</SidebarMenuButton>
|
|
||||||
<SidebarMenuSub>
|
|
||||||
{[1, 2].map((i) => (
|
|
||||||
<SidebarMenuSubItem key={i}>
|
|
||||||
<SidebarMenuSubButton className="animate-pulse pl-8">
|
|
||||||
<div className="h-3 w-20 rounded-md bg-gray-200" />
|
|
||||||
</SidebarMenuSubButton>
|
|
||||||
</SidebarMenuSubItem>
|
|
||||||
))}
|
|
||||||
</SidebarMenuSub>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
|
|
||||||
{/* Third item - simple item */}
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton className="animate-pulse">
|
|
||||||
<div className="h-4 w-4 rounded-md bg-gray-200" />
|
|
||||||
<div className="h-4 w-24 rounded-md bg-gray-200 ml-2" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroup>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isItemActive = (item: { url?: string; items?: { url: string }[] }) => {
|
const isItemActive = (item: { url?: string; items?: { url: string }[] }) => {
|
||||||
@@ -171,3 +135,51 @@ export function NavMain({
|
|||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|||||||
55
apps/console/src/components/NavMainSkeleton.tsx
Normal file
55
apps/console/src/components/NavMainSkeleton.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SidebarGroup,
|
||||||
|
SidebarGroupLabel,
|
||||||
|
SidebarMenu,
|
||||||
|
SidebarMenuButton,
|
||||||
|
SidebarMenuItem,
|
||||||
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
|
export function NavMainSkeleton() {
|
||||||
|
return (
|
||||||
|
<SidebarGroup>
|
||||||
|
<SidebarGroupLabel className="pl-3">
|
||||||
|
<div className="py-0.5">
|
||||||
|
<div className="h-3 w-24 rounded-sm bg-gray-300 animate-pulse" />
|
||||||
|
</div>
|
||||||
|
</SidebarGroupLabel>
|
||||||
|
<SidebarMenu className="space-y-1.5">
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<SidebarMenuButton className="animate-pulse">
|
||||||
|
<div className="h-4 w-4 rounded-md bg-lime-5" />
|
||||||
|
<div className="py-[3px]">
|
||||||
|
<div className="h-3.5 w-32 rounded-md bg-gray-300" />
|
||||||
|
</div>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<SidebarMenuButton className="animate-pulse">
|
||||||
|
<div className="h-4 w-4 rounded-md bg-lime-6" />
|
||||||
|
<div className="py-[3px]">
|
||||||
|
<div className="h-3.5 w-32 rounded-md bg-gray-300" />
|
||||||
|
</div>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<SidebarMenuButton className="animate-pulse">
|
||||||
|
<div className="h-4 w-4 rounded-md bg-lime-6" />
|
||||||
|
<div className="py-[3px]">
|
||||||
|
<div className="h-3.5 w-32 rounded-md bg-gray-300" />
|
||||||
|
</div>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<SidebarMenuButton className="animate-pulse">
|
||||||
|
<div className="h-4 w-4 rounded-md bg-lime-6" />
|
||||||
|
<div className="py-[3px]">
|
||||||
|
<div className="h-3.5 w-32 rounded-md bg-gray-300" />
|
||||||
|
</div>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
</SidebarMenu>
|
||||||
|
</SidebarGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { type LucideIcon } from "lucide-react";
|
|
||||||
import { useLocation } from "react-router";
|
|
||||||
|
|
||||||
|
import { LifeBuoy, Send } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
SidebarGroupContent,
|
SidebarGroupContent,
|
||||||
@@ -10,43 +9,27 @@ import {
|
|||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
|
||||||
export function NavSecondary({
|
const navItems = [
|
||||||
items,
|
{
|
||||||
...props
|
title: "Support",
|
||||||
}: {
|
url: "#",
|
||||||
items: {
|
icon: LifeBuoy,
|
||||||
title: string;
|
},
|
||||||
url: string;
|
{
|
||||||
icon: LucideIcon;
|
title: "Feedback",
|
||||||
}[];
|
url: "#",
|
||||||
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
icon: Send,
|
||||||
const location = useLocation();
|
},
|
||||||
const noOrganizationSelected = location.pathname === "/";
|
];
|
||||||
|
|
||||||
if (noOrganizationSelected) {
|
|
||||||
return (
|
|
||||||
<SidebarGroup {...props}>
|
|
||||||
<SidebarGroupContent>
|
|
||||||
<SidebarMenu>
|
|
||||||
{[1, 2].map((i) => (
|
|
||||||
<SidebarMenuItem key={i}>
|
|
||||||
<SidebarMenuButton size="sm" className="animate-pulse">
|
|
||||||
<div className="h-3 w-3 rounded-lg bg-gray-200" />
|
|
||||||
<div className="h-3 w-16 rounded-lg bg-gray-200 ml-2" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
))}
|
|
||||||
</SidebarMenu>
|
|
||||||
</SidebarGroupContent>
|
|
||||||
</SidebarGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export function NavSecondary(
|
||||||
|
props: React.ComponentPropsWithoutRef<typeof SidebarGroup>
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<SidebarGroup {...props}>
|
<SidebarGroup {...props}>
|
||||||
<SidebarGroupContent>
|
<SidebarGroupContent>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
{items.map((item) => (
|
{navItems.map((item) => (
|
||||||
<SidebarMenuItem key={item.title}>
|
<SidebarMenuItem key={item.title}>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
asChild={item.url !== "#"}
|
asChild={item.url !== "#"}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { NavUser_viewer$key } from "./__generated__/NavUser_viewer.graphql";
|
import { NavUser_viewer$key } from "./__generated__/NavUser_viewer.graphql";
|
||||||
|
import { NavUserSkeleton } from "./NavUserSkeleton";
|
||||||
|
|
||||||
export const navUserFragment = graphql`
|
export const navUserFragment = graphql`
|
||||||
fragment NavUser_viewer on User {
|
fragment NavUser_viewer on User {
|
||||||
@@ -44,28 +45,14 @@ export function NavUser({ viewer }: { viewer: NavUser_viewer$key }) {
|
|||||||
const { logout } = useAuth();
|
const { logout } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { organizationId } = useParams();
|
const { organizationId } = useParams();
|
||||||
const noOrganizationSelected = !organizationId;
|
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
await logout();
|
await logout();
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (noOrganizationSelected) {
|
if (!organizationId) {
|
||||||
return (
|
return <NavUserSkeleton />;
|
||||||
<SidebarMenu>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton size="lg" className="animate-pulse">
|
|
||||||
<div className="h-8 w-8 rounded-lg bg-gray-200" />
|
|
||||||
<div className="flex-1 space-y-1">
|
|
||||||
<div className="h-4 w-3/4 rounded-lg bg-gray-200" />
|
|
||||||
<div className="h-3 w-1/2 rounded-lg bg-gray-200" />
|
|
||||||
</div>
|
|
||||||
<div className="ml-auto h-4 w-4 rounded-lg bg-gray-200" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChevronsUpDown } from "lucide-react";
|
|
||||||
|
|
||||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
@@ -17,20 +14,14 @@ export function NavUserSkeleton() {
|
|||||||
isActive
|
isActive
|
||||||
disabled
|
disabled
|
||||||
size="lg"
|
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"
|
||||||
>
|
>
|
||||||
<Avatar className="h-9 w-9">
|
<div className="bg-gray-400 size-9 rounded-full animate-pulse" />
|
||||||
<AvatarFallback className="bg-slate-400 text-gray-100 animate-pulse" />
|
<div className="flex-1 space-y-[3px] animate-pulse">
|
||||||
</Avatar>
|
<div className="h-3.5 w-20 rounded-sm bg-gray-400" />
|
||||||
<div className="grid flex-1">
|
<div className="h-3.5 w-30 rounded-sm bg-gray-400" />
|
||||||
<div className="py-[3px]">
|
|
||||||
<div className="h-3.5 animate-pulse w-20 rounded-sm bg-slate-500" />
|
|
||||||
</div>
|
|
||||||
<div className="py-[3px]">
|
|
||||||
<div className="h-3.5 animate-pulse w-30 rounded-sm bg-slate-500" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ChevronsUpDown className="ml-auto size-4" />
|
<div className="ml-auto size-4 rounded-lg bg-gray-400" />
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
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 { graphql, useFragment } from "react-relay";
|
||||||
import { Link, useNavigate, useParams } from "react-router";
|
import { Link, useNavigate, useParams } from "react-router";
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
OrganizationSwitcher_organizations$data,
|
OrganizationSwitcher_organizations$data,
|
||||||
} from "./__generated__/OrganizationSwitcher_organizations.graphql";
|
} from "./__generated__/OrganizationSwitcher_organizations.graphql";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { OrganizationSwitcherSkeleton } from "./OrganizationSwitcherSkeleton";
|
||||||
|
|
||||||
export const organizationSwitcherFragment = graphql`
|
export const organizationSwitcherFragment = graphql`
|
||||||
fragment OrganizationSwitcher_organizations on User {
|
fragment OrganizationSwitcher_organizations on User {
|
||||||
@@ -95,20 +96,7 @@ export function OrganizationSwitcher({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!hasOrganizations) {
|
if (!hasOrganizations) {
|
||||||
return (
|
return <OrganizationSwitcherSkeleton />;
|
||||||
<SidebarMenu>
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton size="lg" className="animate-pulse">
|
|
||||||
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-gray-200" />
|
|
||||||
<div className="flex-1 space-y-1">
|
|
||||||
<div className="h-4 w-3/4 rounded-lg bg-gray-200" />
|
|
||||||
<div className="h-3 w-1/2 rounded-lg bg-gray-200" />
|
|
||||||
</div>
|
|
||||||
<div className="ml-auto h-4 w-4 rounded-lg bg-gray-200" />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
</SidebarMenu>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -118,32 +106,33 @@ export function OrganizationSwitcher({
|
|||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
size="lg"
|
size="lg"
|
||||||
className={`data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground gap-2.5 ${
|
className={cn(
|
||||||
!currentOrganization
|
"data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground gap-2.5",
|
||||||
? "border border-dashed border-gray-400"
|
!currentOrganization && "border border-dashed border-gray-300"
|
||||||
: ""
|
)}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-slate-400 text-sidebar-primary-foreground overflow-hidden">
|
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-slate-400 text-gray-100">
|
||||||
{currentOrganization ? (
|
{currentOrganization ? (
|
||||||
<LogoComponent org={currentOrganization} className="size-8" />
|
<LogoComponent org={currentOrganization} className="size-8" />
|
||||||
) : (
|
) : (
|
||||||
<Building className="size-8 text-gray-400" />
|
<div className="size-8 bg-slate-400 rounded-sm" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid text-left leading-tight">
|
<div className="grid text-left leading-tight">
|
||||||
<span
|
{currentOrganization ? (
|
||||||
className={`truncate font-medium text-lg leading-5 ${
|
<>
|
||||||
!currentOrganization ? "text-gray-500" : "text-gray-900"
|
<span className="truncate font-medium text-lg leading-5 text-gray-900">
|
||||||
}`}
|
{currentOrganization.name}
|
||||||
>
|
</span>
|
||||||
{currentOrganization
|
<span className="truncate text-xs text-gray-500 font-medium">
|
||||||
? currentOrganization.name
|
Free
|
||||||
: "Select Organization"}
|
</span>
|
||||||
</span>
|
</>
|
||||||
<span className="truncate text-xs text-gray-500 font-medium">
|
) : (
|
||||||
{currentOrganization ? "Free" : "No organization selected"}
|
<span className="truncate text-gray-500 font-medium">
|
||||||
</span>
|
Select Organization
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ChevronsUpDown className="ml-auto" />
|
<ChevronsUpDown className="ml-auto" />
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChevronsUpDown } from "lucide-react";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
@@ -16,16 +14,16 @@ export function OrganizationSwitcherSkeleton() {
|
|||||||
size="lg"
|
size="lg"
|
||||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground gap-2.5"
|
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground gap-2.5"
|
||||||
>
|
>
|
||||||
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-slate-300 text-sidebar-primary-foreground animate-pulse" />
|
<div className="size-8 items-center justify-center rounded-md bg-gray-300 animate-pulse" />
|
||||||
<div className="grid text-left leading-tight">
|
<div className="flex flex-col items-start">
|
||||||
<div className="py-[1px]">
|
<div className="py-[1px]">
|
||||||
<div className="h-4.5 animate-pulse w-15 rounded-sm bg-slate-400" />
|
<div className="h-4.5 animate-pulse w-16 rounded-sm bg-gray-300" />
|
||||||
</div>
|
</div>
|
||||||
<div className="py-0.5">
|
<div className="py-0.5">
|
||||||
<div className="h-3 animate-pulse w-8 rounded-sm bg-slate-400" />
|
<div className="h-3 animate-pulse w-8 rounded-sm bg-gray-300" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ChevronsUpDown className="ml-auto" />
|
<div className="ml-auto size-4 rounded-lg bg-gray-300 animate-pulse" />
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
|
|||||||
Reference in New Issue
Block a user