From 0b4895bb9781673cb42a964006c78359e6f5cff0 Mon Sep 17 00:00:00 2001 From: Aleksei Pavlov Date: Fri, 19 Jun 2026 11:45:24 +0300 Subject: [PATCH] =?UTF-8?q?refactor(admin):=20top=20nav=20=E2=86=92=20left?= =?UTF-8?q?=20sidebar=20with=20grouped=20sections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Top horizontal nav was getting cramped at 8 items. Sidebar: - 240px fixed left, full-height - 3 groups: Контент (Статьи / Черновики / Заметки / Зеро), Публикации (Каналы / Автогенерация), Система (Настройки) - Сайт + Выход pinned at bottom - Mobile: hides off-screen with hamburger toggle + backdrop overlay Layout: main now pl-60 on md+ (slides under sidebar), no padding on mobile since sidebar overlays. Content keeps max-w-6xl + mx-auto, so visual layout on individual pages doesn't change. --- app/admin/(protected)/layout.js | 8 +- components/admin/AdminNav.js | 148 +++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 42 deletions(-) diff --git a/app/admin/(protected)/layout.js b/app/admin/(protected)/layout.js index cc11ada..50d5750 100644 --- a/app/admin/(protected)/layout.js +++ b/app/admin/(protected)/layout.js @@ -8,8 +8,12 @@ export default async function AdminLayout({ children }) { return (
-
- {children} + {/* Отступ слева под фиксированный sidebar (240px = w-60); на мобилке без отступа, + sidebar выезжает поверх контента через бэкдроп */} +
+
+ {children} +
); diff --git a/components/admin/AdminNav.js b/components/admin/AdminNav.js index 0dbda34..677505e 100644 --- a/components/admin/AdminNav.js +++ b/components/admin/AdminNav.js @@ -1,22 +1,48 @@ 'use client'; import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; -import { LayoutDashboard, FileText, Radio, Zap, Settings, LogOut, ExternalLink, MessageCircle, Clock, Coffee } from 'lucide-react'; +import { useState } from 'react'; +import { + LayoutDashboard, FileText, Radio, Zap, Settings, LogOut, ExternalLink, + MessageCircle, Clock, Coffee, Menu, X, +} from 'lucide-react'; -const NAV = [ - { href: '/admin', label: 'Дашборд', icon: LayoutDashboard, exact: true }, - { href: '/admin/articles', label: 'Статьи', icon: FileText }, - { href: '/admin/drafts', label: 'Черновики', icon: Clock }, - { href: '/admin/channels', label: 'Каналы', icon: Radio }, - { href: '/admin/autogen', label: 'Автогенерация', icon: Zap }, - { href: '/admin/notes', label: 'Заметки', icon: MessageCircle }, - { href: '/admin/zero', label: 'Зеро', icon: Coffee }, - { href: '/admin/settings', label: 'Настройки', icon: Settings }, +// Группы пунктов меню — масштабируется, без давки сверху +const GROUPS = [ + { + label: null, // главная без заголовка группы + items: [ + { href: '/admin', label: 'Дашборд', icon: LayoutDashboard, exact: true }, + ], + }, + { + label: 'Контент', + items: [ + { href: '/admin/articles', label: 'Статьи', icon: FileText }, + { href: '/admin/drafts', label: 'Черновики', icon: Clock }, + { href: '/admin/notes', label: 'Заметки', icon: MessageCircle }, + { href: '/admin/zero', label: 'Зеро', icon: Coffee }, + ], + }, + { + label: 'Публикации', + items: [ + { href: '/admin/channels', label: 'Каналы', icon: Radio }, + { href: '/admin/autogen', label: 'Автогенерация', icon: Zap }, + ], + }, + { + label: 'Система', + items: [ + { href: '/admin/settings', label: 'Настройки', icon: Settings }, + ], + }, ]; export default function AdminNav() { const pathname = usePathname(); const router = useRouter(); + const [mobileOpen, setMobileOpen] = useState(false); async function logout() { await fetch('/admin/api/logout', { method: 'POST' }); @@ -25,53 +51,95 @@ export default function AdminNav() { } return ( -
-
+ <> + {/* Mobile hamburger button */} + + + {/* Mobile backdrop */} + {mobileOpen && ( +
setMobileOpen(false)} + /> + )} + + {/* Sidebar */} +
+ + ); }