Overview
SIGEAC’s navigation components provide a complete solution for app-wide navigation including responsive headers, user profile menus, company selection, and theme toggles.
Navigation Components
Navbar
Main header component with responsive layout.
Location : components/layout/Navbar.tsx:14
Props
Page title displayed in the navbar
Usage Example
import { Navbar } from '@/components/layout/Navbar' ;
export function Layout ({ children }) {
return (
< div >
< Navbar title = "Dashboard" />
< main > { children } </ main >
</ div >
);
}
Features
Sticky positioning : Stays at top during scroll
Backdrop blur : Modern glass effect
Mobile menu : Sheet menu trigger for small screens
Company selector : Dropdown for multi-company users
Theme toggle : Light/dark mode switcher
User menu : Profile and logout options
Responsive : Adapts layout for mobile/desktop
Structure
< header className = "sticky top-0 z-10 w-full bg-background/95 shadow backdrop-blur" >
< div className = "mx-4 sm:mx-8 flex h-14 items-center justify-between" >
{ /* Left: Menu + Title */ }
< div className = "flex items-center gap-4" >
< SheetMenu /> { /* Mobile only */ }
< h1 className = "text-xs sm:text-sm xl:text-base font-bold" >
{ title }
</ h1 >
</ div >
{ /* Center: Company Select (Desktop) */ }
< div className = "hidden xl:flex items-center" >
< CompanySelect />
</ div >
{ /* Center: Company Select (Mobile Popover) */ }
< div className = "flex xl:hidden items-center" >
< Popover >
< PopoverTrigger asChild >
< button className = "flex items-center justify-center w-10 h-10" >
< ChevronDown className = "w-5 h-5" />
</ button >
</ PopoverTrigger >
< PopoverContent >
< CompanySelect />
</ PopoverContent >
</ Popover >
</ div >
{ /* Right: Theme + User */ }
< div className = "flex items-center gap-2" >
< ThemeToggler />
< UserNav />
</ div >
</ div >
</ header >
UserNav
User profile dropdown menu.
Location : components/layout/UserNav.tsx
import { UserNav } from '@/components/layout/UserNav' ;
< UserNav />
Features
User avatar with initials
Display name and email
Profile link
Settings link
Logout action
Implementation
import { Avatar , AvatarFallback , AvatarImage } from '@/components/ui/avatar' ;
import {
DropdownMenu ,
DropdownMenuContent ,
DropdownMenuItem ,
DropdownMenuLabel ,
DropdownMenuSeparator ,
DropdownMenuTrigger ,
} from '@/components/ui/dropdown-menu' ;
import { useAuth } from '@/contexts/AuthContext' ;
export function UserNav () {
const { user , logout } = useAuth ();
const initials = user
? ` ${ user . first_name ?.[ 0 ] || '' }${ user . last_name ?.[ 0 ] || '' } `
: 'U' ;
return (
< DropdownMenu >
< DropdownMenuTrigger asChild >
< Button variant = "ghost" className = "relative h-10 w-10 rounded-full" >
< Avatar className = "h-10 w-10" >
< AvatarImage src = { user ?. avatar } alt = { user ?. username } />
< AvatarFallback > { initials } </ AvatarFallback >
</ Avatar >
</ Button >
</ DropdownMenuTrigger >
< DropdownMenuContent className = "w-56" align = "end" >
< DropdownMenuLabel >
< div className = "flex flex-col space-y-1" >
< p className = "text-sm font-medium" >
{ user ?. first_name } { user ?. last_name }
</ p >
< p className = "text-xs text-muted-foreground" >
{ user ?. email }
</ p >
</ div >
</ DropdownMenuLabel >
< DropdownMenuSeparator />
< DropdownMenuItem asChild >
< Link href = "/profile" > Perfil </ Link >
</ DropdownMenuItem >
< DropdownMenuItem asChild >
< Link href = "/settings" > Configuración </ Link >
</ DropdownMenuItem >
< DropdownMenuSeparator />
< DropdownMenuItem onClick = { logout } >
Cerrar Sesión
</ DropdownMenuItem >
</ DropdownMenuContent >
</ DropdownMenu >
);
}
ThemeToggler
Light/dark mode toggle button.
Location : components/layout/ThemeToggler.tsx
import { ThemeToggler } from '@/components/layout/ThemeToggler' ;
< ThemeToggler />
Implementation
import { Moon , Sun } from 'lucide-react' ;
import { useTheme } from 'next-themes' ;
import { Button } from '@/components/ui/button' ;
export function ThemeToggler () {
const { theme , setTheme } = useTheme ();
return (
< Button
variant = "ghost"
size = "icon"
onClick = { () => setTheme ( theme === 'light' ? 'dark' : 'light' ) }
aria-label = "Toggle theme"
>
< Sun className = "h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
< Moon className = "absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
</ Button >
);
}
CompanySelect
Multi-company selector with search.
Location : components/selects/CompanySelect.tsx
import CompanySelect from '@/components/selects/CompanySelect' ;
< CompanySelect />
Features
Search companies by name
Selected company indicator
Stores selection in global state
Updates context throughout app
Implementation
import { useState } from 'react' ;
import { Check , ChevronsUpDown } from 'lucide-react' ;
import {
Command ,
CommandEmpty ,
CommandGroup ,
CommandInput ,
CommandItem ,
CommandList ,
} from '@/components/ui/command' ;
import {
Popover ,
PopoverContent ,
PopoverTrigger ,
} from '@/components/ui/popover' ;
import { Button } from '@/components/ui/button' ;
import { useCompanyStore } from '@/stores/CompanyStore' ;
import { useGetCompanies } from '@/hooks/sistema/useGetCompanies' ;
export default function CompanySelect () {
const [ open , setOpen ] = useState ( false );
const { selectedCompany , setSelectedCompany } = useCompanyStore ();
const { data : companies , isLoading } = useGetCompanies ();
return (
< Popover open = { open } onOpenChange = { setOpen } >
< PopoverTrigger asChild >
< Button
variant = "outline"
role = "combobox"
aria-expanded = { open }
className = "w-[250px] justify-between"
>
{ selectedCompany ? selectedCompany . name : "Seleccione empresa..." }
< ChevronsUpDown className = "ml-2 h-4 w-4 shrink-0 opacity-50" />
</ Button >
</ PopoverTrigger >
< PopoverContent className = "w-[250px] p-0" >
< Command >
< CommandInput placeholder = "Buscar empresa..." />
< CommandList >
< CommandEmpty > No se encontraron empresas. </ CommandEmpty >
< CommandGroup >
{ companies ?. map (( company ) => (
< CommandItem
key = { company . id }
value = { company . name }
onSelect = { () => {
setSelectedCompany ( company );
setOpen ( false );
} }
>
< Check
className = { cn (
"mr-2 h-4 w-4" ,
selectedCompany ?. id === company . id
? "opacity-100"
: "opacity-0"
) }
/>
{ company . name }
</ CommandItem >
)) }
</ CommandGroup >
</ CommandList >
</ Command >
</ PopoverContent >
</ Popover >
);
}
Mobile slide-out menu.
Location : components/sidebar/SheetMenu.tsx
import { SheetMenu } from '@/components/sidebar/SheetMenu' ;
< SheetMenu /> { /* Shows only on mobile */ }
Implementation
import { Menu as MenuIcon } from 'lucide-react' ;
import { Button } from '@/components/ui/button' ;
import {
Sheet ,
SheetContent ,
SheetTrigger ,
} from '@/components/ui/sheet' ;
import { Menu } from '@/components/sidebar/Menu' ;
export function SheetMenu () {
return (
< Sheet >
< SheetTrigger asChild className = "lg:hidden" >
< Button variant = "ghost" size = "icon" >
< MenuIcon className = "h-5 w-5" />
</ Button >
</ SheetTrigger >
< SheetContent side = "left" className = "w-64 p-0" >
< Menu isOpen = { true } />
</ SheetContent >
</ Sheet >
);
}
Layout Components
ContentLayout
Standard page layout with navbar and content area.
import { ContentLayout } from '@/components/layout/ContentLayout' ;
export default function Page () {
return (
< ContentLayout title = "Page Title" >
{ /* Page content */ }
</ ContentLayout >
);
}
ProtectedLayout
Authentication-wrapped layout.
import { ProtectedLayout } from '@/components/layout/ProtectedLayout' ;
export default function DashboardPage () {
return (
< ProtectedLayout >
< ContentLayout title = "Dashboard" >
{ /* Protected content */ }
</ ContentLayout >
</ ProtectedLayout >
);
}
Breadcrumbs
Show navigation hierarchy:
import {
Breadcrumb ,
BreadcrumbItem ,
BreadcrumbLink ,
BreadcrumbList ,
BreadcrumbPage ,
BreadcrumbSeparator ,
} from '@/components/ui/breadcrumb' ;
import { ChevronRight } from 'lucide-react' ;
export function PageBreadcrumbs () {
return (
< Breadcrumb >
< BreadcrumbList >
< BreadcrumbItem >
< BreadcrumbLink href = "/" > Inicio </ BreadcrumbLink >
</ BreadcrumbItem >
< BreadcrumbSeparator >
< ChevronRight className = "h-4 w-4" />
</ BreadcrumbSeparator >
< BreadcrumbItem >
< BreadcrumbLink href = "/employees" > Empleados </ BreadcrumbLink >
</ BreadcrumbItem >
< BreadcrumbSeparator >
< ChevronRight className = "h-4 w-4" />
</ BreadcrumbSeparator >
< BreadcrumbItem >
< BreadcrumbPage > Lista </ BreadcrumbPage >
</ BreadcrumbItem >
</ BreadcrumbList >
</ Breadcrumb >
);
}
Complete Layout Example
Full application layout combining all navigation components:
import { Sidebar } from '@/components/layout/Sidebar' ;
import { Navbar } from '@/components/layout/Navbar' ;
import { useState } from 'react' ;
import { cn } from '@/lib/utils' ;
export function AppLayout ({ children , title }) {
const [ sidebarOpen , setSidebarOpen ] = useState ( true );
return (
< div className = "flex h-screen overflow-hidden" >
{ /* Sidebar */ }
< Sidebar isOpen = { sidebarOpen } setIsOpen = { setSidebarOpen } />
{ /* Main Content */ }
< div
className = { cn (
"flex-1 flex flex-col transition-all" ,
sidebarOpen ? "lg:ml-64" : "lg:ml-16"
) }
>
{ /* Navbar */ }
< Navbar title = { title } />
{ /* Page Content */ }
< main className = "flex-1 overflow-y-auto p-4 lg:p-8" >
{ children }
</ main >
{ /* Footer */ }
< footer className = "border-t p-4 text-center text-sm text-muted-foreground" >
© 2024 SIGEAC. Todos los derechos reservados.
</ footer >
</ div >
</ div >
);
}
Responsive Patterns
Mobile Navigation
{ /* Mobile: Sheet menu trigger */ }
< div className = "lg:hidden" >
< SheetMenu />
</ div >
{ /* Desktop: Persistent sidebar */ }
< div className = "hidden lg:block" >
< Sidebar />
</ div >
Adaptive Title Sizing
< h1 className = "text-xs sm:text-sm xl:text-base font-bold" >
{ title }
</ h1 >
Conditional Company Select
{ /* Desktop: Always visible */ }
< div className = "hidden xl:flex" >
< CompanySelect />
</ div >
{ /* Mobile: In popover */ }
< div className = "flex xl:hidden" >
< Popover >
< PopoverTrigger >< ChevronDown /></ PopoverTrigger >
< PopoverContent >
< CompanySelect />
</ PopoverContent >
</ Popover >
</ div >
Best Practices
Use the same navbar across all pages for familiar navigation experience.
Highlight active menu items and provide breadcrumbs for deep navigation.
Test navigation on small screens and ensure touch targets are adequate.
Store theme selection and sidebar state in localStorage.
Show skeleton loaders while company list or user data is loading.
Provide keyboard shortcuts
Add hotkeys for common actions like theme toggle or company switch.
Sidebar - Main navigation menu
Forms - User profile and settings forms