Initial commit: TopSysOps site + admin panel

Multi-page public site (Next.js), Node/Express backend with Postgres,
and Vite admin/account app, restructured from a single-page layout
into dedicated service, promotions, cases, about, FAQ, and blog pages
for SEO.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013otXCiTZsxdZ4SJKZ9wUky
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-08-25 23:12:16 +03:00
commit 130c5382cd
108 files changed
+7146

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import { Sun, Moon, Menu, X, Terminal } from 'lucide-react';
import { useTheme } from './ThemeProvider.jsx';
import StatusDot from './StatusDot.jsx';
const NAV_ITEMS = [
{ href: '/', label: 'Главная' },
{ href: '/uslugi', label: 'Услуги' },
{ href: '/aktsii', label: 'Акции' },
{ href: '/keysy', label: 'Кейсы' },
{ href: '/o-kompanii', label: 'О компании' },
{ href: '/blog', label: 'Блог' },
{ href: '/kontakty', label: 'Контакты' },
];
export default function Header() {
const { dark, toggle } = useTheme();
const [open, setOpen] = useState(false);
return (
<header className="sticky top-0 z-40 backdrop-blur border-b bg-slate-50/85 border-slate-200 dark:bg-slate-950/85 dark:border-slate-800">
<div className="max-w-6xl mx-auto px-5 sm:px-8">
<div className="flex items-center justify-between h-16">
<Link href="/" className="flex items-center gap-2 font-display">
<Terminal className="h-5 w-5 text-emerald-500" />
<span className="text-base font-bold tracking-tight">TopSysOps</span>
</Link>
<nav className="hidden md:flex items-center gap-1">
{NAV_ITEMS.map((item) => (
<Link
key={item.href}
href={item.href}
className="px-3 py-2 rounded-md text-sm font-medium transition-colors text-slate-600 hover:text-slate-900 hover:bg-slate-100 dark:text-slate-300 dark:hover:text-white dark:hover:bg-slate-900"
>
{item.label}
</Link>
))}
</nav>
<div className="flex items-center gap-2">
<button
aria-label={dark ? 'Включить светлую тему' : 'Включить тёмную тему'}
onClick={toggle}
className="p-2 rounded-md transition-colors hover:bg-slate-100 text-slate-700 dark:hover:bg-slate-900 dark:text-amber-400"
>
{dark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
</button>
<button
aria-label="Открыть меню"
onClick={() => setOpen(!open)}
className="md:hidden p-2 rounded-md hover:bg-slate-100 dark:hover:bg-slate-900"
>
{open ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
</div>
</div>
{open && (
<nav className="md:hidden pb-4 flex flex-col gap-1">
{NAV_ITEMS.map((item) => (
<Link
key={item.href}
href={item.href}
onClick={() => setOpen(false)}
className="text-left px-3 py-2 rounded-md text-sm font-medium text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-900"
>
{item.label}
</Link>
))}
</nav>
)}
</div>
<div className="border-t px-5 sm:px-8 py-1.5 text-xs flex items-center gap-4 max-w-6xl mx-auto border-slate-200 text-slate-500 dark:border-slate-900 dark:text-slate-400">
<StatusDot tone="ok" pulse label="система активна" mono />
<span className="hidden sm:inline font-mono">24/7</span>
<span className="hidden sm:inline font-mono">отклик ~15 мин</span>
<a href="/admin" className="ml-auto underline decoration-dotted underline-offset-2 hover:text-emerald-500">
вход для сотрудников
</a>
</div>
</header>
);
}