diff --git a/estimatortable.html b/estimatortable.html index 193dc31..cb7cc71 100644 --- a/estimatortable.html +++ b/estimatortable.html @@ -202,12 +202,34 @@
Scroll horizontally and vertically • First two columns are frozen
| ${prettyLabel(col.name)} | `).join(''); @@ -498,8 +536,11 @@ // Ensure header reflects current order/visibility renderTableHeader(); - // Re-render body to reflect new order/visibility - scheduleTableReload(); + // Re-render body only if data is already loaded + if (allRecords && allRecords.length > 0) { + renderTableRows(); + } + // Note: Don't call scheduleTableReload here - it will be called after initial load } // Get column settings @@ -521,12 +562,23 @@ async function checkAuth() { if (pb.authStore.isValid) { // Already logged in + console.log('User authenticated, loading...'); + loadingIndicator.classList.remove('hidden'); + await loadAppSettings(); + console.log('Settings loaded'); + await startSettingsWatch(); + console.log('Settings watch started'); + showTable(); + console.log('Table shown'); + await loadTableData(); + console.log('Data loaded'); } else { // Need to login + console.log('User not authenticated'); loginContainer.classList.remove('hidden'); } } @@ -536,9 +588,11 @@ try { loginBtn.disabled = true; loginBtn.textContent = 'Logging in...'; + loadingIndicator.classList.remove('hidden'); const authData = await pb.collection('users').authWithOAuth2({ provider: 'microsoft' }); + console.log('Login successful'); await loadAppSettings(); await startSettingsWatch(); showTable(); @@ -548,6 +602,7 @@ alert('Login failed. Please try again.'); loginBtn.disabled = false; loginBtn.textContent = 'Login with Microsoft'; + loadingIndicator.classList.add('hidden'); } }); @@ -559,6 +614,9 @@ userDisplay.classList.remove('hidden'); userDisplayName.textContent = pb.authStore.model.name || pb.authStore.model.email || 'User'; } + + // Setup search listener after table is shown + setupSearchListener(); } // Settings button handler @@ -803,46 +861,13 @@ throw new Error(result.message || 'Failed to fetch jobs'); } - const records = result.data; - console.log(`Loaded ${records.length} jobs ${result.cached ? 'from cache' : 'from PocketBase'}`); + allRecords = result.data; // Store all records for filtering + console.log(`Loaded ${allRecords.length} jobs ${result.cached ? 'from cache' : 'from PocketBase'}`); + console.log('First record:', allRecords[0]); - tableBody.innerHTML = ''; - const visibleColumns = getVisibleColumns(); - - records.forEach(record => { - const row = document.createElement('tr'); - row.className = 'border-b border-gray-200'; - - const rowIndex = Array.from(tableBody.children).length; - - const cellsHtml = visibleColumns.map((col, idx) => { - const columnName = col.name; - const value = record[columnName]; - const titleValue = getCellTitle(columnName, record); - const className = columnClassMap[columnName] || 'px-4 py-3'; - let style = ''; - const colSettings = getColumnSettings(columnName); - if (colSettings && value && colSettings.choices && colSettings.choices.length > 0) { - const choice = colSettings.choices.find(c => c.value === value); - if (choice) { - const bgColor = rowIndex % 2 === 0 ? choice.bgColor : choice.bgColorAlt; - style += `background-color: ${bgColor} !important;`; - if (choice.fontStyle === 'bold') { - style += 'font-weight: bold;'; - } else if (choice.fontStyle === 'italic') { - style += 'font-style: italic;'; - } - } - } - const styleAttr = style ? `style="${style}"` : ''; - const titleAttr = titleValue ? ` title="${titleValue}"` : ''; - const content = renderCellContent(columnName, record, value); - return `${content} | `; - }).join(''); - - row.innerHTML = cellsHtml; - tableBody.appendChild(row); - }); + // Render with current search filter + renderTableRows(); + console.log('Render complete'); isLoadingTableData = false; loadingIndicator.classList.add('hidden'); @@ -854,6 +879,114 @@ } } + // Render table rows with optional filtering + function renderTableRows() { + if (!tableBody) { + console.error('tableBody element not found'); + return; + } + + if (!allRecords || allRecords.length === 0) { + console.log('No records to render'); + tableBody.innerHTML = '||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| No data available | |||||||||||||||||||||||||||||||||||||||||||||||||
| No matching results | ${content} | `; + }).join(''); + + row.innerHTML = cellsHtml; + tableBody.appendChild(row); + }); + } + + // Filter records based on search term + function filterRecords(records, searchTerm) { + if (!searchTerm || !searchTerm.trim()) { + return records; + } + + const term = searchTerm.toLowerCase().trim(); + const searchFields = [ + 'Job_Number', + 'Job_Full_Name', + 'Job_Address', + 'Estimator', + 'Office_Rep', + 'Project_Manager', + 'Company_Client', + 'Contact_Person' + ]; + + return records.filter(record => { + return searchFields.some(field => { + const value = record[field]; + if (!value) return false; + return String(value).toLowerCase().includes(term); + }); + }); + } + + // Setup search input listener + function setupSearchListener() { + const searchInput = document.getElementById('searchInput'); + if (!searchInput) return; + + searchInput.addEventListener('input', (e) => { + currentSearchTerm = e.target.value; + renderTableRows(); + }); + } + // Handle checkbox changes document.addEventListener('change', async (e) => { if (e.target.type === 'checkbox' && e.target.dataset.field) {