feat: Dynamically adjust cost scale and ticks based on visible entries

This commit is contained in:
Paul Gauthier (aider) 2025-04-14 16:16:14 -07:00
parent 119fbc995c
commit 23f182aab3

View file

@ -34,6 +34,10 @@ document.addEventListener('DOMContentLoaded', function() {
if (currentMode === 'select') { if (currentMode === 'select') {
updateSelectAllCheckboxState(); updateSelectAllCheckboxState();
} }
// Update cost bars and ticks since visible rows may have changed
updateCostBars();
updateCostTicks();
} }
function getVisibleMainRows() { function getVisibleMainRows() {
@ -89,7 +93,6 @@ document.addEventListener('DOMContentLoaded', function() {
// Get the first header cell (for the toggle/checkbox column) // Get the first header cell (for the toggle/checkbox column)
const firstHeaderCell = document.querySelector('table thead th:first-child'); const firstHeaderCell = document.querySelector('table thead th:first-child');
// Show/hide header checkbox based on mode // Show/hide header checkbox based on mode
selectAllCheckbox.style.display = mode === 'select' ? 'inline-block' : 'none'; selectAllCheckbox.style.display = mode === 'select' ? 'inline-block' : 'none';
@ -193,6 +196,10 @@ document.addEventListener('DOMContentLoaded', function() {
// Update the select-all checkbox state after updating the view // Update the select-all checkbox state after updating the view
updateSelectAllCheckboxState(); updateSelectAllCheckboxState();
// Update cost bars and ticks since visible/selected rows may have changed
updateCostBars();
updateCostTicks();
} }
@ -209,27 +216,58 @@ document.addEventListener('DOMContentLoaded', function() {
} }
}); });
// Process cost bars // Function to calculate the appropriate max display cost based on visible/selected entries
function calculateDisplayMaxCost() {
// Get the appropriate set of rows based on the current mode and selection state
let rowsToConsider;
if (currentMode === 'view' && selectedRows.size > 0) {
// In view mode with selections, only consider selected rows
rowsToConsider = Array.from(allMainRows).filter(row => {
const rowIndex = row.querySelector('.row-selector')?.dataset.rowIndex;
return rowIndex && selectedRows.has(rowIndex) && !row.classList.contains('hidden-by-search');
});
} else {
// In other modes or without selections, consider all visible rows
rowsToConsider = getVisibleMainRows();
}
// Find the maximum cost among the rows to consider
let maxCost = 0;
rowsToConsider.forEach(row => {
const costBar = row.querySelector('.cost-bar');
if (costBar) {
const cost = parseFloat(costBar.dataset.cost || '0');
if (cost > maxCost) maxCost = cost;
}
});
// Cap at 50 if any entries exceed that amount, otherwise use actual max
return maxCost > 50 ? 50 : Math.max(1, maxCost); // Ensure at least 1 to avoid division by zero
}
// Process cost bars with dynamic scale
function updateCostBars() {
const costBars = document.querySelectorAll('.cost-bar'); const costBars = document.querySelectorAll('.cost-bar');
const MAX_DISPLAY_COST = 50; // $50 limit for visual display const currentMaxDisplayCost = calculateDisplayMaxCost();
// Remove existing special indicators first
document.querySelectorAll('.dark-section, .tear-line').forEach(el => el.remove());
costBars.forEach(bar => { costBars.forEach(bar => {
const cost = parseFloat(bar.dataset.cost); const cost = parseFloat(bar.dataset.cost);
const maxCost = parseFloat(bar.dataset.maxCost);
if (cost > 0 && maxCost > 0) { if (cost > 0) {
// Use $50 as the max for display purposes // Calculate percentage based on the dynamic display max
const displayMaxCost = Math.min(MAX_DISPLAY_COST, maxCost); const percent = Math.min(cost, currentMaxDisplayCost) / currentMaxDisplayCost * 100;
// Calculate percentage based on the display max
const percent = Math.min(cost, displayMaxCost) / displayMaxCost * 100;
// Clamp percentage between 0 and 100 // Clamp percentage between 0 and 100
bar.style.width = Math.max(0, Math.min(100, percent)) + '%'; bar.style.width = Math.max(0, Math.min(100, percent)) + '%';
// Mark bars that exceed the limit // Mark bars that exceed the limit (only if our display max is capped at 50)
if (cost > MAX_DISPLAY_COST) { if (currentMaxDisplayCost === 50 && cost > 50) {
// Create a darker section at the end with diagonal stripes // Create a darker section at the end with diagonal stripes
const darkSection = document.createElement('div'); const darkSection = document.createElement('div');
darkSection.className = 'bar-viz'; darkSection.className = 'bar-viz dark-section';
darkSection.style.width = '15%'; // From 85% to 100% darkSection.style.width = '15%'; // From 85% to 100%
darkSection.style.left = '85%'; darkSection.style.left = '85%';
darkSection.style.backgroundColor = 'rgba(13, 110, 253, 0.6)'; // Darker blue darkSection.style.backgroundColor = 'rgba(13, 110, 253, 0.6)'; // Darker blue
@ -241,6 +279,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Add a dashed "tear line" at the transition point // Add a dashed "tear line" at the transition point
const tearLine = document.createElement('div'); const tearLine = document.createElement('div');
tearLine.className = 'tear-line';
tearLine.style.position = 'absolute'; tearLine.style.position = 'absolute';
tearLine.style.left = '85%'; tearLine.style.left = '85%';
// Center the tear line vertically and make it 1.5x as tall as the bar // Center the tear line vertically and make it 1.5x as tall as the bar
@ -258,18 +297,38 @@ document.addEventListener('DOMContentLoaded', function() {
bar.style.width = '0%'; bar.style.width = '0%';
} }
}); });
}
// Calculate and add cost ticks dynamically // Call this initially to set up the bars
updateCostBars();
// Update cost ticks dynamically based on current max display cost
function updateCostTicks() {
const costCells = document.querySelectorAll('.cost-bar-cell'); const costCells = document.querySelectorAll('.cost-bar-cell');
if (costCells.length > 0) { if (costCells.length === 0) return;
const MAX_DISPLAY_COST = 50; // $50 limit for visual display
// Generate fixed tick values at $0, $10, $20, $30, $40, $50 const currentMaxDisplayCost = calculateDisplayMaxCost();
const tickValues = [0, 10, 20, 30, 40, 50];
// Calculate percentage positions for each tick on the linear scale // Remove existing ticks first
document.querySelectorAll('.cost-tick').forEach(tick => tick.remove());
// Generate appropriate tick values based on current max
let tickValues = [];
if (currentMaxDisplayCost === 50) {
// Fixed ticks at $0, $10, $20, $30, $40, $50 when we're at the cap
tickValues = [0, 10, 20, 30, 40, 50];
} else {
// Dynamic ticks based on actual max
const tickCount = 5; // Create 5 segments (6 ticks including 0)
for (let i = 0; i <= tickCount; i++) {
tickValues.push(Math.round((i / tickCount) * currentMaxDisplayCost * 100) / 100);
}
}
// Calculate percentage positions for each tick
const tickPercentages = tickValues.map(tickCost => { const tickPercentages = tickValues.map(tickCost => {
return (tickCost / MAX_DISPLAY_COST) * 100; return (tickCost / currentMaxDisplayCost) * 100;
}); });
// Add tick divs to each cost cell // Add tick divs to each cost cell
@ -286,9 +345,6 @@ document.addEventListener('DOMContentLoaded', function() {
const tick = document.createElement('div'); const tick = document.createElement('div');
tick.className = 'cost-tick'; tick.className = 'cost-tick';
tick.style.left = `${percent}%`; tick.style.left = `${percent}%`;
// No dollar amount labels
cell.appendChild(tick); cell.appendChild(tick);
} }
}); });
@ -296,6 +352,9 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
// Call this initially to set up the ticks
updateCostTicks();
// --- New Event Listeners --- // --- New Event Listeners ---
@ -340,6 +399,12 @@ document.addEventListener('DOMContentLoaded', function() {
} }
// Update select-all checkbox state // Update select-all checkbox state
updateSelectAllCheckboxState(); updateSelectAllCheckboxState();
// Update cost bars and ticks if in view mode, as selection affects what's shown
if (currentMode === 'view') {
updateCostBars();
updateCostTicks();
}
} }
}); // End of tableBody listener }); // End of tableBody listener
@ -369,6 +434,10 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
// After bulk change, ensure the selectAll checkbox state is correct (not indeterminate) // After bulk change, ensure the selectAll checkbox state is correct (not indeterminate)
updateSelectAllCheckboxState(); updateSelectAllCheckboxState();
// Update cost bars and ticks after selection changes
updateCostBars();
updateCostTicks();
}); });
// Listener for search input // Listener for search input