170 lines
7.7 KiB
HTML
170 lines
7.7 KiB
HTML
<!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 '&';
|
||
if (m === '<') return '<';
|
||
if (m === '>') return '>';
|
||
if (m === '"') return '"';
|
||
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>
|