fix: sidebar in user management

This commit is contained in:
Nathnael
2026-07-22 11:54:54 +00:00
parent 5002ccb0df
commit 8389b2735d
5 changed files with 271 additions and 309 deletions

View File

@@ -626,7 +626,9 @@ const filterSidebarByPermission = (
const filterItems = (items: SidebarItem[]): SidebarItem[] =>
items
.map((item) =>
item.children ? { ...item, children: filterItems(item.children) } : item,
item.children
? { ...item, children: filterItems(item.children) }
: item,
)
.filter((item) => {
if (etGl || djGl) {
@@ -642,18 +644,6 @@ const filterSidebarByPermission = (
return true;
});
// Recursive: a group's own permission gates the whole subtree, leaves are
// checked individually, and a group with no surviving children disappears.
const filterItems = (items: SidebarItem[]): SidebarItem[] =>
items.flatMap((item) => {
if (item.children?.length) {
if (item.permission && !permissionAllowed(item)) return [];
const children = filterItems(item.children);
return children.length ? [{ ...item, children }] : [];
}
return itemAllowed(item) ? [item] : [];
});
return sections
.map((section) => ({
...section,

View File

@@ -88,8 +88,8 @@ export const resolveModuleConfig = (config: TenantConfig): ModuleConfig => ({
});
const defaultConfig: TenantConfig = {
appName: "Smart Office",
organizationName: "Smart Office",
appName: "EDR Freight",
organizationName: "Ethio-Djibouti Railways",
canUseAttachmentFromDMS: false,
logo: "/assets/TriaTradinglogo.png",
primaryColor: "#1b354d",
@@ -116,8 +116,8 @@ const defaultConfig: TenantConfig = {
const tenantConfigs: Record<string, TenantConfig> = {
localhost: {
appName: "Smart Office",
organizationName: "Addis Ababa City Administration",
appName: "EDR Freight",
organizationName: "Ethio-Djibouti Railways",
logo: "",
primaryColor: "#0EA371",
moduleConfig: {

View File

@@ -104,9 +104,9 @@ const Top: React.FC<HeaderProps> = ({
const activePositionName =
currentLanguage === "am"
? userDetails?.employee?.[0]?.positions?.[0]?.name?.am ||
userDetails?.employee?.[0]?.positions?.[0]?.name?.en
userDetails?.employee?.[0]?.positions?.[0]?.name?.en
: userDetails?.employee?.[0]?.positions?.[0]?.name?.en ||
userDetails?.employee?.[0]?.positions?.[0]?.name?.am;
userDetails?.employee?.[0]?.positions?.[0]?.name?.am;
const normalizedUserType = userDetails?.userType?.trim().toLowerCase() || "";
const userTypeLabel =
@@ -114,8 +114,8 @@ const Top: React.FC<HeaderProps> = ({
? t("header.user")
: normalizedUserType
? normalizedUserType
.replace(/[_-]/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase())
.replace(/[_-]/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase())
: "";
const roleLabel =
@@ -123,8 +123,8 @@ const Top: React.FC<HeaderProps> = ({
userTypeLabel ||
(normalizedUserType
? userDetails?.roles?.[0]?.key
?.replace(/[:_]/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase())
?.replace(/[:_]/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase())
: "");
const { permissionKeys } = useUserDetail(userDetails as MeDto);
@@ -159,57 +159,57 @@ const Top: React.FC<HeaderProps> = ({
},
...(moduleConfig.recordManagement
? [
{
id: "recordManagement",
label: t("nav.Record Management", "Record Management"),
path: "/record-management/dashboard",
},
]
{
id: "recordManagement",
label: t("nav.Record Management", "Record Management"),
path: "/record-management/dashboard",
},
]
: []),
...(moduleConfig.performance
? [
{
id: "performanceManagement",
label: t("nav.PerformanceManagement", "Performance Management"),
path: "/performance-management/plan-years",
},
]
{
id: "performanceManagement",
label: t("nav.PerformanceManagement", "Performance Management"),
path: "/performance-management/plan-years",
},
]
: []),
...(moduleConfig.objective
? [
{
id: "objectiveManagement",
label: t("nav.objectiveManagement", "Objective Management"),
path: "/objective-management/plan-years",
},
]
{
id: "objectiveManagement",
label: t("nav.objectiveManagement", "Objective Management"),
path: "/objective-management/plan-years",
},
]
: []),
...(moduleConfig.dms
? [
{
id: "documentManagement",
label: t("nav.DocumentManagement", "Document Management"),
path: "/dms/dashboard",
},
]
{
id: "documentManagement",
label: t("nav.DocumentManagement", "Document Management"),
path: "/dms/dashboard",
},
]
: []),
...(isOrgAdmin && moduleConfig.siteManagement
? [
{
id: "orgAdmin",
label: t("nav.admin", "Admin"),
path: "/user-management/user_management-dashboard",
},
]
{
id: "orgAdmin",
label: t("nav.admin", "Admin"),
path: "/user-management/user_management-dashboard",
},
]
: []),
...(isSuperAdmin
? [
{
id: "superAdmin",
label: t("OrganizationAdmin", "Super Admin"),
path: "/user-management/dashboard",
},
]
{
id: "superAdmin",
label: t("OrganizationAdmin", "Super Admin"),
path: "/user-management/dashboard",
},
]
: []),
];
@@ -256,8 +256,8 @@ const Top: React.FC<HeaderProps> = ({
};
return (
<div className="flex h-24 flex-col">
<header className="fixed left-0 right-0 top-0 z-40 h-16 border-b border-primary-100/70 bg-white/90 shadow-sm backdrop-blur-md dark:border-gray-800 dark:bg-gray-900/90">
<div className="flex h-14 flex-col">
<header className="fixed left-0 right-0 top-0 z-40 h-14 border-b border-primary-100/70 bg-white/90 shadow-sm backdrop-blur-md dark:border-gray-800 dark:bg-gray-900/90">
<div className="flex h-full items-center justify-between px-2 sm:px-4 md:px-6">
<div className="flex min-w-0 flex-1 items-center gap-2 md:gap-3">
<DropdownMenu>
@@ -394,7 +394,7 @@ const Top: React.FC<HeaderProps> = ({
className={cn(
"relative h-9 w-9 rounded-xl bg-white/90 text-gray-600 shadow-sm ring-1 ring-inset ring-black/5 transition-colors hover:bg-primary-50 hover:text-primary-700 hover:ring-primary-200 dark:bg-gray-800/90 dark:text-gray-300 dark:ring-white/10 dark:hover:bg-primary-900/20 dark:hover:text-primary-300 dark:hover:ring-primary-700/40 sm:h-10 sm:w-10",
openNotifications &&
"bg-primary-50 text-primary-700 ring-primary-300 dark:bg-primary-900/30 dark:text-primary-300 dark:ring-primary-700/60",
"bg-primary-50 text-primary-700 ring-primary-300 dark:bg-primary-900/30 dark:text-primary-300 dark:ring-primary-700/60",
)}
aria-label={t("header.notifications")}
onClick={() =>
@@ -409,7 +409,7 @@ const Top: React.FC<HeaderProps> = ({
className={cn(
"h-4 w-4 transition-colors",
openNotifications &&
"fill-primary-100 text-primary-700 dark:fill-primary-900/40 dark:text-primary-300",
"fill-primary-100 text-primary-700 dark:fill-primary-900/40 dark:text-primary-300",
)}
/>
{unseenCount > 0 && (
@@ -479,7 +479,7 @@ const Top: React.FC<HeaderProps> = ({
className={cn(
"flex cursor-pointer items-center justify-between rounded-lg px-3 py-2.5 text-sm text-gray-700 transition-colors hover:bg-primary-50 dark:text-gray-200 dark:hover:bg-primary-900/30",
currentLanguage === lang.value &&
"bg-primary-100 text-primary-800 dark:bg-primary-800/50 dark:text-white",
"bg-primary-100 text-primary-800 dark:bg-primary-800/50 dark:text-white",
)}
>
<div className="flex items-center gap-2">

View File

@@ -1,4 +1,4 @@
import { NavLink } from "react-router-dom";
import { Link, useLocation } from "react-router-dom";
import {
Archive,
BarChart,
@@ -8,28 +8,48 @@ import {
FileText,
Globe,
Settings,
ShieldAlert,
Users2,
UsersRound,
} from "lucide-react";
import { useAuth } from "@/shared/context/AuthContext";
import { usePermissions } from "@/shared/context/PermissionContext";
import { useTranslation } from "react-i18next";
import { useAuth } from "@/shared/context/AuthContext";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/shared/common/ui/sidebar";
export interface MenuItem {
label: string;
href: string;
icon: React.ReactNode;
roles?: string[];
permissions?: string[];
isPrimary?: boolean;
displayLabel?: string;
children?: MenuItem[];
/** Sidebar section this item is bucketed under. */
group: string;
}
// Section render order; groups with no role-visible items are skipped.
const GROUP_ORDER = [
"Overview",
"Organizations",
"Content",
"Records",
"Configuration",
"Archive",
"System",
];
export const AppMenuTabs = () => {
const { user } = useAuth();
const { pathname } = useLocation();
const { setOpenMobile } = useSidebar();
const { t } = useTranslation();
const userRoles = user?.roles.map((role) => role.key) || [];
const menuItems: MenuItem[] = [
@@ -38,282 +58,193 @@ export const AppMenuTabs = () => {
href: "/user-management/dashboard",
icon: <BarChart className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
group: "Overview",
},
{
label: "organizations",
href: "/user-management/organizations",
icon: <Building2 className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
group: "Organizations",
},
{
label: "organizationAdmins", // Shortened for mobile
displayLabel: "organizationAdmins", // Full label for desktop
label: "organizationAdmins",
href: "/user-management/organization_admins",
icon: <Users2 className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
group: "Organizations",
},
{
label: "externalUsers", // Shortened for mobile
displayLabel: "externalUsers", // Full label for desktop
label: "externalUsers",
href: "/user-management/external_users",
icon: <Users2 className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
group: "Organizations",
},
{
label: "dashboard",
href: "/user-management/user_management-dashboard",
icon: <BarChart className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
group: "Overview",
},
{
label: "userManagement",
href: "/user-management/user_management",
icon: <UsersRound className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
group: "Overview",
},
// {
// label: "userPositionApproval",
// href: "/user-management/user-position-approval",
// icon: <UsersRound className="h-4 w-4" />,
// roles: ["admin", "organization_admin", "unit_admin"],
// permissions: ["can:activateEmployee"],
// isPrimary: true,
// },
// {
// label: "All Records",
// displayLabel: "All Records",
// href: "/user-management/all-records",
// icon: <FileText className="h-4 w-4" />,
// roles: ["admin", "organization_admin", "unit_admin"],
// permissions: ["can:canViewAllRecords"],
// isPrimary: true,
// },
{
label: "contentManagement", // Shortened for mobile
displayLabel: "contentManagement", // Full label for desktop
label: "contentManagement",
href: "/user-management/content-management",
icon: <FileText className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
group: "Content",
},
{
label: "webManagement",
displayLabel: "webManagement",
href: "/user-management/web-management",
icon: <Globe className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
},
{
label: "Position",
displayLabel: "positionTypes",
href: "/user-management/position-management",
icon: <FileText className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin", "super_admin"],
isPrimary: true,
},
{
label: "migratedRecords",
displayLabel: "migratedRecords",
href: "/user-management/migrated-records-management",
icon: <FileText className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
},
{
label: "settings",
displayLabel: "settings",
href: "/user-management/organization-settings",
icon: <Settings className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
group: "Content",
},
{
label: "Bulk",
displayLabel: "bulkUpload",
href: "/user-management/bulk-upload",
icon: <FileText className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
group: "Content",
},
{
label: "Archive Users",
displayLabel: "Archive Users",
href: "/user-management/archive-users",
icon: <Archive className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
},
{
label: "Archived Organizations",
displayLabel: "Archived Organizations",
href: "/user-management/archived-organizations",
icon: <Archive className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
},
{
label: "Archive Users",
displayLabel: "Archive Users",
href: "/user-management/archives",
icon: <Archive className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
},
{
label: "Archived Units & Positions",
displayLabel: "Archived Units & Positions",
href: "/user-management/archived",
icon: <Archive className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
isPrimary: true,
},
{
label: "Sector Reports",
displayLabel: "Sector Reports",
href: "/user-management/sector-reports",
icon: <ChartAreaIcon className="h-5 w-5" />,
roles: ["unit_admin", "admin", "organization_admin"],
isPrimary: true,
},
{
label: "activityLog",
href: "/user-management/activity_log",
icon: <ClipboardList className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: false,
},
{
label: "setting",
href: "/user-management/settings",
icon: <Settings className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: false,
},
{
label: "Letter Template",
href: "/user-management/templates",
label: "Position",
href: "/user-management/position-management",
icon: <FileText className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: false,
roles: ["admin", "organization_admin", "unit_admin", "super_admin"],
group: "Configuration",
},
{
label: "settings",
href: "/user-management/organization-settings",
icon: <Settings className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
group: "Configuration",
},
{
label: "Add Site",
href: "/user-management/add-site",
icon: <Globe className="h-4 w-4" />,
roles: ["super_admin"],
isPrimary: true,
group: "Configuration",
},
{
label: "migratedRecords",
href: "/user-management/migrated-records-management",
icon: <FileText className="h-4 w-4" />,
roles: ["super_admin"],
group: "Records",
},
{
label: "Sector Reports",
href: "/user-management/sector-reports",
icon: <ChartAreaIcon className="h-4 w-4" />,
roles: ["unit_admin", "admin", "organization_admin"],
group: "Records",
},
{
label: "Archive Users",
href: "/user-management/archive-users",
icon: <Archive className="h-4 w-4" />,
roles: ["super_admin"],
group: "Archive",
},
{
label: "Archived Organizations",
href: "/user-management/archived-organizations",
icon: <Archive className="h-4 w-4" />,
roles: ["super_admin"],
group: "Archive",
},
{
label: "Archive Users",
href: "/user-management/archives",
icon: <Archive className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
group: "Archive",
},
{
label: "Archived Units & Positions",
href: "/user-management/archived",
icon: <Archive className="h-4 w-4" />,
roles: ["admin", "organization_admin", "unit_admin"],
group: "Archive",
},
{
label: "activityLog",
href: "/user-management/activity_log",
icon: <ClipboardList className="h-4 w-4" />,
roles: ["super_admin"],
group: "System",
},
{
label: "setting",
href: "/user-management/settings",
icon: <Settings className="h-4 w-4" />,
roles: ["super_admin"],
group: "System",
},
{
label: "Letter Template",
href: "/user-management/templates",
icon: <FileText className="h-4 w-4" />,
roles: ["super_admin"],
group: "System",
},
// {
// label: "branding.title",
// href: "/user-management/web-management/Branding/Branding",
// icon: <ShieldAlert className="h-4 w-4" />,
// roles: ["super_admin"],
// isPrimary: true,
// },
];
const { permissions } = usePermissions();
const { t } = useTranslation();
const filteredMenu = menuItems.filter((item) =>
item?.roles?.some((r) => userRoles.includes(r)),
);
const primaryMenuItems = filteredMenu.filter(
(item) => item.isPrimary !== false,
);
const secondaryMenuItems = filteredMenu.filter(
(item) => item.isPrimary === false,
item.roles?.some((r) => userRoles.includes(r)),
);
const isActive = (href: string) =>
pathname === href || pathname.startsWith(`${href}/`);
return (
// Sticky (not fixed) so it stays in flow: content below never needs a
// magic offset matching this bar's responsive height. top-16 keeps it
// pinned just below the fixed 64px <Top> header while scrolling.
// -mt-8 cancels the excess of <Top>'s in-flow h-24 wrapper over its 64px
// fixed header, so the bar sits flush under the header with no jump.
// shrink-0 is load-bearing: as a flex item with overflow-hidden this bar
// would otherwise be flex-squashed to zero height when the page overflows.
<div className="sticky top-16 -mt-8 z-30 shrink-0 bg-white dark:bg-gray-900 border-b dark:border-gray-800 overflow-hidden flex flex-col">
{/* Mobile View - Two separate rows */}
<div className="md:hidden flex flex-col pt-5 pb-3 px-4 space-y-3 mt-2">
{/* Primary items row */}
<div className="flex items-center overflow-x-auto scrollbar-none">
{primaryMenuItems.map((item) => (
<NavLink
key={item.href}
to={item.href}
className={({ isActive }) =>
`flex items-center gap-1 px-3 py-2.5 mr-2 text-xs font-medium transition-colors whitespace-nowrap ${
isActive
? "text-primary dark:text-primary-400 border-b-2 border-primary dark:border-primary-400"
: "text-slate-700 dark:text-gray-300 hover:text-primary dark:hover:text-primary-400"
}`
}
>
{item.icon}
<span className="max-w-[80px] truncate">
{t(`organization.${item.label}`, item.label)}
</span>
</NavLink>
))}
</div>
<>
{GROUP_ORDER.map((group) => {
const items = filteredMenu.filter((item) => item.group === group);
if (items.length === 0) return null;
{/* Secondary items row (if any) */}
{secondaryMenuItems.length > 0 && (
<div className="flex items-center overflow-x-auto scrollbar-none border-t dark:border-gray-800 pt-3">
{secondaryMenuItems.map((item) => (
<NavLink
key={item.href}
to={item.href}
className={({ isActive }) =>
`flex items-center gap-1 px-3 py-2.5 mr-2 text-xs font-medium transition-colors whitespace-nowrap ${
isActive
? "text-primary dark:text-primary-400 border-b-2 border-primary dark:border-primary-400"
: "text-slate-700 dark:text-gray-300 hover:text-primary dark:hover:text-primary-400"
}`
}
>
{item.icon}
<span className="max-w-[80px] truncate">
{t(`organization.${item.label}`, item.label)}
</span>
</NavLink>
))}
</div>
)}
</div>
{/* Desktop View - Single row with all items */}
<div className="hidden md:flex pt-5 pb-3 px-8 mt-1">
<div className="flex items-center gap-2 overflow-x-auto scrollbar-none">
{filteredMenu.map((item) => (
<NavLink
key={item.href}
to={item.href}
className={({ isActive }) =>
`flex items-center gap-2 px-4 py-3 text-sm font-medium transition-colors whitespace-nowrap ${
isActive
? "text-primary dark:text-primary-400 border-b-2 border-primary dark:border-primary-400"
: "text-slate-700 dark:text-gray-300 hover:text-primary dark:hover:text-primary-400"
}`
}
>
{item.icon}
<span className="max-w-full truncate">
{t(`organization.${item.label}`, item.label)}
</span>
</NavLink>
))}
</div>
</div>
</div>
return (
<SidebarGroup key={group} className="pb-0">
<SidebarGroupLabel>{group}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => {
const label = t(`organization.${item.label}`, item.label);
return (
<SidebarMenuItem key={item.href}>
<SidebarMenuButton
asChild
isActive={isActive(item.href)}
tooltip={label}
>
<Link
to={item.href}
onClick={() => setOpenMobile(false)}
>
{item.icon}
<span>{label}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
})}
</>
);
};

View File

@@ -1,23 +1,26 @@
import { useLocation } from "react-router-dom";
import { Outlet } from "react-router-dom";
import { Link, Outlet, useLocation } from "react-router-dom";
import { AppMenuTabs } from "./AppMenuTabs";
import { useSidebar } from "@/shared/common/ui/sidebar";
import {
Sidebar,
SidebarContent,
SidebarHeader,
SidebarInset,
SidebarRail,
SidebarTrigger,
} from "@/shared/common/ui/sidebar";
import Top from "@/record-management/components/common/Top";
import { useAuth } from "@/shared/context/AuthContext";
export const AppLayout = () => {
const { pathname } = useLocation();
const isAuthPage = pathname === "/";
const { toggleSidebar } = useSidebar();
const { user } = useAuth();
const userRoles = user?.roles?.map((role) => role.key) || [];
const isSuperAdmin = userRoles.includes("super_admin");
// html/body/#root are `overflow: hidden` (index.css) — the host chrome
// scrolls inside FreightDashboardLayout. This subtree renders its own
// full-page layout instead, so it must be its own scroll container or
// nothing scrolls. Top/AppMenuTabs are `fixed`, unaffected by the scroller.
// Standalone full-page scroll container for the auth screen — the host
// chrome is `overflow: hidden`, so this subtree must scroll itself.
if (isAuthPage) {
return (
<div className="h-dvh w-full overflow-y-auto bg-gray-50">
@@ -27,17 +30,55 @@ export const AppLayout = () => {
}
return (
// <Top> renders its own in-flow h-24 wrapper around the fixed 64px header,
// so flow already clears the header — no extra top padding here.
// AppMenuTabs is sticky and in flow, so content starts right below it.
<div className="w-full h-dvh overflow-y-auto flex flex-col bg-background text-foreground">
<Top onToggleSidebar={toggleSidebar} showRecordManagementShortcut={!isSuperAdmin} />
<AppMenuTabs />
<div className="px-2 pb-2 pt-4 sm:px-4 sm:pb-4 sm:pt-6 flex-1">
<div className="w-full overflow-x-auto">
<Outlet />
<>
{/* <Top> is a shared component rendered as a full-width `fixed` 64px bar,
so the sidebar is offset to start beneath it (top-16) rather than
owning the top-left corner. Collapses to an icon rail on desktop; on
mobile it's a Sheet drawer opened by the <SidebarTrigger> below (Top's
own burger is a module-nav dropdown, not the sidebar toggle). */}
<Sidebar collapsible="icon" className="top-14! h-[calc(100svh-4rem)]!">
<SidebarHeader className="border-b border-sidebar-border">
<Link
to="/user-management"
className="flex items-center gap-2 px-1 py-1.5"
>
<img
src="/assets/logo.svg"
alt="EDR"
className="size-7 shrink-0 object-contain"
/>
<div className="flex flex-col group-data-[collapsible=icon]:hidden">
<span className="text-sm font-semibold leading-tight text-sidebar-foreground">
User Management
</span>
<span className="text-[10px] font-medium text-muted-foreground">
EDR Freight
</span>
</div>
</Link>
</SidebarHeader>
<SidebarContent className="pb-10">
<AppMenuTabs />
</SidebarContent>
<SidebarRail />
</Sidebar>
<SidebarInset className="min-w-0">
<Top showRecordManagementShortcut={!isSuperAdmin} />
{/* Top provides its own in-flow h-24 spacer clearing the fixed header. */}
{/* Mobile-only drawer opener — desktop shows the sidebar/rail directly. */}
<div className="flex items-center gap-2 px-3 md:hidden">
<SidebarTrigger className="h-9 w-9 rounded-lg" />
<span className="text-sm font-medium text-muted-foreground">
Menu
</span>
</div>
</div>
</div>
<div className="flex-1 pb-2 sm:px-4 sm:pb-4 ">
<div className="w-full overflow-x-auto">
<Outlet />
</div>
</div>
</SidebarInset>
</>
);
};