(async () => { const mount = document.getElementById('nam-econ-dashboard'); if (!mount) return; // 1. Define your sets at top-level const money = new Set([ 'gdp_from_mfg', 'goods_exported', 'goods_imported', 'goods_trade_balance', 'mfg_quarterly_profits' ]); const pct = new Set([ 'gdp_growth_rate', 'mfg_pct_gdp', 'capacity_utilization', 'unemployment_rate' ]); // 2. Fetch data and metadata const [rows, meta] = await Promise.all([ fetch(NAM_DASH.data_url, { cache:'no-cache' }).then(r => r.json()), fetch(NAM_DASH.meta_url, { cache:'no-cache' }).then(r => r.json()) ]); const metaMap = Object.fromEntries(meta.map(m => [m.indicator, m])); // 3. Formatter uses those sets function formatValue(ind, raw) { const v = Number(raw); const abs = Math.abs(v); const isInt = Number.isInteger(abs); // Thousands with comma separators if (ind === 'mfg_job_openings') { return v.toLocaleString(undefined, { maximumFractionDigits: 0 }); } // Money ? $ prefix, B suffix if (money.has(ind)) { const num = isInt ? abs.toFixed(0) : abs.toFixed(1); return (v < 0 ? '-$' : '$') + num + 'B'; } // Percent ? % suffix if (pct.has(ind)) { const num = isInt ? abs.toFixed(0) : abs.toFixed(1); return (v < 0 ? '-' : '') + num + '%'; } // Default numeric return isInt ? abs.toFixed(0) : abs.toFixed(1); } // 4. Group by indicator and render const byInd = rows.reduce((m, r) => { (m[r.indicator] ||= []).push(r); return m; }, {}); for (const [ind, series] of Object.entries(byInd)) { const info = metaMap[ind]; if (!info) continue; series.sort((a,b) => new Date(a.date) - new Date(b.date)); const diff = info.current_value - info.previous_value; const diffSign = diff > 0 ? '+' : diff < 0 ? '-' : ''; const diffFormatted = formatValue(ind, Math.abs(diff)); const card = document.createElement('article'); card.className = 'card'; card.innerHTML = `
${info.title}

${formatValue(ind, info.current_value)}

${info.current_date}

△ vs ${info.previous_date}: ${diffSign}${diffFormatted}

`; mount.appendChild(card); // 5. Draw chart with axis callbacks using the same sets new Chart(document.getElementById(`c-${ind}`), { type: 'line', data: { labels: series.map(d => d.date), datasets: [{ data: series.map(d => d.value), borderWidth: 2, pointRadius: 0, tension: .25, fill: false }] }, options: { plugins: { legend:{ display:false } }, scales: { x: { type: 'time', time: { unit: 'year' }, grid: { display:false } }, y: { title: { display:true, text: info.unit || '' }, ticks: { callback: val => { const abs = Math.abs(val); const isInt = Number.isInteger(abs); if (money.has(ind)) { return (val < 0 ? '-' : '') + (isInt ? abs.toFixed(0) : abs.toFixed(1)) + 'B'; } if (pct.has(ind)) { return (isInt ? abs.toFixed(0) : abs.toFixed(1)) + '%'; } return isInt ? abs.toFixed(0) : abs.toFixed(1); } } } }, responsive: true, maintainAspectRatio: false } }); } })();