major fixes for inventory

This commit is contained in:
erik 2025-07-02 10:29:36 +00:00
parent 00ef1d1f4b
commit 4d19e29847
10 changed files with 969 additions and 203 deletions

View file

@ -20,6 +20,11 @@ let currentSort = {
// Store current search results for client-side sorting
let currentResultsData = null;
// Pagination state
let currentPage = 1;
let itemsPerPage = 5000; // 5k items per page for good performance
let totalPages = 1;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
// Get DOM elements after DOM is loaded
@ -39,7 +44,7 @@ function initializeEventListeners() {
// Form submission
searchForm.addEventListener('submit', async (e) => {
e.preventDefault();
await performSearch();
await performSearch(true); // Reset to page 1 on new search
});
// Clear button
@ -167,6 +172,10 @@ function clearAllFields() {
// Reset slot filter
document.getElementById('slotFilter').value = '';
// Reset pagination
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>';
@ -196,7 +205,12 @@ function handleSlotFilterChange() {
/**
* Perform the search based on form inputs
*/
async function performSearch() {
async function performSearch(resetPage = false) {
// Reset to page 1 if this is a new search (not pagination)
if (resetPage) {
currentPage = 1;
}
searchResults.innerHTML = '<div class="loading">🔍 Searching inventory...</div>';
try {
@ -214,11 +228,13 @@ async function performSearch() {
// Store results for client-side re-sorting
currentResultsData = data;
// Update pagination state
updatePaginationState(data);
// Apply client-side slot filtering
applySlotFilter(data);
// Sort the results client-side before displaying
sortResults(data);
// Display results (already sorted by server)
displayResults(data);
} catch (error) {
@ -280,6 +296,8 @@ function buildSearchParameters() {
addParam(params, 'max_damage_rating', 'searchMaxDamageRating');
addParam(params, 'min_heal_boost_rating', 'searchMinHealBoost');
addParam(params, 'max_heal_boost_rating', 'searchMaxHealBoost');
addParam(params, 'min_vitality_rating', 'searchMinVitalityRating');
addParam(params, 'min_damage_resist_rating', 'searchMinDamageResistRating');
// Requirements parameters
addParam(params, 'min_level', 'searchMinLevel');
@ -308,8 +326,19 @@ function buildSearchParameters() {
params.append('legendary_cantrips', allSpells.join(','));
}
// Pagination only - sorting will be done client-side
params.append('limit', '1000'); // Show all items on one page
// Equipment slot filters
const selectedSlots = getSelectedSlots();
if (selectedSlots.length > 0) {
params.append('slot_names', selectedSlots.join(','));
}
// Pagination parameters
params.append('page', currentPage);
params.append('limit', itemsPerPage);
// Sorting parameters
params.append('sort_by', currentSort.field);
params.append('sort_dir', currentSort.direction);
return params;
}
@ -357,6 +386,22 @@ function getSelectedProtections() {
return selectedProtections;
}
/**
* Get selected equipment slots from checkboxes
*/
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 => {
selectedSlots.push(cb.value);
});
return selectedSlots;
}
/**
* Display search results in the UI
*/
@ -383,12 +428,16 @@ function displayResults(data) {
<th class="sortable" data-sort="character_name">Character${getSortIcon('character_name')}</th>
<th class="sortable" data-sort="name">Item Name${getSortIcon('name')}</th>
<th class="sortable" data-sort="item_type_name">Type${getSortIcon('item_type_name')}</th>
<th class="text-right sortable" data-sort="slot_name">Slot${getSortIcon('slot_name')}</th>
<th class="text-right sortable" data-sort="coverage">Coverage${getSortIcon('coverage')}</th>
<th class="text-right sortable" data-sort="armor_level">Armor${getSortIcon('armor_level')}</th>
<th class="sortable" data-sort="spell_names">Spells/Cantrips${getSortIcon('spell_names')}</th>
<th class="text-right narrow-col sortable" data-sort="slot_name">Slot${getSortIcon('slot_name')}</th>
<th class="text-right narrow-col sortable" data-sort="coverage">Coverage${getSortIcon('coverage')}</th>
<th class="text-right sortable" data-sort="armor">Armor${getSortIcon('armor')}</th>
<th class="set-col sortable" data-sort="item_set">Set${getSortIcon('item_set')}</th>
<th class="medium-col sortable" data-sort="spell_names">Spells/Cantrips${getSortIcon('spell_names')}</th>
<th class="text-right sortable" data-sort="crit_damage_rating">Crit Dmg${getSortIcon('crit_damage_rating')}</th>
<th class="text-right sortable" data-sort="damage_rating">Dmg Rating${getSortIcon('damage_rating')}</th>
<th class="text-right sortable" data-sort="heal_boost_rating">Heal Boost${getSortIcon('heal_boost_rating')}</th>
<th class="text-right sortable" data-sort="vitality_rating">Vitality${getSortIcon('vitality_rating')}</th>
<th class="text-right sortable" data-sort="damage_resist_rating">Dmg Resist${getSortIcon('damage_resist_rating')}</th>
<th class="text-right sortable" data-sort="last_updated">Last Updated${getSortIcon('last_updated')}</th>
</tr>
</thead>
@ -399,14 +448,19 @@ function displayResults(data) {
const armor = item.armor_level > 0 ? item.armor_level : '-';
const critDmg = item.crit_damage_rating > 0 ? item.crit_damage_rating : '-';
const dmgRating = item.damage_rating > 0 ? item.damage_rating : '-';
const healBoostRating = item.heal_boost_rating > 0 ? item.heal_boost_rating : '-';
const vitalityRating = item.vitality_rating > 0 ? item.vitality_rating : '-';
const damageResistRating = item.damage_resist_rating > 0 ? item.damage_resist_rating : '-';
const status = item.is_equipped ? '⚔️ Equipped' : '📦 Inventory';
const statusClass = item.is_equipped ? 'status-equipped' : 'status-inventory';
// Use the slot_name provided by the API instead of incorrect mapping
const slot = item.slot_name || 'Unknown';
// Replace commas with line breaks for better display
const slot = item.slot_name ? item.slot_name.replace(/,\s*/g, '<br>') : 'Unknown';
// Coverage placeholder - will need to be added to backend later
const coverage = item.coverage || '-';
// Replace commas with line breaks for better display
const coverage = item.coverage ? item.coverage.replace(/,\s*/g, '<br>') : '-';
// Format last updated timestamp
const lastUpdated = item.last_updated ?
@ -444,17 +498,30 @@ function displayResults(data) {
// Get item type for display
const itemType = item.item_type_name || '-';
// Format equipment set name
let setDisplay = '-';
if (item.item_set) {
// Remove redundant "Set" prefix if present
setDisplay = item.item_set.replace(/^Set\s+/i, '');
// Also handle if it ends with " Set"
setDisplay = setDisplay.replace(/\s+Set$/i, '');
}
html += `
<tr>
<td>${item.character_name}</td>
<td class="item-name">${displayName}</td>
<td>${itemType}</td>
<td class="text-right">${slot}</td>
<td class="text-right">${coverage}</td>
<td class="text-right narrow-col">${slot}</td>
<td class="text-right narrow-col">${coverage}</td>
<td class="text-right">${armor}</td>
<td class="spells-cell">${spellsDisplay}</td>
<td class="set-col" title="${setDisplay}">${setDisplay}</td>
<td class="spells-cell medium-col">${spellsDisplay}</td>
<td class="text-right">${critDmg}</td>
<td class="text-right">${dmgRating}</td>
<td class="text-right">${healBoostRating}</td>
<td class="text-right">${vitalityRating}</td>
<td class="text-right">${damageResistRating}</td>
<td class="text-right">${lastUpdated}</td>
</tr>
`;
@ -465,11 +532,18 @@ function displayResults(data) {
</table>
`;
// Add pagination info if needed
if (data.total_pages > 1) {
// Add pagination controls if needed
if (totalPages > 1) {
const isFirstPage = currentPage === 1;
const isLastPage = currentPage === totalPages;
html += `
<div style="padding: 16px 24px; text-align: center; color: #5f6368; border-top: 1px solid #e8eaed;">
Showing page ${data.page} of ${data.total_pages} pages
<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>
`;
}
@ -566,20 +640,9 @@ function handleSort(field) {
currentSort.direction = 'asc';
}
// Re-display current results with new sorting (no new search needed)
if (currentResultsData) {
// Reset items to original unfiltered data
const originalData = JSON.parse(JSON.stringify(currentResultsData));
// Apply slot filtering first
applySlotFilter(originalData);
// Then apply sorting
sortResults(originalData);
// Display results
displayResults(originalData);
}
// Reset to page 1 and perform new search with updated sort
currentPage = 1;
performSearch();
}
/**
@ -722,3 +785,41 @@ 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);
}
}