/** @file manager.js @version 0.2.0 */ function managerIcon(name) { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.classList.add('icon'); svg.setAttribute('aria-hidden', 'true'); const use = document.createElementNS('http://www.w3.org/2000/svg', 'use'); use.setAttribute('href', '#icon-' + name); svg.append(use); return svg; } async function managerRequest(url, options = {}) { const response = await fetch(url, options); let data; try { data = await response.json(); } catch { throw new Error('Сервер вернул неожиданный ответ. Попробуйте обновить страницу.'); } if (!response.ok) { throw new Error(typeof data.detail === 'string' ? data.detail : 'Проверьте введённые значения.'); } return data; } function managerMessage(text, type = 'info') { const element = document.getElementById('operation-message'); if (element.dataset.message === text && element.dataset.type === type && !element.classList.contains('is-hidden')) return; element.dataset.message = text; element.dataset.type = type; element.className = 'notice notice-' + type; const indicator = type === 'info' ? document.createElement('span') : managerIcon(type === 'success' ? 'check' : 'terminal'); if (type === 'info') { indicator.className = 'spinner'; indicator.setAttribute('aria-hidden', 'true'); } const copy = document.createElement('span'); copy.className = 'notice-copy'; copy.textContent = text; const close = document.createElement('button'); close.type = 'button'; close.className = 'icon-button'; close.setAttribute('aria-label', 'Скрыть сообщение'); close.append(managerIcon('close')); close.addEventListener('click', () => element.classList.add('is-hidden')); element.replaceChildren(indicator, copy, close); } function managerFormError(form, text = '') { const element = form.querySelector('.form-feedback'); if (!element) return; element.textContent = text; element.classList.toggle('is-hidden', !text); } async function managerJob(job) { while (true) { const warnings = (job.warnings || []).join(' '); if (job.status === 'error') throw new Error(job.error); if (job.status === 'done') { managerMessage(job.folder + ': готово.' + (warnings ? ' ' + warnings : ''), warnings ? 'warning' : 'success'); return job; } managerMessage(job.folder + ': ' + job.stage + (warnings ? '. ' + warnings : ''), warnings ? 'warning' : 'info'); await new Promise(resolve => setTimeout(resolve, 1000)); job = await managerRequest('/api/jobs/' + encodeURIComponent(job.id)); } } function managerJson(method, data) { return {method, headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)}; } document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('[data-dialog-open]').forEach(button => { button.addEventListener('click', () => { const dialog = document.getElementById(button.dataset.dialogOpen); managerFormError(dialog); dialog.showModal(); }); }); document.querySelectorAll('[data-dialog-close]').forEach(button => { button.addEventListener('click', () => button.closest('dialog').close()); }); document.querySelectorAll('dialog').forEach(dialog => { dialog.addEventListener('click', event => { if (event.target === dialog) { const rect = dialog.getBoundingClientRect(); if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) dialog.close(); } }); dialog.addEventListener('close', () => dialog.querySelectorAll('input[type=password]').forEach(input => { input.value = ''; })); }); });