(function (global) { var WIDGET_META = { kpis: { title: 'CEO KPI\'s', icon: '📊' }, executive: { title: 'Alles op een rij', icon: '📋' }, briefing: { title: 'Herman briefing', icon: '📝' }, retail: { title: 'Retail operatie', icon: '🏪' }, analytics: { title: 'Data & analytics', icon: '📈' }, approvals: { title: 'Agent goedkeuringen', icon: '✓' }, feed: { title: 'Agent feed', icon: '⚡' }, }; function loadPrefs() { return fetch('/api/preferences/ui').then(function (r) { return r.json(); }).catch(function () { return { dashboard_layout: Object.keys(WIDGET_META), global_viz_mode: 'neo-bars', viz_modes: {} }; }); } function saveLayout(order) { return fetch('/api/preferences/ui', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ dashboard_layout: order }), }); } function saveViz(globalMode, vizModes) { return fetch('/api/preferences/ui', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ global_viz_mode: globalMode, viz_modes: vizModes || {} }), }); } function applyOrder(container, order) { if (!container) return; var map = {}; Array.from(container.children).forEach(function (el) { if (el.dataset && el.dataset.widget) map[el.dataset.widget] = el; }); order.forEach(function (id) { if (map[id]) container.appendChild(map[id]); }); } function initDrag(container, onReorder) { if (!container) return; var dragEl = null; Array.from(container.querySelectorAll('[data-widget]')).forEach(function (el) { el.setAttribute('draggable', 'true'); el.classList.add('hm-widget-draggable'); el.addEventListener('dragstart', function (ev) { dragEl = el; el.classList.add('hm-widget-dragging'); ev.dataTransfer.effectAllowed = 'move'; }); el.addEventListener('dragend', function () { el.classList.remove('hm-widget-dragging'); dragEl = null; var order = Array.from(container.querySelectorAll('[data-widget]')).map(function (n) { return n.dataset.widget; }); if (onReorder) onReorder(order); }); el.addEventListener('dragover', function (ev) { ev.preventDefault(); if (!dragEl || dragEl === el) return; var rect = el.getBoundingClientRect(); var after = ev.clientY > rect.top + rect.height / 2; if (after) el.after(dragEl); else el.before(dragEl); }); }); } function buildVizSelector(container, prefs, onChange) { if (!container || !global.VizEngine) return; container.innerHTML = '' + global.VizEngine.MODES.map(function (m) { var active = prefs.global_viz_mode === m.id ? ' active' : ''; return ''; }).join(''); container.querySelectorAll('.viz-mode-btn').forEach(function (btn) { btn.addEventListener('click', function () { container.querySelectorAll('.viz-mode-btn').forEach(function (b) { b.classList.remove('active'); }); btn.classList.add('active'); if (onChange) onChange(btn.dataset.viz); }); }); } global.DashboardLayout = { WIDGET_META: WIDGET_META, loadPrefs: loadPrefs, saveLayout: saveLayout, saveViz: saveViz, applyOrder: applyOrder, initDrag: initDrag, buildVizSelector: buildVizSelector, }; })(window);