Add client-side spell column sorting and improve inventory search
- Implement client-side sorting for all columns including spell_names - Add computed_spell_names CTE for server-side sort fallback - Add resizable columns with localStorage persistence - Add Cloak slot detection by name pattern - Increase items limit to 50000 for full inventory loading - Increase proxy timeout to 60s for large queries - Remove pagination (all items loaded at once for sorting) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
25980edf99
commit
8cae94d87d
6 changed files with 294 additions and 111 deletions
|
|
@ -20,10 +20,9 @@ let currentSort = {
|
|||
// Store current search results for client-side sorting
|
||||
let currentResultsData = null;
|
||||
|
||||
// Pagination state
|
||||
// Items limit - load all items for client-side sorting (backend max is 50000)
|
||||
let currentPage = 1;
|
||||
let itemsPerPage = 5000; // 5k items per page for good performance
|
||||
let totalPages = 1;
|
||||
let itemsPerPage = 50000;
|
||||
|
||||
// Initialize the application
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
|
@ -44,7 +43,7 @@ function initializeEventListeners() {
|
|||
// Form submission
|
||||
searchForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
await performSearch(true); // Reset to page 1 on new search
|
||||
await performSearch();
|
||||
});
|
||||
|
||||
// Clear button
|
||||
|
|
@ -168,14 +167,13 @@ function clearAllFields() {
|
|||
|
||||
// Reset equipment type to armor
|
||||
document.getElementById('armorOnly').checked = true;
|
||||
|
||||
|
||||
// Reset slot filter
|
||||
document.getElementById('slotFilter').value = '';
|
||||
|
||||
// Reset pagination
|
||||
|
||||
// Reset page
|
||||
currentPage = 1;
|
||||
totalPages = 1;
|
||||
|
||||
|
||||
// Reset results and clear stored data
|
||||
currentResultsData = null;
|
||||
searchResults.innerHTML = '<div class="no-results">Enter search criteria above and click "Search Items" to find inventory items.</div>';
|
||||
|
|
@ -205,12 +203,8 @@ function handleSlotFilterChange() {
|
|||
/**
|
||||
* Perform the search based on form inputs
|
||||
*/
|
||||
async function performSearch(resetPage = false) {
|
||||
// Reset to page 1 if this is a new search (not pagination)
|
||||
if (resetPage) {
|
||||
currentPage = 1;
|
||||
}
|
||||
|
||||
async function performSearch() {
|
||||
currentPage = 1;
|
||||
searchResults.innerHTML = '<div class="loading">🔍 Searching inventory...</div>';
|
||||
|
||||
try {
|
||||
|
|
@ -227,10 +221,7 @@ async function performSearch(resetPage = false) {
|
|||
|
||||
// Store results for client-side re-sorting
|
||||
currentResultsData = data;
|
||||
|
||||
// Update pagination state
|
||||
updatePaginationState(data);
|
||||
|
||||
|
||||
// Apply client-side slot filtering
|
||||
applySlotFilter(data);
|
||||
|
||||
|
|
@ -396,12 +387,8 @@ function getSelectedProtections() {
|
|||
*/
|
||||
function getSelectedSlots() {
|
||||
const selectedSlots = [];
|
||||
// Get armor slots
|
||||
document.querySelectorAll('#armor-slots input[type="checkbox"]:checked').forEach(cb => {
|
||||
selectedSlots.push(cb.value);
|
||||
});
|
||||
// Get jewelry slots
|
||||
document.querySelectorAll('#jewelry-slots input[type="checkbox"]:checked').forEach(cb => {
|
||||
// Get all selected slot checkboxes from the all-slots container
|
||||
document.querySelectorAll('#all-slots input[type="checkbox"]:checked').forEach(cb => {
|
||||
selectedSlots.push(cb.value);
|
||||
});
|
||||
return selectedSlots;
|
||||
|
|
@ -505,12 +492,16 @@ function displayResults(data) {
|
|||
// Get item type for display
|
||||
const itemType = item.item_type_name || '-';
|
||||
|
||||
// Format equipment set name
|
||||
// Format equipment set name - prefer translated name
|
||||
let setDisplay = '-';
|
||||
if (item.item_set) {
|
||||
// Remove redundant "Set" prefix if present
|
||||
if (item.item_set_name) {
|
||||
// Use the translated set name from backend
|
||||
setDisplay = item.item_set_name;
|
||||
// Remove redundant "Set" suffix if present for cleaner display
|
||||
setDisplay = setDisplay.replace(/\s+Set$/i, '');
|
||||
} else if (item.item_set) {
|
||||
// Fallback to raw set ID if name not available
|
||||
setDisplay = item.item_set.replace(/^Set\s+/i, '');
|
||||
// Also handle if it ends with " Set"
|
||||
setDisplay = setDisplay.replace(/\s+Set$/i, '');
|
||||
}
|
||||
|
||||
|
|
@ -540,24 +531,8 @@ function displayResults(data) {
|
|||
</table>
|
||||
`;
|
||||
|
||||
// Add pagination controls if needed
|
||||
if (totalPages > 1) {
|
||||
const isFirstPage = currentPage === 1;
|
||||
const isLastPage = currentPage === totalPages;
|
||||
|
||||
html += `
|
||||
<div class="pagination-controls">
|
||||
<button onclick="goToPage(1)" ${isFirstPage ? 'disabled' : ''}>First</button>
|
||||
<button onclick="previousPage()" ${isFirstPage ? 'disabled' : ''}>← Previous</button>
|
||||
<span class="pagination-info">Page ${currentPage} of ${totalPages} (${data.total_count} total items)</span>
|
||||
<button onclick="nextPage()" ${isLastPage ? 'disabled' : ''}>Next →</button>
|
||||
<button onclick="goToPage(${totalPages})" ${isLastPage ? 'disabled' : ''}>Last</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
searchResults.innerHTML = html;
|
||||
|
||||
|
||||
// Add click event listeners to sortable headers
|
||||
document.querySelectorAll('.sortable').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
|
|
@ -565,6 +540,98 @@ function displayResults(data) {
|
|||
handleSort(sortField);
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize resizable columns
|
||||
initResizableColumns();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize resizable columns for the results table
|
||||
*/
|
||||
function initResizableColumns() {
|
||||
const table = document.querySelector('.results-table');
|
||||
if (!table) return;
|
||||
|
||||
const headers = table.querySelectorAll('th');
|
||||
|
||||
headers.forEach((header, index) => {
|
||||
// Skip last column (no need to resize)
|
||||
if (index === headers.length - 1) return;
|
||||
|
||||
// Create resize handle
|
||||
const resizeHandle = document.createElement('div');
|
||||
resizeHandle.className = 'resize-handle';
|
||||
header.appendChild(resizeHandle);
|
||||
|
||||
let startX, startWidth;
|
||||
|
||||
resizeHandle.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation(); // Prevent sorting when resizing
|
||||
|
||||
startX = e.pageX;
|
||||
startWidth = header.offsetWidth;
|
||||
|
||||
header.classList.add('resizing');
|
||||
resizeHandle.classList.add('resizing');
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const diff = e.pageX - startX;
|
||||
const newWidth = Math.max(30, startWidth + diff); // Minimum 30px width
|
||||
header.style.width = newWidth + 'px';
|
||||
header.style.minWidth = newWidth + 'px';
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
header.classList.remove('resizing');
|
||||
resizeHandle.classList.remove('resizing');
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
|
||||
// Save column widths to localStorage
|
||||
saveColumnWidths();
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
});
|
||||
|
||||
// Restore saved column widths
|
||||
restoreColumnWidths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save column widths to localStorage
|
||||
*/
|
||||
function saveColumnWidths() {
|
||||
const headers = document.querySelectorAll('.results-table th');
|
||||
const widths = [];
|
||||
headers.forEach(header => {
|
||||
widths.push(header.style.width || '');
|
||||
});
|
||||
localStorage.setItem('inventoryColumnWidths', JSON.stringify(widths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore column widths from localStorage
|
||||
*/
|
||||
function restoreColumnWidths() {
|
||||
const saved = localStorage.getItem('inventoryColumnWidths');
|
||||
if (!saved) return;
|
||||
|
||||
try {
|
||||
const widths = JSON.parse(saved);
|
||||
const headers = document.querySelectorAll('.results-table th');
|
||||
headers.forEach((header, index) => {
|
||||
if (widths[index]) {
|
||||
header.style.width = widths[index];
|
||||
header.style.minWidth = widths[index];
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Failed to restore column widths:', e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Apply client-side slot filtering
|
||||
|
|
@ -636,7 +703,7 @@ function sortResults(data) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle column sorting
|
||||
* Handle column sorting - client-side sorting for all columns
|
||||
*/
|
||||
function handleSort(field) {
|
||||
// If clicking the same field, toggle direction
|
||||
|
|
@ -647,10 +714,17 @@ function handleSort(field) {
|
|||
currentSort.field = field;
|
||||
currentSort.direction = 'asc';
|
||||
}
|
||||
|
||||
// Reset to page 1 and perform new search with updated sort
|
||||
currentPage = 1;
|
||||
performSearch();
|
||||
|
||||
// If we have cached results, sort them client-side and re-display
|
||||
if (currentResultsData && currentResultsData.items) {
|
||||
// Make a copy to preserve original order
|
||||
const sortedData = JSON.parse(JSON.stringify(currentResultsData));
|
||||
sortResults(sortedData);
|
||||
displayResults(sortedData);
|
||||
} else {
|
||||
// No cached results, need to fetch first
|
||||
performSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -794,40 +868,4 @@ function displaySetAnalysisResults(data) {
|
|||
setAnalysisResults.innerHTML = html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update pagination state from API response
|
||||
*/
|
||||
function updatePaginationState(data) {
|
||||
totalPages = data.total_pages || 1;
|
||||
// Current page is already tracked in currentPage
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to a specific page
|
||||
*/
|
||||
function goToPage(page) {
|
||||
if (page < 1 || page > totalPages || page === currentPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentPage = page;
|
||||
performSearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to next page
|
||||
*/
|
||||
function nextPage() {
|
||||
if (currentPage < totalPages) {
|
||||
goToPage(currentPage + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to previous page
|
||||
*/
|
||||
function previousPage() {
|
||||
if (currentPage > 1) {
|
||||
goToPage(currentPage - 1);
|
||||
}
|
||||
}
|
||||
// Pagination functions removed - using client-side sorting with all results loaded
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue