Tikettinumerointi (VVNKKNN) + automaattinen vastaus

Tikettinumero:
- Uudet tiketit saavat juoksevan numeron VVNKKNN-formaatissa
  (vuosi+kuukausi sekoitettu sekvenssiin, esim. 2600301)
- Numero näkyy tikettilistassa ja detail-näkymässä (#-merkillä)
- Sähköpostin aihe muotoa "Tiketti #2600301: Alkuperäinen aihe"
- Vastaukset ketjuuntuvat automaattisesti

Autoreply:
- Postilaatikkokohtainen asetus: checkbox + viestisisältö
- Uusi tiketti lähettää automaattisen vastauksen asiakkaalle
- Autoreply näkyy tiketin viestiketjussa ( Automaattinen vastaus)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 00:24:17 +02:00
parent 96a35c7e0b
commit 07e9c63c47
4 changed files with 107 additions and 12 deletions

View File

@@ -1295,7 +1295,7 @@ function renderTickets() {
<td onclick="event.stopPropagation()"><input type="checkbox" class="ticket-checkbox" data-ticket-id="${t.id}" ${checked}></td>
<td><span class="ticket-status ticket-status-${t.status}">${ticketStatusLabels[t.status] || t.status}</span></td>
<td><span class="ticket-type ticket-type-${t.type || 'muu'}">${typeLabel}</span></td>
<td>${prioBadge}${companyBadge}<strong>${esc(t.subject)}</strong></td>
<td>${prioBadge}${companyBadge}${t.ticket_number ? `<span style="color:#888;font-size:0.8rem;margin-right:0.3rem;">#${t.ticket_number}</span>` : ''}<strong>${esc(t.subject)}</strong></td>
<td>${esc(t.mailbox_name || t.from_name || t.from_email)}</td>
<td>${t.customer_name ? esc(t.customer_name) : '<span style="color:#ccc;">-</span>'}</td>
<td style="text-align:center;">${lastType} ${t.message_count}</td>
@@ -1361,7 +1361,7 @@ async function showTicketDetail(id, companyId = '') {
document.getElementById('ticket-detail-header').innerHTML = `
<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:1rem;margin-bottom:1.25rem;">
<div>
<h2 style="color:#0f3460;margin-bottom:0.25rem;font-size:1.2rem;">${esc(ticket.subject)}</h2>
<h2 style="color:#0f3460;margin-bottom:0.25rem;font-size:1.2rem;">${ticket.ticket_number ? `<span style="color:#888;font-weight:normal;font-size:0.9rem;">#${ticket.ticket_number}</span> ` : ''}${esc(ticket.subject)}</h2>
<div style="font-size:0.85rem;color:#888;" id="ticket-sender-line">
${esc(ticket.from_name)} &lt;${esc(ticket.from_email)}&gt; · Luotu ${esc(ticket.created)}
</div>
@@ -1552,9 +1552,10 @@ async function showTicketDetail(id, companyId = '') {
const thread = document.getElementById('ticket-thread');
thread.innerHTML = (ticket.messages || []).map(m => {
const isOut = m.type === 'reply_out';
const isAutoReply = m.type === 'auto_reply';
const isNote = m.type === 'note';
const typeClass = isOut ? 'ticket-msg-out' : (isNote ? 'ticket-msg-note' : 'ticket-msg-in');
const typeIcon = isOut ? '&#8594; Vastaus' : (isNote ? '&#128221; Muistiinpano' : '&#8592; Saapunut');
const typeClass = (isOut || isAutoReply) ? 'ticket-msg-out' : (isNote ? 'ticket-msg-note' : 'ticket-msg-in');
const typeIcon = isAutoReply ? '&#9889; Automaattinen vastaus' : (isOut ? '&#8594; Vastaus' : (isNote ? '&#128221; Muistiinpano' : '&#8592; Saapunut'));
return `<div class="ticket-message ${typeClass}">
<div class="ticket-msg-header">
<span class="ticket-msg-type">${typeIcon}</span>
@@ -2541,7 +2542,13 @@ function showMailboxForm(mb = null) {
} else {
sameCheck.checked = true;
}
// Autoreply
const arCheck = document.getElementById('mailbox-form-auto-reply');
arCheck.checked = mb ? !!mb.auto_reply_enabled : false;
document.getElementById('mailbox-form-auto-reply-body').value = mb ? (mb.auto_reply_body || '') : '';
toggleAutoReplyFields();
toggleSmtpFields();
document.getElementById('smtp-test-result').style.display = 'none';
document.getElementById('mailbox-form-container').style.display = '';
}
@@ -2550,6 +2557,12 @@ function toggleSmtpFields() {
document.getElementById('smtp-custom-fields').style.display = same ? 'none' : '';
}
function toggleAutoReplyFields() {
const enabled = document.getElementById('mailbox-form-auto-reply').checked;
document.getElementById('auto-reply-fields').style.display = enabled ? '' : 'none';
}
document.getElementById('mailbox-form-auto-reply').addEventListener('change', toggleAutoReplyFields);
function editMailbox(id) {
const mb = mailboxesData.find(m => m.id === id);
if (mb) showMailboxForm(mb);
@@ -2588,6 +2601,8 @@ document.getElementById('btn-save-mailbox').addEventListener('click', async () =
smtp_password: useSame ? imapPass : document.getElementById('mailbox-form-smtp-pass').value,
smtp_encryption: document.getElementById('mailbox-form-smtp-encryption').value,
aktiivinen: true,
auto_reply_enabled: document.getElementById('mailbox-form-auto-reply').checked,
auto_reply_body: document.getElementById('mailbox-form-auto-reply-body').value,
};
try {
const saved = await apiCall('mailbox_save', 'POST', data);