Implement proper deposit tracking for coin trackers
- Added deposit/withdrawal history section to each coin tracker - Created addDepositEntry function for managing deposits - Fixed updateInvestedChart to use actual deposit history instead of flawed logic - Total Deposited Value now reflects actual money invested, not market performance - Portfolio Value and Total Deposited Value are now properly separated - Supports migration of existing Initial Capital as first deposit
This commit is contained in:
@@ -424,6 +424,86 @@
|
|||||||
initialCapitalDiv.appendChild(initialCapitalInput);
|
initialCapitalDiv.appendChild(initialCapitalInput);
|
||||||
contentDiv.appendChild(initialCapitalDiv);
|
contentDiv.appendChild(initialCapitalDiv);
|
||||||
|
|
||||||
|
// Create deposit tracking section
|
||||||
|
const depositSectionDiv = document.createElement("div");
|
||||||
|
depositSectionDiv.style.marginBottom = "10px";
|
||||||
|
depositSectionDiv.style.padding = "10px";
|
||||||
|
depositSectionDiv.style.border = "1px solid #444";
|
||||||
|
depositSectionDiv.style.borderRadius = "5px";
|
||||||
|
depositSectionDiv.style.backgroundColor = "#2a2a2a";
|
||||||
|
|
||||||
|
const depositSectionLabel = document.createElement("h4");
|
||||||
|
depositSectionLabel.textContent = "Deposit/Withdrawal History";
|
||||||
|
depositSectionLabel.style.marginTop = "0";
|
||||||
|
depositSectionLabel.style.marginBottom = "10px";
|
||||||
|
depositSectionDiv.appendChild(depositSectionLabel);
|
||||||
|
|
||||||
|
// Deposit list container
|
||||||
|
const depositListDiv = document.createElement("div");
|
||||||
|
depositListDiv.className = "deposit-list";
|
||||||
|
depositSectionDiv.appendChild(depositListDiv);
|
||||||
|
|
||||||
|
// Add deposit controls
|
||||||
|
const addDepositDiv = document.createElement("div");
|
||||||
|
addDepositDiv.style.marginTop = "10px";
|
||||||
|
|
||||||
|
const depositDateInput = document.createElement("input");
|
||||||
|
depositDateInput.type = "date";
|
||||||
|
depositDateInput.value = getToday();
|
||||||
|
depositDateInput.style.marginRight = "5px";
|
||||||
|
|
||||||
|
const depositAmountInput = document.createElement("input");
|
||||||
|
depositAmountInput.type = "number";
|
||||||
|
depositAmountInput.step = "0.01";
|
||||||
|
depositAmountInput.placeholder = "Amount (+/-)";
|
||||||
|
depositAmountInput.style.marginRight = "5px";
|
||||||
|
depositAmountInput.style.width = "120px";
|
||||||
|
|
||||||
|
const depositNotesInput = document.createElement("input");
|
||||||
|
depositNotesInput.type = "text";
|
||||||
|
depositNotesInput.placeholder = "Notes (optional)";
|
||||||
|
depositNotesInput.style.marginRight = "5px";
|
||||||
|
depositNotesInput.style.width = "150px";
|
||||||
|
|
||||||
|
const addDepositBtn = document.createElement("button");
|
||||||
|
addDepositBtn.textContent = "Add Deposit/Withdrawal";
|
||||||
|
addDepositBtn.style.fontSize = "12px";
|
||||||
|
addDepositBtn.addEventListener("click", () => {
|
||||||
|
if (depositAmountInput.value && depositDateInput.value) {
|
||||||
|
addDepositEntry(depositListDiv, {
|
||||||
|
date: depositDateInput.value,
|
||||||
|
amount: parseFloat(depositAmountInput.value),
|
||||||
|
notes: depositNotesInput.value || ""
|
||||||
|
}, trackerId);
|
||||||
|
depositAmountInput.value = "";
|
||||||
|
depositNotesInput.value = "";
|
||||||
|
saveCoinTrackers();
|
||||||
|
updateInvestedChart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addDepositDiv.appendChild(depositDateInput);
|
||||||
|
addDepositDiv.appendChild(depositAmountInput);
|
||||||
|
addDepositDiv.appendChild(depositNotesInput);
|
||||||
|
addDepositDiv.appendChild(addDepositBtn);
|
||||||
|
depositSectionDiv.appendChild(addDepositDiv);
|
||||||
|
|
||||||
|
contentDiv.appendChild(depositSectionDiv);
|
||||||
|
|
||||||
|
// Load existing deposits if available
|
||||||
|
if (data && data.deposits && data.deposits.length > 0) {
|
||||||
|
data.deposits.forEach(deposit => {
|
||||||
|
addDepositEntry(depositListDiv, deposit, trackerId);
|
||||||
|
});
|
||||||
|
} else if (data && data.initialCapital && parseFloat(data.initialCapital) > 0) {
|
||||||
|
// Migrate existing initial capital as first deposit
|
||||||
|
addDepositEntry(depositListDiv, {
|
||||||
|
date: "2025-01-01", // Default date for migrated data
|
||||||
|
amount: parseFloat(data.initialCapital),
|
||||||
|
notes: "Migrated from Initial Capital"
|
||||||
|
}, trackerId);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a container with positioning context for the table
|
// Create a container with positioning context for the table
|
||||||
const tableWrapper = document.createElement("div");
|
const tableWrapper = document.createElement("div");
|
||||||
tableWrapper.className = "table-wrapper";
|
tableWrapper.className = "table-wrapper";
|
||||||
@@ -621,6 +701,76 @@
|
|||||||
updateRowYieldProjections(tr);
|
updateRowYieldProjections(tr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function to add deposit/withdrawal entries to the deposit history
|
||||||
|
const addDepositEntry = (depositListDiv, depositData, trackerId) => {
|
||||||
|
const depositDiv = document.createElement("div");
|
||||||
|
depositDiv.style.display = "flex";
|
||||||
|
depositDiv.style.alignItems = "center";
|
||||||
|
depositDiv.style.marginBottom = "5px";
|
||||||
|
depositDiv.style.padding = "5px";
|
||||||
|
depositDiv.style.backgroundColor = "#3a3a3a";
|
||||||
|
depositDiv.style.borderRadius = "3px";
|
||||||
|
|
||||||
|
const dateSpan = document.createElement("span");
|
||||||
|
dateSpan.textContent = depositData.date;
|
||||||
|
dateSpan.style.minWidth = "100px";
|
||||||
|
dateSpan.style.marginRight = "10px";
|
||||||
|
|
||||||
|
const amountSpan = document.createElement("span");
|
||||||
|
const amount = parseFloat(depositData.amount);
|
||||||
|
amountSpan.textContent = amount >= 0 ? `+$${amount.toFixed(2)}` : `-$${Math.abs(amount).toFixed(2)}`;
|
||||||
|
amountSpan.style.minWidth = "80px";
|
||||||
|
amountSpan.style.marginRight = "10px";
|
||||||
|
amountSpan.style.color = amount >= 0 ? "#4CAF50" : "#f44336";
|
||||||
|
amountSpan.style.fontWeight = "bold";
|
||||||
|
|
||||||
|
const notesSpan = document.createElement("span");
|
||||||
|
notesSpan.textContent = depositData.notes || "";
|
||||||
|
notesSpan.style.flex = "1";
|
||||||
|
notesSpan.style.marginRight = "10px";
|
||||||
|
notesSpan.style.fontStyle = "italic";
|
||||||
|
notesSpan.style.color = "#ccc";
|
||||||
|
|
||||||
|
const deleteBtn = document.createElement("button");
|
||||||
|
deleteBtn.textContent = "×";
|
||||||
|
deleteBtn.style.background = "#f44336";
|
||||||
|
deleteBtn.style.color = "white";
|
||||||
|
deleteBtn.style.border = "none";
|
||||||
|
deleteBtn.style.borderRadius = "50%";
|
||||||
|
deleteBtn.style.width = "20px";
|
||||||
|
deleteBtn.style.height = "20px";
|
||||||
|
deleteBtn.style.fontSize = "12px";
|
||||||
|
deleteBtn.style.cursor = "pointer";
|
||||||
|
deleteBtn.title = "Delete deposit";
|
||||||
|
deleteBtn.addEventListener("click", () => {
|
||||||
|
if (confirm("Are you sure you want to delete this deposit record?")) {
|
||||||
|
depositDiv.remove();
|
||||||
|
saveCoinTrackers();
|
||||||
|
updateInvestedChart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
depositDiv.appendChild(dateSpan);
|
||||||
|
depositDiv.appendChild(amountSpan);
|
||||||
|
depositDiv.appendChild(notesSpan);
|
||||||
|
depositDiv.appendChild(deleteBtn);
|
||||||
|
|
||||||
|
// Insert deposits in chronological order
|
||||||
|
let inserted = false;
|
||||||
|
const existingDeposits = depositListDiv.children;
|
||||||
|
for (let i = 0; i < existingDeposits.length; i++) {
|
||||||
|
const existingDate = existingDeposits[i].querySelector('span').textContent;
|
||||||
|
if (depositData.date < existingDate) {
|
||||||
|
depositListDiv.insertBefore(depositDiv, existingDeposits[i]);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inserted) {
|
||||||
|
depositListDiv.appendChild(depositDiv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Enhanced version of addDynamicEntry that supports transaction types
|
// Enhanced version of addDynamicEntry that supports transaction types
|
||||||
const addDynamicEntryWithData = (tbody, rowData, trackerId) => {
|
const addDynamicEntryWithData = (tbody, rowData, trackerId) => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
@@ -790,19 +940,32 @@
|
|||||||
let totalDeposited = {};
|
let totalDeposited = {};
|
||||||
let dailyYieldTotals = {}; // Add this to track daily yields
|
let dailyYieldTotals = {}; // Add this to track daily yields
|
||||||
|
|
||||||
// Calculate total initial capital across all trackers
|
// Collect all deposit data from all trackers
|
||||||
let totalInitialCapital = 0;
|
let allDeposits = {};
|
||||||
Array.from(trackerDivs).forEach(div => {
|
|
||||||
if (div.dataset.showInChart === "false") return;
|
|
||||||
// Get initialCapital value
|
|
||||||
const initialCapitalInput = div.querySelector(".content input[type='number']");
|
|
||||||
if (initialCapitalInput) {
|
|
||||||
totalInitialCapital += parseFloat(initialCapitalInput.value || "0") || 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Array.from(trackerDivs).forEach(div => {
|
Array.from(trackerDivs).forEach(div => {
|
||||||
if (div.dataset.showInChart === "false") return;
|
if (div.dataset.showInChart === "false") return;
|
||||||
|
|
||||||
|
// Collect deposits for this tracker
|
||||||
|
const depositListDiv = div.querySelector(".deposit-list");
|
||||||
|
if (depositListDiv) {
|
||||||
|
Array.from(depositListDiv.children).forEach(depositDiv => {
|
||||||
|
const dateSpan = depositDiv.querySelector('span:nth-child(1)');
|
||||||
|
const amountSpan = depositDiv.querySelector('span:nth-child(2)');
|
||||||
|
|
||||||
|
if (dateSpan && amountSpan) {
|
||||||
|
const date = dateSpan.textContent;
|
||||||
|
const amountText = amountSpan.textContent;
|
||||||
|
const amount = parseFloat(amountText.replace(/[+\-$]/g, '')) * (amountText.startsWith('-') ? -1 : 1);
|
||||||
|
|
||||||
|
if (!allDeposits[date]) {
|
||||||
|
allDeposits[date] = 0;
|
||||||
|
}
|
||||||
|
allDeposits[date] += amount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const table = div.querySelector("table.coin-table");
|
const table = div.querySelector("table.coin-table");
|
||||||
if (table) {
|
if (table) {
|
||||||
const tbody = table.querySelector("tbody");
|
const tbody = table.querySelector("tbody");
|
||||||
@@ -829,9 +992,6 @@
|
|||||||
// Calculate daily yield in dollars and accumulate for each date
|
// Calculate daily yield in dollars and accumulate for each date
|
||||||
const yieldInDollars = currentVal * (dailyYieldPercent / 100);
|
const yieldInDollars = currentVal * (dailyYieldPercent / 100);
|
||||||
dailyYieldTotals[date] = (dailyYieldTotals[date] || 0) + yieldInDollars;
|
dailyYieldTotals[date] = (dailyYieldTotals[date] || 0) + yieldInDollars;
|
||||||
|
|
||||||
// Set totalDeposited to the initial capital for all dates
|
|
||||||
totalDeposited[date] = totalInitialCapital;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (const date in trackerDaily) {
|
for (const date in trackerDaily) {
|
||||||
@@ -840,6 +1000,20 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Calculate cumulative deposits over time
|
||||||
|
const sortedDepositDates = Object.keys(allDeposits).sort();
|
||||||
|
let cumulativeDeposits = 0;
|
||||||
|
const allDates = new Set([...Object.keys(portfolioValues), ...sortedDepositDates]);
|
||||||
|
|
||||||
|
Array.from(allDates).sort().forEach(date => {
|
||||||
|
// Add any deposits that occurred on this date
|
||||||
|
if (allDeposits[date]) {
|
||||||
|
cumulativeDeposits += allDeposits[date];
|
||||||
|
}
|
||||||
|
// Set total deposited for this date to the cumulative amount
|
||||||
|
totalDeposited[date] = cumulativeDeposits;
|
||||||
|
});
|
||||||
|
|
||||||
// Update daily yield history with new calculations
|
// Update daily yield history with new calculations
|
||||||
for (const date in dailyYieldTotals) {
|
for (const date in dailyYieldTotals) {
|
||||||
dailyYieldHistory[date] = dailyYieldTotals[date];
|
dailyYieldHistory[date] = dailyYieldTotals[date];
|
||||||
@@ -861,7 +1035,7 @@
|
|||||||
let labels = dates.map(date => new Date(date).toLocaleDateString());
|
let labels = dates.map(date => new Date(date).toLocaleDateString());
|
||||||
let dailyFeesData = dates.map(date => dailyYieldHistory[date] || 0);
|
let dailyFeesData = dates.map(date => dailyYieldHistory[date] || 0);
|
||||||
let portfolioData = dates.map(date => portfolioValues[date] || persistedHistory[date]);
|
let portfolioData = dates.map(date => portfolioValues[date] || persistedHistory[date]);
|
||||||
let depositedData = dates.map(date => totalDeposited[date] || persistedHistory[date] * 0.9);
|
let depositedData = dates.map(date => totalDeposited[date] || 0);
|
||||||
|
|
||||||
// Update or create the chart
|
// Update or create the chart
|
||||||
if (investedChart) {
|
if (investedChart) {
|
||||||
@@ -1155,6 +1329,26 @@
|
|||||||
const initialCapitalInput = div.querySelector(".content input[type='number']");
|
const initialCapitalInput = div.querySelector(".content input[type='number']");
|
||||||
const initialCapital = initialCapitalInput ? initialCapitalInput.value : "0.00";
|
const initialCapital = initialCapitalInput ? initialCapitalInput.value : "0.00";
|
||||||
|
|
||||||
|
// Get deposit history
|
||||||
|
const depositListDiv = div.querySelector(".deposit-list");
|
||||||
|
const deposits = [];
|
||||||
|
if (depositListDiv) {
|
||||||
|
Array.from(depositListDiv.children).forEach(depositDiv => {
|
||||||
|
const dateSpan = depositDiv.querySelector('span:nth-child(1)');
|
||||||
|
const amountSpan = depositDiv.querySelector('span:nth-child(2)');
|
||||||
|
const notesSpan = depositDiv.querySelector('span:nth-child(3)');
|
||||||
|
|
||||||
|
if (dateSpan && amountSpan) {
|
||||||
|
const date = dateSpan.textContent;
|
||||||
|
const amountText = amountSpan.textContent;
|
||||||
|
// Parse amount from "+$123.45" or "-$123.45" format
|
||||||
|
const amount = parseFloat(amountText.replace(/[+\-$]/g, '')) * (amountText.startsWith('-') ? -1 : 1);
|
||||||
|
const notes = notesSpan ? notesSpan.textContent : "";
|
||||||
|
deposits.push({ date, amount, notes });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const table = div.querySelector("table.coin-table");
|
const table = div.querySelector("table.coin-table");
|
||||||
const tbody = table.querySelector("tbody");
|
const tbody = table.querySelector("tbody");
|
||||||
const rows = [];
|
const rows = [];
|
||||||
@@ -1171,8 +1365,8 @@
|
|||||||
rows.push({ date, time, currentValue, income, compound, dailyYield, notes, created });
|
rows.push({ date, time, currentValue, income, compound, dailyYield, notes, created });
|
||||||
});
|
});
|
||||||
const hasData = rows.some(row => row.currentValue !== 0 || row.income !== 0 || row.dailyYield !== 0);
|
const hasData = rows.some(row => row.currentValue !== 0 || row.income !== 0 || row.dailyYield !== 0);
|
||||||
if (hasData) {
|
if (hasData || deposits.length > 0) {
|
||||||
trackers.push({ id: trackerId, coinName, platform, initialCapital, rows, showInChart, active });
|
trackers.push({ id: trackerId, coinName, platform, initialCapital, deposits, rows, showInChart, active });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
updateCoinTrackersToBackend(trackers);
|
updateCoinTrackersToBackend(trackers);
|
||||||
|
|||||||
Reference in New Issue
Block a user