import { Link } from '@tanstack/react-router' import { useEffect, useState } from 'react' import { useTheme } from 'next-themes' import { ChevronsUpDownIcon, LogOutIcon, MonitorIcon, MoonIcon, PaletteIcon, SettingsIcon, SunIcon, } from 'lucide-react' import { cn } from '@cfdm/ui/lib/utils' import { Avatar, AvatarFallback, } from '@cfdm/ui/components/avatar' import { Button } from '@cfdm/ui/components/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@cfdm/ui/components/dropdown-menu' import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar, } from '@cfdm/ui/components/sidebar' import { can, clearToken, getClaims, isAuthEnabled, redirectToPortalLogout, resetPortalHandoff, } from '@/lib/auth' import { readContrast, readDensity, writeContrast, writeDensity, type ContrastMode, type DensityMode, } from '@/lib/appearance-mode' /** Sidebar footer account menu — ReUI app-shell-1 NavUser. @see https://reui.io/preview/base/app-shell-1 */ const THEMES = [ { value: 'light', label: 'Светлая', icon: , }, { value: 'dark', label: 'Тёмная', icon: , }, { value: 'system', label: 'Системная', icon: , }, ] as const function ThemeSegmentedToggle() { const { theme, setTheme } = useTheme() const [mounted, setMounted] = useState(false) useEffect(() => { setMounted(true) }, []) const currentTheme = mounted ? (theme ?? 'system') : 'system' return (
e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()} > {THEMES.map(({ value, label, icon }) => { const isActive = currentTheme === value return ( ) })}
) } function SegmentedChoice({ label, value, options, onChange, }: { label: string value: T options: readonly { value: T; label: string }[] onChange: (value: T) => void }) { return (
event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()} > {options.map((option) => { const isActive = value === option.value return ( ) })}
) } function AppearanceToggles() { const [contrast, setContrast] = useState('standard') const [density, setDensity] = useState('normal') const [mounted, setMounted] = useState(false) useEffect(() => { setContrast(readContrast()) setDensity(readDensity()) setMounted(true) }, []) if (!mounted) return null return ( <> Контраст
{ setContrast(next) writeContrast(next) }} />
Плотность
{ setDensity(next) writeDensity(next) }} />
) } function initials(name: string, email: string): string { const base = name.trim() || email.trim() if (!base) return '?' const parts = base.split(/\s+/).filter(Boolean) if (parts.length >= 2) { return `${parts[0]![0] ?? ''}${parts[1]![0] ?? ''}`.toUpperCase() } return base.slice(0, 2).toUpperCase() } export function NavUser() { const { isMobile } = useSidebar() const claims = getClaims() const authOn = isAuthEnabled() const name = claims?.name?.trim() || (authOn ? 'Пользователь' : 'Гость') const email = claims?.email?.trim() || (authOn ? '' : 'auth выключен') const fallback = initials(name, email) function handleSignOut() { clearToken() resetPortalHandoff() if (authOn) { redirectToPortalLogout() return } window.location.href = '/login' } return ( } > {fallback}
{name} {email || '—'}
{fallback}
{name} {email || '—'}
{can('cfdm:settings:admin') ? ( } > Настройки ) : null} Тема
Выйти
) }