/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/components/Layout/Layout.tsx
145 строк
5 KB
h0tnanny
логи + redme + changelog
08 фев 2026, 23:46
08 фев 2026, 23:46
84e5b0a
Код
Авторство
О чём код?
import { Activity, Cpu, GitBranch, LayoutDashboard, Lock, Settings, LogOut, Radio, FileText } from 'lucide-react'; import { Link, useLocation, useNavigate } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import styles from './Layout.module.scss'; interface LayoutProps { children: React.ReactNode; } export function Layout({ children }: LayoutProps) { const { user, logout } = useAuth(); const location = useLocation(); const navigate = useNavigate(); const navItems = [ { path: '/', label: 'Панель', icon: LayoutDashboard }, { path: '/equipment', label: 'Источники данных', icon: Cpu }, { path: '/workflows', label: 'Workflow', icon: GitBranch }, { path: '/state', label: 'Мониторинг', icon: Activity }, { path: '/settings', label: 'Настройки', icon: Settings }, { path: '/permissions', label: 'Разрешения', icon: Lock }, ]; // Определяем, нужен ли полноэкранный режим для текущей страницы const isFullscreen = location.pathname.startsWith('/workflows'); // Проверяем активность навигационного элемента const isActive = (path: string) => { if (path === '/') { return location.pathname === '/'; } // Для /admin — точное совпадение, чтобы не конфликтовать с /admin/emulator if (path === '/admin') { return location.pathname === '/admin'; } return location.pathname.startsWith(path); }; const handleLogout = async () => { await logout(); navigate('/login'); }; const displayName = user ? [user.lastName, user.firstName].filter(Boolean).join(' ') || user.username : ''; return ( <div className={styles.layout}> <aside className={styles.sidebar}> <Link to="/" className={styles.logo} title="Перейти на Панель"> <Cpu size={32} /> <h1>IoT Platform</h1> </Link> <nav className={styles.nav}> {navItems.map((item) => { const Icon = item.icon; return ( <Link key={item.path} to={item.path} className={`${styles.navItem} ${ isActive(item.path) ? styles.active : '' }`} > <span className={styles.navItemIcon} aria-hidden> <Icon size={20} strokeWidth={2} /> </span> <span>{item.label}</span> </Link> ); })} {user?.role === 'admin' && ( <> <Link key="/admin" to="/admin" className={`${styles.navItem} ${isActive('/admin') ? styles.active : ''}`} > <span className={styles.navItemIcon} aria-hidden> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /> </svg> </span> <span>Администрирование</span> </Link> <Link key="/admin/logs" to="/admin/logs" className={`${styles.navItem} ${isActive('/admin/logs') ? styles.active : ''}`} > <span className={styles.navItemIcon} aria-hidden> <FileText size={20} strokeWidth={2} /> </span> <span>Системные логи</span> </Link> <Link key="/admin/emulator" to="/admin/emulator" className={`${styles.navItem} ${isActive('/admin/emulator') ? styles.active : ''}`} > <span className={styles.navItemIcon} aria-hidden> <Radio size={20} strokeWidth={2} /> </span> <span>Эмулятор устройств</span> </Link> </> )} </nav> <div className={styles.sidebarFooter}> {/* Кнопка выхода */} <button className={styles.logoutBtn} onClick={handleLogout} title="Выйти из системы" > <LogOut size={20} /> <span>Выйти</span> </button> </div> </aside> <main className={styles.main}> {user && ( <header className={styles.mainHeader}> <Link to="/settings" className={styles.userButton} title="Перейти в настройки" > <div className={styles.userName}>{displayName}</div> <div className={styles.userRole}> {user.role === 'admin' ? 'Администратор' : 'Пользователь'} </div> </Link> </header> )} <div className={`${styles.content} ${isFullscreen ? styles.fullscreen : ''}`}> {children} </div> </main> </div> ); }