/** * drag-drop.js - Drag and drop service reordering */ const STORAGE_KEY_ORDER = 'services-order'; const USER_ID = 'default'; // Could be customized per user in the future // Load saved order from server (with localStorage fallback) async function loadSavedOrder() { try { // Try to load from server first const response = await fetch(`/api/order?user_id=${USER_ID}`); if (response.ok) { const serverOrder = await response.json(); if (Object.keys(serverOrder).length > 0) { // Sync server order to localStorage localStorage.setItem(STORAGE_KEY_ORDER, JSON.stringify(serverOrder)); return serverOrder; } } // Fallback to localStorage const saved = localStorage.getItem(STORAGE_KEY_ORDER); return saved ? JSON.parse(saved) : {}; } catch (e) { console.error('Error loading saved order:', e); // Fallback to localStorage try { const saved = localStorage.getItem(STORAGE_KEY_ORDER); return saved ? JSON.parse(saved) : {}; } catch (e2) { return {}; } } } // Save order to server and localStorage async function saveSavedOrder(orderMap) { try { // Save to localStorage immediately localStorage.setItem(STORAGE_KEY_ORDER, JSON.stringify(orderMap)); // Also save to server const response = await fetch('/api/order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: USER_ID, order: orderMap }) }); if (!response.ok) { console.warn('Failed to save order to server, using localStorage only'); } } catch (e) { console.error('Error saving order to server:', e); // localStorage save already happened above, so at least we have local persistence } } // Apply saved order to service groups async function applySavedOrder() { const orderMap = await loadSavedOrder(); document.querySelectorAll('.service-group').forEach(group => { const groupName = group.querySelector('h2')?.textContent; if (!groupName || !orderMap[groupName]) return; const container = group.querySelector('.services-grid'); if (!container) return; const cards = Array.from(container.querySelectorAll('.service-card')); const orderedIds = orderMap[groupName]; // Sort cards based on saved order cards.sort((a, b) => { const aId = a.dataset.serviceId; const bId = b.dataset.serviceId; const aIndex = orderedIds.indexOf(aId); const bIndex = orderedIds.indexOf(bId); if (aIndex === -1 && bIndex === -1) return 0; if (aIndex === -1) return 1; if (bIndex === -1) return -1; return aIndex - bIndex; }); // Re-append in sorted order cards.forEach(card => container.appendChild(card)); }); } // Initialize drag and drop function initDragDrop() { let draggedElement = null; let draggedGroup = null; document.addEventListener('dragstart', (e) => { if (e.target.classList.contains('service-card')) { draggedElement = e.target; draggedGroup = e.target.closest('.service-group'); e.target.style.opacity = '0.5'; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', e.target.innerHTML); } }); document.addEventListener('dragend', (e) => { if (e.target.classList.contains('service-card')) { e.target.style.opacity = '1'; // Remove all drop indicators document.querySelectorAll('.service-card').forEach(card => { card.classList.remove('drag-over'); }); // Save the new order saveCurrentOrder(); } }); document.addEventListener('dragover', (e) => { if (e.preventDefault) { e.preventDefault(); } const target = e.target.closest('.service-card'); if (target && target !== draggedElement && draggedElement) { const targetGroup = target.closest('.service-group'); // Only allow reordering within the same group if (targetGroup === draggedGroup) { e.dataTransfer.dropEffect = 'move'; // Remove previous indicators document.querySelectorAll('.service-card').forEach(card => { card.classList.remove('drag-over'); }); // Add indicator target.classList.add('drag-over'); } } return false; }); document.addEventListener('drop', (e) => { if (e.stopPropagation) { e.stopPropagation(); } const target = e.target.closest('.service-card'); if (target && target !== draggedElement && draggedElement) { const targetGroup = target.closest('.service-group'); // Only allow dropping within the same group if (targetGroup === draggedGroup) { const container = target.parentNode; const targetIndex = Array.from(container.children).indexOf(target); const draggedIndex = Array.from(container.children).indexOf(draggedElement); if (draggedIndex < targetIndex) { container.insertBefore(draggedElement, target.nextSibling); } else { container.insertBefore(draggedElement, target); } } } target?.classList.remove('drag-over'); return false; }); // Make service cards draggable document.querySelectorAll('.service-card').forEach(card => { card.setAttribute('draggable', 'true'); }); } // Save current order of all groups function saveCurrentOrder() { const orderMap = {}; document.querySelectorAll('.service-group').forEach(group => { const groupName = group.querySelector('h2')?.textContent; if (!groupName) return; const cards = group.querySelectorAll('.service-card'); const order = Array.from(cards).map(card => card.dataset.serviceId); orderMap[groupName] = order; }); saveSavedOrder(orderMap); } // Export for use in other modules window.dragDropModule = { init: initDragDrop, applySavedOrder: applySavedOrder };