/
direrome
/
project
Обзор
Документация
Войти
/
direrome
/
project
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
static/script.js
1 020 строк
39 KB
Ynree
first_commit
02 июн 2025, 10:19
02 июн 2025, 10:19
cb6f647
Код
Авторство
О чём код?
let analyticsChart = null; let currentUser = null; // Проверка наличия Tailwind CSS function checkTailwind() { const testDiv = document.createElement('div'); testDiv.className = 'hidden'; document.body.appendChild(testDiv); const isHidden = window.getComputedStyle(testDiv).display === 'none'; document.body.removeChild(testDiv); if (!isHidden) { console.warn('Tailwind CSS не загружен, используются запасные стили'); document.documentElement.classList.add('no-tailwind'); } return isHidden; } // Инициализация при загрузке страницы document.addEventListener('DOMContentLoaded', () => { checkTailwind(); checkAuth(); setupEventListeners(); loadCSRFToken(); initializeTheme(); initializeScrollTop(); setActiveNav(); initializeSlider(); initializeScrollAnimations(); }); // Проверка аутентификации async function checkAuth() { try { const response = await fetch('/api/check_auth'); const data = await response.json(); const loginBtn = document.getElementById('loginBtn'); const logoutBtn = document.getElementById('logoutBtn'); const dashboardBtn = document.getElementById('dashboardBtn'); if (data.authenticated) { currentUser = data.user_id; loginBtn?.classList.add('hidden'); logoutBtn?.classList.remove('hidden'); dashboardBtn?.classList.remove('hidden'); if (window.location.pathname === '/dashboard') { loadDashboardData(); } } else { loginBtn?.classList.remove('hidden'); logoutBtn?.classList.add('hidden'); dashboardBtn?.classList.add('hidden'); if (window.location.pathname === '/dashboard') { window.location.href = '/'; } } } catch (error) { console.error('Ошибка проверки аутентификации:', error); showNotification('Ошибка соединения с сервером', 'error'); } } // Загрузка CSRF-токена async function loadCSRFToken() { try { const response = await fetch('/api/csrf_token'); const data = await response.json(); console.log('CSRF-токен загружен:', data.csrf_token); ['csrf_token', 'csrf_token_login', 'csrf_token_register', 'csrf_token_product', 'csrf_token_integration', 'csrf_token_settings', 'csrf_token_contact'].forEach(id => { const element = document.getElementById(id); if (element) element.value = data.csrf_token; else console.warn(`CSRF token element not found: ${id}`); }); } catch (error) { console.error('Ошибка загрузки CSRF-токена:', error); showNotification('Ошибка загрузки CSRF-токена', 'error'); } } // Настройка обработчиков событий function setupEventListeners() { // Главная страница document.getElementById('loginBtn')?.addEventListener('click', showLoginModal); document.getElementById('logoutBtn')?.addEventListener('click', logoutUser); document.getElementById('ctaBtn')?.addEventListener('click', showLoginModal); document.getElementById('showRegister')?.addEventListener('click', showRegisterForm); document.getElementById('showLogin')?.addEventListener('click', showLoginForm); document.getElementById('closeModal')?.addEventListener('click', closeModal); document.getElementById('loginForm')?.addEventListener('submit', handleLogin); document.getElementById('registerForm')?.addEventListener('submit', handleRegister); document.getElementById('sidebarLogoutBtn')?.addEventListener('click', logoutUser); document.getElementById('contactForm')?.addEventListener('submit', handleContact); // Dashboard document.getElementById('filterAnalytics')?.addEventListener('click', filterAnalyticsData); document.getElementById('exportAnalytics')?.addEventListener('click', exportAnalyticsData); document.getElementById('addProductForm')?.addEventListener('submit', addProduct); document.getElementById('addIntegrationForm')?.addEventListener('submit', addIntegration); document.getElementById('settingsForm')?.addEventListener('submit', updateSettings); // Боковая панель const sidebarToggle = document.getElementById('sidebarToggle'); if (sidebarToggle) { sidebarToggle.addEventListener('click', () => { const sidebar = document.getElementById('sidebar'); const isOpen = sidebar.classList.contains('open'); toggleSidebar(!isOpen); }); } const sidebarClose = document.getElementById('sidebarClose'); if (sidebarClose) { sidebarClose.addEventListener('click', () => { toggleSidebar(false); }); } // Переключатель темы const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.addEventListener('click', toggleTheme); } // Навигация document.querySelectorAll('#sidebar nav a').forEach(link => { link.addEventListener('click', () => { setActiveNav(); if (window.innerWidth < 768) { toggleSidebar(false); } }); }); // Закрытие модального окна по клику на фон document.getElementById('modal')?.addEventListener('click', (e) => { if (e.target === e.currentTarget) { closeModal(); } }); } // Инициализация слайдера function initializeSlider() { const slides = document.querySelectorAll('.slide'); const prev = document.querySelector('.prev'); const next = document.querySelector('.next'); let currentSlide = 0; if (!slides.length || !prev || !next) return; function showSlide(index) { slides.forEach((slide, i) => { slide.classList.toggle('active', i === index); slide.classList.toggle('hidden', i !== index); }); } prev.addEventListener('click', () => { currentSlide = (currentSlide - 1 + slides.length) % slides.length; showSlide(currentSlide); }); next.addEventListener('click', () => { currentSlide = (currentSlide + 1) % slides.length; showSlide(currentSlide); }); setInterval(() => { currentSlide = (currentSlide + 1) % slides.length; showSlide(currentSlide); }, 3000); } // Анимация при скролле function initializeScrollAnimations() { const elements = document.querySelectorAll('.animate-on-scroll'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('show'); observer.unobserve(entry.target); } }); }, { threshold: 0.1 }); elements.forEach(element => observer.observe(element)); }; // Обработка формы контактов async function handleContact(e) { e.preventDefault(); const button = document.querySelector('#contactForm button'); const contactBtnText = document.getElementById('contactBtnText'); const contactBtnLoading = document.getElementById('contactBtnLoading'); if (!button || !contactBtnText || !contactBtnLoading) return; button.disabled = true; contactBtnText.classList.add('hidden'); contactBtnLoading.classList.remove('hidden'); const name = document.getElementById('contactName').value.trim(); const email = document.getElementById('contactEmail').value.trim(); const message = document.getElementById('contactMessage').value.trim(); const csrf_token = document.getElementById('csrf_token_contact').value; if (!name || !email || !message) { showNotification('Заполните все поля', 'error'); button.disabled = false; contactBtnText.classList.remove('hidden'); contactBtnLoading.classList.add('hidden'); return; } try { const response = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ name, email, message }) }); const data = await response.json(); if (response.ok) { showNotification('Сообщение отправлено'); document.getElementById('contactForm').reset(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка отправки сообщения:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; contactBtnText.classList.remove('hidden'); contactBtnLoading.classList.add('hidden'); } } // Управление боковой панелью function toggleSidebar(open) { const sidebar = document.getElementById('sidebar'); if (!sidebar) return; if (open) { sidebar.classList.add('open'); if (window.innerWidth < 768) { document.body.style.overflow = 'hidden'; } } else { sidebar.classList.remove('open'); document.body.style.overflow = ''; } } // Управление темой function initializeTheme() { const savedTheme = localStorage.getItem('theme') || 'dark'; document.documentElement.setAttribute('data-theme', savedTheme); updateThemeIcon(savedTheme); } function toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'light' ? 'dark' : 'light'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); updateThemeIcon(newTheme); } function updateThemeIcon(theme) { const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { themeToggle.innerHTML = theme === 'light' ? '<span class="text-lg">🌙</span>' : '<span class="text-lg">☀️</span>'; } } // Управление кнопкой "Наверх" function initializeScrollTop() { const scrollTopBtn = document.getElementById('scrollTopBtn'); if (scrollTopBtn) { window.addEventListener('scroll', () => { if (window.scrollY > 300) { scrollTopBtn.classList.add('visible'); } else { scrollTopBtn.classList.remove('visible'); } }); scrollTopBtn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } } // Установка активного пункта навигации function setActiveNav() { const hash = window.location.hash || '#analytics'; document.querySelectorAll('#sidebar nav a').forEach(link => { if (link.getAttribute('href') === hash) { link.setAttribute('aria-current', 'page'); } else { link.removeAttribute('aria-current'); } }); } // Показать модальное окно входа function showLoginModal() { const modal = document.getElementById('modal'); const loginFormContainer = document.getElementById('loginFormContainer'); const registerFormContainer = document.getElementById('registerFormContainer'); if (!modal || !loginFormContainer || !registerFormContainer) { console.error('Modal elements not found:', { modal, loginFormContainer, registerFormContainer }); showNotification('Ошибка отображения формы входа', 'error'); return; } modal.classList.remove('hidden'); loginFormContainer.classList.remove('hidden'); loginFormContainer.classList.add('animate-form-in'); registerFormContainer.classList.add('hidden'); registerFormContainer.classList.remove('animate-form-in'); document.body.classList.add('modal-open'); window.scrollTo({ top: 0, behavior: 'instant' }); modal.style.display = 'flex'; } // Показать форму регистрации function showRegisterForm(e) { e.preventDefault(); const loginFormContainer = document.getElementById('loginFormContainer'); const registerFormContainer = document.getElementById('registerFormContainer'); const modal = document.getElementById('modal'); if (!loginFormContainer || !registerFormContainer || !modal) { console.error('Form containers not found'); showNotification('Ошибка отображения формы', 'error'); return; } modal.classList.remove('hidden'); loginFormContainer.classList.add('hidden'); loginFormContainer.classList.remove('animate-form-in'); registerFormContainer.classList.remove('hidden'); registerFormContainer.classList.add('animate-form-in'); document.body.classList.add('modal-open'); modal.style.display = 'flex'; } // Показать форму входа function showLoginForm(e) { e.preventDefault(); const loginFormContainer = document.getElementById('loginFormContainer'); const registerFormContainer = document.getElementById('registerFormContainer'); const modal = document.getElementById('modal'); if (!loginFormContainer || !registerFormContainer || !modal) { console.error('Form containers not found'); showNotification('Ошибка отображения формы', 'error'); return; } modal.classList.remove('hidden'); registerFormContainer.classList.add('hidden'); registerFormContainer.classList.remove('animate-form-in'); loginFormContainer.classList.remove('hidden'); loginFormContainer.classList.add('animate-form-in'); document.body.classList.add('modal-open'); modal.style.display = 'flex'; } // Закрыть модальное окно function closeModal() { const modal = document.getElementById('modal'); const loginForm = document.getElementById('loginForm'); const registerForm = document.getElementById('registerForm'); if (!modal) { console.error('Modal not found'); return; } modal.classList.add('hidden'); loginForm?.reset(); registerForm?.reset(); document.body.classList.remove('modal-open'); modal.style.display = 'none'; } // Обработка входа async function handleLogin(e) { e.preventDefault(); const button = document.querySelector('#loginForm button'); const loginBtnText = document.getElementById('loginBtnText'); const loginBtnLoading = document.getElementById('loginBtnLoading'); if (!button || !loginBtnText || !loginBtnLoading) { console.error('Login form elements not found'); showNotification('Ошибка формы входа', 'error'); return; } button.disabled = true; loginBtnText.classList.add('hidden'); loginBtnLoading.classList.remove('hidden'); const username = document.getElementById('loginUsername').value.trim(); const password = document.getElementById('loginPassword').value.trim(); const twofaCode = document.getElementById('login2fa').value.trim(); const csrf_token = document.getElementById('csrf_token_login').value; try { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ username, password, twofaCode }) }); const data = await response.json(); if (response.ok) { showNotification('Вход выполнен успешно'); closeModal(); window.location.href = '/dashboard'; } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка входа:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; loginBtnText.classList.remove('hidden'); loginBtnLoading.classList.add('hidden'); } } // Обработка регистрации async function handleRegister(e) { e.preventDefault(); const button = document.querySelector('#registerForm button'); const registerBtnText = document.getElementById('registerBtnText'); const registerBtnLoading = document.getElementById('registerBtnLoading'); if (!button || !registerBtnText || !registerBtnLoading) return; button.disabled = true; registerBtnText.classList.add('hidden'); registerBtnLoading.classList.remove('hidden'); const username = document.getElementById('registerUsername').value.trim(); const email = document.getElementById('registerEmail').value.trim(); const password = document.getElementById('registerPassword').value.trim(); const confirmPassword = document.getElementById('confirmPassword').value.trim(); const enable2fa = document.getElementById('enable2fa').checked; const csrf_token = document.getElementById('csrf_token_register').value; if (password !== confirmPassword) { showNotification('Пароли не совпадают', 'error'); button.disabled = false; registerBtnText.classList.remove('hidden'); registerBtnLoading.classList.add('hidden'); return; } try { const response = await fetch('/api/register', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ username, email, password, enable2fa }) }); const data = await response.json(); if (response.ok) { showNotification('Регистрация прошла успешно'); showLoginForm(new Event('click')); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка регистрации:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; registerBtnText.classList.remove('hidden'); registerBtnLoading.classList.add('hidden'); } } // Выход из системы async function logoutUser() { try { const csrfToken = document.getElementById('csrf_token')?.value || ''; if (!csrfToken) { console.warn('CSRF-токен не найден'); showNotification('Ошибка: CSRF-токен не найден', 'error'); return; } const response = await fetch('/api/logout', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken } }); if (response.ok) { showNotification('Выход выполнен успешно'); currentUser = null; if (analyticsChart) { analyticsChart.destroy(); analyticsChart = null; } window.location.href = '/'; } else { const data = await response.json(); showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка выхода:', error); showNotification('Ошибка соединения с сервером', 'error'); } } // Загрузка данных dashboard function loadDashboardData() { loadAnalyticsData(); loadProducts(); loadOrders(); loadIntegrations(); } // Загрузка аналитики async function loadAnalyticsData(startDate = null, endDate = null) { try { let url = '/api/analytics'; if (startDate && endDate) { url += `?startDate=${startDate}&endDate=${endDate}`; } const response = await fetch(url); const data = await response.json(); if (response.ok) { updateAnalyticsChart(data); updateAnalyticsMetrics(data); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка загрузки аналитики:', error); showNotification('Ошибка загрузки аналитики', 'error'); } } // Фильтрация аналитики function filterAnalyticsData() { const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; if (!startDate || !endDate) { showNotification('Выберите диапазон дат', 'error'); return; } loadAnalyticsData(startDate, endDate); } // Обновление графика function updateAnalyticsChart(data) { const ctx = document.getElementById('analyticsChart')?.getContext('2d'); if (!ctx) return; if (analyticsChart) { analyticsChart.destroy(); } const labels = data.byMarketplace.map(item => item.marketplace); const sales = data.byMarketplace.map(item => item.sales); const revenue = data.byMarketplace.map(item => item.revenue); analyticsChart = new Chart(ctx, { type: 'line', data: { labels, datasets: [ { label: 'Продажи', data: sales, borderColor: 'rgb(59, 130, 246)', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.1, fill: true }, { label: 'Доход', data: revenue, borderColor: 'rgb(16, 185, 129)', backgroundColor: 'rgba(16, 185, 129, 0.1)', tension: 0.1, fill: true } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' }, tooltip: { mode: 'index', intersect: false } }, scales: { y: { beginAtZero: true } } } }); } // Обновление метрик function updateAnalyticsMetrics(data) { const sales = document.getElementById('sales'); const revenue = document.getElementById('revenue'); const expenses = document.getElementById('expenses'); if (sales) sales.textContent = `${data.sales} ₽`; if (revenue) revenue.textContent = `${data.revenue} ₽`; if (expenses) expenses.textContent = `${data.expenses} ₽`; } // Экспорт аналитики function exportAnalyticsData() { const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; let url = '/api/export-analytics'; if (startDate && endDate) { url += `?startDate=${startDate}&endDate=${endDate}`; } window.location.href = url; } // Загрузка товаров async function loadProducts() { try { const response = await fetch('/api/products'); const data = await response.json(); if (response.ok) { renderProducts(data.products); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка загрузки товаров:', error); showNotification('Ошибка загрузки товаров', 'error'); } } // Отображение товаров function renderProducts(products) { const productList = document.getElementById('productList'); if (!productList) return; productList.innerHTML = ''; if (products.length === 0) { productList.innerHTML = '<tr><td colspan="3" class="text-center py-4">Нет товаров</td></tr>'; return; } products.forEach(product => { const row = document.createElement('tr'); row.innerHTML = ` <td class="p-2">${product.name}</td> <td class="p-2">${product.price} ₽</td> <td class="p-2"><button class="delete-btn text-red-600 hover:text-red-700" data-id="${product.id}">Удалить</button></td> `; productList.appendChild(row); }); document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', () => deleteProduct(button.dataset.id)); }); } // Добавление товара async function addProduct(e) { e.preventDefault(); const button = document.querySelector('#addProductForm button'); const addProductBtnText = document.getElementById('addProductBtnText'); const addProductBtnLoading = document.getElementById('addProductBtnLoading'); if (!button || !addProductBtnText || !addProductBtnLoading) return; button.disabled = true; addProductBtnText.classList.add('hidden'); addProductBtnLoading.classList.remove('hidden'); const name = document.getElementById('productName').value.trim(); const price = parseFloat(document.getElementById('productPrice').value); const csrf_token = document.getElementById('csrf_token_product').value; if (!name || isNaN(price)) { showNotification('Заполните все поля', 'error'); button.disabled = false; addProductBtnText.classList.remove('hidden'); addProductBtnLoading.classList.add('hidden'); return; } try { const response = await fetch('/api/products', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ name, price }) }); const data = await response.json(); if (response.ok) { showNotification('Товар добавлен'); document.getElementById('addProductForm').reset(); loadProducts(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка добавления товара:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; addProductBtnText.classList.remove('hidden'); addProductBtnLoading.classList.add('hidden'); } } // Удаление товара async function deleteProduct(productId) { if (!confirm('Вы уверены, что хотите удалить этот товар?')) return; try { const response = await fetch(`/api/products/${productId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.getElementById('csrf_token_product').value } }); const data = await response.json(); if (response.ok) { showNotification('Товар удален'); loadProducts(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка удаления товара:', error); showNotification('Ошибка соединения с сервером', 'error'); } } // Загрузка заказов async function loadOrders() { try { const response = await fetch('/api/orders'); const data = await response.json(); if (response.ok) { renderOrders(data.orders); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка загрузки заказов:', error); showNotification('Ошибка загрузки заказов', 'error'); } } // Отображение заказов function renderOrders(orders) { const orderList = document.getElementById('orderList'); if (!orderList) return; orderList.innerHTML = ''; if (orders.length === 0) { orderList.innerHTML = '<tr><td colspan="4" class="text-center py-4">Нет заказов</td></tr>'; return; } orders.forEach(order => { const row = document.createElement('tr'); row.innerHTML = ` <td class="p-2">${order.id}</td> <td class="p-2">${order.product_name}</td> <td class="p-2"> <select class="orderStatus border rounded p-1" data-id="${order.id}"> <option value="pending" ${order.status === 'pending' ? 'selected' : ''}>В обработке</option> <option value="shipped" ${order.status === 'shipped' ? 'selected' : ''}>Отправлен</option> <option value="delivered" ${order.status === 'delivered' ? 'selected' : ''}>Доставлен</option> </select> </td> <td class="p-2"><button class="delete-btn text-red-600 hover:text-red-700" data-id="${order.id}">Удалить</button></td> `; orderList.appendChild(row); }); document.querySelectorAll('.orderStatus').forEach(select => { select.addEventListener('change', () => updateOrderStatus(select.dataset.id, select.value)); }); document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', () => deleteOrder(button.dataset.id)); }); } // Обновление статуса заказа async function updateOrderStatus(orderId, status) { try { const response = await fetch(`/api/orders/${orderId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.getElementById('csrf_token_product').value }, body: JSON.stringify({ status }) }); const data = await response.json(); if (response.ok) { showNotification('Статус заказа обновлен'); loadOrders(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка обновления статуса заказа:', error); showNotification('Ошибка обновления статуса заказа', 'error'); } } // Удаление заказа async function deleteOrder(orderId) { if (!confirm('Вы уверены, что хотите удалить этот заказ?')) return; try { const response = await fetch(`/api/orders/${orderId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.getElementById('csrf_token_product').value } }); const data = await response.json(); if (response.ok) { showNotification('Заказ удален'); loadOrders(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка удаления заказа:', error); showNotification('Ошибка удаления заказа', 'error'); } } // Загрузка интеграций async function loadIntegrations() { try { const response = await fetch('/api/integrations'); const data = await response.json(); if (response.ok) { renderIntegrations(data.integrations); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка загрузки интеграций:', error); showNotification('Ошибка загрузки интеграций', 'error'); } } // Отображение интеграций function renderIntegrations(integrations) { const integrationList = document.getElementById('integrationList'); if (!integrationList) return; integrationList.innerHTML = ''; if (integrations.length === 0) { integrationList.innerHTML = '<tr><td colspan="3" class="text-center py-4">Нет интеграций</td></tr>'; return; } const marketplaceNames = { 'ozon': 'Ozon', 'wildberries': 'Wildberries' }; integrations.forEach(integration => { const row = document.createElement('tr'); row.innerHTML = ` <td class="p-2">${marketplaceNames[integration.marketplace] || integration.marketplace}</td> <td class="p-2">${integration.status === 'active' ? 'Активна' : 'Неактивна'}</td> <td class="p-2"><button class="delete-btn text-red-600 hover:text-red-700" data-id="${integration.id}">Удалить</button></td> `; integrationList.appendChild(row); }); document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', () => deleteIntegration(button.dataset.id)); }); } // Добавление интеграции async function addIntegration(e) { e.preventDefault(); const button = document.querySelector('#addIntegrationForm button'); const addIntegrationBtnText = document.getElementById('addIntegrationBtnText'); const addIntegrationBtnLoading = document.getElementById('addIntegrationBtnLoading'); if (!button || !addIntegrationBtnText || !addIntegrationBtnLoading) { console.error('Integration form elements not found'); showNotification('Ошибка формы интеграции', 'error'); return; } button.disabled = true; addIntegrationBtnText.classList.add('hidden'); addIntegrationBtnLoading.classList.remove('hidden'); const marketplace = document.getElementById('marketplace').value; const apiKey = document.getElementById('apiKey').value.trim(); const csrf_token = document.getElementById('csrf_token_integration').value; if (!marketplace || !apiKey) { showNotification('Заполните все поля', 'error'); button.disabled = false; addIntegrationBtnText.classList.remove('hidden'); addIntegrationBtnLoading.classList.add('hidden'); return; } try { const response = await fetch('/api/integrations', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ marketplace, apiKey }) }); const data = await response.json(); if (response.ok) { showNotification('Интеграция добавлена'); document.getElementById('addIntegrationForm').reset(); loadIntegrations(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка добавления интеграции:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; addIntegrationBtnText.classList.remove('hidden'); addIntegrationBtnLoading.classList.add('hidden'); } } // Удаление интеграции async function deleteIntegration(integrationId) { if (!confirm('Вы уверены, что хотите удалить эту интеграцию?')) return; try { const response = await fetch(`/api/integrations/${integrationId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.getElementById('csrf_token_integration').value } }); const data = await response.json(); if (response.ok) { showNotification('Интеграция удалена'); loadIntegrations(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка удаления интеграции:', error); showNotification('Ошибка соединения с сервером', 'error'); } } document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && !document.getElementById('modal').classList.contains('hidden')) { closeModal(); } }); // Обновление настроек async function updateSettings(e) { e.preventDefault(); const button = document.querySelector('#settingsForm button'); const settingsBtnText = document.getElementById('settingsBtnText'); const settingsBtnLoading = document.getElementById('settingsBtnLoading'); if (!button || !settingsBtnText || !settingsBtnLoading) return; button.disabled = true; settingsBtnText.classList.add('hidden'); settingsBtnLoading.classList.remove('hidden'); const currentPassword = document.getElementById('currentPassword').value.trim(); const newPassword = document.getElementById('newPassword').value.trim(); const confirmNewPassword = document.getElementById('confirmNewPassword').value.trim(); const csrf_token = document.getElementById('csrf_token_settings').value; if (!currentPassword || !newPassword || !confirmNewPassword) { showNotification('Заполните все поля', 'error'); button.disabled = false; settingsBtnText.classList.remove('hidden'); settingsBtnLoading.classList.add('hidden'); return; } if (newPassword !== confirmNewPassword) { showNotification('Новые пароли не совпадают', 'error'); button.disabled = false; settingsBtnText.classList.remove('hidden'); settingsBtnLoading.classList.add('hidden'); return; } try { const response = await fetch('/api/settings', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf_token }, body: JSON.stringify({ currentPassword, newPassword }) }); const data = await response.json(); if (response.ok) { showNotification('Пароль изменён успешно'); document.getElementById('settingsForm').reset(); } else { showNotification(data.message, 'error'); } } catch (error) { console.error('Ошибка обновления настроек:', error); showNotification('Ошибка соединения с сервером', 'error'); } finally { button.disabled = false; settingsBtnText.classList.remove('hidden'); settingsBtnLoading.classList.add('hidden'); } } // Показ уведомления function showNotification(message, type = 'success') { const notification = document.getElementById('notification'); const notificationText = document.getElementById('notificationText'); if (!notification || !notificationText) { console.error('Notification elements not found'); return; } notificationText.textContent = message; notification.classList.remove('bg-green-500', 'bg-red-500'); notification.classList.add(type === 'success' ? 'bg-green-500' : 'bg-red-500'); notification.classList.remove('hidden'); setTimeout(() => { notification.classList.add('hidden'); }, 3000); }