feat: funzione per l'invio della posta elettronica da otrs-turbo. fix: corretta consuntivazione automatica. feat: aggiunto link apri in otrs anche nel numero del ticket in cima
This commit is contained in:
@@ -52,10 +52,14 @@ const TicketDetailView = {
|
||||
return sum + (isNaN(val) ? 0 : val);
|
||||
}, 0);
|
||||
|
||||
// Display ticket number in the topbar
|
||||
// Display ticket number in the topbar with OTRS link if available
|
||||
const titleEl = document.getElementById('page-title');
|
||||
if (titleEl) {
|
||||
titleEl.innerHTML = `Ticket #${id}`;
|
||||
if (data.otrsWebUrl) {
|
||||
titleEl.innerHTML = `<a href="${data.otrsWebUrl}" target="_blank" style="color:inherit; text-decoration:none; display:inline-flex; align-items:center; gap:6px;" title="Apri in OTRS">Ticket #${ticket.tn || id} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;opacity:0.75;"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3"/></svg></a>`;
|
||||
} else {
|
||||
titleEl.innerHTML = `Ticket #${ticket.tn || id}`;
|
||||
}
|
||||
}
|
||||
|
||||
this.originalValues = {
|
||||
@@ -184,6 +188,11 @@ const TicketDetailView = {
|
||||
</div>
|
||||
<div style="display:flex; gap:var(--space-md); align-items:center;">
|
||||
<input type="number" step="any" min="0" class="note-subject-input" id="note-time-units" placeholder="Tempo (minuti)" style="width:140px; margin-bottom:0; height:32px; padding:4px 10px; font-size:0.85rem;" />
|
||||
<button class="btn btn-ghost btn-sm" id="btn-open-email-compose" style="height:32px; display:flex; align-items:center; gap:var(--space-xs); border-color:var(--accent-secondary); color:var(--accent-secondary);"
|
||||
data-ticket-id="${ticket.id}" data-ticket-tn="${ticket.tn}" data-ticket-title="${App.escapeHtml(ticket.title)}" data-customer-email="${App.escapeHtml(ticket.customer_email || '')}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
||||
Invia Email
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" id="note-send" style="height:32px; display:flex; align-items:center; gap:var(--space-xs);">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>
|
||||
Invia Nota
|
||||
@@ -219,6 +228,7 @@ const TicketDetailView = {
|
||||
<span class="article-from">${App.escapeHtml(a.a_from || a.creator_first + ' ' + a.creator_last || 'Sistema')}</span>
|
||||
${a.channel_name ? `<span style="font-size:0.72rem;color:var(--text-muted);">via ${a.channel_name}</span>` : ''}
|
||||
<button class="btn-delete-article" data-article-id="${a.article_id}" style="background:none; border:none; cursor:pointer; font-size:0.85rem; padding: 2px; margin-left: var(--space-xs); display:inline-flex; align-items:center; opacity: 0.6; transition: opacity 0.2s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.6" title="Elimina Articolo">🗑️</button>
|
||||
<button class="btn-email-article" data-article-id="${a.article_id}" style="background:none; border:none; cursor:pointer; font-size:0.85rem; padding: 2px; margin-left: var(--space-xs); display:inline-flex; align-items:center; opacity: 0.6; transition: opacity 0.2s;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.6" title="Rispondi via email (Quota questo articolo)">✉️</button>
|
||||
</div>
|
||||
<div style="display:flex; gap: var(--space-sm); align-items:center;">
|
||||
<div class="time-edit-container" data-article-id="${a.article_id}" style="display:inline-flex; align-items:center; gap:4px; position:relative;">
|
||||
@@ -409,7 +419,7 @@ const TicketDetailView = {
|
||||
this.noteQuill = null;
|
||||
}
|
||||
|
||||
this.bindEvents();
|
||||
this.bindEvents(ticket, articles, container);
|
||||
|
||||
} catch (err) {
|
||||
container.innerHTML = `
|
||||
@@ -423,7 +433,7 @@ const TicketDetailView = {
|
||||
}
|
||||
},
|
||||
|
||||
bindEvents() {
|
||||
bindEvents(ticket, articles, container) {
|
||||
// Quick-edit change detection
|
||||
const fields = document.querySelectorAll('.quick-edit-select, #qe-customer-user-id, #qe-customer-id');
|
||||
const saveBtn = document.getElementById('qe-save');
|
||||
@@ -670,6 +680,63 @@ const TicketDetailView = {
|
||||
});
|
||||
}
|
||||
|
||||
// Email Compose Button
|
||||
const btnEmailCompose = document.getElementById('btn-open-email-compose');
|
||||
if (btnEmailCompose) {
|
||||
btnEmailCompose.addEventListener('click', () => {
|
||||
if (window.EmailCompose) {
|
||||
EmailCompose.open({
|
||||
ticketId: parseInt(btnEmailCompose.dataset.ticketId, 10),
|
||||
ticketTn: btnEmailCompose.dataset.ticketTn,
|
||||
ticketTitle: btnEmailCompose.dataset.ticketTitle,
|
||||
customerEmail: btnEmailCompose.dataset.customerEmail,
|
||||
});
|
||||
} else {
|
||||
Toast.error('Modulo email non disponibile');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Article Email Reply Buttons
|
||||
document.querySelectorAll('.btn-email-article').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const articleId = parseInt(btn.dataset.articleId, 10);
|
||||
const article = articles.find(art => art.article_id == articleId);
|
||||
if (!article) return;
|
||||
|
||||
if (window.EmailCompose) {
|
||||
const hasHtml = (article.a_content_type || '').toLowerCase().includes('html') || article.a_body.includes('</') || article.a_body.includes('/>');
|
||||
const quotedBody = hasHtml ? article.a_body : App.escapeHtml(article.a_body || '').replace(/\n/g, '<br>');
|
||||
|
||||
const initialBodyHtml = `
|
||||
<p><br></p>
|
||||
<p>Il ${App.formatDateTime(article.create_time)}, <strong>${App.escapeHtml(article.a_from || 'Sistema')}</strong> ha scritto:</p>
|
||||
<blockquote style="border-left: 2px solid var(--border-subtle, #444); padding-left: 12px; margin-left: 8px; color: var(--text-secondary);">
|
||||
${quotedBody}
|
||||
</blockquote>
|
||||
<p><br></p>
|
||||
`;
|
||||
|
||||
// Extract sender email if possible for CC/To
|
||||
let customerEmail = ticket.customer_email || '';
|
||||
const matchEmail = (article.a_from || '').match(/<([^>]+)>/) || (article.a_from || '').match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/);
|
||||
if (matchEmail) {
|
||||
customerEmail = matchEmail[1];
|
||||
}
|
||||
|
||||
EmailCompose.open({
|
||||
ticketId: ticket.id,
|
||||
ticketTn: ticket.tn,
|
||||
ticketTitle: ticket.title,
|
||||
customerEmail: customerEmail,
|
||||
initialBodyHtml: initialBodyHtml,
|
||||
});
|
||||
} else {
|
||||
Toast.error('Modulo email non disponibile');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Retrodate Ticket Event Listeners
|
||||
const btnEditTicketDate = document.getElementById('btn-edit-ticket-date');
|
||||
const ticketDateEditor = document.getElementById('ticket-date-editor');
|
||||
|
||||
Reference in New Issue
Block a user