v1.8.0: YandexGPT-only NLP, unknown questions system, callback dedup, consent flow fixes, DELETE cascade, UTC+3, auto-detect sidebar menu, tel:/mailto: links
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{% extends "page.html" %}
|
||||
{% block page_content %}
|
||||
<style>
|
||||
.tickets-table { width:100%; border-collapse:collapse; }
|
||||
.tickets-table { width:100%; border-collapse:collapse; overflow-x:auto; display:block; }
|
||||
.tickets-table th, .tickets-table td { padding:10px 14px; text-align:left; border-bottom:1px solid var(--border); font-size:13px; }
|
||||
.tickets-table th { font-weight:600; color:var(--text-muted); background:var(--bg-card); }
|
||||
.tickets-table td { vertical-align:middle; }
|
||||
@@ -18,6 +18,8 @@
|
||||
.ticket-detail-card .field .value { font-size:14px; }
|
||||
.filter-bar { display:flex; gap:12px; margin-bottom:16px; align-items:center; flex-wrap:wrap; }
|
||||
.filter-bar select, .filter-bar input { padding:8px 12px; border:1px solid var(--border); border-radius:6px; font-size:13px; }
|
||||
.contact-info { font-size:12px; color:var(--text-muted); }
|
||||
.contact-info strong { color:var(--text-primary); }
|
||||
</style>
|
||||
|
||||
<div class="card">
|
||||
@@ -42,6 +44,7 @@
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Заголовок</th>
|
||||
<th>Контакт</th>
|
||||
<th>Приоритет</th>
|
||||
<th>Статус</th>
|
||||
<th>Дата</th>
|
||||
@@ -49,7 +52,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tickets-body">
|
||||
<tr><td colspan="6" style="text-align:center;padding:30px;color:var(--text-muted);">Загрузка...</td></tr>
|
||||
<tr><td colspan="7" style="text-align:center;padding:30px;color:var(--text-muted);">Загрузка...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -57,6 +60,7 @@
|
||||
<div class="ticket-detail-card" id="ticket-detail">
|
||||
<h3 id="ticket-detail-title">Заявка #</h3>
|
||||
<div class="field"><label>Описание</label><div class="value" id="ticket-detail-desc"></div></div>
|
||||
<div class="field"><label>Контактные данные</label><div class="value" id="ticket-detail-contact"></div></div>
|
||||
<div class="field"><label>Приоритет</label><div class="value" id="ticket-detail-priority"></div></div>
|
||||
<div class="field"><label>Статус</label>
|
||||
<select id="ticket-detail-status" onchange="updateTicketStatus()">
|
||||
@@ -75,18 +79,21 @@ var currentTicketId = null;
|
||||
|
||||
function loadTickets(){
|
||||
var tbody = document.getElementById('tickets-body');
|
||||
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;padding:30px;color:var(--text-muted);">Загрузка...</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;padding:30px;color:var(--text-muted);">Загрузка...</td></tr>';
|
||||
fetch('/api/bot/tickets').then(function(r){ return r.json() }).then(function(data){
|
||||
allTickets = data;
|
||||
document.getElementById('ticket-count').textContent = data.length + ' заявок';
|
||||
renderTickets(data);
|
||||
}).catch(function(err){
|
||||
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;padding:30px;color:var(--danger);">Ошибка загрузки</td></tr>';
|
||||
console.error('loadTickets error:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function renderTickets(tickets){
|
||||
var tbody = document.getElementById('tickets-body');
|
||||
if(!tickets.length){
|
||||
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;padding:30px;color:var(--text-muted);">Нет заявок</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;padding:30px;color:var(--text-muted);">Нет заявок</td></tr>';
|
||||
return;
|
||||
}
|
||||
var html = '';
|
||||
@@ -95,9 +102,11 @@ function renderTickets(tickets){
|
||||
if(t.status === 'В работе') statusClass = 'status-v-rabote';
|
||||
else if(t.status === 'Закрыта') statusClass = 'status-zakryta';
|
||||
var dateStr = t.created_at ? new Date(t.created_at).toLocaleString() : '';
|
||||
var contact = t.contact_name || t.contact_phone || t.contact_email || '—';
|
||||
html += '<tr>' +
|
||||
'<td>' + t.id + '</td>' +
|
||||
'<td><a href="javascript:void(0)" onclick="showDetail(' + t.id + ')">' + escapeHtml(t.title) + '</a></td>' +
|
||||
'<td class="contact-info"><strong>' + escapeHtml(t.contact_name || '—') + '</strong><br>' + escapeHtml(t.contact_phone || '') + (t.contact_phone && t.contact_email ? '<br>' : '') + escapeHtml(t.contact_email || '') + '</td>' +
|
||||
'<td>' + t.priority + '</td>' +
|
||||
'<td><span class="status-badge ' + statusClass + '">' + t.status + '</span></td>' +
|
||||
'<td style="font-size:12px;color:var(--text-muted);">' + dateStr + '</td>' +
|
||||
@@ -131,6 +140,12 @@ function showDetail(id){
|
||||
if(!ticket) return;
|
||||
document.getElementById('ticket-detail-title').textContent = 'Заявка #' + ticket.id;
|
||||
document.getElementById('ticket-detail-desc').textContent = ticket.description || '—';
|
||||
var contactHtml = '';
|
||||
if(ticket.contact_name) contactHtml += '<strong>' + escapeHtml(ticket.contact_name) + '</strong><br>';
|
||||
if(ticket.contact_phone) contactHtml += 'Тел: ' + escapeHtml(ticket.contact_phone) + '<br>';
|
||||
if(ticket.contact_email) contactHtml += 'Email: ' + escapeHtml(ticket.contact_email);
|
||||
if(!contactHtml) contactHtml = '—';
|
||||
document.getElementById('ticket-detail-contact').innerHTML = contactHtml;
|
||||
document.getElementById('ticket-detail-priority').textContent = ticket.priority;
|
||||
document.getElementById('ticket-detail-status').value = ticket.status;
|
||||
document.getElementById('ticket-detail-date').textContent = ticket.created_at ? new Date(ticket.created_at).toLocaleString() : '—';
|
||||
|
||||
Reference in New Issue
Block a user