diff --git a/.agents/rules/1.md b/.agents/rules/1.md deleted file mode 100644 index 513a1d3..0000000 --- a/.agents/rules/1.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -trigger: always_on -glob: -description: ---- - diff --git a/.agents/skills/SKILL.md b/.agents/skills/SKILL.md deleted file mode 100644 index ec02af3..0000000 --- a/.agents/skills/SKILL.md +++ /dev/null @@ -1,29 +0,0 @@ -Контекст проекта: - -Backend: Java 17, Spring Framework. Учитывай возможности этой версии языка и стандарты фреймворка. - -Frontend: HTML, CSS, JavaScript. - -Правила написания кода и комментариев: - -Проверка: Перед написанием кода изучи проект, его структуру и используемые технологии. Не предлагай решения, которые не соответствуют текущей архитектуре или стеку. - -Язык: Все комментарии и объяснения должны быть строго на русском языке. - -Комментирование кода: Оставляй комментарии, объясняющие, за что отвечает та или иная часть кода. Перед крупными или смысловыми блоками обязательно ставь поясняющие метки (например: ``, /* таблица subjects */, // логика обработки subjects). - -Обоснование решений: При написании нового кода кратко и максимально понятно объясняй, почему мы используем именно это решение, а не другое. - -Современные подходы: На фронтенде используй самые современные и актуальные подходы (например, Flexbox, CSS Grid, семантические теги). - -Правила работы с ошибками (Обучающий режим): - -Если ты находишь ошибку в моем коде, не пиши сразу готовый исправленный код. - -Дай мне точную подсказку, чтобы я мог сам найти и исправить баг (например: "У тебя не закрыт тег в 15 строке", "Ты забыл поставить аннотацию в контроллере Spring" или "Проверь отступы в таком-то классе"). Моя цель — научиться. - -Правила работы с дизайном (UI/UX): - -Перед добавлением новых стилей всегда сначала изучай, какие стили уже используются в проекте, чтобы сохранять единообразие. - -Если ты видишь, что текущий дизайн откровенно плох, нелогичен или устарел — смело предлагай свои идеи по улучшению (цветовая палитра, отступы, шрифты). Я открыт к предложениям по улучшению визуала. \ No newline at end of file diff --git a/.agents/skills/AutoUpdateDocs.md b/.agents/skills/auto-update-docs/SKILL.md similarity index 100% rename from .agents/skills/AutoUpdateDocs.md rename to .agents/skills/auto-update-docs/SKILL.md diff --git a/frontend/admin/css/components.css b/frontend/admin/css/components.css index 7c24c52..466d3fc 100755 --- a/frontend/admin/css/components.css +++ b/frontend/admin/css/components.css @@ -753,4 +753,45 @@ tbody tr:hover { display: flex; align-items: center; gap: 8px; +} + +/* ===== Theme Toggle Button ===== */ +.theme-toggle { + width: 42px; + height: 42px; + border: none; + border-radius: 50%; + background: var(--bg-card); + border: 1px solid var(--bg-card-border); + color: var(--text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + transition: background 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease; + z-index: 100; + flex-shrink: 0; +} + +.theme-toggle svg { + width: 20px; + height: 20px; + transition: transform 0.4s ease; +} + +.theme-toggle:hover { + transform: scale(1.1); + box-shadow: 0 4px 16px var(--accent-glow); +} + +.theme-toggle:active { + transform: scale(0.95); +} + +.theme-toggle--fixed { + position: fixed; + top: 1.25rem; + right: 1.25rem; } \ No newline at end of file diff --git a/frontend/admin/css/layout.css b/frontend/admin/css/layout.css index d6386ac..7b32ed7 100755 --- a/frontend/admin/css/layout.css +++ b/frontend/admin/css/layout.css @@ -19,6 +19,27 @@ .sidebar-header { padding: 1.25rem; border-bottom: 1px solid var(--bg-card-border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.sidebar-close-btn { + background: transparent; + border: none; + color: var(--text-secondary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0.25rem; + border-radius: var(--radius-sm); + transition: all var(--transition); +} + +.sidebar-close-btn:hover { + background: var(--bg-card-border); + color: var(--text-primary); } .logo { @@ -99,7 +120,7 @@ border-top: 1px solid var(--bg-card-border); } -.btn-logout { +.btn-settings { width: 100%; display: flex; align-items: center; @@ -116,16 +137,110 @@ position: relative; } -.btn-logout:hover { - background: rgba(248, 113, 113, 0.1); +.btn-settings:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.settings-chevron { + margin-left: auto; + transition: transform 0.3s ease; + flex-shrink: 0; +} + +.settings-dropdown.open .settings-chevron { + transform: rotate(180deg); +} + +/* Settings Dropdown Menu */ +.settings-dropdown { + position: relative; +} + +.settings-menu { + position: absolute; + bottom: calc(100% + 0.5rem); + left: 0; + right: 0; + background: rgba(15, 23, 42, 0.97); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--bg-card-border); + border-radius: var(--radius-sm); + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35); + padding: 0.4rem; + z-index: 200; + opacity: 0; + visibility: hidden; + transform: translateY(8px); + transition: opacity 0.25s ease, visibility 0.25s ease, transform 0.25s ease; +} + +[data-theme="light"] .settings-menu { + background: rgba(255, 255, 255, 0.98); + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.1); +} + +.settings-dropdown.open .settings-menu { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.settings-menu-item { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.6rem 0.75rem; + border: none; + border-radius: 8px; + background: none; + color: var(--text-primary); + font-family: inherit; + font-size: 0.88rem; + cursor: pointer; + text-decoration: none; + transition: background 0.2s ease; + width: 100%; +} + +.settings-menu-item:hover { + background: var(--bg-hover); +} + +.settings-menu-item--danger { color: var(--error); } +.settings-menu-item--danger:hover { + background: rgba(248, 113, 113, 0.1); +} + +.settings-menu-divider { + height: 1px; + background: var(--bg-card-border); + margin: 0.25rem 0.5rem; +} + /* ===== Main ===== */ .main { flex: 1; margin-left: 260px; min-height: 100vh; + transition: margin-left 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +/* Desktop Collapse State */ +@media (min-width: 769px) { + .sidebar.collapsed { + transform: translateX(-100%); + } + .main.sidebar-collapsed { + margin-left: 0; + } + .main.sidebar-collapsed .menu-toggle { + display: block; + } } .topbar { @@ -180,7 +295,9 @@ backdrop-filter: blur(2px); z-index: 9; opacity: 0; - transition: opacity var(--transition); + visibility: hidden; + pointer-events: none; + transition: opacity var(--transition), visibility var(--transition); } /* ===== Responsive Mobile ===== */ @@ -212,5 +329,7 @@ .sidebar-overlay.open { opacity: 1; + visibility: visible; + pointer-events: auto; } } \ No newline at end of file diff --git a/frontend/admin/index.html b/frontend/admin/index.html index 158ee8d..868315e 100755 --- a/frontend/admin/index.html +++ b/frontend/admin/index.html @@ -36,6 +36,12 @@ Magistr +
diff --git a/frontend/admin/js/main.js b/frontend/admin/js/main.js index dbbd55a..fc86867 100755 --- a/frontend/admin/js/main.js +++ b/frontend/admin/js/main.js @@ -1,4 +1,7 @@ -import './otel.js'; +// OTel: загружаем только на продакшене (не на localhost) +if (!['localhost', '127.0.0.1'].includes(window.location.hostname)) { + import('./otel.js').catch(e => console.warn('OTel init skipped:', e.message)); +} import { isAuthenticatedAsAdmin } from './api.js'; import { applyRippleEffect, closeAllDropdownsOnOutsideClick } from './utils.js'; @@ -37,7 +40,9 @@ const navItems = document.querySelectorAll('.nav-item[data-tab]'); const sidebar = document.querySelector('.sidebar'); const sidebarOverlay = document.getElementById('sidebar-overlay'); const menuToggle = document.getElementById('menu-toggle'); +const sidebarCloseBtn = document.getElementById('sidebar-close-btn'); const btnLogout = document.getElementById('btn-logout'); +const main = document.querySelector('.main'); // Initial auth check if (!isAuthenticatedAsAdmin()) { @@ -48,16 +53,56 @@ if (!isAuthenticatedAsAdmin()) { applyRippleEffect(); closeAllDropdownsOnOutsideClick(); -// Menu Toggle +// Init sidebar state from localStorage on load +if (window.innerWidth > 768 && localStorage.getItem('sidebar-collapsed') === 'true') { + sidebar.classList.add('collapsed'); + main.classList.add('sidebar-collapsed'); +} + +// Menu Toggle (Hamburger) menuToggle.addEventListener('click', () => { - sidebar.classList.toggle('open'); - sidebarOverlay.classList.toggle('open'); + if (window.innerWidth <= 768) { + sidebar.classList.toggle('open'); + sidebarOverlay.classList.toggle('open'); + } else { + sidebar.classList.remove('collapsed'); + main.classList.remove('sidebar-collapsed'); + localStorage.setItem('sidebar-collapsed', 'false'); + } }); + +// Sidebar Close (X button) +sidebarCloseBtn?.addEventListener('click', () => { + if (window.innerWidth <= 768) { + sidebar.classList.remove('open'); + sidebarOverlay.classList.remove('open'); + } else { + sidebar.classList.toggle('collapsed'); + main.classList.toggle('sidebar-collapsed'); + localStorage.setItem('sidebar-collapsed', sidebar.classList.contains('collapsed')); + } +}); + sidebarOverlay.addEventListener('click', () => { sidebar.classList.remove('open'); sidebarOverlay.classList.remove('open'); }); +// Settings Dropdown +const settingsDropdown = document.getElementById('settings-dropdown'); +const btnSettings = document.getElementById('btn-settings'); + +btnSettings.addEventListener('click', (e) => { + e.stopPropagation(); + settingsDropdown.classList.toggle('open'); +}); + +document.addEventListener('click', (e) => { + if (!settingsDropdown.contains(e.target)) { + settingsDropdown.classList.remove('open'); + } +}); + // Logout btnLogout.addEventListener('click', () => { localStorage.removeItem('token'); diff --git a/frontend/admin/settings/css/layout.css b/frontend/admin/settings/css/layout.css new file mode 100644 index 0000000..ff48f7e --- /dev/null +++ b/frontend/admin/settings/css/layout.css @@ -0,0 +1,255 @@ +/* ===== Sidebar ===== */ +.sidebar { + width: 260px; + min-height: 100vh; + background: var(--bg-sidebar); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-right: 1px solid var(--bg-card-border); + display: flex; + flex-direction: column; + position: fixed; + left: 0; + top: 0; + bottom: 0; + z-index: 10; + transition: background 0.4s ease, border-color 0.4s ease, transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +.sidebar-header { + padding: 1.25rem; + border-bottom: 1px solid var(--bg-card-border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.sidebar-close-btn { + background: transparent; + border: none; + color: var(--text-secondary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0.25rem; + border-radius: var(--radius-sm); + transition: all var(--transition); +} + +.sidebar-close-btn:hover { + background: var(--bg-card-border); + color: var(--text-primary); +} + +.logo { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 1.15rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.sidebar-nav { + flex: 1; + padding: 0.75rem; +} + +.nav-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + margin-bottom: 0.25rem; + border-radius: var(--radius-sm); + color: var(--text-secondary); + text-decoration: none; + font-size: 0.95rem; + font-weight: 500; + transition: all var(--transition); + position: relative; + overflow: hidden; +} + +.nav-item::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 3px; + background: var(--accent); + border-radius: 0 4px 4px 0; + transform: scaleY(0); + transition: transform var(--transition); + opacity: 0; +} + +.nav-item:hover { + background: var(--bg-hover); + color: var(--text-primary); + transform: translateX(4px); +} + +.nav-item.active { + background: rgba(139, 92, 246, 0.12); + color: var(--accent-hover); +} + +[data-theme="light"] .nav-item.active { + background: rgba(99, 102, 241, 0.18); +} + +.nav-item.active::before { + transform: scaleY(1); + opacity: 1; +} + +.nav-item svg { + transition: transform var(--transition); +} + +.nav-item:hover svg, +.nav-item.active svg { + transform: scale(1.15) rotate(-5deg); +} + +.sidebar-footer { + padding: 0.75rem; + border-top: 1px solid var(--bg-card-border); +} + +.btn-back { + width: 100%; + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.65rem 0.8rem; + border: none; + border-radius: var(--radius-sm); + background: none; + color: var(--text-secondary); + font-family: inherit; + font-size: 0.9rem; + cursor: pointer; + text-decoration: none; + transition: background var(--transition), color var(--transition); +} + +.btn-back:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +/* ===== Main ===== */ +.main { + flex: 1; + margin-left: 260px; + min-height: 100vh; + transition: margin-left 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +/* Desktop Collapse State */ +@media (min-width: 769px) { + .sidebar.collapsed { + transform: translateX(-100%); + } + .main.sidebar-collapsed { + margin-left: 0; + } + .main.sidebar-collapsed .menu-toggle { + display: block; + } +} + +.topbar { + padding: 1.5rem 2rem; + border-bottom: 1px solid var(--bg-card-border); + transition: border-color 0.4s ease; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.topbar h1 { + font-size: 1.3rem; + font-weight: 700; + letter-spacing: -0.02em; + flex: 1; +} + +.content { + padding: 1.5rem 2rem; + display: flex; + flex-direction: column; + gap: 1.5rem; + animation: fadeIn 0.2s ease; +} + +/* ===== Mobile Menu Toggle ===== */ +.menu-toggle { + display: none; + padding: 0.4rem; + background: none; + border: none; + color: var(--text-primary); + cursor: pointer; + border-radius: var(--radius-sm); + transition: background var(--transition); +} + +.menu-toggle:hover { + background: var(--bg-hover); +} + +.sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(2px); + z-index: 9; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity var(--transition), visibility var(--transition); +} + +/* ===== Responsive Mobile ===== */ +@media (max-width: 768px) { + .sidebar { + transform: translateX(-100%); + } + + .sidebar.open { + transform: translateX(0); + } + + .main { + margin-left: 0; + } + + .topbar { + padding: 1rem 1.25rem; + } + + .content { + padding: 1.25rem; + } + + .menu-toggle, + .sidebar-overlay { + display: block; + } + + .sidebar-overlay.open { + opacity: 1; + visibility: visible; + pointer-events: auto; + } +} diff --git a/frontend/admin/settings/css/main.css b/frontend/admin/settings/css/main.css new file mode 100644 index 0000000..f491eef --- /dev/null +++ b/frontend/admin/settings/css/main.css @@ -0,0 +1,153 @@ +/* ===== Reset & Base ===== */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-primary: #0a0a0f; + --bg-sidebar: rgba(255, 255, 255, 0.02); + --bg-card: rgba(255, 255, 255, 0.03); + --bg-card-border: rgba(255, 255, 255, 0.05); + --bg-input: rgba(255, 255, 255, 0.04); + --bg-input-focus: rgba(255, 255, 255, 0.08); + --bg-hover: rgba(255, 255, 255, 0.06); + + --text-primary: #f8fafc; + --text-secondary: #94a3b8; + --text-placeholder: #475569; + + --accent: #8b5cf6; + --accent-hover: #a78bfa; + --accent-glow: rgba(139, 92, 246, 0.4); + --accent-secondary: #ec4899; + + --error: #ef4444; + --success: #10b981; + --warning: #f59e0b; + + --radius-sm: 10px; + --radius-md: 16px; + --transition: 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +/* ===== Light Theme ===== */ +[data-theme="light"] { + --bg-primary: #f8fafc; + --bg-sidebar: rgba(255, 255, 255, 0.7); + --bg-card: rgba(255, 255, 255, 0.75); + --bg-card-border: rgba(0, 0, 0, 0.08); + --bg-input: rgba(0, 0, 0, 0.03); + --bg-input-focus: rgba(0, 0, 0, 0.06); + --bg-hover: rgba(0, 0, 0, 0.05); + --text-primary: #0f172a; + --text-secondary: #475569; + --text-placeholder: #94a3b8; + --accent: #6366f1; + --accent-hover: #4f46e5; + --accent-glow: rgba(99, 102, 241, 0.3); + --accent-secondary: #d946ef; + --error: #ef4444; + --success: #10b981; + --warning: #f59e0b; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + min-height: 100vh; + display: flex; + transition: background 0.4s ease, color 0.4s ease; +} + +/* ===== Animations ===== */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ===== Theme Toggle ===== */ +.theme-toggle { + width: 42px; + height: 42px; + border: none; + border-radius: 50%; + background: var(--bg-card); + border: 1px solid var(--bg-card-border); + color: var(--text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + transition: background 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease; + z-index: 100; + flex-shrink: 0; +} + +.theme-toggle svg { + width: 20px; + height: 20px; + transition: transform 0.4s ease; +} + +.theme-toggle:hover { + transform: scale(1.1); + box-shadow: 0 4px 16px var(--accent-glow); +} + +.theme-toggle:active { + transform: scale(0.95); +} + +.theme-toggle--fixed { + position: fixed; + top: 1.25rem; + right: 1.25rem; +} + +/* ===== Settings Placeholder ===== */ +.settings-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 4rem 2rem; + animation: fadeIn 0.4s ease both; +} + +.settings-placeholder .icon-wrap { + width: 80px; + height: 80px; + border-radius: 50%; + background: linear-gradient(135deg, rgba(139, 92, 246, 0.15), rgba(236, 72, 153, 0.15)); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.5rem; + box-shadow: 0 0 30px var(--accent-glow); +} + +.settings-placeholder h2 { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.settings-placeholder p { + color: var(--text-secondary); + font-size: 0.95rem; + max-width: 400px; + line-height: 1.6; +} diff --git a/frontend/admin/settings/index.html b/frontend/admin/settings/index.html new file mode 100644 index 0000000..4bf476e --- /dev/null +++ b/frontend/admin/settings/index.html @@ -0,0 +1,89 @@ + + + + + + +Этот раздел находится в разработке. Здесь будут доступны общие настройки системы.
+