Files
Homepage/js/drag-drop.js

206 lines
5.9 KiB
JavaScript

/**
* 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
};