fix: visualizzazione delle immagini nelle note e mail inviate. fix: corretto veramente problema del fusorario delle mail. feat: tooltip fullscreen per le immagini.
This commit is contained in:
@@ -49,6 +49,41 @@ const TicketDetailView = {
|
||||
});
|
||||
},
|
||||
|
||||
openImageLightbox(src) {
|
||||
let overlay = document.getElementById('image-lightbox-overlay');
|
||||
if (!overlay) {
|
||||
overlay = document.createElement('div');
|
||||
overlay.id = 'image-lightbox-overlay';
|
||||
overlay.style.cssText = `
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: zoom-out;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
`;
|
||||
overlay.innerHTML = `
|
||||
<img id="image-lightbox-img" style="max-width: 90vw; max-height: 90vh; border-radius: 4px; box-shadow: 0 8px 32px rgba(0,0,0,0.5); cursor: default; transition: transform 0.2s ease;" />
|
||||
`;
|
||||
overlay.addEventListener('click', () => {
|
||||
overlay.style.opacity = '0';
|
||||
setTimeout(() => overlay.remove(), 200);
|
||||
});
|
||||
overlay.querySelector('img').addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
const imgEl = overlay.querySelector('img');
|
||||
imgEl.src = src;
|
||||
overlay.offsetHeight;
|
||||
overlay.style.opacity = '1';
|
||||
},
|
||||
|
||||
async render(id) {
|
||||
this.ticketId = id;
|
||||
this.noteAttachments = [];
|
||||
@@ -252,11 +287,33 @@ const TicketDetailView = {
|
||||
<div class="articles-timeline">
|
||||
${articles.length > 0 ? articles.map(a => {
|
||||
const hasHtml = (a.a_content_type || '').toLowerCase().includes('html') || a.a_body.includes('</') || a.a_body.includes('/>');
|
||||
|
||||
let processedBody = a.a_body || '';
|
||||
const articleAttachments = (attachments || []).filter(att => att.article_id === a.article_id);
|
||||
|
||||
if (hasHtml) {
|
||||
articleAttachments.forEach(att => {
|
||||
if (att.content_id) {
|
||||
const cleanCid = att.content_id.replace(/[<>]/g, '').trim();
|
||||
if (cleanCid) {
|
||||
const escapedCid = cleanCid.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
const regex = new RegExp(`cid:<?${escapedCid}>?`, 'gi');
|
||||
processedBody = processedBody.replace(regex, `/api/tickets/attachments/${att.id}`);
|
||||
}
|
||||
}
|
||||
if (att.filename) {
|
||||
const escapedFilename = att.filename.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
const regexFilename = new RegExp(`cid:<?${escapedFilename}>?`, 'gi');
|
||||
processedBody = processedBody.replace(regexFilename, `/api/tickets/attachments/${att.id}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const displayBody = this.htmlMode && hasHtml
|
||||
? `<iframe srcdoc="${a.a_body.replace(/"/g, '"')}" style="width:100%; border:none; background:var(--bg-card); border-radius:var(--radius-md); min-height:220px; font-family:inherit; color-scheme: dark;"></iframe>`
|
||||
? `<iframe srcdoc="${processedBody.replace(/"/g, '"')}" style="width:100%; border:none; background:var(--bg-card); border-radius:var(--radius-md); min-height:220px; font-family:inherit; color-scheme: dark;"></iframe>`
|
||||
: `<div class="article-body">${App.escapeHtml(a.a_body || '')}</div>`;
|
||||
|
||||
const articleAttachments = (attachments || []).filter(att => att.article_id === a.article_id && att.filename !== 'file-1' && att.filename !== 'file-2');
|
||||
const visibleAttachments = articleAttachments.filter(att => att.filename !== 'file-1' && att.filename !== 'file-2');
|
||||
|
||||
return `
|
||||
<div class="article-card sender-${(a.sender_type || 'system').toLowerCase()}">
|
||||
@@ -301,10 +358,10 @@ const TicketDetailView = {
|
||||
${a.a_subject ? `<div class="article-subject">${App.escapeHtml(a.a_subject)}</div>` : ''}
|
||||
${displayBody}
|
||||
|
||||
${articleAttachments.length > 0 ? `
|
||||
${visibleAttachments.length > 0 ? `
|
||||
<div class="article-attachments">
|
||||
${articleAttachments.map(att => `
|
||||
<a href="/api/tickets/attachments/${att.id}" class="attachment-badge" target="_blank" download="${att.filename}">
|
||||
${visibleAttachments.map(att => `
|
||||
<a href="/api/tickets/attachments/${att.id}" class="attachment-badge" target="_blank" download="${att.filename}" data-is-image="${(att.content_type || '').startsWith('image/')}">
|
||||
<span>📎</span>
|
||||
<strong>${App.escapeHtml(att.filename)}</strong>
|
||||
<span class="attachment-size">(${Math.round(att.content_size / 1024)} KB)</span>
|
||||
@@ -882,7 +939,28 @@ const TicketDetailView = {
|
||||
|
||||
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>');
|
||||
|
||||
let processedQuoted = article.a_body || '';
|
||||
if (hasHtml) {
|
||||
const articleAttachments = (attachments || []).filter(att => att.article_id === article.article_id);
|
||||
articleAttachments.forEach(att => {
|
||||
if (att.content_id) {
|
||||
const cleanCid = att.content_id.replace(/[<>]/g, '').trim();
|
||||
if (cleanCid) {
|
||||
const escapedCid = cleanCid.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
const regex = new RegExp(`cid:<?${escapedCid}>?`, 'gi');
|
||||
processedQuoted = processedQuoted.replace(regex, `/api/tickets/attachments/${att.id}`);
|
||||
}
|
||||
}
|
||||
if (att.filename) {
|
||||
const escapedFilename = att.filename.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
const regexFilename = new RegExp(`cid:<?${escapedFilename}>?`, 'gi');
|
||||
processedQuoted = processedQuoted.replace(regexFilename, `/api/tickets/attachments/${att.id}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const quotedBody = hasHtml ? processedQuoted : App.escapeHtml(article.a_body || '').replace(/\n/g, '<br>');
|
||||
|
||||
const initialBodyHtml = `
|
||||
<p><br></p>
|
||||
@@ -1188,5 +1266,41 @@ const TicketDetailView = {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Inline image preview & lightbox binding
|
||||
document.querySelectorAll('.articles-timeline iframe').forEach(iframe => {
|
||||
const attachImageClick = () => {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (doc) {
|
||||
doc.querySelectorAll('img').forEach(img => {
|
||||
img.style.cursor = 'zoom-in';
|
||||
img.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.openImageLightbox(img.src);
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Cannot attach click listeners to iframe images', err);
|
||||
}
|
||||
};
|
||||
|
||||
iframe.addEventListener('load', attachImageClick);
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (doc && doc.readyState === 'complete') {
|
||||
attachImageClick();
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.attachment-badge[data-is-image="true"]').forEach(badge => {
|
||||
badge.style.cursor = 'zoom-in';
|
||||
badge.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.openImageLightbox(badge.href);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user