fix: aggiunta nota ticket unito. feat: possiblità di visualizzare ticket in schede. feat: possibilità di creare un nuovo gruppo dall'interfaccia del ticket.

This commit is contained in:
2026-07-12 19:17:07 +02:00
parent e764c37d46
commit 748f777a31
10 changed files with 585 additions and 82 deletions
+136 -19
View File
@@ -8,6 +8,9 @@ const EmailCompose = (() => {
let quillEditor = null;
let attachmentsList = [];
let currentOptions = {};
let toTagsCtrl = null;
let ccTagsCtrl = null;
let bccTagsCtrl = null;
// ── CSS ──────────────────────────────────────────────────────────────────────
function injectStyles() {
@@ -70,6 +73,10 @@ const EmailCompose = (() => {
border-color: var(--accent-primary);
box-shadow: 0 0 0 3px rgba(var(--accent-rgb,99,102,241),0.12);
}
#email-compose-modal .ec-tags-input.drag-over {
border-color: var(--accent-primary);
background: rgba(99,102,241,0.06);
}
#email-compose-modal .ec-tag {
display: inline-flex; align-items: center; gap: 4px;
background: var(--accent-primary); color: #fff;
@@ -145,7 +152,7 @@ const EmailCompose = (() => {
}
// ── Tag Input Helper ──────────────────────────────────────────────────────────
function makeTagInput(containerId, initialEmails = [], onFocus = null) {
function makeTagInput(containerId, initialEmails = [], onFocus = null, controllers = null) {
const container = document.getElementById(containerId);
const tags = [...initialEmails];
@@ -156,8 +163,16 @@ const EmailCompose = (() => {
tags.forEach((email, idx) => {
const tagEl = document.createElement('span');
tagEl.className = 'ec-tag';
tagEl.draggable = true;
tagEl.innerHTML = `${App.escapeHtml(email)}<button type="button" data-idx="${idx}">✕</button>`;
tagEl.querySelector('button').addEventListener('click', () => {
tagEl.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', email);
e.dataTransfer.setData('source-container-id', containerId);
});
tagEl.querySelector('button').addEventListener('click', (e) => {
e.stopPropagation();
tags.splice(idx, 1);
render();
});
@@ -197,8 +212,48 @@ const EmailCompose = (() => {
container.addEventListener('click', () => input.focus());
}
container.addEventListener('dragover', (e) => {
e.preventDefault();
container.classList.add('drag-over');
});
container.addEventListener('dragleave', () => {
container.classList.remove('drag-over');
});
container.addEventListener('drop', (e) => {
e.preventDefault();
container.classList.remove('drag-over');
const email = e.dataTransfer.getData('text/plain');
const sourceContainerId = e.dataTransfer.getData('source-container-id');
if (email && sourceContainerId && sourceContainerId !== containerId && controllers) {
const sourceCtrl = controllers[sourceContainerId];
if (sourceCtrl) {
sourceCtrl.removeTag(email);
ctrl.addTag(email);
}
}
});
render();
return { getTags: () => [...tags], addTag: (email) => { if (!tags.includes(email)) { tags.push(email); render(); } } };
const ctrl = {
getTags: () => [...tags],
addTag: (email) => {
if (!tags.includes(email)) {
tags.push(email);
render();
}
},
removeTag: (email) => {
const idx = tags.indexOf(email);
if (idx > -1) {
tags.splice(idx, 1);
render();
}
}
};
return ctrl;
}
// ── Build Modal HTML ──────────────────────────────────────────────────────────
@@ -356,19 +411,30 @@ const EmailCompose = (() => {
document.body.appendChild(overlay);
// Init tag inputs with focus tracking
const initialTo = options.initialTo || (options.customerEmail ? [options.customerEmail] : []);
const initialCc = options.initialCc || [];
const draft = options.draft;
const initialTo = draft ? draft.to : (options.initialTo || (options.customerEmail ? [options.customerEmail] : []));
const initialCc = draft ? draft.cc : (options.initialCc || []);
const initialBcc = draft ? draft.bcc : [];
let lastFocusedCtrl = null;
const toTagsCtrl = makeTagInput('ec-to-container', initialTo, () => { lastFocusedCtrl = toTagsCtrl; });
const ccTagsCtrl = makeTagInput('ec-cc-container', initialCc, () => { lastFocusedCtrl = ccTagsCtrl; });
const bccTagsCtrl = makeTagInput('ec-bcc-container', [], () => { lastFocusedCtrl = bccTagsCtrl; });
const controllers = {};
toTagsCtrl = makeTagInput('ec-to-container', initialTo, () => { lastFocusedCtrl = toTagsCtrl; }, controllers);
ccTagsCtrl = makeTagInput('ec-cc-container', initialCc, () => { lastFocusedCtrl = ccTagsCtrl; }, controllers);
bccTagsCtrl = makeTagInput('ec-bcc-container', initialBcc, () => { lastFocusedCtrl = bccTagsCtrl; }, controllers);
controllers['ec-to-container'] = toTagsCtrl;
controllers['ec-cc-container'] = ccTagsCtrl;
controllers['ec-bcc-container'] = bccTagsCtrl;
lastFocusedCtrl = toTagsCtrl;
// Subject
const subjectEl = document.getElementById('ec-subject');
const tn = options.ticketTn || '';
const title = options.ticketTitle || '';
subjectEl.value = tn ? `Re: [Ticket#${tn}] ${title}` : title;
if (draft) {
subjectEl.value = draft.subject || '';
} else {
const tn = options.ticketTn || '';
const title = options.ticketTitle || '';
subjectEl.value = tn ? `Re: [Ticket#${tn}] ${title}` : title;
}
// Signature and groups select
const sigSelect = document.getElementById('ec-signature-select');
@@ -377,6 +443,11 @@ const EmailCompose = (() => {
const defaultSigHtml = await loadSignatures(agentId, sigSelect);
await loadAddressGroups(agentId, groupsSelect);
if (draft) {
sigSelect.value = draft.signature || '';
document.getElementById('ec-helpdesk-cc-select').value = draft.helpdeskCc || '0';
}
groupsSelect.addEventListener('change', () => {
const selectedOpt = groupsSelect.options[groupsSelect.selectedIndex];
if (!selectedOpt || !selectedOpt.value) return;
@@ -410,13 +481,21 @@ const EmailCompose = (() => {
// Insert initial body and signature
let initialHtml = '';
if (options.initialBodyHtml) {
initialHtml += options.initialBodyHtml;
if (draft) {
if (draft.body) {
initialHtml = draft.body;
}
attachmentsList = draft.attachments || [];
renderFileList();
} else {
initialHtml += '<p><br></p>';
}
if (defaultSigHtml) {
initialHtml += '<!-- sig -->' + defaultSigHtml;
if (options.initialBodyHtml) {
initialHtml += options.initialBodyHtml;
} else {
initialHtml += '<p><br></p>';
}
if (defaultSigHtml) {
initialHtml += '<!-- sig -->' + defaultSigHtml;
}
}
quillEditor.clipboard.dangerouslyPasteHTML(initialHtml);
quillEditor.setSelection(0, 0);
@@ -454,7 +533,15 @@ const EmailCompose = (() => {
// Close handlers
document.getElementById('ec-close').addEventListener('click', close);
document.getElementById('ec-cancel').addEventListener('click', close);
overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
saveDraft();
const ov = document.getElementById('email-compose-overlay');
if (ov) ov.remove();
if (quillEditor) { quillEditor = null; }
attachmentsList = [];
}
});
// Send
document.getElementById('ec-send').addEventListener('click', () => sendEmail(toTagsCtrl, ccTagsCtrl, bccTagsCtrl));
@@ -505,6 +592,7 @@ const EmailCompose = (() => {
});
Toast.success(`Email inviata a ${to.join(', ')}`);
App.clearDraft(currentOptions.ticketId, 'email');
close();
} catch (err) {
Toast.error('Errore invio email: ' + err.message);
@@ -515,12 +603,41 @@ const EmailCompose = (() => {
// ── Close ─────────────────────────────────────────────────────────────────────
function close() {
if (currentOptions.ticketId) {
App.clearDraft(currentOptions.ticketId, 'email');
}
const overlay = document.getElementById('email-compose-overlay');
if (overlay) overlay.remove();
if (quillEditor) { quillEditor = null; }
attachmentsList = [];
}
return { open, close };
function saveDraft() {
const overlay = document.getElementById('email-compose-overlay');
if (!overlay || !currentOptions.ticketId) return;
const to = toTagsCtrl ? toTagsCtrl.getTags() : [];
const cc = ccTagsCtrl ? ccTagsCtrl.getTags() : [];
const bcc = bccTagsCtrl ? bccTagsCtrl.getTags() : [];
const subject = document.getElementById('ec-subject') ? document.getElementById('ec-subject').value.trim() : '';
const body = quillEditor ? quillEditor.root.innerHTML.trim() : '';
const signature = document.getElementById('ec-signature-select') ? document.getElementById('ec-signature-select').value : '';
const helpdeskCc = document.getElementById('ec-helpdesk-cc-select') ? document.getElementById('ec-helpdesk-cc-select').value : '0';
App.saveDraft(currentOptions.ticketId, {
type: 'email',
to,
cc,
bcc,
subject,
body,
signature,
helpdeskCc,
attachments: [...attachmentsList],
options: currentOptions
});
}
return { open, close, saveDraft };
})();
window.EmailCompose = EmailCompose;