// Direct conversion handler script document.addEventListener("DOMContentLoaded", function() { console.log("Direct conversion handler loaded"); // Initialize conversion history table on page load initializeConversionHistory(); // Give the main script time to load, then add our direct handler setTimeout(function() { // Get the conversion button const button = document.getElementById('executeConversion'); if (!button) { console.error("Could not find conversion button"); return; } // Replace the button with a clone to remove any existing event listeners const newButton = button.cloneNode(true); button.parentNode.replaceChild(newButton, button); // Add our direct event handler newButton.addEventListener('click', function(event) { event.preventDefault(); // Prevent form submission or default button behavior console.log("Direct handler: Convert button clicked"); // Get form values const fromAsset = document.getElementById('fromAsset').value; const toAsset = document.getElementById('toAsset').value; const dollarAmountInput = document.getElementById('conversionAmount'); const dollarAmount = parseFloat(dollarAmountInput ? dollarAmountInput.value : '0') || 0; const transferType = document.getElementById('conversionType').value; console.log("Form values:", {fromAsset, toAsset, dollarAmount, transferType}); // Validate inputs if (!fromAsset || !toAsset || dollarAmount <= 0) { alert("Please fill in all fields with valid values."); console.warn("Invalid form values:", {fromAsset, toAsset, dollarAmount}); return; } try { // If the main script's recordAssetTransfer function exists, use that instead // This prevents duplication of logic and potential conflicts if (typeof window.recordAssetTransfer === 'function') { console.log("Using main script's recordAssetTransfer function"); // Pass null as the selectedIncomeData parameter since we don't have income selection in direct-converter window.recordAssetTransfer(fromAsset, toAsset, dollarAmount, transferType, null); // Clear the input after successful transfer if (dollarAmountInput) { dollarAmountInput.value = ''; } return; } // Get the tracker elements directly const fromTracker = document.getElementById(fromAsset); const toTracker = document.getElementById(toAsset); if (!fromTracker || !toTracker) { console.error("Could not find tracker elements:", {fromTracker, toTracker}); alert("Error: Could not find one or both asset trackers."); return; } // Get coin names for logging const fromName = fromTracker.querySelector(".coin-tracker-header input.coin-name-input").value; const toName = toTracker.querySelector(".coin-tracker-header input.coin-name-input").value; // Get table elements const sourceTable = fromTracker.querySelector("table.coin-table"); const destTable = toTracker.querySelector("table.coin-table"); if (!sourceTable || !destTable) { console.error("Could not find tables:", {sourceTable, destTable}); alert("Error: Could not find tracker tables."); return; } // Get tbody elements const sourceTbody = sourceTable.querySelector("tbody"); const destTbody = destTable.querySelector("tbody"); if (!sourceTbody || !destTbody) { console.error("Could not find tbody elements:", {sourceTbody, destTbody}); alert("Error: Could not find tracker table bodies."); return; } // Get current date and time const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD const options = { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Europe/Berlin' }; const currentTime = new Date().toLocaleTimeString('en-GB', options); // Generate a transaction ID for linking entries const transactionId = `transfer_${Date.now()}`; // Process the transfer based on type if (transferType === 'income') { // For income transfer, try to find an income row to add a note to let foundIncomeRow = false; // Get all rows as an array and reverse to check newest rows first const sourceRows = Array.from(sourceTbody.rows).reverse(); console.log(`Checking ${sourceRows.length} source rows for income to transfer`); // Try to find a suitable income row, starting with the newest (last) rows for (const row of sourceRows) { const incomeInput = row.cells[3].querySelector("input"); const income = parseFloat(incomeInput?.value) || 0; console.log(`Checking row with income: ${income}, need: ${dollarAmount}`); if (income > 0 && income >= dollarAmount) { // Found a suitable income row, update its notes const notesCell = row.cells[7]; // Notes cell const notesInput = notesCell ? notesCell.querySelector("input") : null; if (notesInput) { const currentNotes = notesInput.value; const transferNote = `TRANSFERRED: $${dollarAmount.toFixed(2)} to buy ${toName}`; notesInput.value = currentNotes ? `${currentNotes}; ${transferNote}` : transferNote; console.log(`Added transfer note to row with income ${income}`); // Mark this row as involved in the transaction row.dataset.transactionId = transactionId; row.dataset.transactionType = "income-source"; // Add explicit transaction type for income source row.dataset.incomeUsed = dollarAmount; row.dataset.usedForTracker = toAsset; console.log(`Marked existing income row as transaction type: income-source`); // Make the row visually distinct row.style.backgroundColor = "rgba(255, 165, 0, 0.1)"; // Light orange background foundIncomeRow = true; break; } } } // Only if we couldn't find a suitable income row, create a new entry if (!foundIncomeRow) { // Create a note entry rather than a negative income entry addTableRow(sourceTbody, { date: today, time: currentTime, transactionId: transactionId, transactionType: "income-transfer-note", currentValue: 0, // Don't change principal income: 0, // No negative income - just a note compound: 0, dailyYield: 0, notes: 'TRANSFERRED: $' + dollarAmount.toFixed(2) + ' to buy ' + toName }); } console.log("Added source row for income transfer"); } else { // Regular principal transfer - subtract from current value of source addTableRow(sourceTbody, { date: today, time: currentTime, transactionId: transactionId, transactionType: "conversion-out", currentValue: -dollarAmount, // Reduce current value by transfer amount income: 0, compound: 0, dailyYield: 0, notes: 'Moved $' + dollarAmount.toFixed(2) + ' to ' + toName }); console.log("Added source row for principal transfer"); } // Get the last current value from the destination tracker // This is key to the fix: instead of just transferring the amount as a standalone value, // we add it to the last known value of the destination asset let lastDestValue = 0; const destRows = Array.from(destTbody.rows); if (destRows.length > 0) { // Always use the value from the last row in the destination tracker const lastRow = destRows[destRows.length - 1]; lastDestValue = parseFloat(lastRow.cells[2].querySelector("input").value) || 0; } // Add row to destination tracker (add to the last current value) const newDestValue = lastDestValue + dollarAmount; addTableRow(destTbody, { date: today, time: currentTime, transactionId: transactionId, transactionType: transferType === 'income' ? "income-transfer-in" : "conversion-in", currentValue: newDestValue, // Add to the last destination value income: 0, compound: 0, dailyYield: 0, notes: transferType === 'income' ? 'BOUGHT: ' + toName + ' using $' + dollarAmount.toFixed(2) + ' income from ' + fromName + ' (previous value: $' + lastDestValue.toFixed(2) + ')' : 'BOUGHT: ' + toName + ' using $' + dollarAmount.toFixed(2) + ' from ' + fromName + ' (previous value: $' + lastDestValue.toFixed(2) + ')' }); console.log("Added destination row with accumulated value:", { lastDestValue: lastDestValue, dollarAmount: dollarAmount, newDestValue: newDestValue }); // Log the transfer to conversion history logConversion({ date: today, time: currentTime, fromName: fromName, toName: toName, dollarAmount: dollarAmount, transferType: transferType, destPreviousValue: lastDestValue, // Store the previous value for reference destNewValue: newDestValue, // Store the new combined value transactionId: transactionId // Store the transaction ID for linked deletion }); // Update the tracker summaries if (typeof updateCoinTrackerSummary === 'function') { updateCoinTrackerSummary(fromAsset); updateCoinTrackerSummary(toAsset); // Update the header display values directly const fromTracker = document.getElementById(fromAsset); // Remove 'var' to avoid redeclaration const toTracker = document.getElementById(toAsset); if (fromTracker) { const fromValueSpan = fromTracker.querySelector(".last-value"); if (fromValueSpan) { // Always use the value from the last row in the source tracker let lastSourceValue = 0; const sourceRows = Array.from(sourceTbody.rows); if (sourceRows.length > 0) { const lastRow = sourceRows[sourceRows.length - 1]; lastSourceValue = parseFloat(lastRow.cells[2].querySelector("input").value) || 0; } fromValueSpan.textContent = 'Current Value: $' + lastSourceValue.toFixed(2); } } if (toTracker) { const toValueSpan = toTracker.querySelector(".last-value"); if (toValueSpan) { // Always use the value from the last row in the destination tracker let lastDestValueHeader = 0; const destRowsHeader = Array.from(destTbody.rows); if (destRowsHeader.length > 0) { const lastRow = destRowsHeader[destRowsHeader.length - 1]; lastDestValueHeader = parseFloat(lastRow.cells[2].querySelector("input").value) || 0; } toValueSpan.textContent = 'Current Value: $' + lastDestValueHeader.toFixed(2); } } } // Explicitly trigger the header update logic after adding rows updateCoinTrackerSummary(fromAsset); updateCoinTrackerSummary(toAsset); // Save tracker changes if (typeof saveCoinTrackers === 'function') { saveCoinTrackers(); } // Update conversion statistics if (typeof updateConversionStats === 'function') { updateConversionStats(); } // Update total investment display if (typeof updateTotalInvested === 'function') { updateTotalInvested(); } // Clear the amount input field if (dollarAmountInput) { dollarAmountInput.value = ''; } // Remove the alert message after transfer // alert('Transferred $' + dollarAmount.toFixed(2) + ' from ' + fromName + ' to ' + toName); } catch (error) { console.error("Error during asset transfer:", error); alert("Error during transfer: " + error.message); } }); console.log("Event listener attached to the new button."); console.log("Direct conversion handler added successfully"); }, 1000); // Wait 1 second for the page to fully load }); // Function to add a row to a tracker table function addTableRow(tbody, data) { if (!tbody) { console.error("Could not find tbody to add row"); return; } const tr = document.createElement("tr"); tr.dataset.created = Date.now(); // Store transaction ID if provided (for linked entries) if (data.transactionId) { tr.dataset.transactionId = data.transactionId; console.log(`Setting transaction ID on new row: ${data.transactionId}`); } // Store transaction type if provided if (data.transactionType) { tr.dataset.transactionType = data.transactionType; console.log(`Setting transaction type on new row: ${data.transactionType}`); } // Date cell const tdDate = document.createElement("td"); const inputDate = document.createElement("input"); inputDate.type = "date"; inputDate.value = data.date || ""; tdDate.appendChild(inputDate); tr.appendChild(tdDate); // Time cell const tdTime = document.createElement("td"); const inputTime = document.createElement("input"); inputTime.type = "time"; inputTime.value = data.time || ""; tdTime.appendChild(inputTime); tr.appendChild(tdTime); // Current Value cell const tdCurrent = document.createElement("td"); tdCurrent.innerHTML = "$"; const inputCurrent = document.createElement("input"); inputCurrent.type = "number"; inputCurrent.step = "0.01"; inputCurrent.value = parseFloat(data.currentValue).toFixed(2) || "0.00"; tdCurrent.appendChild(inputCurrent); tr.appendChild(tdCurrent); // Income cell const tdIncome = document.createElement("td"); const inputIncome = document.createElement("input"); inputIncome.type = "number"; inputIncome.step = "0.01"; inputIncome.value = data.income || ""; tdIncome.appendChild(inputIncome); tr.appendChild(tdIncome); // Compound cell const tdCompound = document.createElement("td"); const inputCompound = document.createElement("input"); inputCompound.type = "number"; inputCompound.step = "0.01"; inputCompound.value = data.compound || ""; tdCompound.appendChild(inputCompound); tr.appendChild(tdCompound); // Daily Yield cell const tdYield = document.createElement("td"); const inputYield = document.createElement("input"); inputYield.type = "number"; inputYield.step = "0.01"; inputYield.value = data.dailyYield || ""; tdYield.appendChild(inputYield); tr.appendChild(tdYield); // Actions cell const tdActions = document.createElement("td"); const delBtn = document.createElement("button"); delBtn.textContent = "Delete"; delBtn.className = "deleteRowBtn"; delBtn.addEventListener("click", () => { console.log("Delete button clicked for row"); console.log("Row transaction ID:", tr.dataset.transactionId); console.log("Row transaction type:", tr.dataset.transactionType); // For rows that are part of transfers, handle deletion differently const isDestinationRow = tr.dataset.transactionId && (tr.dataset.transactionType === 'conversion-in' || tr.dataset.transactionType === 'income-transfer-in'); console.log("Is destination row:", isDestinationRow); if (isDestinationRow) { // If it's a destination row (conversion-in), we should also clean up the source note if (confirm("Delete this transfer entry? This will remove the destination entry and clear the transfer note from the source.")) { console.log("Confirmed deletion of destination row"); const transactionId = tr.dataset.transactionId; // STEP 1: Find source rows across all trackers and clear their notes const trackers = document.querySelectorAll('.coin-tracker'); // Process all trackers looking for source rows trackers.forEach(tracker => { // Skip the tracker this row belongs to if (tracker === tr.closest('.coin-tracker')) { console.log("Skipping current tracker for source rows"); return; } const tbody = tracker.querySelector('table.coin-table tbody'); if (!tbody) return; // Get all rows in this tracker const allRows = tbody.querySelectorAll('tr'); // Process each row looking for source rows by multiple methods allRows.forEach(row => { // Method 1: Check by transaction ID and type const isSourceByID = row.dataset && row.dataset.transactionId === transactionId && row.dataset.transactionType !== 'conversion-in' && row.dataset.transactionType !== 'income-transfer-in'; // Method 2: Check by notes content let isSourceByNotes = false; const notesCell = row.cells && row.cells[7] ? row.cells[7] : null; const notesInput = notesCell ? notesCell.querySelector("input") : null; const notes = notesInput ? notesInput.value : ""; // Look for the source pattern in notes const trackerNameInput = tracker.querySelector(".coin-tracker-header .coin-name-input"); const trackerName = trackerNameInput ? trackerNameInput.value : ""; if (notesInput && (notes.includes("TRANSFERRED:") || notes.includes("Moved $"))) { isSourceByNotes = true; } // If this is a source row by either method, just clear its notes if (isSourceByID || isSourceByNotes) { console.log(`Found source row in ${trackerName || tracker.id}`); console.log(`Source notes before: "${notes}"`); // Never delete source rows, just clear their notes if (notesInput) { const transferTextPattern = /TRANSFERRED: \$[\d.]+( to buy [^;]+)?/; const movedTextPattern = /Moved \$[\d.]+( to [^;]+)?/; let newNotes = notes; newNotes = newNotes.replace(transferTextPattern, ""); newNotes = newNotes.replace(movedTextPattern, ""); // Clean up any trailing or leading semicolons and double semicolons newNotes = newNotes.replace(/^;\s*/, "").replace(/;\s*$/, "").replace(/;\s*;/g, ";"); notesInput.value = newNotes; console.log(`Source notes after: "${newNotes}"`); } // Tag this row if it wasn't already tagged if (!isSourceByID) { row.dataset.transactionId = transactionId; row.dataset.transactionType = "source-recovered"; } } }); }); // STEP 2: Now that we've processed all source rows, remove ONLY this destination row console.log("Removing only this destination row"); tr.remove(); } } else { // Standard deletion for non-transfer rows if (tr.dataset.transactionId) { // This is a source row of a transfer - don't allow deletion console.log("Attempted to delete a source row directly - prevented"); alert("This row is part of a transfer. To remove it, please delete the corresponding entry in the conversion history."); } else { // Regular row, can be deleted console.log("Removing regular (non-transfer) row"); tr.remove(); } } if (typeof saveCoinTrackers === 'function') { saveCoinTrackers(); } }); tdActions.appendChild(delBtn); tr.appendChild(tdActions); // Notes cell const tdNotes = document.createElement("td"); const notesInput = document.createElement("input"); notesInput.type = "text"; notesInput.value = data.notes || ""; notesInput.placeholder = "Transaction notes"; notesInput.style.width = "100%"; tdNotes.appendChild(notesInput); tr.appendChild(tdNotes); // Add row to table tbody.appendChild(tr); } // Helper function to process potential source rows function processPotentialSourceRow(row, notesCell, notesInput, transactionId) { if (notesCell && notesInput) { const currentNotes = notesInput.value; console.log("Processing potential source row notes:", currentNotes); const transferTextPattern = /TRANSFERRED: \$[\d.]+( to buy [^;]+)?/; const movedTextPattern = /Moved \$[\d.]+( to [^;]+)?/; let newNotes = currentNotes; newNotes = newNotes.replace(transferTextPattern, ""); newNotes = newNotes.replace(movedTextPattern, ""); // Clean up any trailing or leading semicolons and double semicolons newNotes = newNotes.replace(/^;\s*/, "").replace(/;\s*$/, "").replace(/;\s*;/g, ";"); notesInput.value = newNotes; console.log("Updated potential source row notes to:", newNotes); // Mark this row as a source row for future operations if (transactionId) { row.dataset.transactionId = transactionId; row.dataset.transactionType = "source-recovered"; console.log(`Tagged row as source-recovered with transaction ID ${transactionId}`); } } } // Function to log conversions to history function logConversion(conversionData) { try { // Always re-read localStorage to avoid index bugs let conversionHistory = JSON.parse(localStorage.getItem("conversionHistory") || "[]"); conversionData.timestamp = Date.now(); conversionHistory.push(conversionData); localStorage.setItem("conversionHistory", JSON.stringify(conversionHistory)); updateConversionHistoryTable(); } catch (error) { console.error("Error logging conversion:", error); // Don't show alert to user as this is not critical } } // Function to update the conversion history table function updateConversionHistoryTable() { try { let conversionHistory = JSON.parse(localStorage.getItem("conversionHistory") || "[]"); const tableBody = document.querySelector("#conversionHistoryTable tbody"); if (!tableBody) { console.warn("Conversion history table not found in the DOM"); return; // Exit if the table doesn't exist } // Continue only if the table exists tableBody.innerHTML = ""; // Sort by date, newest first conversionHistory.sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0)); conversionHistory.forEach((conversion, index) => { // Skip if conversion is undefined or null if (!conversion) return; const row = document.createElement("tr"); row.dataset.index = index; // Date const dateCell = document.createElement("td"); dateCell.textContent = (conversion.date ? conversion.date : "") + " " + (conversion.time ? conversion.time : ""); row.appendChild(dateCell); // From Asset const fromCell = document.createElement("td"); fromCell.textContent = conversion.fromName; row.appendChild(fromCell); // Amount const amountCell = document.createElement("td"); const dollarAmount = conversion.dollarAmount || conversion.sentAmount || 0; if (conversion.transferType === 'income') { amountCell.textContent = '$' + dollarAmount.toFixed(2) + ' (Income)'; amountCell.style.color = '#4CAF50'; } else { amountCell.textContent = '$' + dollarAmount.toFixed(2); } row.appendChild(amountCell); // To Asset const toCell = document.createElement("td"); toCell.textContent = conversion.toName; row.appendChild(toCell); // Received const receivedCell = document.createElement("td"); if (conversion.destPreviousValue !== undefined && conversion.destNewValue !== undefined) { receivedCell.textContent = '$' + dollarAmount.toFixed(2) + ' → $' + conversion.destNewValue.toFixed(2); receivedCell.title = 'Previous value: $' + conversion.destPreviousValue.toFixed(2) + ', Added: $' + dollarAmount.toFixed(2) + ', New value: $' + conversion.destNewValue.toFixed(2); } else { receivedCell.textContent = '$' + dollarAmount.toFixed(2); } row.appendChild(receivedCell); // Actions const actionsCell = document.createElement("td"); const deleteBtn = document.createElement("button"); deleteBtn.textContent = "Delete"; deleteBtn.style.padding = "3px 8px"; deleteBtn.style.background = "#ff4444"; deleteBtn.style.color = "white"; deleteBtn.style.border = "none"; deleteBtn.style.borderRadius = "3px"; deleteBtn.style.cursor = "pointer"; deleteBtn.addEventListener("click", function() { if (confirm("Are you sure you want to delete this conversion record? This will remove ONLY the destination entry (the target coin purchase), but keep the source entry with a cleared note. The source row will NEVER be deleted.")) { // Always re-read localStorage to avoid index bugs let conversionHistory = JSON.parse(localStorage.getItem("conversionHistory") || "[]"); const conversionRecord = conversionHistory[index]; // If the conversion has a transaction ID, find and handle related tracker entries if (conversionRecord && conversionRecord.transactionId) { const transactionId = conversionRecord.transactionId; const fromAsset = conversionRecord.fromName; const toAsset = conversionRecord.toName; // Get all coin trackers const trackers = document.querySelectorAll('.coin-tracker'); // STEP 1: First find and process ONLY the destination entries across all trackers trackers.forEach(tracker => { const tbody = tracker.querySelector('table.coin-table tbody'); if (!tbody) return; // Find destination entries first - we ONLY delete these const destRows = tbody.querySelectorAll(`tr[data-transaction-id="${transactionId}"][data-transaction-type="conversion-in"], tr[data-transaction-id="${transactionId}"][data-transaction-type="income-transfer-in"]`); console.log(`Found ${destRows.length} destination rows in tracker ${tracker.id}`); // Delete ONLY destination rows destRows.forEach(row => { // Make sure this is a tracker for the destination asset before deleting const trackerNameInput = tracker.querySelector(".coin-tracker-header .coin-name-input"); const trackerName = trackerNameInput ? trackerNameInput.value : ""; if (trackerName === toAsset || tracker.id === toAsset) { console.log(`Removing destination row in ${toAsset}`); row.remove(); // ONLY destination rows are deleted } else { console.log(`NOT removing destination row in ${trackerName || tracker.id} because it's not the target asset ${toAsset}`); } }); }); // STEP 2: Now find and modify ONLY source entries across all trackers trackers.forEach(tracker => { const tbody = tracker.querySelector('table.coin-table tbody'); if (!tbody) return; // Get all rows that might be source entries const allRows = tbody.querySelectorAll('tr'); allRows.forEach(row => { // Check if this is a source row by the transaction ID and NOT being a destination type const isSourceByID = row.dataset && row.dataset.transactionId === transactionId && row.dataset.transactionType !== 'conversion-in' && row.dataset.transactionType !== 'income-transfer-in'; // Additional check for source rows by notes content let isSourceByNotes = false; const notesCell = row.cells && row.cells[7] ? row.cells[7] : null; const notesInput = notesCell ? notesCell.querySelector("input") : null; const notes = notesInput ? notesInput.value : ""; if (notes.includes(`TRANSFERRED: $`) && notes.includes(`to buy ${toAsset}`)) { isSourceByNotes = true; } if (notes.includes(`Moved $`) && notes.includes(`to ${toAsset}`)) { isSourceByNotes = true; } if (isSourceByID || isSourceByNotes) { console.log(`Found source row: ${isSourceByID ? "by ID" : "by notes"}`); console.log(`Source row notes before: "${notes}"`); // NEVER delete source rows, just clear the transfer note if (notesInput) { const transferTextPattern = /TRANSFERRED: \$[\d.]+( to buy [^;]+)?/; const movedTextPattern = /Moved \$[\d.]+( to [^;]+)?/; let newNotes = notes; newNotes = newNotes.replace(transferTextPattern, ""); newNotes = newNotes.replace(movedTextPattern, ""); // Clean up any trailing or leading semicolons and double semicolons newNotes = newNotes.replace(/^;\s*/, "").replace(/;\s*$/, "").replace(/;\s*;/g, ";"); notesInput.value = newNotes; console.log(`Source row notes after: "${newNotes}"`); console.log(`KEPT source row - only cleared transfer note`); // Tag this row if it wasn't already tagged if (!isSourceByID) { row.dataset.transactionId = transactionId; row.dataset.transactionType = "source-recovered"; console.log("Tagged source row for future operations"); } } } }); }); } }); // Update all trackers after deletion document.querySelectorAll('.coin-tracker').forEach(tracker => { const trackerId = tracker.id; if (typeof updateCoinTrackerSummary === 'function') { updateCoinTrackerSummary(trackerId); } else if (typeof window.updateCoinTrackerSummary === 'function') { window.updateCoinTrackerSummary(trackerId); } }); // Save all trackers if (typeof saveCoinTrackers === 'function') { saveCoinTrackers(); } else if (typeof window.saveCoinTrackers === 'function') { window.saveCoinTrackers(); } } // Remove from the array conversionHistory.splice(index, 1); localStorage.setItem("conversionHistory", JSON.stringify(conversionHistory)); updateConversionHistoryTable(); // Call updateConversionStats only if it exists (it's defined in script.js) try { if (typeof updateConversionStats === 'function') { updateConversionStats(); } else if (typeof window.updateConversionStats === 'function') { window.updateConversionStats(); } } catch (error) { console.error("Error calling updateConversionStats:", error); } // Update total investment display if available try { if (typeof updateTotalInvested === 'function') { updateTotalInvested(); } else if (typeof window.updateTotalInvested === 'function') { window.updateTotalInvested(); } } catch (error) { console.error("Error updating total invested:", error); } } }); actionsCell.appendChild(deleteBtn); row.appendChild(actionsCell); tableBody.appendChild(row); }); // Safely call updateConversionStats if it exists try { if (typeof updateConversionStats === 'function') { updateConversionStats(); } else if (typeof window.updateConversionStats === 'function') { window.updateConversionStats(); } } catch (error) { console.error("Error calling updateConversionStats after table update:", error); } } catch (error) { console.error("Error updating conversion history table:", error); } } // Function to initialize the conversion history table on page load function initializeConversionHistory() { try { updateConversionHistoryTable(); } catch (error) { console.error("Error initializing conversion history:", error); } }