style: riviste parti dell'interfaccia dei ticket. feat: aggiunta possiblità di modificare il tempo inserito nelle note ed aggiunta voce per tempo nel menu azioni rapide
This commit is contained in:
@@ -21,9 +21,13 @@ const TicketListView = {
|
||||
|
||||
// Build query params
|
||||
const isMyTickets = window.location.hash.startsWith('#/tickets/my');
|
||||
if (isMyTickets) {
|
||||
Filters.currentMode = isMyTickets ? 'my' : 'general';
|
||||
Filters.load(); // Load state for current mode
|
||||
|
||||
if (isMyTickets && !Filters.state.user_id) {
|
||||
const activeAgentId = localStorage.getItem('activeAgentId') || '1';
|
||||
Filters.state.user_id = activeAgentId;
|
||||
Filters.save();
|
||||
}
|
||||
|
||||
const params = Filters.toQueryParams();
|
||||
@@ -99,36 +103,52 @@ const TicketListView = {
|
||||
|
||||
${Filters.renderBar(App.lookups)}
|
||||
|
||||
<!-- Pagination Top -->
|
||||
${this.renderPagination(page, per_page, total, total_pages, true)}
|
||||
|
||||
<!-- Ticket Table -->
|
||||
<div class="ticket-table-wrapper">
|
||||
<table class="ticket-table" id="ticket-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sortable ${this.sortBy === 'tn' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="tn">N°</th>
|
||||
<th style="width: 75px;" class="sortable ${this.sortBy === 'tn' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="tn">N°</th>
|
||||
<th style="width: 125px;" class="sortable ${this.sortBy === 'create_time' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="create_time">Creato</th>
|
||||
<th style="width: 90px;" class="sortable ${this.sortBy === 'state' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="state">Stato</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>
|
||||
<th class="sortable ${this.sortBy === 'priority' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="priority">Priorità</th>
|
||||
<th class="sortable ${this.sortBy === 'queue' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="queue">Coda</th>
|
||||
<th>Owner</th>
|
||||
<th>Cliente</th>
|
||||
<th class="sortable ${this.sortBy === 'create_time' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="create_time">Creato</th>
|
||||
<th style="width: 130px;" class="sortable ${this.sortBy === 'queue' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="queue">Coda</th>
|
||||
<th style="width: 120px;">Owner</th>
|
||||
<th style="width: 140px;">Cliente</th>
|
||||
<th style="width: 80px;" class="sortable ${this.sortBy === 'priority' ? (this.sortDir === 'ASC' ? 'sort-asc' : 'sort-desc') : ''}" data-sort="priority">Priorità</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${tickets.length > 0 ? tickets.map(t => `
|
||||
<tr data-ticket-id="${t.id}" class="${this.selectedIds.has(String(t.id)) ? 'selected' : ''} ${this.selectedOrder[0] === String(t.id) ? 'first-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)}">${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-secondary);font-size:0.82rem;">${t.customer_first ? `${t.customer_first} ${t.customer_last}` : (t.customer_user_id || '—')}</td>
|
||||
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDateTime(t.create_time)}</td>
|
||||
</tr>
|
||||
`).join('') : `
|
||||
${tickets.length > 0 ? tickets.map(t => {
|
||||
const displayQueue = t.queue_name.includes('::') ? t.queue_name.split('::').pop() : t.queue_name;
|
||||
const shortQueue = displayQueue.length > 15 ? displayQueue.substring(0, 12) + '...' : displayQueue;
|
||||
return `
|
||||
<tr data-ticket-id="${t.id}" class="${this.selectedIds.has(String(t.id)) ? 'selected' : ''} ${this.selectedOrder[0] === String(t.id) ? 'first-selected' : ''}" style="cursor:pointer;">
|
||||
<td>
|
||||
<span class="ticket-tn" style="display:inline-flex; align-items:center;">
|
||||
<a href="#/tickets/${t.id}" class="ticket-tn-link" onclick="event.stopPropagation()">${t.tn}</a>
|
||||
${data.otrsBaseUrl ? `
|
||||
<a href="${data.otrsBaseUrl}index.pl?Action=AgentTicketZoom;TicketID=${t.id}" target="_blank" title="Apri in OTRS" onclick="event.stopPropagation()" style="display:inline-flex; align-items:center; text-decoration:none;">
|
||||
<span style="display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; background:#1070ca; color:#fff; border-radius:50%; font-size:10px; font-weight:800; font-family:sans-serif; margin-left:6px; vertical-align:middle; line-height:16px;">O</span>
|
||||
</a>
|
||||
` : ''}
|
||||
</span>
|
||||
</td>
|
||||
<td style="color:var(--text-tertiary);font-size:0.78rem;">${App.formatDateTime(t.create_time)}</td>
|
||||
<td><span class="badge badge-state" data-state-type="${(t.state_type || '').toLowerCase()}">${t.state_name}</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 class="queue-cell"><span class="badge badge-queue">${App.escapeHtml(shortQueue)}</span><div class="queue-tooltip">${App.escapeHtml(t.queue_name)}</div></td>
|
||||
<td style="color:var(--text-secondary);font-size:0.82rem;">${t.owner_first || ''} ${t.owner_last || ''}</td>
|
||||
<td style="color:var(--text-secondary);font-size:0.82rem;">${t.customer_first ? `${t.customer_first} ${t.customer_last}` : (t.customer_user_id || '—')}</td>
|
||||
<td><span class="badge badge-priority" data-priority="${App.priorityIndex(t.priority_name)}">${App.priorityIndex(t.priority_name)}</span></td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('') : `
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<td colspan="8">
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">📭</div>
|
||||
<div class="empty-state-text">Nessun ticket trovato</div>
|
||||
@@ -141,9 +161,16 @@ const TicketListView = {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
${total_pages > 1 ? `
|
||||
<div class="pagination">
|
||||
<!-- Pagination Bottom -->
|
||||
${this.renderPagination(page, per_page, total, total_pages, false)}
|
||||
`;
|
||||
},
|
||||
|
||||
renderPagination(page, per_page, total, total_pages, isTop) {
|
||||
const marginStyle = isTop ? 'margin-bottom: var(--space-md); margin-top: 0;' : 'margin-top: var(--space-md); margin-bottom: 0;';
|
||||
if (total_pages > 1) {
|
||||
return `
|
||||
<div class="pagination" style="${marginStyle}">
|
||||
<div class="pagination-info">
|
||||
Mostrando ${((page - 1) * per_page) + 1}–${Math.min(page * per_page, total)} di ${total} ticket
|
||||
</div>
|
||||
@@ -155,13 +182,15 @@ const TicketListView = {
|
||||
<button class="pagination-btn" data-page="${total_pages}" ${page >= total_pages ? 'disabled' : ''}>»</button>
|
||||
</div>
|
||||
</div>
|
||||
` : `
|
||||
<div class="pagination">
|
||||
`;
|
||||
} else {
|
||||
return `
|
||||
<div class="pagination" style="${marginStyle}">
|
||||
<div class="pagination-info">${total} ticket totali</div>
|
||||
<div></div>
|
||||
</div>
|
||||
`}
|
||||
`;
|
||||
`;
|
||||
}
|
||||
},
|
||||
|
||||
renderPageButtons(current, total) {
|
||||
@@ -312,12 +341,7 @@ const TicketListView = {
|
||||
batchCustomerSearchInput.addEventListener('input', () => {
|
||||
clearTimeout(batchCustomerDebounce);
|
||||
const q = batchCustomerSearchInput.value.trim();
|
||||
if (q.length < 2) {
|
||||
batchCustomerSuggestionsDiv.style.display = 'none';
|
||||
if (batchCustomerUserIdInput) batchCustomerUserIdInput.value = '';
|
||||
if (batchCustomerIdInput) batchCustomerIdInput.value = '';
|
||||
return;
|
||||
}
|
||||
// Do not block empty query to allow all results on focus
|
||||
|
||||
batchCustomerDebounce = setTimeout(async () => {
|
||||
try {
|
||||
@@ -352,6 +376,12 @@ const TicketListView = {
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
batchCustomerSearchInput.addEventListener('focus', () => {
|
||||
batchCustomerSearchInput.value = '';
|
||||
if (batchCustomerUserIdInput) batchCustomerUserIdInput.value = '';
|
||||
if (batchCustomerIdInput) batchCustomerIdInput.value = '';
|
||||
batchCustomerSearchInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
}
|
||||
|
||||
// Close suggestions on click outside
|
||||
|
||||
Reference in New Issue
Block a user