diff options
Diffstat (limited to 'assets/js/app.js')
| -rw-r--r-- | assets/js/app.js | 254 |
1 files changed, 92 insertions, 162 deletions
diff --git a/assets/js/app.js b/assets/js/app.js index ec873d4..de6d886 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,171 +1,101 @@ +"use strict"; function formatMoney(num) { - return parseFloat(num).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,"); + return num.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,"); +} +function getInputValue(id) { + const el = document.getElementById(id); + return el ? parseFloat(el.value) : NaN; +} +function getThemeColors() { + const dark = window.matchMedia("(prefers-color-scheme: dark)").matches; + return dark + ? { bgColor: "#1a1d27", fgColor: "#e8eaf0", gridColor: "#2d3141", lineColor: "#3d4258" } + : { bgColor: "#ffffff", fgColor: "#1a1d23", gridColor: "#e2e6ea", lineColor: "#c8cdd5" }; +} +function buildAxisLayout(title, colors) { + const { bgColor, fgColor, gridColor, lineColor } = colors; + return { + title: { text: title, font: { color: fgColor, size: 13 } }, + paper_bgcolor: bgColor, + plot_bgcolor: bgColor, + font: { color: fgColor, family: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }, + margin: { t: 40, r: 16, b: 48, l: 60 }, + yaxis: { gridcolor: gridColor, zerolinecolor: gridColor, linecolor: lineColor }, + xaxis: { title: { text: "Months", font: { size: 11 } }, gridcolor: gridColor, zerolinecolor: gridColor, linecolor: lineColor }, + }; } - class Data { - constructor(balance, contribution, returnRate, inflationRate, years, money) { - this.balance = balance; - this.contribution = contribution; - this.returnRate = returnRate; - this.inflationRate = inflationRate; - this.years = years; - this.money = money; - } - summary() { - const summaryRow = "<tr> <td>$ " + formatMoney(this.balance) + "</td> <td>$ " + formatMoney(this.contribution) + "</td> <td>" + (this.returnRate).toFixed(2) + " %</td> <td>" + (this.inflationRate).toFixed(2) + " %</td> </tr>"; - return summaryRow; - } - adjustedRate() { - const adjustedRate = ((1 + (this.returnRate / 100)) / (1 + (this.inflationRate / 100)) - 1); - return adjustedRate; - } - graphData(monthsArr, balanceArr, interestArr) { - let matched = window.matchMedia('(prefers-color-scheme: dark)').matches; - if(matched) { - var bgColor = "#202020"; - var fgColor = "#ffffff"; - var gridColor = "#3b3b3b"; - var lineColor = "#5b5b5b"; - } else { - var bgColor = "#ffffff"; - var fgColor = "#222222"; - var gridColor = "#c8c8c8"; - var lineColor = "#555555"; + constructor(balance, contribution, returnRate, inflationRate) { + this.balance = balance; + this.contribution = contribution; + this.returnRate = returnRate; + this.inflationRate = inflationRate; + } + summaryRow() { + return `<tr> + <td>$${formatMoney(this.balance)}</td> + <td>$${formatMoney(this.contribution)}</td> + <td>${this.returnRate.toFixed(2)}%</td> + <td>${this.inflationRate.toFixed(2)}%</td> + </tr>`; + } + adjustedRate() { + return (1 + this.returnRate / 100) / (1 + this.inflationRate / 100) - 1; } - - const balanceTrace = { - x: monthsArr, - y: balanceArr, - type: "scatter", - line: { - color: "#24ab48", - width: 2 - } - }; - const interestTrace = { - x: monthsArr, - y: interestArr, - type: "scatter", - line: { - color: "#0062ff", - width: 2 - } - }; - const balanceTitle = { - title: { - text: "Total Balance", - color: fgColor - }, - paper_bgcolor: bgColor, - plot_bgcolor: bgColor, - font: { - color: fgColor - }, - yaxis: { - title: "Total Balance", - gridcolor: gridColor, - zerolinecolor: gridColor, - linecolor: lineColor - }, - xaxis: { - title: "Months", - gridcolor: gridColor, - zerolinecolor: gridColor, - linecolor: lineColor - } - }; - const interestTitle = { - title: { - text: "Accrued Interest", - color: fgColor - }, - paper_bgcolor: bgColor, - plot_bgcolor: bgColor, - font: { - color: fgColor - }, - yaxis: { - title: "Accrued Interest", - gridcolor: gridColor, - zerolinecolor: gridColor, - linecolor: lineColor - }, - xaxis: { - title: "Months", - gridcolor: gridColor, - zerolinecolor: gridColor, - linecolor: lineColor - } - }; - return [balanceTrace, interestTrace, balanceTitle, interestTitle]; - } } - -function retirementYears() { - $("tbody").empty(); - const myData = new Data(parseFloat($("#begBalance").val()), parseFloat($("#monthlyContr").val()), parseFloat($("#returnRate").val()), parseFloat($("#inflationRate").val()), parseFloat($("#years").val()), null); - const summary = myData.summary(); - $(".infoTable tbody").append(summary); - $(".infoTable, .resultsTable, #infoTableHeading, #resultsTableHeading").show(); - var interestArr = [], - balanceArr = [], - monthsArr = []; - const adjustedRate = myData.adjustedRate(); - const monthlyContr = myData.contribution; - let pVal = myData.balance; - for (var i = 0; i < (myData.years * 12); i++) { - var interest = (pVal * (adjustedRate / 12)).toFixed(2); - var newBalance = (parseFloat(pVal) + parseFloat(interest) + monthlyContr).toFixed(2); - $(".resultsTable tbody").append("<tr> <td>" + (i + 1) + "</td> <td>$ " + formatMoney(interest) + "</td> <td>$ " + formatMoney(monthlyContr) + "</td> <td>$ " + formatMoney(newBalance) + "</td> </tr>"); - if ((i + 1) === 1 || (i + 1) % 12 === 0 || i === (myData.years * 12)) { - interestArr.push(parseInt(interest * 100) / 100); - balanceArr.push(parseInt(newBalance * 100) / 100); - monthsArr.push(i + 1); +function showResults(data, monthsArr, balanceArr, interestArr) { + const infoTbody = document.querySelector("#infoTable tbody"); + infoTbody.innerHTML = data.summaryRow(); + document.querySelectorAll(".table-section").forEach((el) => (el.style.display = "block")); + const graphCard = document.getElementById("graphCard"); + graphCard.style.display = "block"; + const colors = getThemeColors(); + const balanceTrace = { x: monthsArr, y: balanceArr, type: "scatter", line: { color: "#4f6ef7", width: 2 } }; + const interestTrace = { x: monthsArr, y: interestArr, type: "scatter", line: { color: "#22c55e", width: 2 } }; + Plotly.newPlot("balChartContainer", [balanceTrace], buildAxisLayout("Total Balance", colors), { responsive: true }); + Plotly.newPlot("intChartContainer", [interestTrace], buildAxisLayout("Accrued Interest", colors), { responsive: true }); +} +function runCalculation(data, stopCondition) { + document.querySelectorAll("tbody").forEach((el) => (el.innerHTML = "")); + const resultsTbody = document.querySelector(".resultsTable tbody"); + const adjustedRate = data.adjustedRate(); + const monthlyContr = data.contribution; + const monthsArr = []; + const balanceArr = []; + const interestArr = []; + let pVal = data.balance; + let i = 0; + while (!stopCondition(pVal, i)) { + const month = i + 1; + const interest = pVal * (adjustedRate / 12); + const newBalance = pVal + interest + monthlyContr; + const row = document.createElement("tr"); + row.innerHTML = `<td>${month}</td><td>$${formatMoney(interest)}</td><td>$${formatMoney(monthlyContr)}</td><td>$${formatMoney(newBalance)}</td>`; + resultsTbody.appendChild(row); + if (month === 1 || month % 12 === 0) { + interestArr.push(Math.round(interest * 100) / 100); + balanceArr.push(Math.round(newBalance * 100) / 100); + monthsArr.push(month); + } + pVal = newBalance; + i++; } - pVal = newBalance; - } - $(".graphCard").css("display", "block"); - const graphData = myData.graphData(monthsArr, balanceArr, interestArr); - Plotly.newPlot("balChartContainer", [graphData[0]], graphData[2], { - responsive: true - }); - Plotly.newPlot("intChartContainer", [graphData[1]], graphData[3], { - responsive: true - }); + showResults(data, monthsArr, balanceArr, interestArr); +} +function retirementYears() { + var _a; + const data = new Data(getInputValue("begBalance"), getInputValue("monthlyContr"), getInputValue("returnRate"), getInputValue("inflationRate")); + const years = getInputValue("years"); + const totalMonths = years * 12; + runCalculation(data, (_bal, i) => i >= totalMonths); + (_a = document.getElementById("graphCard")) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" }); } - function retirementMoney() { - $("tbody").empty(); - const myData = new Data(parseFloat($("#begBalance").val()), parseFloat($("#monthlyContr").val()), parseFloat($("#returnRate").val()), parseFloat($("#inflationRate").val()), null, parseFloat($("#money").val())); - const summary = myData.summary(); - $(".infoTable tbody").append(summary); - $(".infoTable, .resultsTable, #infoTableHeading, #resultsTableHeading").show(); - var interestArr = [], - balanceArr = [], - monthsArr = []; - const adjustedRate = myData.adjustedRate(); - const monthlyContr = myData.contribution; - let pVal = myData.balance; - let money = myData.money; - for (var i = 0; pVal < money; i++) { - var months = i + 1; - var newBalance = (pVal + (pVal * (adjustedRate / 12)) + monthlyContr); - $(".resultsTable tbody").append("<tr> <td>" + months + "</td> <td>$ " + formatMoney((pVal * (adjustedRate / 12))) + "</td> <td>$ " + formatMoney(monthlyContr) + "</td> <td>$ " + formatMoney(newBalance) + "</td> </tr>"); - var roundedInterest = parseInt((pVal * (adjustedRate / 12)) * 100) / 100; - var roundedBalance = parseInt(newBalance * 100) / 100; - if (months === 1 || months % 12 === 0 || pVal > money) { - interestArr.push(roundedInterest); - balanceArr.push(roundedBalance); - monthsArr.push(months); - } - pVal = newBalance; - } - $(".graphCard").css("display", "block"); - const graphData = myData.graphData(monthsArr, balanceArr, interestArr); - Plotly.newPlot("balChartContainer", [graphData[0]], graphData[2], { - responsive: true - }); - Plotly.newPlot("intChartContainer", [graphData[1]], graphData[3], { - responsive: true - }); + var _a; + const data = new Data(getInputValue("begBalance"), getInputValue("monthlyContr"), getInputValue("returnRate"), getInputValue("inflationRate")); + const target = getInputValue("money"); + runCalculation(data, (bal) => bal >= target); + (_a = document.getElementById("graphCard")) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: "smooth" }); } +window.retirementYears = retirementYears; +window.retirementMoney = retirementMoney; |
