initial comment

This commit is contained in:
2026-01-24 17:43:28 -05:00
commit fe40adfd38
72 changed files with 19614 additions and 0 deletions

202
pagination-replacement.js Normal file
View File

@@ -0,0 +1,202 @@
// PAGINATION REPLACEMENT CODE FOR dashboard.html
// Replace lines 1439-1625 with this code
// File Quality Analysis with Pagination
let selectedFiles = new Set();
let currentAttributeFilter = null; // Track the current attribute filter
let currentStatusFilter = 'all'; // Track the current status filter
let currentPage = 1;
let totalPages = 1;
let filesPerPage = 100;
let totalFiles = 0;
async function loadFileQuality() {
const tbody = document.getElementById('qualityTableBody');
// Clear selections when reloading (not when paginating)
// selectedFiles stays intact during pagination
try {
// Calculate offset from current page
const offset = (currentPage - 1) * filesPerPage;
// Build URL with state filter and attribute filter
let url = `/api/files?limit=${filesPerPage}&offset=${offset}`;
if (currentStatusFilter !== 'all') {
url += `&state=${currentStatusFilter}`;
}
if (currentAttributeFilter && currentAttributeFilter !== 'all') {
url += `&filter=${currentAttributeFilter}`;
}
const response = await fetch(url);
const result = await response.json();
if (result.success) {
const files = result.data;
let html = '';
if (files.length === 0 && currentPage === 1) {
html = '<tr><td colspan="8" style="text-align: center; padding: 40px; color: #64748b;">No files found</td></tr>';
} else {
files.forEach(file => {
const savings = file.original_size && file.encoded_size ?
((file.original_size - file.encoded_size) / file.original_size * 100).toFixed(1) : '-';
const stateBadgeColors = {
'discovered': '#8b5cf6',
'pending': '#fbbf24',
'processing': '#3b82f6',
'completed': '#10b981',
'failed': '#ef4444',
'skipped': '#94a3b8'
};
const statusIcon = file.state === 'completed' ? '✅' :
file.state === 'failed' ? '❌' :
file.state === 'processing' ? '⏳' :
file.state === 'skipped' ? '⏭️' : '⏸️';
const disableCheckbox = file.state === 'processing' ? 'disabled' : '';
// Create tooltip for state badge
let stateTooltip = '';
if (file.state === 'discovered') {
stateTooltip = 'Discovered - select to encode';
} else if (file.state === 'pending') {
stateTooltip = 'Selected - will encode when you click Encode Selected';
} else if (file.state === 'processing') {
stateTooltip = 'Currently encoding...';
} else if (file.state === 'completed' && file.encoder_used) {
stateTooltip = `Completed with ${file.encoder_used}`;
} else if (file.state === 'failed' && file.error_message) {
stateTooltip = `Failed: ${file.error_message}`;
} else if (file.state === 'skipped' && file.error_message) {
stateTooltip = `Skipped: ${file.error_message}`;
}
const rowClass = file.state === 'processing' ? 'row-processing' : '';
// Escape all user-controlled content
const escapedPath = escapeHtml(file.relative_path);
const escapedPathAttr = escapeAttr(file.relative_path);
const escapedState = escapeHtml(file.state);
const escapedTooltip = escapeAttr(stateTooltip);
// Check if this file is selected
const isChecked = selectedFiles.has(file.id) ? 'checked' : '';
html += `
<tr class="${rowClass}" data-file-id="${file.id}" style="border-bottom: 1px solid #334155; transition: all 0.2s ease;">
<td style="padding: 12px; text-align: center;">
<input type="checkbox" class="file-checkbox" data-file-id="${file.id}"
onchange="toggleFileSelection(${file.id})"
${disableCheckbox} ${isChecked}
style="cursor: ${file.state === 'processing' ? 'not-allowed' : 'pointer'}; transform: scale(1.2);">
</td>
<td style="padding: 12px; max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${escapedPathAttr}">
<span style="font-weight: 500;">${escapedPath}</span>
</td>
<td style="padding: 12px;">
<span style="background: ${stateBadgeColors[file.state] || '#64748b'}; padding: 6px 12px; border-radius: 6px; font-size: 0.85em; cursor: help; font-weight: 600;"
title="${escapedTooltip}">
${escapedState}
</span>
</td>
<td style="padding: 12px; color: #94a3b8;">-</td>
<td style="padding: 12px; font-weight: 500;">${file.original_size ? formatBytes(file.original_size) : '-'}</td>
<td style="padding: 12px; font-weight: 500;">${file.encoded_size ? formatBytes(file.encoded_size) : '-'}</td>
<td style="padding: 12px; font-weight: 600; color: ${savings !== '-' && parseFloat(savings) > 0 ? '#10b981' : '#64748b'};">
${savings !== '-' ? escapeHtml(savings) + '%' : '-'}
</td>
<td style="padding: 12px; font-size: 1.2em;">${statusIcon}</td>
</tr>
`;
});
}
tbody.innerHTML = html;
// Update pagination controls
// Calculate total pages (estimate based on if we got less than limit)
if (files.length < filesPerPage) {
totalPages = currentPage;
} else {
// We don't know total, just enable next
totalPages = currentPage + 1;
}
updatePaginationControls();
document.getElementById('selectAll').checked = false;
}
} catch (error) {
console.error('Failed to load file quality:', error);
tbody.innerHTML = '<tr><td colspan="8" style="text-align: center; padding: 40px; color: #ef4444;">Error loading files</td></tr>';
}
}
function updatePaginationControls() {
const paginationContainer = document.getElementById('paginationControls');
if (!paginationContainer) return;
const startFile = (currentPage - 1) * filesPerPage + 1;
const endFile = currentPage * filesPerPage;
let html = `
<div style="display: flex; align-items: center; justify-content: center; gap: 15px; padding: 20px;">
<button class="btn" onclick="goToPage(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}
style="background: #3b82f6; color: white; ${currentPage === 1 ? 'opacity: 0.5; cursor: not-allowed;' : ''}">
← Previous
</button>
<div style="color: #94a3b8; font-weight: 500;">
Page ${currentPage} | Showing ${startFile}-${endFile}
</div>
<button class="btn" onclick="goToPage(${currentPage + 1})"
style="background: #3b82f6; color: white;">
Next →
</button>
<div style="display: flex; align-items: center; gap: 8px;">
<label style="color: #94a3b8;">Go to page:</label>
<input type="number" id="pageInput" min="1" value="${currentPage}"
style="width: 80px; padding: 8px; border-radius: 6px; background: #1e293b; color: #e2e8f0; border: 1px solid #334155; text-align: center;"
onkeypress="if(event.key==='Enter') goToPageInput()">
<button class="btn" onclick="goToPageInput()" style="background: #10b981; color: white;">Go</button>
</div>
</div>
`;
paginationContainer.innerHTML = html;
}
function goToPage(page) {
if (page < 1) return;
currentPage = page;
loadFileQuality();
}
function goToPageInput() {
const input = document.getElementById('pageInput');
const page = parseInt(input.value);
if (page && page > 0) {
goToPage(page);
}
}
function changeStatusFilter(status) {
currentStatusFilter = status;
currentPage = 1; // Reset to first page
selectedFiles.clear(); // Clear selections when changing filter
loadFileQuality();
}
function toggleFileSelection(fileId) {
if (selectedFiles.has(fileId)) {
selectedFiles.delete(fileId);
} else {
selectedFiles.add(fileId);
}
updateSelectedCount();
}