NEKReport/addin/admin.html

170 lines
7.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Управление типами перевозок</title>
<style>
body { font-family: Segoe UI, Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { margin-top: 0; color: #0078d4; }
.type-card { border: 1px solid #ccc; border-radius: 6px; padding: 15px; margin-bottom: 20px; background: #fafafa; }
.type-card h3 { margin-top: 0; cursor: pointer; color: #0078d4; }
.type-card .content { display: none; margin-top: 15px; }
.type-card.expanded .content { display: block; }
.field { margin-bottom: 10px; }
.field label { display: block; font-weight: bold; margin-bottom: 3px; }
.field input, .field textarea { width: 100%; padding: 8px; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; }
.field textarea { min-height: 80px; }
.hint { color: #666; font-size: 12px; margin-top: 4px; }
.buttons { margin-top: 10px; }
button { padding: 8px 16px; margin-right: 10px; border: none; border-radius: 4px; cursor: pointer; font-weight: 500; }
.btn-primary { background: #0078d4; color: white; }
.btn-danger { background: #d32f2f; color: white; }
.btn-success { background: #2e7d32; color: white; }
#addBtn { margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<h1>⚙️ Управление типами перевозок</h1>
<button id="addBtn" class="btn-success"> Создать новый тип</button>
<div id="typesList"></div>
</div>
<script>
const API_BASE = '/llm';
async function loadTypes() {
const res = await fetch(API_BASE + '/shipping-types');
const types = await res.json();
renderTypes(types);
}
function renderTypes(types) {
const container = document.getElementById('typesList');
container.innerHTML = '';
types.forEach(type => {
const card = document.createElement('div');
card.className = 'type-card';
card.dataset.id = type.id;
const header = document.createElement('h3');
header.textContent = type.name || 'Новый тип';
header.addEventListener('click', () => card.classList.toggle('expanded'));
const contentDiv = document.createElement('div');
contentDiv.className = 'content';
contentDiv.innerHTML = `
<div class="field">
<label>Название типа</label>
<input type="text" class="name" value="${escapeHtml(type.name)}">
</div>
<div class="field">
<label>Email сотрудника (кому адресуют письма для этого типа)</label>
<input type="text" class="employee_email" value="${escapeHtml(type.employee_email || '')}">
<div class="hint">Можно несколько адресов через запятую, например: air@septem.pro, avia@septem.pro</div>
</div>
<div class="field">
<label>Критерии</label>
<textarea class="criteria">${escapeHtml(type.criteria || '')}</textarea>
</div>
<div class="field">
<label>Ключевые слова (через запятую)</label>
<input type="text" class="keywords" value="${escapeHtml((type.keywords || []).join(', '))}">
</div>
<div class="field">
<label>Шаблон письма-подтверждения</label>
<textarea class="confirmation">${escapeHtml(type.confirmation_template || '')}</textarea>
</div>
<div class="field">
<label>Шаблон письма-запроса информации</label>
<textarea class="info">${escapeHtml(type.info_request_template || '')}</textarea>
</div>
<div class="buttons">
<button class="btn-primary save">💾 Сохранить</button>
<button class="btn-danger delete">🗑️ Удалить</button>
</div>
`;
card.appendChild(header);
card.appendChild(contentDiv);
container.appendChild(card);
card.querySelector('.save').addEventListener('click', () => saveType(type.id, card));
card.querySelector('.delete').addEventListener('click', () => deleteType(type.id));
});
}
function escapeHtml(text) {
if (!text) return '';
return String(text).replace(/[&<>"]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
if (m === '"') return '&quot;';
return m;
});
}
async function saveType(id, card) {
const name = card.querySelector('.name').value.trim();
const employee_email = card.querySelector('.employee_email').value.trim();
const criteria = card.querySelector('.criteria').value;
const keywordsStr = card.querySelector('.keywords').value;
const keywords = keywordsStr.split(',').map(s => s.trim()).filter(s => s);
const confirmation_template = card.querySelector('.confirmation').value;
const info_request_template = card.querySelector('.info').value;
const data = { name, employee_email, criteria, keywords, confirmation_template, info_request_template };
const url = API_BASE + '/shipping-types/' + id;
const res = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
card.querySelector('h3').textContent = name || 'Новый тип';
alert('Сохранено');
} else {
alert('Ошибка сохранения');
}
}
async function deleteType(id) {
if (!confirm('Удалить этот тип?')) return;
const res = await fetch(API_BASE + '/shipping-types/' + id, { method: 'DELETE' });
if (res.ok) {
loadTypes();
} else {
alert('Ошибка удаления');
}
}
document.getElementById('addBtn').addEventListener('click', async () => {
const newType = {
name: 'Новый тип',
employee_email: '',
criteria: '',
keywords: [],
confirmation_template: '',
info_request_template: ''
};
const res = await fetch(API_BASE + '/shipping-types', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newType)
});
if (res.ok) {
loadTypes();
} else {
alert('Ошибка создания');
}
});
loadTypes();
</script>
</body>
</html>