feat: добавлена админ-панель и главная страница с навигацией по курсам
Основные изменения: Админ-панель: - Создана защищенная паролем админ-панель по пути /admin - Реализована система авторизации с сессионными куками - Добавлен CRUD для управления группами (создание, редактирование, удаление) - Добавлено поле "курс" (1-5) для каждой группы с возможностью редактирования Структура данных: - Миграция групп из TypeScript файла в JSON формат (groups.json) - Обновлена структура данных: добавлено поле course - Реализована автоматическая миграция старых данных в новый формат - Создан groups-loader для работы с JSON файлом Главная страница: - Создана главная страница с аккордеоном по курсам (1-5) - Группы сгруппированы по курсам для удобной навигации - Добавлены кнопки: "Добавить группу", переключение темы и GitHub - Убрана верхняя навигация с главной страницы Навигация: - Добавлена кнопка "К группам" в начало навигации на страницах расписания - На мобильных устройствах скрыты кнопки групп, оставлена только кнопка возврата - Улучшена адаптивность навигации Технические улучшения: - Исправлена проблема с tailwind-scrollbar-hide (заменен плагин на CSS класс) - Обновлены все компоненты для работы с новой структурой данных групп - Добавлена поддержка переменных окружения ADMIN_PASSWORD и ADMIN_SESSION_SECRET
This commit is contained in:
@@ -6,27 +6,51 @@ import { useTheme } from 'next-themes'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FaGithub } from 'react-icons/fa'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import cx from 'classnames'
|
||||
import { NavContext, NavContextProvider } from '@/shared/context/nav-context'
|
||||
import { groups } from '@/shared/data/groups'
|
||||
import { GITHUB_REPO_URL } from '@/shared/constants/urls'
|
||||
import { GroupsData } from '@/shared/data/groups-loader'
|
||||
|
||||
export function NavBar({ cacheAvailableFor }: {
|
||||
export function NavBar({ cacheAvailableFor, groups }: {
|
||||
cacheAvailableFor: string[]
|
||||
groups: GroupsData
|
||||
}) {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const theme = resolvedTheme || 'light'
|
||||
// Используем состояние для предотвращения проблем с гидратацией
|
||||
const [mounted, setMounted] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
// На сервере используем 'light' по умолчанию, чтобы избежать различий в гидратации
|
||||
const theme = mounted ? (resolvedTheme || 'light') : 'light'
|
||||
|
||||
return (
|
||||
<NavContextProvider cacheAvailableFor={cacheAvailableFor}>
|
||||
<header className="sticky top-0 w-full p-2 bg-background z-[1] pb-0 mb-2 shadow-header">
|
||||
<nav className={cx('rounded-lg p-2 w-full flex gap-2 md:justify-between', { 'bg-slate-200': theme === 'light', 'bg-slate-900': theme === 'dark' })}>
|
||||
<nav className={cx('rounded-lg p-2 w-full flex gap-2 md:justify-between', { 'bg-slate-200': theme === 'light', 'bg-slate-900': theme === 'dark' })} suppressHydrationWarning>
|
||||
<div className="flex-1 min-w-0 overflow-x-auto scrollbar-hide">
|
||||
<ul className="flex gap-2 flex-nowrap">
|
||||
{Object.entries(groups).map(([id, [, name]]) => (
|
||||
<NavBarItem key={id} url={`/${id}`}>{name}</NavBarItem>
|
||||
))}
|
||||
<li className="flex-shrink-0">
|
||||
<Link href="/">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="min-h-[44px] whitespace-nowrap gap-2"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">К группам</span>
|
||||
</Button>
|
||||
</Link>
|
||||
</li>
|
||||
{Object.entries(groups).map(([id, group]) => (
|
||||
<li key={id} className="hidden md:list-item flex-shrink-0">
|
||||
<NavBarItem url={`/${id}`}>{group.name}</NavBarItem>
|
||||
</li>
|
||||
))}
|
||||
<li className="flex-shrink-0 hidden md:list-item">
|
||||
<AddGroupButton />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user