fix(analytics): align yoy and detail table contracts
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
This commit is contained in:
@@ -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']],
|
||||
|
||||
Reference in New Issue
Block a user