window.beursHub = function () { return { tab: new URLSearchParams(location.search).get('tab') || 'beurs', busy: false, lastRefresh: '', listed: [], unlisted: [], summary: {}, trends: { halal_meat_trends: [], top_dishes: [], market_trends: [] }, concepts: [], events: [], platformStats: {}, eventFilter: '', pollTimer: null, indicatorStyle: { left: '0%', width: '25%' }, init() { this.updateIndicator(); this.refreshAll(); this.pollTimer = setInterval(() => { if (this.tab === 'events') this.loadEvents(false); }, 5000); }, setTab(t) { this.tab = t; const url = new URL(location.href); url.searchParams.set('tab', t); history.replaceState({}, '', url); this.updateIndicator(); if (t === 'trends' && !this.trends.halal_meat_trends?.length) this.loadTrends(); if (t === 'concepten' && !this.concepts.length) this.loadConcepts(); if (t === 'events') this.loadEvents(); }, updateIndicator() { const tabs = ['beurs', 'trends', 'concepten', 'events']; const i = tabs.indexOf(this.tab); const w = 100 / tabs.length; this.indicatorStyle = { left: (i * w) + '%', width: w + '%' }; }, sparkPoints(arr) { if (!arr || !arr.length) return ''; const min = Math.min.apply(null, arr); const max = Math.max.apply(null, arr); const range = max - min || 1; return arr.map(function (v, idx) { var x = (idx / (arr.length - 1 || 1)) * 100; var y = 28 - ((v - min) / range) * 24; return x.toFixed(1) + ',' + y.toFixed(1); }).join(' '); }, async refreshAll() { this.busy = true; try { await Promise.all([ this.loadMarket(), this.loadTrends(), this.loadConcepts(), this.loadEvents(false), ]); this.lastRefresh = new Date().toLocaleString('nl-NL'); if (typeof Cockpit !== 'undefined') Cockpit.toast('Beurs data bijgewerkt', 'success'); } catch (e) { if (typeof Cockpit !== 'undefined') Cockpit.toast(e.message, 'error'); } this.busy = false; }, async loadMarket() { const d = await fetch('/api/retail/market/supermarkets').then(function (r) { return r.json(); }); this.listed = d.listed || []; this.unlisted = d.unlisted_nl || []; this.summary = d.summary || {}; }, async loadTrends() { const d = await fetch('/api/retail/market/food-trends').then(function (r) { return r.json(); }); this.trends = d; }, async loadConcepts() { const d = await fetch('/api/retail/market/concepts?limit=6').then(function (r) { return r.json(); }); this.concepts = d.concepts || []; }, async loadEvents(toast) { var q = this.eventFilter ? '?limit=80&agent=' + encodeURIComponent(this.eventFilter) : '?limit=80'; try { var r = await fetch('/api/live/platform' + q).then(function (x) { return x.json(); }); this.events = r.events || []; this.platformStats = r.stats || {}; } catch (e) { if (toast !== false && typeof Cockpit !== 'undefined') Cockpit.toast(e.message, 'error'); } }, }; };