4 changed files with 256 additions and 6 deletions
@ -0,0 +1,154 @@ |
|||||
|
// Simple Dashboard Drag and Drop
|
||||
|
let isCustomizing = false; |
||||
|
let draggedCard = null; |
||||
|
|
||||
|
// Initialize on page load
|
||||
|
document.addEventListener('DOMContentLoaded', function () { |
||||
|
loadLayout(); |
||||
|
}); |
||||
|
|
||||
|
function toggleCustomize() { |
||||
|
const btn = document.getElementById('customize-btn'); |
||||
|
const help = document.getElementById('customization-help'); |
||||
|
const cards = document.querySelectorAll('.draggable-card'); |
||||
|
const handles = document.querySelectorAll('.drag-handle'); |
||||
|
|
||||
|
isCustomizing = !isCustomizing; |
||||
|
|
||||
|
if (isCustomizing) { |
||||
|
btn.textContent = '💾 Save Layout'; |
||||
|
btn.classList.add('saving'); |
||||
|
help.style.display = 'block'; |
||||
|
|
||||
|
// Enable dragging
|
||||
|
cards.forEach(card => { |
||||
|
card.draggable = true; |
||||
|
card.classList.add('customizable'); |
||||
|
card.addEventListener('dragstart', handleDragStart); |
||||
|
card.addEventListener('dragover', handleDragOver); |
||||
|
card.addEventListener('drop', handleDrop); |
||||
|
card.addEventListener('dragend', handleDragEnd); |
||||
|
}); |
||||
|
|
||||
|
// Show handles
|
||||
|
handles.forEach(handle => handle.style.display = 'block'); |
||||
|
|
||||
|
} else { |
||||
|
btn.textContent = '📝 Customize Layout'; |
||||
|
btn.classList.remove('saving'); |
||||
|
help.style.display = 'none'; |
||||
|
|
||||
|
// Disable dragging
|
||||
|
cards.forEach(card => { |
||||
|
card.draggable = false; |
||||
|
card.classList.remove('customizable'); |
||||
|
card.removeEventListener('dragstart', handleDragStart); |
||||
|
card.removeEventListener('dragover', handleDragOver); |
||||
|
card.removeEventListener('drop', handleDrop); |
||||
|
card.removeEventListener('dragend', handleDragEnd); |
||||
|
}); |
||||
|
|
||||
|
// Hide handles
|
||||
|
handles.forEach(handle => handle.style.display = 'none'); |
||||
|
|
||||
|
// Save the current layout
|
||||
|
saveLayout(); |
||||
|
showMessage('Layout saved!', 'success'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleDragStart(e) { |
||||
|
draggedCard = e.target.closest('.draggable-card'); |
||||
|
draggedCard.classList.add('dragging'); |
||||
|
e.dataTransfer.effectAllowed = 'move'; |
||||
|
} |
||||
|
|
||||
|
function handleDragOver(e) { |
||||
|
e.preventDefault(); |
||||
|
e.dataTransfer.dropEffect = 'move'; |
||||
|
|
||||
|
const card = e.target.closest('.draggable-card'); |
||||
|
if (card && card !== draggedCard) { |
||||
|
const grid = document.getElementById('dashboard-grid'); |
||||
|
const cards = Array.from(grid.children); |
||||
|
const draggedIndex = cards.indexOf(draggedCard); |
||||
|
const targetIndex = cards.indexOf(card); |
||||
|
|
||||
|
if (draggedIndex < targetIndex) { |
||||
|
grid.insertBefore(draggedCard, card.nextSibling); |
||||
|
} else { |
||||
|
grid.insertBefore(draggedCard, card); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function handleDrop(e) { |
||||
|
e.preventDefault(); |
||||
|
} |
||||
|
|
||||
|
function handleDragEnd(e) { |
||||
|
if (draggedCard) { |
||||
|
draggedCard.classList.remove('dragging'); |
||||
|
draggedCard = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function saveLayout() { |
||||
|
const cards = document.querySelectorAll('.draggable-card'); |
||||
|
const layout = Array.from(cards).map((card, index) => ({ |
||||
|
id: card.dataset.widgetId, |
||||
|
position: index |
||||
|
})); |
||||
|
localStorage.setItem('dashboard-layout', JSON.stringify(layout)); |
||||
|
} |
||||
|
|
||||
|
function loadLayout() { |
||||
|
const saved = localStorage.getItem('dashboard-layout'); |
||||
|
if (!saved) return; |
||||
|
|
||||
|
try { |
||||
|
const layout = JSON.parse(saved); |
||||
|
const grid = document.getElementById('dashboard-grid'); |
||||
|
|
||||
|
// Sort layout by position
|
||||
|
layout.sort((a, b) => a.position - b.position); |
||||
|
|
||||
|
// Reorder cards
|
||||
|
layout.forEach(item => { |
||||
|
const card = document.querySelector(`[data-widget-id="${item.id}"]`); |
||||
|
if (card) grid.appendChild(card); |
||||
|
}); |
||||
|
} catch (e) { |
||||
|
console.warn('Failed to load layout:', e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function showMessage(text, type) { |
||||
|
// Create message element
|
||||
|
const msg = document.createElement('div'); |
||||
|
msg.textContent = text; |
||||
|
msg.style.cssText = ` |
||||
|
position: fixed; |
||||
|
top: 20px; |
||||
|
right: 20px; |
||||
|
padding: 12px 20px; |
||||
|
border-radius: 4px; |
||||
|
color: white; |
||||
|
font-weight: bold; |
||||
|
z-index: 10000; |
||||
|
background: ${type === 'success' ? '#10b981' : '#ef4444'}; |
||||
|
transform: translateX(300px); |
||||
|
transition: transform 0.3s ease; |
||||
|
`;
|
||||
|
|
||||
|
document.body.appendChild(msg); |
||||
|
|
||||
|
// Show message
|
||||
|
setTimeout(() => msg.style.transform = 'translateX(0)', 100); |
||||
|
|
||||
|
// Hide message
|
||||
|
setTimeout(() => { |
||||
|
msg.style.transform = 'translateX(300px)'; |
||||
|
setTimeout(() => document.body.removeChild(msg), 300); |
||||
|
}, 2000); |
||||
|
} |
||||
@ -1,17 +1,31 @@ |
|||||
{{define "content"}} |
{{define "content"}} |
||||
<h2>Dashboard</h2> |
<div class="dashboard-header"> |
||||
|
<h2>Dashboard</h2> |
||||
|
<button id="customize-btn" class="customize-btn" onclick="toggleCustomize()"> |
||||
|
📝 Customize Layout |
||||
|
</button> |
||||
|
</div> |
||||
|
|
||||
<p>Welcome to the ServiceTrade Tools Dashboard.</p> |
<p>Welcome to the ServiceTrade Tools Dashboard.</p> |
||||
|
|
||||
<div class="dashboard-grid"> |
<div id="customization-help" class="customization-help" style="display: none;"> |
||||
<div class="dashboard-item"> |
💡 <strong>Tip:</strong> Drag and drop the cards to arrange them in your preferred order. |
||||
|
</div> |
||||
|
|
||||
|
<div id="dashboard-grid" class="dashboard-grid"> |
||||
|
<div class="dashboard-item draggable-card" data-widget-id="invoice-search" data-widget-title="Update Invoice Status"> |
||||
|
<div class="drag-handle" style="display: none;">⋮⋮</div> |
||||
{{template "invoice_search" .}} |
{{template "invoice_search" .}} |
||||
</div> |
</div> |
||||
<div class="dashboard-item"> |
|
||||
|
<div class="dashboard-item draggable-card" data-widget-id="document-upload" data-widget-title="Document Uploads"> |
||||
|
<div class="drag-handle" style="display: none;">⋮⋮</div> |
||||
{{template "document_upload" .}} |
{{template "document_upload" .}} |
||||
</div> |
</div> |
||||
<div class="dashboard-item"> |
|
||||
|
<div class="dashboard-item draggable-card" data-widget-id="document-remove" data-widget-title="Document Removal"> |
||||
|
<div class="drag-handle" style="display: none;">⋮⋮</div> |
||||
{{template "document_remove" .}} |
{{template "document_remove" .}} |
||||
</div> |
</div> |
||||
<!-- Add more dashboard items as needed --> |
|
||||
</div> |
</div> |
||||
{{end}} |
{{end}} |
||||
Loading…
Reference in new issue