Compare commits

...
10 changed files with 230 additions and 97 deletions

No files matched your search

+36
View File
@@ -31,4 +31,40 @@ factSubmitButton.addEventListener("click", () => {
console.error("Error fetching fact:", error); console.error("Error fetching fact:", error);
Swal.fire('Error fetching fact', 'Please try again later', 'error'); Swal.fire('Error fetching fact', 'Please try again later', 'error');
}); });
});
document.addEventListener("DOMContentLoaded", () => {
const drawer = document.getElementById("drawer-navigation");
const toggle = document.getElementById("drawer-toggle");
function openDrawer() {
drawer.classList.remove("translate-x-full");
// Add backdrop
const backdrop = document.createElement("div");
backdrop.className = "fixed inset-0 bg-gray-900/50 z-30 drawer-backdrop";
document.body.appendChild(backdrop);
// Optional: prevent scroll
document.body.classList.add("overflow-hidden");
backdrop.addEventListener("click", closeDrawer);
}
function closeDrawer() {
drawer.classList.add("translate-x-full");
const backdrop = document.querySelector(".drawer-backdrop");
if (backdrop) backdrop.remove();
document.body.classList.remove("overflow-hidden");
}
toggle.addEventListener("click", openDrawer);
// Also close on X button if needed
const closeBtn = drawer.querySelector("[data-drawer-hide]");
if (closeBtn) {
closeBtn.addEventListener("click", closeDrawer);
}
}); });
+16 -7
View File
@@ -1,5 +1,5 @@
const getRates = require("../util/exchangeRate"); const getRates = require("../util/exchangeRate");
const { calculatePlanProgress, updatePlanProgressInDB } = require("../util/calculations"); const { calculateProgress, updatePlanProgressInDB } = require("../util/calculations");
const suggestions = require("../util/suggestions"); const suggestions = require("../util/suggestions");
const status = require("../util/statuses"); const status = require("../util/statuses");
const ObjectId = require('mongodb').ObjectId; const ObjectId = require('mongodb').ObjectId;
@@ -86,13 +86,19 @@ module.exports = (middleware, users, plans, assets) => {
toCurrencyRates: [], toCurrencyRates: [],
}; };
} }
let planSchema = await plans.find({ userId: new ObjectId(user) }).project({ const userPlansFromDB = await plans.find({ userId: new ObjectId(req.session.userId) }).toArray();
name: 1, retirementAssets: 1, progress: 1, _id: 1,
}).toArray(); for (const plan of userPlansFromDB) {
const progress = await calculateProgress(plan, assets, users, req.session.user._id);
await updatePlanProgressInDB(plan._id, progress.percentage, plans);
}
const updatedUserPlans = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray();
res.render('dashboard', { res.render('dashboard', {
user: req.session.user, user: req.session.user,
geoData: req.session.geoData, geoData: req.session.geoData,
plans: planSchema, }); plans: updatedUserPlans, });
return res.status(status.Ok); return res.status(status.Ok);
}); });
@@ -114,8 +120,8 @@ module.exports = (middleware, users, plans, assets) => {
const userPlansFromDB = await plans.find({ userId: new ObjectId(req.session.userId) }).toArray(); const userPlansFromDB = await plans.find({ userId: new ObjectId(req.session.userId) }).toArray();
for (const plan of userPlansFromDB) { for (const plan of userPlansFromDB) {
const percentage = await calculatePlanProgress(plan, assets, req.session.user._id); const progress = await calculateProgress(plan, assets, users, req.session.user._id);
await updatePlanProgressInDB(plan._id, percentage, plans); await updatePlanProgressInDB(plan._id, progress.percentage, plans);
} }
const updatedUserPlans = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray(); const updatedUserPlans = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray();
@@ -151,12 +157,15 @@ module.exports = (middleware, users, plans, assets) => {
req.session.errMessage = "Plan not found or you do not have permission to view it."; req.session.errMessage = "Plan not found or you do not have permission to view it.";
return res.status(status.NotFound).redirect('/plans'); return res.status(status.NotFound).redirect('/plans');
} }
const progress = await calculateProgress(plan, assets, users, req.session.userId);
res.render('planDetail', { res.render('planDetail', {
user: req.session.user, user: req.session.user,
plan: plan, plan: plan,
geoData: req.session.geoData, geoData: req.session.geoData,
assets: userAssets, assets: userAssets,
progress: progress,
suggestions: await suggestions.generateSuggestions(), suggestions: await suggestions.generateSuggestions(),
}); });
+69 -1
View File
@@ -42,4 +42,72 @@ async function updatePlanProgressInDB(planId, percentage, plans) {
} }
} }
module.exports = { calculatePlanProgress, updatePlanProgressInDB };
async function calculateProgress(plan, assets, users, userId) {
if (!plan || typeof plan !== 'object') {
console.error("Error with the plan");
return;
}
if (!assets || typeof assets.find !== 'function') {
console.error("Error with the assets collection");
return;
}
if (!users || typeof users.findOne !== 'function') {
console.error("Error with the users collection");
return;
}
if (!userId) {
console.error("No user ID provided");
return;
}
try {
const userAssets = await assets.find({ userId: new ObjectId(userId) }).toArray();
const totalUserAssetValue = userAssets.reduce((total, asset) => total + asset.value, 0);
const totalUserPlanValue = plan.retirementAssets;
const userDoc = await users.findOne({ _id: new ObjectId(userId) });
if (!userDoc || !userDoc.dob) {
console.error("User document or DOB not found for userId:", userId);
return { monthlyInvestment: NaN, totalCostOfRetirement: NaN, monthsUntilRetirement: NaN, yearsRetired: NaN, percentage: NaN };
}
const userDob = new Date(userDoc.dob);
if (isNaN(userDob.getTime())) {
console.error("userDob is an invalid date. Aborting calculation.");
return { monthlyInvestment: NaN, totalCostOfRetirement: NaN, monthsUntilRetirement: NaN, yearsRetired: NaN, percentage: NaN };
}
const today = new Date();
const userUnalivedBy = new Date(userDob);
userUnalivedBy.setFullYear(userUnalivedBy.getFullYear() + 90);
const yearOfRetirement = userDob.getFullYear() + plan.retirementAge;
const monthsUntilRetirement = (yearOfRetirement - today.getFullYear()) * 12;
const yearsRetired = userUnalivedBy.getFullYear() - yearOfRetirement;
const totalCostOfRetirement = ((plan.retirementExpenses + plan.retirementLiabilities) * 12) * yearsRetired;
const monthlyInvestment = (totalUserPlanValue - totalUserAssetValue + totalCostOfRetirement) / monthsUntilRetirement;
const percentageCalculated = (totalUserAssetValue / (totalUserPlanValue + totalCostOfRetirement)) * 100;
const progress = {};
progress.monthlyInvestment = Math.round(monthlyInvestment);
progress.totalCostOfRetirement = totalCostOfRetirement;
progress.monthsUntilRetirement = monthsUntilRetirement;
progress.yearsRetired = yearsRetired;
progress.yearsUntilRetirement = (yearOfRetirement - today.getFullYear());
progress.percentage = percentageCalculated;
return progress;
} catch (err) {
console.error("Error in calculateProgress:", err);
return;
}
}
module.exports = { calculatePlanProgress, updatePlanProgressInDB, calculateProgress};
+23 -23
View File
@@ -1,27 +1,27 @@
<%- include("./partials/fileHeader") %> <%- include("./partials/fileHeader") %>
<%- include("./partials/header") %> <%- include("./partials/header") %>
<main class="pt-24 pb-14"> <main class="pt-24 pb-14">
<!-- Buttons --> <!-- Buttons -->
<div class="flex justify-end gap-4 px-5 py-6"> <div class="flex justify-end gap-4 px-5 py-6">
<a href="/assets?popup" <a href="/assets?popup"
class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800" class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800"
type="button">Add Asset</a> type="button">Add Asset</a>
<a href="/newPlan" <a href="/newPlan"
class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800" class="text-center cursor-pointer min-w-[150px] bg-green-600 px-4 py-2 text-white rounded hover:bg-green-800"
type="button">Create new plan </a> type="button">Create new plan </a>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center sm:px-6 w-full ">
<div class="mb-12 grid gap-y-4 gap-x-6 md:grid-cols-2 mr-3 ml-3 "> <div class="mb-12 grid gap-y-4 gap-x-6 md:grid-cols-2 ">
<% plans.forEach(plan=> { %> <% plans.forEach(plan=> { %>
<%- include('./partials/dashboardBox.ejs', {plan: plan}) %> <%- include('./partials/dashboardBox.ejs', {plan: plan}) %>
<% }); %> <% }); %>
</div> </div>
</div> </div>
</main> </main>
<script src="/static/scripts/dollarFormat.js"></script> <script src="/static/scripts/dollarFormat.js"></script>
<%- include("./partials/navBar") %> <%- include("./partials/navBar") %>
<%- include("./partials/scriptLoader") %> <%- include("./partials/scriptLoader") %>
<%- include("./partials/footer") %> <%- include("./partials/footer") %>
-1
View File
@@ -2,7 +2,6 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<main class="container mx-auto p-4 pt-30"> <main class="container mx-auto p-4 pt-30">
<h2 class="text-xl text-white font-semibold mb-4">Welcome: <%= user.name %></h2>
<div class="max-w-md mx-auto bg-white p-8 mb-10 rounded-lg shadow-md"> <div class="max-w-md mx-auto bg-white p-8 mb-10 rounded-lg shadow-md">
<h3 class="text-lg font-medium mb-6">Retirement Plan</h3> <h3 class="text-lg font-medium mb-6">Retirement Plan</h3>
<form action="/newPlan" method="post" class="space-y-4"> <form action="/newPlan" method="post" class="space-y-4">
+6 -5
View File
@@ -1,6 +1,6 @@
<div class="mt-12"> <div class="mt-12">
<a href="/plans/<%=plan._id%>"> <a href="/plans/<%=plan._id%>">
<div class=" relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md w-full min-h-[150px] min-w-[380px] <div class=" relative flex flex-col bg-clip-border rounded-xl bg-white text-gray-700 shadow-md w-full min-h-[130px] min-w-[340px]
md:min-h-[220px] md:min-w-[500px]"> md:min-h-[220px] md:min-w-[500px]">
<!-- <div --> <!-- <div -->
<!-- class="bg-clip-border mx-4 rounded-xl overflow-hidden bg-gradient-to-tr from-blue-600 to-blue-400 text-white shadow-blue-500/40 shadow-lg absolute mt-2 grid h-16 w-16 place-items-center"> --> <!-- class="bg-clip-border mx-4 rounded-xl overflow-hidden bg-gradient-to-tr from-blue-600 to-blue-400 text-white shadow-blue-500/40 shadow-lg absolute mt-2 grid h-16 w-16 place-items-center"> -->
@@ -15,17 +15,18 @@
</h4> </h4>
</div> </div>
<div class="border-t border-blue-gray-50 p-10"> <div class="border-t border-blue-gray-50 p-10">
<p class=" antialiased font-sans text-base leading-relaxed font-normal text-blue-gray-600">
<div class="float-left font-bold"> <div class="float-left font-bold">
Status bar: <p class=" antialiased font-sans text-base leading-relaxed font-normal text-blue-gray-600">
Status bar:
</p>
</div> </div>
<strong class="text-green-500"> <strong class="text-green-500">
<div class="float-right mb-2"> <div class="float-right mb-2">
<%=Math.floor(plan.progress) %>% complete <%=Math.floor(plan.progress) > 100 ? 100 : Math.floor(plan.progress) %>% complete
</div> </div>
<div class="bg-gray-400 mt-7"> <div class="bg-gray-400 mt-7">
<div class="bg-green-600 h-2.5 rounded-full dark:bg-green-500" <div class="bg-green-600 h-2.5 rounded-full dark:bg-green-500"
style="width: <%= plan.progress %>%;"></div> style="width: <%= plan.progress > 100 ? 100 : plan.progress %>%;"></div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -13,7 +13,7 @@
</div> </div>
<footer class="mt-16"> <footer class="mt-16">
<div class="fixed bottom-0 left-0 z-50 hidden lg:block w-full h-16 bg-gray-800 border-t border-gray-700 p-6 text-center text-gray-400 text-sm"> <div class="fixed bottom-0 left-0 z-30 hidden lg:block w-full h-16 bg-gray-800 border-t border-gray-700 p-6 text-center text-gray-400 text-sm">
<div class="container mx-auto"> <div class="container mx-auto">
<p>&copy; 2025 RCalculator. All rights reserved.</p> <p>&copy; 2025 RCalculator. All rights reserved.</p>
</div> </div>
+45 -45
View File
@@ -1,6 +1,6 @@
<header> <header>
<nav class="fixed bg-gray-800 border-b border-gray-700 z-50"> <nav class="fixed bg-gray-800 border-b border-gray-700 z-30">
<div class="w-screen flex flex-col md:flex-row flex-wrap items-center justify-between p-3"> <div class="w-screen flex flex-col md:flex-row flex-wrap items-center justify-between p-3">
<a href="/home" class="flex xl:w-[320px] items-center space-x-3 rtl:space-x-reverse"> <a href="/home" class="flex xl:w-[320px] items-center space-x-3 rtl:space-x-reverse">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" /> <img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" />
@@ -86,7 +86,7 @@
</li> </li>
<li> <li>
<div class="text-center"> <div class="text-center">
<button class="flex flex-row text-white hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm focus:outline-none dark:focus:ring-blue-800" type="button" data-drawer-target="drawer-navigation" data-drawer-show="drawer-navigation" data-drawer-placement="right" aria-controls="drawer-navigation"> <button class="flex flex-row text-white hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm focus:outline-none dark:focus:ring-blue-800" type="button" id="drawer-toggle" data-drawer-target="drawer-navigation" data-drawer-placement="right" aria-controls="drawer-navigation">
<svg class="w-[30px] h-[30px] text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24"> <svg class="w-[30px] h-[30px] text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/> <path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
</svg> </svg>
@@ -101,49 +101,49 @@
</div> </div>
<!-- drawer component --> <!-- drawer component -->
<div id="drawer-navigation" class="hidden lg:block fixed top-0 right-0 z-40 w-64 h-screen p-4 overflow-y-auto transition-transform translate-x-full bg-white dark:bg-gray-800" tabindex="-1" aria-labelledby="drawer-navigation-label">
<h5 id="drawer-navigation-label" class="text-base font-semibold text-gray-500 uppercase dark:text-gray-400"><%= user.name %></h5>
<button type="button" data-drawer-hide="drawer-navigation" aria-controls="drawer-navigation" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 end-2.5 inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" >
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
<span class="sr-only">Close menu</span>
</button>
<div class="">
<div class="w-full pt-5 pl-2.5">
<a href="/logout" class="text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-20 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Logout
</a>
</div>
</div>
<div class="py-4 overflow-y-auto">
<ul class="space-y-2 font-medium">
<li>
<a href="/profile" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 22 21">
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
</svg>
<span class="ms-3">Profile</span>
</a>
</li>
<li>
<a href="/settings" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 22 21">
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
</svg>
<span class="ms-3">Settings</span>
</a>
</li>
<li>
<a href="aboutUs" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
</svg>
<span class="flex-1 ms-3 whitespace-nowrap">About Us</span>
</a>
</li>
</ul>
</div>
</div>
</div> </div>
</nav> </nav>
<div id="drawer-navigation" class="lg:block fixed top-0 right-0 z-60 w-64 h-screen p-4 overflow-y-auto transition-transform translate-x-full bg-white dark:bg-gray-800" tabindex="-1" aria-labelledby="drawer-navigation-label">
<h5 id="drawer-navigation-label" class="text-base font-semibold text-gray-500 uppercase dark:text-gray-400"><%= user.name %></h5>
<button type="button" data-drawer-hide="drawer-navigation" aria-controls="drawer-navigation" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 end-2.5 inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" >
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
<span class="sr-only">Close menu</span>
</button>
<div class="">
<div class="w-full pt-5 pl-2.5">
<a href="/logout" class="text-white bg-blue-700 hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 font-medium rounded-full text-sm px-20 py-2.5 text-center me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Logout
</a>
</div>
</div>
<div class="py-4 overflow-y-auto">
<ul class="space-y-2 font-medium">
<li>
<a href="/profile" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 22 21">
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
</svg>
<span class="ms-3">Profile</span>
</a>
</li>
<li>
<a href="/settings" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 22 21">
<path d="M16.975 11H10V4.025a1 1 0 0 0-1.066-.998 8.5 8.5 0 1 0 9.039 9.039.999.999 0 0 0-1-1.066h.002Z"/>
<path d="M12.5 0c-.157 0-.311.01-.565.027A1 1 0 0 0 11 1.02V10h8.975a1 1 0 0 0 1-.935c.013-.188.028-.374.028-.565A8.51 8.51 0 0 0 12.5 0Z"/>
</svg>
<span class="ms-3">Settings</span>
</a>
</li>
<li>
<a href="aboutUs" class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group">
<svg class="shrink-0 w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
<path d="M14 2a3.963 3.963 0 0 0-1.4.267 6.439 6.439 0 0 1-1.331 6.638A4 4 0 1 0 14 2Zm1 9h-1.264A6.957 6.957 0 0 1 15 15v2a2.97 2.97 0 0 1-.184 1H19a1 1 0 0 0 1-1v-1a5.006 5.006 0 0 0-5-5ZM6.5 9a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9ZM8 10H5a5.006 5.006 0 0 0-5 5v2a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-2a5.006 5.006 0 0 0-5-5Z"/>
</svg>
<span class="flex-1 ms-3 whitespace-nowrap">About Us</span>
</a>
</li>
</ul>
</div>
</div>
</header> </header>
+33 -12
View File
@@ -1,10 +1,8 @@
<%- include("./partials/fileHeader") %> <%- include("./partials/fileHeader") %>
<%- include("./partials/header") %> <%- include("./partials/header") %>
<main class="container mx-auto p-4"> <main class="container mx-auto p-4 pt-28">
<h2 class="text-xl text-white font-semibold mb-4">Welcome: <%= user.name %></h2>
<div class="max-w-lg mx-auto bg-white p-6 sm:p-8 rounded-xl shadow-lg space-y-6 dark:bg-gray-800"> <div class="max-w-lg mx-auto bg-white p-6 sm:p-8 rounded-xl shadow-lg space-y-6 dark:bg-gray-800">
<div class="text-center"> <div class="text-center">
<h3 class="text-2xl font-bold text-gray-800 dark:text-white"><%= plan.name %></h3> <h3 class="text-2xl font-bold text-gray-800 dark:text-white"><%= plan.name %></h3>
</div> </div>
@@ -12,10 +10,17 @@
<div> <div>
<div class="flex justify-between mb-1"> <div class="flex justify-between mb-1">
<span class="text-sm font-medium text-blue-700 dark:text-blue-400">Progress</span> <span class="text-sm font-medium text-blue-700 dark:text-blue-400">Progress</span>
<span class="text-sm font-medium text-blue-700 dark:text-blue-400"><%= plan.progress %>%</span> <span class="text-sm font-medium text-blue-700 dark:text-blue-400"><%-plan.progress > 100 ? 100 : plan.progress%>%</span>
</div> </div>
<div class="w-full bg-gray-200 rounded-full h-3 dark:bg-gray-700"> <div class="w-full bg-gray-200 rounded-full h-3 dark:bg-gray-700">
<div class="bg-blue-600 h-3 rounded-full dark:bg-blue-500" style="width: <%= plan.progress %>%;"></div> <div class="bg-blue-600 h-3 rounded-full dark:bg-blue-500" style="width: <%= plan.progress > 100 ? 100 : plan.progress %>%;"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-blue-700 dark:text-blue-400">Calculated Monthly Investment</span>
<span class="text-sm font-medium text-blue-700 dark:text-blue-400"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.monthlyInvestment > 0 ? progress.monthlyInvestment : 0) %></span>
</div> </div>
</div> </div>
@@ -29,7 +34,7 @@
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Total Value:</label> <label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Total Value:</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%= assets.reduce((total, asset) => total + asset.value, 0) %></p> <p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(assets.reduce((total, asset) => total + asset.value, 0)) %></p>
</div> </div>
</div> </div>
</div> </div>
@@ -43,16 +48,32 @@
<p class="mt-1 text-md text-gray-900 dark:text-white"><%= plan.retirementAge %></p> <p class="mt-1 text-md text-gray-900 dark:text-white"><%= plan.retirementAge %></p>
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Monthly Expenses:</label> <label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Monthly Expenses (Minus Liabilities):</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%= plan.retirementExpenses %></p> <p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementExpenses) %></p>
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Retirement Assets:</label> <label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Retirement Assets (Total):</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%= plan.retirementAssets %></p> <p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementAssets) %></p>
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Retirement Liabilities:</label> <label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Target Retirement Liabilities (Monthly):</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%= plan.retirementLiabilities %></p> <p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(plan.retirementLiabilities) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Years Until Retirement:</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%- progress.yearsUntilRetirement %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Years Retired:</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%- progress.yearsRetired %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Total Cost of Retirement:</label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.totalCostOfRetirement) %></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-600 dark:text-gray-400">Total amount needed (Assets + Retirement Expenses): </label>
<p class="mt-1 text-md text-gray-900 dark:text-white"><%- new Intl.NumberFormat('en-US', { style: 'currency', currency: geoData.currency ? geoData.currency : 'CAD' }).format(progress.totalCostOfRetirement + progress.monthlyInvestment * progress.monthsUntilRetirement) %></p>
</div> </div>
</div> </div>
</div> </div>
+1 -2
View File
@@ -2,7 +2,6 @@
<%- include("./partials/header") %> <%- include("./partials/header") %>
<main class="container mx-auto p-4 pt-28"> <main class="container mx-auto p-4 pt-28">
<h2 class="text-xl text-white font-semibold mb-4">Welcome: <%= user.name %></h2>
<div class="max-w-md mx-auto bg-white p-8 mb-10 rounded-lg shadow-md space-y-4"> <div class="max-w-md mx-auto bg-white p-8 mb-10 rounded-lg shadow-md space-y-4">
<h3 class="text-lg font-medium mb-6">My Retirement Plans</h3> <h3 class="text-lg font-medium mb-6">My Retirement Plans</h3>
<a href="/newPlan" class="block max-w-sm p-2"><div class="text-center bg-blue-600 text-white py-1 w-full rounded-md hover:bg-blue-700 hover:text-white font-semibold">New Plan</div></a> <a href="/newPlan" class="block max-w-sm p-2"><div class="text-center bg-blue-600 text-white py-1 w-full rounded-md hover:bg-blue-700 hover:text-white font-semibold">New Plan</div></a>
@@ -11,7 +10,7 @@
<a href="/plans/<%= plan._id %>" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700"> <a href="/plans/<%= plan._id %>" class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow-sm hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><%= plan.name %></h5> <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><%= plan.name %></h5>
<div class="w-full bg-black rounded-full h-2.5 mb-4 dark:bg-black"> <div class="w-full bg-black rounded-full h-2.5 mb-4 dark:bg-black">
<div class="bg-green-600 h-2.5 rounded-full dark:bg-green-500" style="width: <%= plan.progress %>%;"></div> <div class="bg-green-600 h-2.5 rounded-full dark:bg-green-500" style="width: <%= plan.progress > 100 ? 100 : plan.progress %>%;"></div>
</div> </div>
<p class="font-normal text-gray-700 dark:text-gray-400"><%= plan.description %></p> <p class="font-normal text-gray-700 dark:text-gray-400"><%= plan.description %></p>
</a> </a>