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:
root
2025-09-19 15:01:23 +02:00
parent f90f6ed618
commit 7ec360c5d1

View File

@@ -424,6 +424,86 @@
initialCapitalDiv.appendChild(initialCapitalInput);
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
const tableWrapper = document.createElement("div");
tableWrapper.className = "table-wrapper";
@@ -621,6 +701,76 @@
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
const addDynamicEntryWithData = (tbody, rowData, trackerId) => {
const tr = document.createElement("tr");
@@ -790,19 +940,32 @@
let totalDeposited = {};
let dailyYieldTotals = {}; // Add this to track daily yields
// Calculate total initial capital across all trackers
let totalInitialCapital = 0;
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;
}
});
// Collect all deposit data from all trackers
let allDeposits = {};
Array.from(trackerDivs).forEach(div => {
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");
if (table) {
const tbody = table.querySelector("tbody");
@@ -829,9 +992,6 @@
// Calculate daily yield in dollars and accumulate for each date
const yieldInDollars = currentVal * (dailyYieldPercent / 100);
dailyYieldTotals[date] = (dailyYieldTotals[date] || 0) + yieldInDollars;
// Set totalDeposited to the initial capital for all dates
totalDeposited[date] = totalInitialCapital;
}
});
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
for (const date in dailyYieldTotals) {
dailyYieldHistory[date] = dailyYieldTotals[date];
@@ -861,7 +1035,7 @@
let labels = dates.map(date => new Date(date).toLocaleDateString());
let dailyFeesData = dates.map(date => dailyYieldHistory[date] || 0);
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
if (investedChart) {
@@ -1155,6 +1329,26 @@
const initialCapitalInput = div.querySelector(".content input[type='number']");
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 tbody = table.querySelector("tbody");
const rows = [];
@@ -1171,8 +1365,8 @@
rows.push({ date, time, currentValue, income, compound, dailyYield, notes, created });
});
const hasData = rows.some(row => row.currentValue !== 0 || row.income !== 0 || row.dailyYield !== 0);
if (hasData) {
trackers.push({ id: trackerId, coinName, platform, initialCapital, rows, showInChart, active });
if (hasData || deposits.length > 0) {
trackers.push({ id: trackerId, coinName, platform, initialCapital, deposits, rows, showInChart, active });
}
});
updateCoinTrackersToBackend(trackers);