206 lines
5.9 KiB
JavaScript
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
|
|
};
|