This commit is contained in:
Zuev
2026-10-02 04:07:56 +03:00
parent 97bf3cc50a
commit fc5801d683
42 changed files with 1201 additions and 305 deletions

View File

@@ -1,14 +1,14 @@
import { Suspense, useEffect, useRef, useState } from 'react';
import { ADMIN_APP_ROLES, SETTINGS_ROLES } from '../../admin/js/role-capabilities.js';
import { applyRippleEffect, closeAllDropdownsOnOutsideClick } from '../../admin/js/utils.js';
import { initAllCustomDropdowns, startDropdownAutoObserver } from '../../admin/js/dropdown.js';
import { confirmAbandonDirtyChanges } from '../../admin/js/dirty-state.js';
import { trackUxEvent } from '../../telemetry.js';
import { logoutAndRedirect } from '../shared/auth-client.js';
import { SESSION_PHASES } from '../shared/session-policy.js';
import { useAuthorizedSession } from '../shared/use-authorized-session.js';
import { AsyncState } from '../shared/ui/AsyncState.jsx';
import { useSidebar } from '../shared/use-sidebar.js';
import { SidebarToggle } from '../shared/ui/SidebarToggle.jsx';
import { REACT_TABS } from './react-tabs.js';
import {
ADMIN_ROUTES,
@@ -24,7 +24,6 @@ import {
} from './admin-service.js';
const THEME_STORAGE_KEY = 'theme';
const SIDEBAR_STORAGE_KEY = 'sidebar-collapsed';
const ICONS = Object.freeze({
dashboard: (
@@ -166,7 +165,7 @@ function AdminFrame({ children }) {
return (
<main className="main">
<header className="topbar">
<div className="topbar-title-group">
<div className="topbar-title-group">
<h1>Админ-панель</h1>
</div>
</header>
@@ -185,22 +184,11 @@ export function AdminWorkspace({ role }) {
: tabFromHash(window.location.hash, role)
));
const activeTabRef = useRef(activeTab);
const [collapsed, setCollapsed] = useState(() => (
typeof window !== 'undefined'
&& window.innerWidth > 768
&& localStorage.getItem(SIDEBAR_STORAGE_KEY) === 'true'
));
const { collapsed, mobileOpen, toggleSidebar, closeSidebar } = useSidebar();
const [settingsOpen, setSettingsOpen] = useState(false);
const [pendingCount, setPendingCount] = useState(null);
const settingsRef = useRef(null);
useEffect(() => {
applyRippleEffect();
closeAllDropdownsOnOutsideClick();
initAllCustomDropdowns(document.body);
startDropdownAutoObserver();
}, []);
useEffect(() => {
document.documentElement.dataset.theme = localStorage.getItem(THEME_STORAGE_KEY) || 'light';
}, []);
@@ -255,25 +243,22 @@ export function AdminWorkspace({ role }) {
}, [role]);
async function requestTabSwitch(tab) {
if (tab === activeTabRef.current || !allowedTabs.has(tab)) return;
if (tab === activeTabRef.current) {
closeSidebar();
return;
}
if (!allowedTabs.has(tab)) return;
if (activeTabRef.current && !(await confirmAbandonDirtyChanges())) return;
activeTabRef.current = tab;
setActiveTab(tab);
closeSidebar();
trackUxEvent('раздел_открыт', { tab, role });
if (window.location.hash !== `#${tab}`) {
window.location.hash = tab;
}
}
function toggleSidebar() {
setCollapsed(current => {
const next = !current;
localStorage.setItem(SIDEBAR_STORAGE_KEY, next);
return next;
});
}
function toggleTheme() {
const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
document.documentElement.dataset.theme = next;
@@ -291,7 +276,7 @@ export function AdminWorkspace({ role }) {
return (
<>
<aside className={`sidebar${collapsed ? ' collapsed' : ''}`}>
<aside id="app-sidebar" className={`sidebar${collapsed && !mobileOpen ? ' collapsed' : ''}${mobileOpen ? ' open' : ''}`}>
<div className="sidebar-header">
<div className="logo">
<svg width="32" height="32" viewBox="0 0 40 40" fill="none">
@@ -332,6 +317,7 @@ export function AdminWorkspace({ role }) {
<a
key={item.id}
href={`#${item.id}`}
title={collapsed ? item.label : undefined}
className={`nav-item${item.id === activeTab ? ' active' : ''}`}
data-tab={item.id}
onClick={event => {
@@ -406,10 +392,11 @@ export function AdminWorkspace({ role }) {
</div>
</aside>
<div className="sidebar-overlay" />
<div className={`sidebar-overlay${mobileOpen ? ' open' : ''}`} onClick={closeSidebar} />
<main className={`main${collapsed ? ' sidebar-collapsed' : ''}`}>
<header className="topbar">
<SidebarToggle open={mobileOpen} onClick={toggleSidebar} />
<div className="topbar-title-group">
<h1 id="page-title">{title}</h1>
{badge && !badge.hidden && (