// 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.sourceRow = "true"; // Extra marker to ensure it's recognized as a source row 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", sourceRow: true, // Extra marker to ensure it's recognized as a source row 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", sourceRow: true, // Extra marker to ensure it's recognized as a source row 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}`); } // Add explicit source row marker if this is a source row if (data.sourceRow) { tr.dataset.sourceRow = "true"; console.log("Marked row as source row with additional protection"); } else if (data.transactionType && (data.transactionType === "conversion-out" || data.transactionType === "income-source" || data.transactionType === "income-transfer-note")) { tr.dataset.sourceRow = "true"; console.log("Auto-detected and protected source row based on transaction type"); } // 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'); // Triple-check for source rows to protect them from deletion const isSourceRow = tr.dataset.sourceRow === 'true' || (tr.dataset.transactionId && (tr.dataset.transactionType === 'conversion-out' || tr.dataset.transactionType === 'income-source' || tr.dataset.transactionType === 'income-transfer-note' || tr.dataset.transactionType === 'source-recovered')); console.log("Is destination row:", isDestinationRow); console.log("Is source row (protected from deletion):", isSourceRow); if (isSourceRow) { // Never delete source rows - block the deletion attempt console.log("BLOCKED attempt to delete a source row directly"); alert("This row is a source entry for a transfer and cannot be deleted directly. To remove it, please delete the corresponding entry in the conversion history."); return; // Exit early without deleting } else 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 explicit source row marker (highest priority) const isSourceByMarker = row.dataset && row.dataset.sourceRow === 'true'; // Method 2: Check by transaction ID and transaction type const isSourceByID = row.dataset && row.dataset.transactionId === transactionId && row.dataset.transactionType !== 'conversion-in' && row.dataset.transactionType !== 'income-transfer-in'; // Method 3: Check by specific source transaction types const isSourceByType = row.dataset && row.dataset.transactionType && (row.dataset.transactionType === 'conversion-out' || row.dataset.transactionType === 'income-source' || row.dataset.transactionType === 'income-transfer-note' || row.dataset.transactionType === 'source-recovered'); // Method 4: 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; } // Combine all detection methods const isSourceRowFound = isSourceByMarker || isSourceByID || isSourceByType || isSourceByNotes; // If this is a source row by any method, just clear its notes if (isSourceRowFound) { console.log(`Found source row in ${trackerName || tracker.id} by ${ isSourceByMarker ? "explicit marker" : isSourceByID ? "transaction ID" : isSourceByType ? "transaction type" : "notes" }`); console.log(`Source notes before: "${notes}"`); // Add the explicit source marker to protect this row from future deletion attempts row.dataset.sourceRow = "true"; // 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 (!row) { console.error("processPotentialSourceRow called with null row"); return; } // IMMEDIATELY mark this as a source row for protection - do this FIRST before anything else row.dataset.sourceRow = "true"; console.log("PROTECTED: Marked row as source row to prevent deletion"); 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}`); } } else { console.log("No notes cell/input found for this source row"); } } // 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() { // Make the confirmation message very clear about what will happen if (confirm("CONFIRM DELETION: This will:\n\n1. REMOVE the destination entry (the target coin purchase)\n2. KEEP the source entry but clear its transfer note\n\nSource rows will NEVER be deleted, only their transfer notes will be cleared.")) { // Always re-read localStorage to avoid index bugs let conversionHistory = JSON.parse(localStorage.getItem("conversionHistory") || "[]"); // CRITICAL FIX: The table displays sorted history, but we need to find the record by its actual properties, not index // because the displayed index might not match the storage array index due to sorting const displayedConversions = [...conversionHistory].sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0)); const conversionRecord = displayedConversions[index]; // Find the actual index in the original unsorted array const actualIndex = conversionHistory.findIndex(record => record.transactionId === conversionRecord.transactionId || (record.date === conversionRecord.date && record.time === conversionRecord.time && record.fromName === conversionRecord.fromName && record.toName === conversionRecord.toName && record.dollarAmount === conversionRecord.dollarAmount) ); console.log("=== DELETION DEBUG START ==="); console.log("Displayed index (from sorted array):", index); console.log("Actual index (in storage array):", actualIndex); console.log("Conversion record being deleted:", conversionRecord); console.log("Total conversion history length:", conversionHistory.length); console.log("=== DELETION DEBUG END ==="); // If the conversion has a transaction ID, find and handle related tracker entries if (conversionRecord && conversionRecord.transactionId) { const transactionId = conversionRecord.transactionId; const fromName = conversionRecord.fromName; const toName = conversionRecord.toName; console.log(`Processing deletion for transaction ${transactionId}: ${fromName} → ${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(`=== TRACKER ${tracker.id} DEBUG ===`); console.log(`Found ${destRows.length} destination rows in tracker ${tracker.id}`); console.log("All rows in this tracker's tbody:"); const allRowsDebug = tbody.querySelectorAll('tr'); allRowsDebug.forEach((debugRow, idx) => { console.log(` Row ${idx}: transactionId=${debugRow.dataset.transactionId}, transactionType=${debugRow.dataset.transactionType}, sourceRow=${debugRow.dataset.sourceRow}`); }); console.log(`=== END TRACKER ${tracker.id} DEBUG ===`); // Delete ONLY destination rows with extreme caution destRows.forEach(row => { // TRIPLE-CHECK that this is NOT a source row under any circumstances const isSafeToDelete = // 1. Check it does not have the source marker row.dataset.sourceRow !== 'true' && // 2. Check it is explicitly a destination type (row.dataset.transactionType === 'conversion-in' || row.dataset.transactionType === 'income-transfer-in'); // 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 : ""; // Analyze the row before taking any action debugAnalyzeRow(row, `Row check before potential deletion in ${trackerName || tracker.id}`); if (!isSafeToDelete) { console.log(`SAFETY BLOCK: Prevented deletion of row that might be a source row`); } else if (trackerName === toName) { // One final check that this is DEFINITELY not a source row const analysis = debugAnalyzeRow(row, "FINAL CHECK before deletion"); if (analysis.isSource) { console.error("CRITICAL PROTECTION: Blocked deletion of a source row that was misidentified"); } else { console.log(`Removing destination row in ${toName}`); 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 ${toName}`); } }); }); // STEP 2: Now find and modify ONLY source entries across all trackers console.log("STEP 2: Processing source entries - These should NEVER be deleted, only notes cleared"); trackers.forEach(tracker => { console.log(`Looking for source rows in tracker: ${tracker.id}`); const tbody = tracker.querySelector('table.coin-table tbody'); if (!tbody) { console.log("No tbody found in this tracker, skipping"); return; } // Get all rows that might be source entries const allRows = tbody.querySelectorAll('tr'); console.log(`Found ${allRows.length} total rows to check for source entries`); allRows.forEach(row => { // Method 1: Check by explicit source row marker (highest priority) const isSourceByMarker = row.dataset && row.dataset.sourceRow === 'true'; // Method 2: Check by transaction ID and transaction type const isSourceByID = row.dataset && row.dataset.transactionId === transactionId && row.dataset.transactionType !== 'conversion-in' && row.dataset.transactionType !== 'income-transfer-in'; // Method 3: Check by specific source transaction types const isSourceByType = row.dataset && row.dataset.transactionType && (row.dataset.transactionType === 'conversion-out' || row.dataset.transactionType === 'income-source' || row.dataset.transactionType === 'income-transfer-note' || row.dataset.transactionType === 'source-recovered'); // Method 4: 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 : ""; if (notes.includes(`TRANSFERRED: $`) && notes.includes(`to buy ${toName}`)) { isSourceByNotes = true; } if (notes.includes(`Moved $`) && notes.includes(`to ${toName}`)) { isSourceByNotes = true; } // Combine all source detection methods const isSourceRowFound = isSourceByMarker || isSourceByID || isSourceByType || isSourceByNotes; // CRITICAL PROTECTION: Immediately mark any potential source row to prevent deletion if (isSourceRowFound) { // Add the source marker BEFORE doing anything else to protect this row row.dataset.sourceRow = "true"; console.log(`PROTECTED SOURCE ROW FOUND in ${tracker.id}: ${ isSourceByMarker ? "by explicit marker" : isSourceByID ? "by transaction ID" : isSourceByType ? "by transaction type" : "by notes content" }`); console.log(`Source row notes before: "${notes}"`); // Log detailed row information to help debug console.log("Source row data:", { id: row.id || "No ID", created: row.dataset.created || "No creation timestamp", transactionId: row.dataset.transactionId || "No transaction ID", transactionType: row.dataset.transactionType || "No transaction type" }); // NEVER delete source rows, just clear the transfer note if (notesInput) { // Only modify notes if they contain transfer information relating to this transaction const containsRelevantTransfer = (notes.includes(`TRANSFERRED: $`) && notes.includes(`to buy ${toName}`)) || (notes.includes(`Moved $`) && notes.includes(`to ${toName}`)); if (containsRelevantTransfer) { 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`); } else { console.log(`Source row notes don't contain relevant transfer info for ${toName}, keeping unchanged`); } // 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 using the actual index, not the display index if (actualIndex !== -1) { conversionHistory.splice(actualIndex, 1); console.log(`Removed conversion record at actual index ${actualIndex}`); } else { console.error("Could not find actual index for conversion record, deletion skipped"); } 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); } } // Debug helper function to analyze a row and determine if it's a source row function debugAnalyzeRow(row, label = "Row analysis") { if (!row) { console.error("debugAnalyzeRow called with null row"); return {isSource: false, reason: "null row"}; } const isSourceByMarker = row.dataset && row.dataset.sourceRow === 'true'; const isSourceByType = row.dataset && row.dataset.transactionType && (row.dataset.transactionType === 'conversion-out' || row.dataset.transactionType === 'income-source' || row.dataset.transactionType === 'income-transfer-note' || row.dataset.transactionType === 'source-recovered'); const notesCell = row.cells && row.cells[7] ? row.cells[7] : null; const notesInput = notesCell ? notesCell.querySelector("input") : null; const notes = notesInput ? notesInput.value : ""; const isSourceByNotes = notes.includes('TRANSFERRED:') || notes.includes('Moved $'); const result = { isSource: isSourceByMarker || isSourceByType || isSourceByNotes, byMarker: isSourceByMarker, byType: isSourceByType, byNotes: isSourceByNotes, transactionId: row.dataset.transactionId || "none", transactionType: row.dataset.transactionType || "none", notes: notes }; console.log(`${label}:`, result); return result; }