Aggiunte stored procedure più utili le altre pfff

This commit is contained in:
2026-07-05 18:48:29 +02:00
parent 457c3eacf6
commit 4f41b50d37
10 changed files with 1578 additions and 156 deletions
+40 -59
View File
@@ -51,11 +51,12 @@ const TicketListView = {
const { total, page, per_page, total_pages } = data;
container.innerHTML = `
${Filters.renderBar(App.lookups)}
<!-- Batch Actions Bar -->
<div class="batch-bar" id="batch-bar">
<span class="batch-count" id="batch-count">0 selezionati</span>
<div style="display:flex; align-items:center; gap:var(--space-sm);">
<span class="batch-count" id="batch-count">0 selezionati</span>
<button class="btn btn-ghost btn-xs" id="batch-select-all">Seleziona visibili</button>
</div>
<div class="filter-group">
<span class="filter-label">Stato</span>
<select class="filter-select" id="batch-state">
@@ -81,14 +82,13 @@ const TicketListView = {
<button class="btn btn-ghost btn-xs" id="batch-cancel">Annulla</button>
</div>
${Filters.renderBar(App.lookups)}
<!-- Ticket Table -->
<div class="ticket-table-wrapper">
<table class="ticket-table" id="ticket-table">
<thead>
<tr>
<th class="checkbox-cell">
<input type="checkbox" id="select-all" title="Seleziona tutti" />
</th>
<th class="sortable ${this.sortBy === 'tn' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="tn">N°</th>
<th class="sortable ${this.sortBy === 'title' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="title">Titolo</th>
<th class="sortable ${this.sortBy === 'state' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="state">Stato</th>
@@ -96,27 +96,22 @@ const TicketListView = {
<th class="sortable ${this.sortBy === 'queue' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="queue">Coda</th>
<th>Owner</th>
<th class="sortable ${this.sortBy === 'create_time' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="create_time">Creato</th>
<th class="sortable ${this.sortBy === 'change_time' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="change_time">Modificato</th>
</tr>
</thead>
<tbody>
${tickets.length > 0 ? tickets.map(t => `
<tr data-ticket-id="${t.id}" class="${this.selectedIds.has(String(t.id)) ? 'selected' : ''}">
<td class="checkbox-cell" onclick="event.stopPropagation()">
<input type="checkbox" class="ticket-checkbox" value="${t.id}" ${this.selectedIds.has(String(t.id)) ? 'checked' : ''} />
</td>
<td><span class="ticket-tn">${t.tn}</span></td>
<td class="ticket-title-cell">${App.escapeHtml(t.title || '(senza titolo)')}</td>
<tr data-ticket-id="${t.id}" class="${this.selectedIds.has(String(t.id)) ? 'selected' : ''}" style="cursor:pointer;">
<td><span class="ticket-tn"><a href="#/tickets/${t.id}" class="ticket-tn-link" onclick="event.stopPropagation()">${t.tn}</a></span></td>
<td class="ticket-title-cell"><a href="#/tickets/${t.id}" class="ticket-title-link" onclick="event.stopPropagation()">${App.escapeHtml(t.title || '(senza titolo)')}</a></td>
<td><span class="badge badge-state" data-state-type="${(t.state_type || '').toLowerCase()}">${t.state_name}</span></td>
<td><span class="badge badge-priority" data-priority="${App.priorityIndex(t.priority_name)}">${t.priority_name}</span></td>
<td><span class="badge badge-priority" data-priority="${App.priorityIndex(t.priority_name)}">${App.priorityIndex(t.priority_name)}</span></td>
<td><span class="badge badge-queue">${t.queue_name}</span></td>
<td style="color:var(--text-secondary);font-size:0.82rem;">${t.owner_first || ''} ${t.owner_last || ''}</td>
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDate(t.create_time)}</td>
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDate(t.change_time)}</td>
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDateTime(t.create_time)}</td>
</tr>
`).join('') : `
<tr>
<td colspan="9">
<td colspan="7">
<div class="empty-state">
<div class="empty-state-icon">📭</div>
<div class="empty-state-text">Nessun ticket trovato</div>
@@ -186,42 +181,22 @@ const TicketListView = {
});
});
// Row click → detail
// Row click → Toggle selection
document.querySelectorAll('.ticket-table tbody tr[data-ticket-id]').forEach(row => {
row.addEventListener('click', (e) => {
if (e.target.type === 'checkbox' || e.target.closest('.checkbox-cell')) return;
window.location.hash = `#/tickets/${row.dataset.ticketId}`;
});
});
// Checkbox selection
const selectAll = document.getElementById('select-all');
if (selectAll) {
selectAll.addEventListener('change', (e) => {
const checkboxes = document.querySelectorAll('.ticket-checkbox');
checkboxes.forEach(cb => {
cb.checked = e.target.checked;
const id = cb.value;
if (e.target.checked) {
this.selectedIds.add(id);
} else {
this.selectedIds.delete(id);
}
cb.closest('tr').classList.toggle('selected', e.target.checked);
});
this.updateBatchBar();
});
}
document.querySelectorAll('.ticket-checkbox').forEach(cb => {
cb.addEventListener('change', (e) => {
const id = e.target.value;
if (e.target.checked) {
// 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);
} else {
this.selectedIds.delete(id);
row.classList.add('selected');
}
e.target.closest('tr').classList.toggle('selected', e.target.checked);
this.updateBatchBar();
});
});
@@ -232,17 +207,27 @@ const TicketListView = {
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-checkbox').forEach(cb => {
cb.checked = false;
cb.closest('tr').classList.remove('selected');
document.querySelectorAll('.ticket-table tbody tr').forEach(tr => {
tr.classList.remove('selected');
});
const selectAll = document.getElementById('select-all');
if (selectAll) selectAll.checked = false;
this.updateBatchBar();
});
}
@@ -258,13 +243,9 @@ const TicketListView = {
},
updateBatchBar() {
const bar = document.getElementById('batch-bar');
const count = document.getElementById('batch-count');
if (this.selectedIds.size > 0) {
bar.classList.add('visible');
if (count) {
count.textContent = `${this.selectedIds.size} selezionat${this.selectedIds.size === 1 ? 'o' : 'i'}`;
} else {
bar.classList.remove('visible');
}
},