fix(analytics): align yoy and detail table contracts
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-15 17:07:59 +08:00
parent 9cffe9f8e4
commit ec40d161af
5 changed files with 265 additions and 134 deletions

View File

@@ -488,11 +488,14 @@
return response.json();
})
.then(d => {
document.getElementById('yoy-year1-label').textContent = `${y1}`;
document.getElementById('yoy-year2-label').textContent = `${y2}`;
document.getElementById('yoy-year1-value').textContent = fmtMoney(d.total1 || 0);
document.getElementById('yoy-year2-value').textContent = fmtMoney(d.total2 || 0);
const growth = d.growth || 0;
const breakdown = Array.isArray(d.monthly_breakdown) ? d.monthly_breakdown : [];
const total1 = Number(d.year1 && d.year1.total || 0);
const total2 = Number(d.year2 && d.year2.total || 0);
document.getElementById('yoy-year1-label').textContent = d.year1 && d.year1.label || `${y1}`;
document.getElementById('yoy-year2-label').textContent = d.year2 && d.year2.label || `${y2}`;
document.getElementById('yoy-year1-value').textContent = fmtMoney(total1);
document.getElementById('yoy-year2-value').textContent = fmtMoney(total2);
const growth = Number(d.growth_rate || 0);
const card = document.getElementById('yoy-growth-card');
card.classList.toggle('is-decline', growth < 0);
const arrow = growth >= 0 ? 'up' : 'down';
@@ -504,11 +507,11 @@
yoyChart = new Chart(ctx, {
type: 'line',
data: {
labels: d.labels || [],
labels: breakdown.map(row => row.month_label),
datasets: [
{ label: `${y1}`, data: d.values1 || [], borderColor: MUTED,
{ label: `${y1}`, data: breakdown.map(row => Number(row.year1_value || 0)), borderColor: MUTED,
backgroundColor: mix(MUTED, 8), tension: 0.3, fill: false, borderWidth: 2 },
{ label: `${y2}`, data: d.values2 || [], borderColor: ACCENT,
{ label: `${y2}`, data: breakdown.map(row => Number(row.year2_value || 0)), borderColor: ACCENT,
backgroundColor: mix(ACCENT, 12), tension: 0.3, fill: true, borderWidth: 2.5 }
]
},
@@ -530,8 +533,27 @@
const $tbl = window.jQuery && jQuery('#dataTable');
if (!$tbl || !$tbl.length || !jQuery.fn.DataTable) return;
const url = buildPeriodApiUrl('/api/sales_analysis/table_data');
const numericFields = new Set(['rank', 'avg_price', 'margin_rate', 'return_rate', 'qty', 'amount']);
const columns = Array.from($tbl[0].querySelectorAll('thead th')).map((header) => {
const field = header.dataset.field;
return {
data: field,
defaultContent: '—',
render(value, type) {
if (type !== 'display') return numericFields.has(field) ? Number(value || 0) : value;
if (value === null || value === undefined || value === '') return '—';
if (field === 'amount' || field === 'avg_price') return fmtMoney(value);
if (field === 'margin_rate' || field === 'return_rate') return `${Number(value).toFixed(1)}%`;
if (field === 'qty' || field === 'rank') return fmtNum(value);
const node = document.createElement('span');
node.textContent = String(value);
return node.innerHTML;
}
};
});
$tbl.DataTable({
ajax: { url, dataSrc: 'data' },
columns,
processing: true, serverSide: false,
pageLength: 25, lengthMenu: [10, 25, 50, 100],
order: [[$tbl.find('thead th').length - 1, 'desc']],