v1.8.6: сортировка таблиц, спиннеры number input, единый стиль — fix broken HTML, modal-actions → form-actions, card-wrapper
Tests / test (push) Has been cancelled
Tests / test-max-bot (push) Has been cancelled

This commit is contained in:
2026-06-02 21:25:12 +03:00
parent b2a270cb2e
commit 1199407a72
27 changed files with 279 additions and 60 deletions
@@ -10,10 +10,7 @@
<td><span class="badge badge-{{ a.role }}">{{ {'engineer':'Инженер','technician':'Техник'}[a.role] }}</span></td>
<td>{{ a.assigned_at.strftime('%d.%m.%Y %H:%M') if a.assigned_at else '' }}</td>
<td class="actions">
<form method="post" action="/service/assignments/unassign" style="display:inline">
<input type="hidden" name="id" value="{{ a.id }}">
<button class="btn btn-danger btn-sm">Отвязать</button>
</form>
<button class="btn btn-danger btn-sm" onclick="openConfirmUnassign({{ a.id }},'{{ (a.user.full_name if a.user else '')|e }}','{{ (a.object.name if a.object else '')|e }}')" title="Отвязать"></button>
</td>
</tr>{% endfor %}</tbody></table>
</div>
@@ -25,7 +22,38 @@
<div class="form-group"><label>Объект</label><select name="object_id" required>{% for o in objects %}<option value="{{ o.id }}">{{ o.name }}</option>{% endfor %}</select></div>
<div class="form-group"><label>Сотрудник</label><select name="user_id" required>{% for u in users %}<option value="{{ u.id }}">{{ u.full_name }} ({{ u.role }})</option>{% endfor %}</select></div>
<div class="form-actions"><button type="submit" class="btn btn-primary">Назначить</button>
<button type="button" class="btn btn-secondary" onclick="this.closest('.modal-overlay').classList.remove('open')">Отмена</button></div>
<button type="button" class="btn" onclick="this.closest('.modal-overlay').classList.remove('open')">Отмена</button></div>
</form>
</div></div>
<div class="modal-overlay" id="confirm-unassign">
<div class="modal" style="max-width:420px;">
<h2 style="font-size:16px;margin-bottom:8px;">Отвязать сотрудника?</h2>
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
Назначение <strong id="unassign-info"></strong> будет удалено. Это действие нельзя отменить.
</p>
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmUnassign()">Отвязать</button>
<button type="button" class="btn" onclick="document.getElementById('confirm-unassign').classList.remove('open')">Отмена</button>
</div>
</div>
</div>
<script>
var unassignId = null;
function openConfirmUnassign(id, userName, objectName){
unassignId = id;
document.getElementById('unassign-info').textContent = userName + ' → ' + objectName;
document.getElementById('confirm-unassign').classList.add('open');
}
function confirmUnassign(){
if(!unassignId) return;
var form = document.createElement('form');
form.method = 'POST';
form.action = '/service/assignments/unassign';
var input = document.createElement('input');
input.type = 'hidden'; input.name = 'id'; input.value = unassignId;
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
</script>
{% endblock %}
+1 -1
View File
@@ -39,7 +39,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-blog-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteBlog()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteBlog()">Отмена</button>
</div>
@@ -36,7 +36,7 @@
.modal { background:var(--bg-card); border:1px solid var(--border); border-radius:12px; padding:24px; width:420px; max-width:90vw; }
.modal h2 { color:var(--text-primary); }
.modal p { color:var(--text-secondary); }
.modal-actions { display:flex; gap:10px; }
.form-actions { display:flex; gap:10px; }
.btn-danger { background:var(--danger, #dc3545); color:#fff; border:none; padding:8px 16px; border-radius:6px; cursor:pointer; font-weight:600; }
.btn { background:var(--bg-input); color:var(--text-primary); border:1px solid var(--border); padding:8px 16px; border-radius:6px; cursor:pointer; }
</style>
@@ -109,7 +109,7 @@
Все его диалоги и сообщения будут удалены. Заявки останутся.
Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteUser()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteUser()">Отмена</button>
</div>
@@ -165,6 +165,7 @@ function renderConsents(list){
html += '</tr>';
});
tbody.innerHTML = html;
if(typeof sortTables === 'function') sortTables();
}
function filterConsents(){
@@ -1,27 +1,26 @@
{% extends "page.html" %}
{% block page_content %}
<div class="page-header">
<h1>Диалоги чат-бота</h1>
<div class="header-controls">
<input type="text" id="search-input" placeholder="Поиск по ID или имени..." oninput="filterConversations()" class="filter-input">
<button onclick="loadConversations()" class="btn btn-primary">Обновить</button>
<div class="card">
<div class="card-header"><h2>Диалоги чат-бота</h2>
<button onclick="loadConversations()" class="btn btn-primary btn-sm">Обновить</button>
</div>
<div class="filter-bar">
<input type="text" id="search-input" placeholder="Поиск по ID или имени..." oninput="filterConversations()">
<select id="status-filter" onchange="filterConversations()" class="filter-select">
<option value="">Все статусы</option>
<option value="greeting">Приветствие</option>
<option value="collecting_name">Сбор имени</option>
<option value="collecting_phone">Сбор телефона</option>
<option value="collecting_email">Сбор email</option>
<option value="ticket_creation">Создание заявки</option>
<option value="question">Вопрос</option>
<option value="done">Завершён</option>
</select>
</div>
<script>var USER_ROLE = '{{ user.role }}';</script>
<div id="conversations-container">
<div class="loading"><div class="spinner"></div></div>
</div>
</div>
<script>var USER_ROLE = '{{ user.role }}';</script>
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap;">
<select id="status-filter" onchange="filterConversations()" class="filter-select">
<option value="">Все статусы</option>
<option value="greeting">Приветствие</option>
<option value="collecting_name">Сбор имени</option>
<option value="collecting_phone">Сбор телефона</option>
<option value="collecting_email">Сбор email</option>
<option value="ticket_creation">Создание заявки</option>
<option value="question">Вопрос</option>
<option value="done">Завершён</option>
</select>
</div>
<div id="conversations-container">
<div class="loading"><div class="spinner"></div></div>
</div>
<div class="modal-overlay" id="dialog-modal">
@@ -44,7 +43,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
Диалог с <strong id="delete-conv-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDelete()">Удалить</button>
<button type="button" class="btn" onclick="closeDeleteModal()">Отмена</button>
</div>
@@ -76,13 +75,8 @@
.log-time { font-size:10px; color:var(--text-muted,#6B8299); }
.tab-btn { padding:6px 14px; border:1px solid var(--border,#253248); background:transparent; color:var(--text-primary,#E8EDF2); border-radius:4px; cursor:pointer; font-size:12px; }
.tab-btn.active { background:var(--accent,#00ADEF); border-color:var(--accent,#00ADEF); color:#fff; }
.page-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:16px; flex-wrap:wrap; gap:8px; }
.header-controls { display:flex; gap:8px; align-items:center; }
.filter-input { padding:6px 10px; border:1px solid var(--border,#253248); border-radius:4px; background:var(--bg-body,#1B2838); color:var(--text-primary,#E8EDF2); font-size:12px; width:200px; }
.filter-select { padding:6px 10px; border:1px solid var(--border,#253248); border-radius:4px; background:var(--bg-body,#1B2838); color:var(--text-primary,#E8EDF2); font-size:12px; }
.conv-delete { background:none; border:none; cursor:pointer; font-size:14px; padding:2px 6px; border-radius:4px; opacity:0.5; transition:opacity 0.2s; margin-left:auto; }
.conv-delete:hover { opacity:1; background:rgba(232,107,107,0.15); }
.conv-header { display:flex; align-items:center; gap:8px; }
#delete-modal .modal { max-width:420px; }
#delete-modal h2 { font-size:16px; margin-bottom:8px; }
#delete-modal p { color:var(--text-secondary); font-size:14px; }
+3 -2
View File
@@ -73,7 +73,7 @@
<input type="url" name="source_url" id="kb-source-url" placeholder="https://aegisone.ru/...">
</div>
<div class="form-check"><input type="checkbox" name="is_active" id="kb-is-active" checked><label for="kb-is-active">Активная карточка</label></div>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="submit" class="btn btn-primary">Сохранить</button>
<button type="button" class="btn" onclick="closeModal()">Отмена</button>
</div>
@@ -87,7 +87,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
Карточка <strong id="delete-kb-name"></strong> будет удалена навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteKB()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteKB()">Отмена</button>
</div>
@@ -129,6 +129,7 @@ function renderKB(cards){
'</tr>';
});
tbody.innerHTML = html;
if(typeof sortTables === 'function') sortTables();
}
function filterKB(){
+2 -2
View File
@@ -36,7 +36,7 @@ body { font-family:'Inter','Segoe UI',sans-serif; background:var(--bg-body, #1B2
.modal { background:var(--bg-card, #0A1628); border:1px solid var(--border, #253248); border-radius:12px; padding:24px; width:420px; max-width:90vw; }
.modal h2 { color:var(--text-primary, #E8EDF2); }
.modal p { color:var(--text-muted, #6B8299); }
.modal-actions { display:flex; gap:10px; }
.form-actions { display:flex; gap:10px; }
.btn-danger { background:var(--danger, #dc3545); color:#fff; border:none; padding:8px 16px; border-radius:6px; cursor:pointer; font-weight:600; }
.btn { background:var(--bg-input, #253248); color:var(--text-primary, #E8EDF2); border:1px solid var(--border, #3a4a60); padding:8px 16px; border-radius:6px; cursor:pointer; }
</style>
@@ -72,7 +72,7 @@ body { font-family:'Inter','Segoe UI',sans-serif; background:var(--bg-body, #1B2
Удалить все тестовые диалоги, сообщения и логи обработки?
Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmClearTest()">Сбросить</button>
<button type="button" class="btn" onclick="closeConfirmClearTest()">Отмена</button>
</div>
@@ -121,6 +121,7 @@ function renderTickets(tickets){
'</tr>';
});
tbody.innerHTML = html;
if(typeof sortTables === 'function') sortTables();
}
function filterTickets(){
@@ -79,7 +79,7 @@
<option value="8">Отрасли и кейсы</option>
</select>
</div>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="submit" class="btn btn-primary">Опубликовать в базу знаний</button>
<button type="button" class="btn" onclick="closeModal()">Отмена</button>
</div>
@@ -93,7 +93,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
Вопрос <strong id="ignore-uq-text"></strong> будет отмечен как игнорированный.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmIgnoreUQ()">Игнорировать</button>
<button type="button" class="btn" onclick="closeConfirmIgnoreUQ()">Отмена</button>
</div>
@@ -142,6 +142,7 @@ function renderUQ(items){
html += '</td></tr>';
});
tbody.innerHTML = html;
if(typeof sortTables === 'function') sortTables();
}
function openReview(id){
+1 -1
View File
@@ -37,7 +37,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-case-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteCase()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteCase()">Отмена</button>
</div>
+5 -5
View File
@@ -6,7 +6,7 @@
.modal { background:var(--bg-card); border:1px solid var(--border); border-radius:12px; padding:24px; width:420px; max-width:90vw; }
.modal h2 { color:var(--text-primary); }
.modal p { color:var(--text-secondary); }
.modal-actions { display:flex; gap:10px; }
.form-actions { display:flex; gap:10px; }
.btn-danger { background:var(--danger, #dc3545); color:#fff; border:none; padding:8px 16px; border-radius:6px; cursor:pointer; font-weight:600; }
.btn-primary { background:var(--accent, #00ADEF); color:#fff; border:none; padding:8px 16px; border-radius:6px; cursor:pointer; font-weight:600; }
.btn { background:var(--bg-input); color:var(--text-primary); border:1px solid var(--border); padding:8px 16px; border-radius:6px; cursor:pointer; }
@@ -42,11 +42,11 @@
</div>
<div class="filter-bar" style="margin-top:8px;flex-wrap:wrap;gap:6px;">
<span style="font-size:12px;color:var(--text-muted);">Пользовательские графики:</span>
<button class="btn btn-secondary btn-xs" onclick="saveCustomChart()">Сохранить текущий</button>
<button class="btn btn-secondary btn-sm" onclick="saveCustomChart()">Сохранить текущий</button>
<select id="custom-chart-select" onchange="loadCustomChart(this.value)">
<option value="">— Загрузить —</option>
</select>
<button class="btn btn-danger btn-xs" onclick="deleteCustomChart()">Удалить</button>
<button class="btn btn-danger btn-sm" onclick="deleteCustomChart()">Удалить</button>
</div>
<div style="position:relative;height:400px;">
<canvas id="chart-canvas"></canvas>
@@ -59,7 +59,7 @@
<div class="form-group">
<input type="text" id="chart-name-input" placeholder="Введите название..." style="width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:6px;font-size:14px;background:var(--bg-card);color:var(--text);box-sizing:border-box;">
</div>
<div class="modal-actions" style="justify-content:flex-end;margin-top:16px;">
<div class="form-actions" style="justify-content:flex-end;margin-top:16px;">
<button type="button" class="btn btn-primary" onclick="confirmSaveChart()">Сохранить</button>
<button type="button" class="btn" onclick="closeChartNameModal()">Отмена</button>
</div>
@@ -73,7 +73,7 @@
<strong id="delete-chart-name"></strong> будет удалён навсегда.
Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteChart()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteChart()">Отмена</button>
</div>
@@ -2,7 +2,7 @@
{% block page_content %}
{% for t in tasks %}
<div class="card">
<h3>{{ t.title }}</h3>
<div class="card-header"><h3>{{ t.title }}</h3></div>
<div style="font-size:12px;color:var(--text-muted);margin-bottom:12px;">
{{ t.object.name if t.object else '' }} — {{ t.object.address if t.object else '' }}
<span class="badge badge-{{ {'P1':'critical','P2':'high','P3':'medium','P4':'low'}[t.priority] }}" style="margin-left:8px;">{{ t.priority }}</span>
@@ -71,7 +71,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-coeff-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteCoeff()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteCoeff()">Отмена</button>
</div>
@@ -3,10 +3,9 @@
<div class="card">
<div class="card-header"><h2>Клиенты</h2>
{% if error %}<div class="alert alert-error" style="margin-bottom:12px;padding:8px 12px;background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);border-radius:6px;color:var(--danger);font-size:13px;">{{ error }}</div>{% endif %}
</div>
<button class="btn btn-primary btn-sm" onclick="document.getElementById('modal-customer').classList.add('open')">+ Добавить</button>
</div>
<form method="get" class="search-box"><input type="text" name="search" placeholder="Поиск по названию или ИНН..." value="{{ search }}"><button class="btn btn-primary btn-sm">Найти</button></form>
<form method="get" class="filter-bar"><input type="text" name="search" placeholder="Поиск по названию или ИНН..." value="{{ search }}"><button class="btn btn-primary btn-sm">Найти</button></form>
<div class="table-wrap">
<table><thead><tr><th>Название</th><th>ИНН</th><th>Контакты</th><th>Статус</th><th>Объекты</th><th>Активные SLA</th><th class="actions"></th></tr></thead>
<tbody>{% for d in customers %}<tr>
@@ -59,7 +58,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-customer-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteCustomer()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteCustomer()">Отмена</button>
</div>
+1 -1
View File
@@ -205,7 +205,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-formula-coeff-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteFormulaCoeff()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteFormulaCoeff()">Отмена</button>
</div>
+1 -1
View File
@@ -44,7 +44,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-idea-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteIdea()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteIdea()">Отмена</button>
</div>
+1 -1
View File
@@ -54,7 +54,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-object-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteObject()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteObject()">Отмена</button>
</div>
@@ -46,7 +46,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-qitem-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteQitem()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteQitem()">Отмена</button>
</div>
+1 -1
View File
@@ -49,7 +49,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-task-name"></strong> будет завершена. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-success" onclick="confirmCloseTask()">Завершить</button>
<button type="button" class="btn" onclick="closeConfirmCloseTask()">Отмена</button>
</div>
+1 -1
View File
@@ -59,7 +59,7 @@
<p style="color:var(--text-secondary);font-size:14px;margin-bottom:20px;">
<strong id="delete-user-name"></strong> будет удалён навсегда. Это действие нельзя отменить.
</p>
<div class="modal-actions" style="justify-content:flex-end;">
<div class="form-actions" style="justify-content:flex-end;">
<button type="button" class="btn btn-danger" onclick="confirmDeleteUser()">Удалить</button>
<button type="button" class="btn" onclick="closeConfirmDeleteUser()">Отмена</button>
</div>