From 88ec1418cf56b08f47388c55c8ce7105de58e0dd Mon Sep 17 00:00:00 2001 From: Niek Rabelink Date: Fri, 11 Sep 2026 15:25:19 +0200 Subject: [PATCH] fix(planner): fix widget layout drag-and-drop order persistence across page navigation --- api/planner.php | 2 +- planner.php | 100 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/api/planner.php b/api/planner.php index 30accc8..938a708 100644 --- a/api/planner.php +++ b/api/planner.php @@ -5,7 +5,7 @@ require_once __DIR__ . '/../includes/auth.php'; require_once __DIR__ . '/../includes/integrations/microsoft.php'; -$user = require_auth(); +$user = require_auth(true); $db = get_db_connection(); header('Content-Type: application/json'); diff --git a/planner.php b/planner.php index 1b53817..f9b50d5 100644 --- a/planner.php +++ b/planner.php @@ -2247,17 +2247,25 @@ async function saveWidgetLayoutConfig() { btn.disabled = true; try { - const payload = focizWidgets.map((w, idx) => ({ + focizWidgets.forEach((item, idx) => { + item.order = idx + 1; + }); + const payload = focizWidgets.map((w) => ({ id: w.id, enabled: !!w.enabled, col_span: parseInt(w.col_span, 10) || 6, - order: idx + 1 + order: w.order })); + try { + localStorage.setItem('fociz_widget_layout_cache', JSON.stringify(payload)); + } catch(e) {} + const res = await fetch('/api/planner.php?action=save_widget_layout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ layout: payload }) + body: JSON.stringify({ layout: payload }), + keepalive: true }); const data = await res.json(); if (data.success) { @@ -2322,20 +2330,71 @@ function showWidgetToast(htmlMessage, type = 'success', duration = 3500) { }, duration); } -// --- Achtergrond Auto-Save voor Widget Layout --- -async function persistWidgetLayoutAuto() { +// --- Bepaal de actuele widget layout en exacte volgorde uit het scherm (DOM) --- +function getActiveWidgetLayoutPayload() { + const grid = document.getElementById('focizWidgetGrid'); + const seenIds = new Set(); + const payload = []; + let currentOrder = 1; + + // 1. Zichtbare widgets in het raster in hun exacte visuele volgorde op het scherm + if (grid) { + const wrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper')); + wrappers.forEach(wrap => { + const wId = wrap.getAttribute('data-widget-id'); + if (wId && !seenIds.has(wId)) { + seenIds.add(wId); + const item = focizWidgets.find(x => x.id === wId); + const colSpan = parseInt(wrap.getAttribute('data-col-span'), 10) || (item ? parseInt(item.col_span, 10) : 6); + const widgetObj = { + id: wId, + enabled: true, + col_span: colSpan, + order: currentOrder++ + }; + payload.push(widgetObj); + if (item) { + item.order = widgetObj.order; + item.col_span = colSpan; + item.enabled = true; + } + } + }); + } + + // 2. Uitgeschakelde of verborgen widgets achteraan toevoegen + focizWidgets.forEach(w => { + if (!seenIds.has(w.id)) { + seenIds.add(w.id); + payload.push({ + id: w.id, + enabled: !!w.enabled, + col_span: parseInt(w.col_span, 10) || 6, + order: currentOrder++ + }); + } + }); + + // 3. Sorteer focizWidgets array in het geheugen synchroon met de nieuwe volgorde + focizWidgets.sort((a, b) => (a.order || 0) - (b.order || 0)); + + return payload; +} + +// --- Achtergrond Auto-Save voor Widget Layout met keepalive --- +async function persistWidgetLayoutAuto(customPayload = null) { try { - const payload = focizWidgets.map((w, idx) => ({ - id: w.id, - enabled: !!w.enabled, - col_span: parseInt(w.col_span, 10) || 6, - order: idx + 1 - })); + const payload = customPayload || getActiveWidgetLayoutPayload(); + + try { + localStorage.setItem('fociz_widget_layout_cache', JSON.stringify(payload)); + } catch(e) {} const res = await fetch('/api/planner.php?action=save_widget_layout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ layout: payload }) + body: JSON.stringify({ layout: payload }), + keepalive: true }); const data = await res.json(); return data.success; @@ -2775,21 +2834,8 @@ function cancelWidgetDragging() { // Controleer gewijzigde volgorde en sla asynchroon op via AJAX function saveCurrentGridOrderIfChanged() { - const grid = document.getElementById('focizWidgetGrid'); - if (!grid) return; - const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper')); - - // Synchroniseer de order in focizWidgets met de actuele DOM-volgorde - currentWrappers.forEach((el, index) => { - const wId = el.getAttribute('data-widget-id'); - const item = focizWidgets.find(x => x.id === wId); - if (item) { - item.order = index + 1; - } - }); - - // Altijd opslaan na een drag (zet ook de volgorde opnieuw neer) - persistWidgetLayoutAuto(); + const payload = getActiveWidgetLayoutPayload(); + persistWidgetLayoutAuto(payload); showWidgetToast('Widget indeling opgeslagen', 'success'); }