// logs.js - Jurnale Import page logic
function esc(s) {
if (s == null) return '';
return String(s)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function fmtDatetime(iso) {
if (!iso) return '-';
try {
return new Date(iso).toLocaleString('ro-RO', {
day: '2-digit', month: '2-digit', year: 'numeric',
hour: '2-digit', minute: '2-digit'
});
} catch (e) {
return iso;
}
}
function fmtDuration(startedAt, finishedAt) {
if (!startedAt || !finishedAt) return '-';
const diffMs = new Date(finishedAt) - new Date(startedAt);
if (isNaN(diffMs) || diffMs < 0) return '-';
const secs = Math.round(diffMs / 1000);
if (secs < 60) return secs + 's';
return Math.floor(secs / 60) + 'm ' + (secs % 60) + 's';
}
function statusBadge(status) {
switch ((status || '').toUpperCase()) {
case 'IMPORTED': return 'IMPORTED';
case 'SKIPPED': return 'SKIPPED';
case 'ERROR': return 'ERROR';
default: return `${esc(status || '-')}`;
}
}
function runStatusBadge(status) {
switch ((status || '').toUpperCase()) {
case 'SUCCESS': return 'SUCCESS';
case 'ERROR': return 'ERROR';
case 'RUNNING': return 'RUNNING';
case 'PARTIAL': return 'PARTIAL';
default: return `${esc(status || '')}`;
}
}
async function loadRuns() {
const sel = document.getElementById('runSelector');
sel.innerHTML = '';
try {
const res = await fetch('/api/sync/history?per_page=20');
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
const runs = data.runs || [];
if (runs.length === 0) {
sel.innerHTML = '';
return;
}
sel.innerHTML = '' +
runs.map(r => {
const date = fmtDatetime(r.started_at);
const stats = `${r.total_orders || 0} total / ${r.imported || 0} ok / ${r.errors || 0} err`;
const statusText = (r.status || '').toUpperCase();
return ``;
}).join('');
} catch (err) {
sel.innerHTML = '';
}
}
async function loadRunLog(runId) {
const tbody = document.getElementById('logsBody');
const filterRow = document.getElementById('filterRow');
const runSummary = document.getElementById('runSummary');
tbody.innerHTML = '
| Se incarca... |
';
filterRow.style.display = 'none';
runSummary.style.display = 'none';
try {
const res = await fetch(`/api/sync/run/${encodeURIComponent(runId)}/log`);
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
const run = data.run || {};
const orders = data.orders || [];
// Populate summary bar
document.getElementById('sum-total').textContent = run.total_orders ?? '-';
document.getElementById('sum-imported').textContent = run.imported ?? '-';
document.getElementById('sum-skipped').textContent = run.skipped ?? '-';
document.getElementById('sum-errors').textContent = run.errors ?? '-';
document.getElementById('sum-duration').textContent = fmtDuration(run.started_at, run.finished_at);
runSummary.style.display = '';
if (orders.length === 0) {
tbody.innerHTML = '| Nicio comanda in acest sync run |
';
filterRow.style.display = 'none';
updateFilterCount();
return;
}
tbody.innerHTML = orders.map(order => {
const status = (order.status || '').toUpperCase();
// Parse missing_skus — API returns JSON string or null
let missingSkuTags = '';
if (order.missing_skus) {
try {
const skus = typeof order.missing_skus === 'string'
? JSON.parse(order.missing_skus)
: order.missing_skus;
if (Array.isArray(skus) && skus.length > 0) {
missingSkuTags = '' +
skus.map(s => `${esc(s)}`).join('') +
'
';
}
} catch (e) {
// malformed JSON — skip
}
}
const details = order.error_message
? `${esc(order.error_message)}${missingSkuTags}`
: missingSkuTags || '-';
return `
${esc(order.order_number || '-')} |
${esc(order.customer_name || '-')} |
${order.items_count ?? '-'} |
${statusBadge(status)} |
${details} |
`;
}).join('');
filterRow.style.display = '';
// Reset filter to "Toate"
document.querySelectorAll('[data-filter]').forEach(btn => {
btn.classList.toggle('active', btn.dataset.filter === 'all');
});
applyFilter('all');
} catch (err) {
tbody.innerHTML = `|
${esc(err.message)}
|
`;
filterRow.style.display = 'none';
runSummary.style.display = 'none';
}
}
function applyFilter(filter) {
const rows = document.querySelectorAll('#logsBody tr[data-status]');
let visible = 0;
rows.forEach(row => {
const show = filter === 'all' || row.dataset.status === filter;
row.style.display = show ? '' : 'none';
if (show) visible++;
});
updateFilterCount(visible, rows.length, filter);
}
function updateFilterCount(visible, total, filter) {
const el = document.getElementById('filterCount');
if (!el) return;
if (visible == null) {
el.textContent = '';
return;
}
if (filter === 'all') {
el.textContent = `${total} comenzi`;
} else {
el.textContent = `${visible} din ${total} comenzi`;
}
}
document.addEventListener('DOMContentLoaded', () => {
loadRuns();
// Dropdown change
document.getElementById('runSelector').addEventListener('change', function () {
const runId = this.value;
if (!runId) {
document.getElementById('logsBody').innerHTML = `
|
Selecteaza un sync run din lista de sus
|
`;
document.getElementById('filterRow').style.display = 'none';
document.getElementById('runSummary').style.display = 'none';
return;
}
loadRunLog(runId);
});
// Filter buttons
document.querySelectorAll('[data-filter]').forEach(btn => {
btn.addEventListener('click', function () {
document.querySelectorAll('[data-filter]').forEach(b => b.classList.remove('active'));
this.classList.add('active');
applyFilter(this.dataset.filter);
});
});
});