/**
* ActivityLogView — Storico Attività
* Displays the local SQLite activity log with filters, dual pagination,
* and an expandable JSON detail panel.
*/
const ActivityLogView = {
currentPage: 1,
perPage: 50,
filters: { esito: '', agente_id: '', da: '', a: '' },
async render() {
this.currentPage = 1;
const container = document.getElementById('view-container');
container.innerHTML = '
';
await this._draw();
},
async _draw() {
const container = document.getElementById('view-container');
try {
const params = new URLSearchParams({
page: this.currentPage,
per_page: this.perPage,
});
if (this.filters.esito) params.set('esito', this.filters.esito);
if (this.filters.agente_id) params.set('agente_id', this.filters.agente_id);
if (this.filters.da) params.set('da', this.filters.da);
if (this.filters.a) params.set('a', this.filters.a);
const data = await App.api(`/api/attivita?${params}`);
const { rows, total, page, per_page, total_pages } = data;
container.innerHTML = this._buildHtml(rows, total, page, per_page, total_pages);
this._bind();
} catch (err) {
container.innerHTML = `Errore caricamento: ${App.escapeHtml(err.message)}
`;
}
},
_buildHtml(rows, total, page, per_page, total_pages) {
const filtersHtml = this._buildFilters();
const paginationHtml = this._buildPagination(total, page, per_page, total_pages);
const tableHtml = rows.length === 0
? `
Nessuna attività registrata.
`
: `
| Data/Ora |
Agente |
Azione |
Esito |
≡ |
${rows.map(r => this._buildRow(r)).join('')}
`;
return `
${filtersHtml}
${paginationHtml}
${tableHtml}
${rows.length > 0 ? paginationHtml.replace(/id="pagination-top"/g,'id="pagination-bottom"') : ''}
`;
},
_buildRow(r) {
const dt = r.creato_il ? new Date(r.creato_il).toLocaleString('it-IT') : '—';
const esitoBadge = r.esito === 'successo'
? `✓ successo`
: `✕ errore`;
return `
| ${dt} |
${App.escapeHtml(r.agente_nome || '—')} |
${App.escapeHtml(r.titolo_azione)} |
${esitoBadge} |
|
${App.escapeHtml(this._prettyJson(r.azione))}
|
`;
},
_prettyJson(str) {
try {
return JSON.stringify(JSON.parse(str), null, 2);
} catch (_) {
return str || '';
}
},
_buildFilters() {
return `
`;
},
_buildPagination(total, page, per_page, total_pages) {
if (total_pages <= 1) return '';
const from = (page - 1) * per_page + 1;
const to = Math.min(page * per_page, total);
const pageBtn = (p, label, disabled, active) => {
const isDisabled = disabled || p === page;
return ``;
};
const pages = [];
pages.push(pageBtn(1, '«', page === 1, false));
pages.push(pageBtn(page - 1, '‹', page === 1, false));
const rangeStart = Math.max(1, page - 2);
const rangeEnd = Math.min(total_pages, page + 2);
for (let p = rangeStart; p <= rangeEnd; p++) {
pages.push(pageBtn(p, p, false, p === page));
}
pages.push(pageBtn(page + 1, '›', page === total_pages, false));
pages.push(pageBtn(total_pages, '»', page === total_pages, false));
return `
`;
},
_bind() {
// Filter apply
document.getElementById('btn-apply-filters')?.addEventListener('click', () => {
this.filters.esito = document.getElementById('filter-esito')?.value || '';
this.filters.da = document.getElementById('filter-da')?.value || '';
this.filters.a = document.getElementById('filter-a')?.value || '';
this.currentPage = 1;
this._draw();
});
// Filter reset
document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
this.filters = { esito: '', agente_id: '', da: '', a: '' };
this.currentPage = 1;
this._draw();
});
// Detail expand/collapse
document.querySelectorAll('.act-detail-btn').forEach(btn => {
btn.addEventListener('click', () => {
const rowId = btn.dataset.id;
const detailRow = document.getElementById(`detail-${rowId}`);
if (!detailRow) return;
const isOpen = detailRow.style.display !== 'none';
detailRow.style.display = isOpen ? 'none' : 'table-row';
btn.textContent = isOpen ? '≡' : '✕';
});
});
// Pagination buttons (top and bottom share same .page-btn class)
document.querySelectorAll('.page-btn').forEach(btn => {
btn.addEventListener('click', () => {
const p = parseInt(btn.dataset.page, 10);
if (!isNaN(p)) {
this.currentPage = p;
this._draw();
document.getElementById('view-container')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
},
};