@@ -388,6 +454,53 @@ function seg(key) {
}).join('');
requestAnimationFrame(() => g.querySelectorAll('.kpi').forEach(c => c.classList.add('visible')));
+ // Bloomberg table — channels + marketing
+ const arrowUp = '
';
+ const arrowDown = '
';
+ const arrowNeutral = '
';
+ function changeCell(val, t) {
+ const cls = t === 'up' ? 'up' : t === 'down' ? 'down' : 'neutral';
+ const arrow = t === 'up' ? arrowUp : t === 'down' ? arrowDown : arrowNeutral;
+ const sign = t === 'up' ? '+' : t === 'down' ? '−' : '±';
+ return `
${arrow} ${sign}${val}
`;
+ }
+ const channels = [
+ { id:'SEO:CH', name:'SEO-трафик', value:'1.2М ₽', change:'18', t:'up' },
+ { id:'PPC:CH', name:'Контекстная реклама', value:'980К ₽', change:'12', t:'up' },
+ { id:'SMM:CH', name:'Соцсети', value:'640К ₽', change:'−3', t:'down' },
+ { id:'EMAIL:CH',name:'Email-рассылки', value:'420К ₽', change:'8', t:'up' },
+ { id:'ORG:CH', name:'Органический', value:'380К ₽', change:'2', t:'up' },
+ { id:'REF:CH', name:'Реферальный', value:'156К ₽', change:'24', t:'up' },
+ { id:'AFF:CH', name:'Партнёрские', value:'98К ₽', change:'−12', t:'down' },
+ ];
+ const marketing = [
+ { id:'CAC', name:'Стоимость клиента', value:'680 ₽', change:'8', t:'down' },
+ { id:'AOV', name:'Ср. чек', value:'4 820 ₽', change:'3', t:'up' },
+ { id:'CVR', name:'Конверсия', value:'3.8%', change:'0.5', t:'up' },
+ { id:'CLV', name:'Пож. ценность', value:'12 400 ₽', change:'18', t:'up' },
+ { id:'ROAS', name:'ROAS', value:'4.2×', change:'15', t:'up' },
+ { id:'Bounce', name:'Показ. отказов', value:'32%', change:'1', t:'down' },
+ ];
+
+ function renderTable(body, data) {
+ body.innerHTML = data.map((d, i) => `
+
+ |
+ ${d.id} |
+ ${d.name} |
+ ${d.value} |
+ ${changeCell(d.change, d.t)} |
+
`).join('');
+ // Follow toggle
+ body.querySelectorAll('.b-follow-btn').forEach(btn => {
+ btn.addEventListener('click', () => btn.classList.toggle('active'));
+ });
+ }
+ const tbody1 = document.querySelector('#b-table-channels tbody');
+ const tbody2 = document.querySelector('#b-table-marketing tbody');
+ if (tbody1) renderTable(tbody1, channels);
+ if (tbody2) renderTable(tbody2, marketing);
+
// Sparkline charts
const sparkColors = [];
s.kpis.forEach(d => { sparkColors.push(d.t === 'up' ? '#22c55e' : d.t === 'down' ? '#ef4444' : '#3b82f6'); });