tool per ticket automatici
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
const TicketBulkView = {
|
||||
rowCount: 0,
|
||||
activeRequests: false,
|
||||
savedRows: null,
|
||||
rowAttachments: {},
|
||||
|
||||
async render() {
|
||||
const container = document.getElementById('view-container');
|
||||
@@ -196,6 +198,10 @@ const TicketBulkView = {
|
||||
</td>
|
||||
<td style="text-align:center;">
|
||||
<div class="row-actions">
|
||||
<button type="button" class="btn btn-ghost btn-xs" id="bulk-attach-btn-${id}" title="Allega file" style="position:relative; display:inline-flex; align-items:center; gap:2px; height:24px; padding:0 6px;">
|
||||
📎 <span class="badge-attachments-count" id="bulk-attach-badge-${id}" style="display:none; font-size:10px;">0</span>
|
||||
</button>
|
||||
<input type="file" id="bulk-file-input-${id}" multiple style="display:none;" />
|
||||
<button class="btn btn-ghost btn-xs" id="bulk-expand-btn-${id}" title="Opzioni avanzate">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px;">
|
||||
<path d="M12 5v14M5 12h14"/>
|
||||
@@ -238,6 +244,48 @@ const TicketBulkView = {
|
||||
container.appendChild(tr);
|
||||
container.appendChild(trExp);
|
||||
|
||||
// Initialize attachments for this row
|
||||
this.rowAttachments[id] = savedData ? (savedData.attachments || []) : [];
|
||||
|
||||
// Bind attachment click and input change
|
||||
const attachBtn = tr.querySelector(`#bulk-attach-btn-${id}`);
|
||||
const fileInput = tr.querySelector(`#bulk-file-input-${id}`);
|
||||
const attachBadge = tr.querySelector(`#bulk-attach-badge-${id}`);
|
||||
|
||||
const updateBadge = () => {
|
||||
const count = (this.rowAttachments[id] || []).length;
|
||||
if (attachBadge) {
|
||||
attachBadge.textContent = count;
|
||||
attachBadge.style.display = count > 0 ? 'inline-block' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
if (attachBtn && fileInput) {
|
||||
attachBtn.addEventListener('click', () => fileInput.click());
|
||||
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const files = Array.from(e.target.files);
|
||||
for (const file of files) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const base64Data = reader.result.split(',')[1];
|
||||
if (!this.rowAttachments[id]) {
|
||||
this.rowAttachments[id] = [];
|
||||
}
|
||||
this.rowAttachments[id].push({
|
||||
filename: file.name,
|
||||
content: base64Data,
|
||||
content_type: file.type
|
||||
});
|
||||
updateBadge();
|
||||
this.saveState();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
fileInput.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Populate with savedData if available, otherwise set default values
|
||||
if (savedData) {
|
||||
tr.querySelector(`#bulk-state-${id}`).value = savedData.state_id;
|
||||
@@ -367,6 +415,7 @@ const TicketBulkView = {
|
||||
input.addEventListener('change', () => this.saveState());
|
||||
});
|
||||
|
||||
updateBadge();
|
||||
this.saveState();
|
||||
},
|
||||
|
||||
@@ -430,7 +479,8 @@ const TicketBulkView = {
|
||||
company_display,
|
||||
isExpanded,
|
||||
statusDotClass,
|
||||
statusDotTitle
|
||||
statusDotTitle,
|
||||
attachments: this.rowAttachments[id] || []
|
||||
});
|
||||
});
|
||||
this.savedRows = data;
|
||||
@@ -658,7 +708,8 @@ const TicketBulkView = {
|
||||
customer_id: customerId || undefined,
|
||||
customer_user_id: customerUserId || undefined,
|
||||
subject: subject || undefined,
|
||||
body: body || undefined
|
||||
body: body || undefined,
|
||||
attachments: (this.rowAttachments[id] || []).length > 0 ? this.rowAttachments[id] : undefined
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -707,6 +758,7 @@ const TicketBulkView = {
|
||||
if (failCount === 0) {
|
||||
Toast.success(`Tutti i ${successCount} ticket sono stati creati con successo!`);
|
||||
this.savedRows = [];
|
||||
this.rowAttachments = {};
|
||||
setTimeout(() => {
|
||||
window.location.hash = '#/tickets';
|
||||
}, 1500);
|
||||
|
||||
@@ -4,6 +4,27 @@
|
||||
*/
|
||||
const TicketCreateView = {
|
||||
savedState: null,
|
||||
attachments: [],
|
||||
|
||||
updateAttachmentList() {
|
||||
const listEl = document.getElementById('create-file-list');
|
||||
if (!listEl) return;
|
||||
listEl.innerHTML = this.attachments.map((att, idx) => `
|
||||
<div class="upload-file-item" data-index="${idx}">
|
||||
<span class="upload-file-name">📎 ${App.escapeHtml(att.filename)} (${Math.round(att.content.length * 0.75 / 1024)} KB)</span>
|
||||
<button type="button" class="upload-file-remove" data-index="${idx}">×</button>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Bind remove clicks
|
||||
listEl.querySelectorAll('.upload-file-remove').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
const idx = parseInt(btn.dataset.index, 10);
|
||||
this.attachments.splice(idx, 1);
|
||||
this.updateAttachmentList();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
saveState() {
|
||||
const stateEl = document.getElementById('create-state');
|
||||
@@ -175,6 +196,9 @@ const TicketCreateView = {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attachments List -->
|
||||
<div class="upload-file-list" id="create-file-list"></div>
|
||||
|
||||
<div class="form-actions" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<div>
|
||||
<button type="button" class="btn btn-ghost btn-sm" id="toggle-advanced" style="display: flex; align-items: center; gap: var(--space-xs); padding: 6px 12px; height: auto; margin:0;">
|
||||
@@ -182,7 +206,9 @@ const TicketCreateView = {
|
||||
Opzioni Avanzate
|
||||
</button>
|
||||
</div>
|
||||
<div style="display:flex; gap:var(--space-sm);">
|
||||
<div style="display:flex; gap:var(--space-sm); align-items:center;">
|
||||
<input type="file" id="create-attachments" multiple style="display:none;" />
|
||||
<button type="button" class="btn btn-ghost" id="btn-add-attachments">📎 Allega file</button>
|
||||
<button class="btn btn-ghost" onclick="history.back()">Annulla</button>
|
||||
<button class="btn btn-primary" id="create-submit">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;">
|
||||
@@ -582,6 +608,7 @@ const TicketCreateView = {
|
||||
customer_user_id: customerUserId || undefined,
|
||||
subject: document.getElementById('create-subject').value.trim() || undefined,
|
||||
body: document.getElementById('create-body').value.trim() || undefined,
|
||||
attachments: this.attachments.length > 0 ? this.attachments : undefined
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -595,6 +622,7 @@ const TicketCreateView = {
|
||||
|
||||
Toast.success(`Ticket #${result.tn} creato!`);
|
||||
this.savedState = null; // Clear cached state on success
|
||||
this.attachments = []; // Clear attachments array
|
||||
|
||||
// Navigate to the new ticket
|
||||
window.location.hash = `#/tickets/${result.id}`;
|
||||
@@ -611,6 +639,32 @@ const TicketCreateView = {
|
||||
}
|
||||
});
|
||||
|
||||
// Attachments Upload Handlers
|
||||
const attachBtn = document.getElementById('btn-add-attachments');
|
||||
const fileInput = document.getElementById('create-attachments');
|
||||
|
||||
if (attachBtn && fileInput) {
|
||||
attachBtn.addEventListener('click', () => fileInput.click());
|
||||
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
const files = Array.from(e.target.files);
|
||||
for (const file of files) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const base64Data = reader.result.split(',')[1];
|
||||
this.attachments.push({
|
||||
filename: file.name,
|
||||
content: base64Data,
|
||||
content_type: file.type
|
||||
});
|
||||
this.updateAttachmentList();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
fileInput.value = ''; // Reset input to allow re-selecting the same file
|
||||
});
|
||||
}
|
||||
|
||||
// Save state on any input/change in the form
|
||||
document.querySelectorAll('.create-form input, .create-form select, .create-form textarea').forEach(el => {
|
||||
el.addEventListener('input', () => this.saveState());
|
||||
|
||||
Reference in New Issue
Block a user