/**
* Ticket List View
* Full-featured ticket list with filters, sorting, batch actions, and pagination.
*/
const TicketListView = {
currentPage: 1,
perPage: 50,
sortBy: 'create_time',
sortDir: 'DESC',
selectedIds: new Set(),
searchTimeout: null,
async render() {
const container = document.getElementById('view-container');
container.innerHTML = '
';
try {
// Fetch lookups for filter dropdowns
await App.ensureLookups();
// Build query params
const params = Filters.toQueryParams();
params.set('page', this.currentPage);
params.set('per_page', this.perPage);
params.set('sort_by', this.sortBy);
params.set('sort_dir', this.sortDir);
const searchInput = document.getElementById('global-search');
if (searchInput && searchInput.value.trim()) {
params.set('search', searchInput.value.trim());
}
const data = await App.api(`/api/tickets?${params.toString()}`);
this.renderContent(container, data);
this.bindEvents(data);
} catch (err) {
container.innerHTML = `
⚠️
Errore caricamento ticket
${App.escapeHtml(err.message)}
`;
}
},
renderContent(container, data) {
const tickets = data.tickets || [];
const { total, page, per_page, total_pages } = data;
container.innerHTML = `
0 selezionati
Stato
Coda
Owner
${Filters.renderBar(App.lookups)}
| N° |
Titolo |
Stato |
Priorità |
Coda |
Owner |
Creato |
${tickets.length > 0 ? tickets.map(t => `
| ${t.tn} |
${App.escapeHtml(t.title || '(senza titolo)')} |
${t.state_name} |
${App.priorityIndex(t.priority_name)} |
${t.queue_name} |
${t.owner_first || ''} ${t.owner_last || ''} |
${App.formatDateTime(t.create_time)} |
`).join('') : `
📭
Nessun ticket trovato
Prova a cambiare i filtri o crea un nuovo ticket.
|
`}
${total_pages > 1 ? `
` : `
`}
`;
},
renderPageButtons(current, total) {
const pages = [];
const start = Math.max(1, current - 2);
const end = Math.min(total, current + 2);
for (let i = start; i <= end; i++) {
pages.push(``);
}
return pages.join('');
},
bindEvents(data) {
// Filter events
Filters.bindEvents(() => {
this.currentPage = 1;
this.selectedIds.clear();
this.render();
});
// Sort events
document.querySelectorAll('.ticket-table th.sortable').forEach(th => {
th.addEventListener('click', () => {
const sortKey = th.dataset.sort;
if (this.sortBy === sortKey) {
this.sortDir = this.sortDir === 'ASC' ? 'DESC' : 'ASC';
} else {
this.sortBy = sortKey;
this.sortDir = 'DESC';
}
this.currentPage = 1;
this.render();
});
});
// Row click → Toggle selection
document.querySelectorAll('.ticket-table tbody tr[data-ticket-id]').forEach(row => {
row.addEventListener('click', (e) => {
// If click was on a link, let the browser perform navigation
if (e.target.closest('.ticket-tn-link') || e.target.closest('.ticket-title-link')) {
return;
}
const id = String(row.dataset.ticketId);
if (this.selectedIds.has(id)) {
this.selectedIds.delete(id);
row.classList.remove('selected');
} else {
this.selectedIds.add(id);
row.classList.add('selected');
}
this.updateBatchBar();
});
});
// Batch apply
const batchApply = document.getElementById('batch-apply');
if (batchApply) {
batchApply.addEventListener('click', () => this.applyBatch());
}
// Batch select all visible
const batchSelectAll = document.getElementById('batch-select-all');
if (batchSelectAll) {
batchSelectAll.addEventListener('click', () => {
document.querySelectorAll('.ticket-table tbody tr[data-ticket-id]').forEach(row => {
const id = String(row.dataset.ticketId);
this.selectedIds.add(id);
row.classList.add('selected');
});
this.updateBatchBar();
});
}
// Batch cancel
const batchCancel = document.getElementById('batch-cancel');
if (batchCancel) {
batchCancel.addEventListener('click', () => {
this.selectedIds.clear();
document.querySelectorAll('.ticket-table tbody tr').forEach(tr => {
tr.classList.remove('selected');
});
this.updateBatchBar();
});
}
// Pagination
document.querySelectorAll('.pagination-btn[data-page]').forEach(btn => {
btn.addEventListener('click', () => {
this.currentPage = parseInt(btn.dataset.page);
this.selectedIds.clear();
this.render();
});
});
},
updateBatchBar() {
const count = document.getElementById('batch-count');
if (count) {
count.textContent = `${this.selectedIds.size} selezionat${this.selectedIds.size === 1 ? 'o' : 'i'}`;
}
},
async applyBatch() {
if (this.selectedIds.size === 0) return;
const updates = {};
const batchState = document.getElementById('batch-state')?.value;
const batchQueue = document.getElementById('batch-queue')?.value;
const batchOwner = document.getElementById('batch-owner')?.value;
if (batchState) updates.ticket_state_id = parseInt(batchState);
if (batchQueue) updates.queue_id = parseInt(batchQueue);
if (batchOwner) updates.user_id = parseInt(batchOwner);
if (Object.keys(updates).length === 0) {
Toast.warning('Seleziona almeno un campo da modificare');
return;
}
try {
const res = await App.api('/api/tickets/batch/update', {
method: 'PATCH',
body: JSON.stringify({
ticket_ids: Array.from(this.selectedIds),
updates,
}),
});
Toast.success(res.message || `${this.selectedIds.size} ticket aggiornati`);
this.selectedIds.clear();
this.render();
} catch (err) {
Toast.error('Errore aggiornamento batch: ' + err.message);
}
},
};