86 lines
3.9 KiB
JavaScript
/** @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 = ''; }));
| |||
});
| |||
});
|