Compare commits

...
Author SHA1 Message Date
Joaquin e1626f6ae9 feature/completed getting geolocation data 2025-05-06 14:46:01 -07:00
Joaquin 83be006a83 feature/geolocation implemented 2025-05-06 13:38:00 -07:00
SowinskiBraeden 6d91934229 fix/ignore login err message on signup 2025-05-06 12:49:20 -07:00
SowinskiBraeden 97a1473e21 fix/require name on register 2025-05-06 12:46:21 -07:00
SowinskiBraeden 3bb9ede515 update/cancel changes 2025-05-06 11:52:53 -07:00
SowinskiBraeden b350cdad9f update/keep email input disabled 2025-05-06 11:46:14 -07:00
SowinskiBraeden f0d324151d update/error message style 2025-05-06 11:44:26 -07:00
SowinskiBraeden b88333e3b4 refactor/temp remove personal section 2025-05-06 11:41:14 -07:00
SowinskiBraeden 75bc3bee8f update/save modified account settings 2025-05-06 11:39:49 -07:00
SowinskiBraeden a2b4e8d36d refactor/remove debug statements 2025-05-06 11:19:10 -07:00
SowinskiBraeden 033401dbf7 update/profile page with user data 2025-05-06 11:15:18 -07:00
SowinskiBraeden 1d8ec431b2 fix/tidy auth+user routers 2025-05-06 09:32:39 -07:00
Joaquin 4050e95cc3 fixed conflict 2025-05-06 09:30:30 -07:00
Joaquin ae3ddbeaeb Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into dev 2025-05-06 09:30:22 -07:00
Nícolas Agostini 84cd8b0bae Merge pull request #3 from JoaquinPar/feature/questions
feature/questionnaire page create + saving to MongoDb
2025-05-06 02:10:14 -07:00
Nícolas Agostini 5684e96a79 Merge branch 'dev' into feature/questions 2025-05-06 02:10:05 -07:00
nicoagostini 1e32ccfa17 feature/questionnaire page create + saving to MongoDb 2025-05-06 02:07:58 -07:00
Joaquin 54566f05b7 feature/fully implemented ejs inclusion to main files 2025-05-05 21:56:47 -07:00
Joaquin decdb299b7 Merge branch 'dev' of https://github.com/JoaquinPar/2800-202510-BBY14 into feature/layoutPage 2025-05-05 21:17:50 -07:00
Joaquin c8bf2e49ab feature/completed most of ejs implementation into other pages 2025-05-05 21:12:52 -07:00
nicoagostini f39b8555fa fix/session check fixed 2025-05-05 18:59:59 -07:00
nicoagostini fef7184404 fix/home.ejs include fix 2025-05-05 18:45:59 -07:00
SowinskiBraeden 6a8595d24b fix/impliment middleware 2025-05-05 18:39:43 -07:00
SowinskiBraeden 7c7098636a fix/middleware authentication + bad merge 2025-05-05 18:34:19 -07:00
Nícolas Agostini a446943d4a Merge pull request #2 from JoaquinPar/feature/questionnaire
Just a bug fix
2025-05-05 18:18:55 -07:00
Nícolas Agostini 612bb262d1 Merge branch 'dev' into feature/questionnaire 2025-05-05 18:18:45 -07:00
nicoagostini 6819b82723 Just a bug fix 2025-05-05 18:17:35 -07:00
SowinskiBraeden 5b1b36a553 fix/compare password on sign in 2025-05-05 18:12:19 -07:00
SowinskiBraeden 1114789e57 Merge latest dev into dev 2025-05-05 17:34:24 -07:00
SowinskiBraeden bb6e2eba2c fix/user routing 2025-05-05 17:33:27 -07:00
Joaquin f6640b23cf feature/some progress made on layouts 2025-05-05 17:07:51 -07:00
nicoagostini d0aa7a0fbd Landing moved to be the index page, routes added and landing page image route fixed 2025-05-05 16:46:16 -07:00
Joaquin 4be7ca3a8e Merge branch 'dev' into feature/layoutPage 2025-05-05 16:30:51 -07:00
Joaquin 10541d2428 merge feature/navBar with dev 2025-05-05 16:26:01 -07:00
Joaquin 7fa75457c9 feature/completed navbar/header functionality 2025-05-05 16:24:25 -07:00
Joaquin 8b6ff1354b resolved merge conflict 2025-05-05 14:27:24 -07:00
Joaquin 4bcf8dd812 feature/started work on layout.ejs 2025-05-05 14:21:30 -07:00
SowinskiBraeden fe7152b03b update/create middleware 2025-05-05 14:16:50 -07:00
SowinskiBraeden 87cad63425 update/return status codes 2025-05-05 13:17:30 -07:00
SowinskiBraeden d08fd30888 login/signup function with no sessions 2025-05-02 13:19:14 -07:00
SowinskiBraeden 7ef6282ac0 update login/signup.ejs pages 2025-05-02 13:18:51 -07:00
Joaquin Paredes 02a8374a58 feature/most nav bar functionality done 2025-05-02 13:15:38 -07:00
SowinskiBraeden 0e4d4d7daf fix project structure & static file 2025-05-02 11:41:13 -07:00
knighthawk4227 50dab82945 making login ejs and setting index blank for now 2025-05-02 02:07:55 -07:00
nicoagostini 8832768838 Merge branch 'feature/landingPage' into dev 2025-05-02 02:01:06 -07:00
knighthawk4227 44cee349e9 ejs login and signup with tailwind 2025-05-02 01:53:47 -07:00
30 changed files with 1852 additions and 231 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
mongoURI='mongodb://localhost:27017/' mongoURI='mongodb://localhost:27017/'
database='nameOfDatabase' database='nameOfDatabase'
PORT=8000 PORT=8000
secret='123456789'
+6 -3
View File
@@ -54,9 +54,12 @@ Example:
retirementCalculator/ retirementCalculator/
├── src/ ├── src/
│ ├── views/ │ ├── views/
| ── css/ │ │ ── partials/
| ├── images/ ├── css/
| ── scripts/ ── images/
│ ├── scripts/
│ └── utils/
├── app.js ├── app.js
├── .env.example ├── .env.example
├── .gitignore ├── .gitignore
+71 -61
View File
@@ -1,94 +1,104 @@
const status = require("./src/util/statuses");
const MongoStore = require("connect-mongo");
const session = require("express-session");
const express = require('express'); const express = require('express');
const path = require('path'); const path = require('path');
const dotenv = require('dotenv'); const joi = require('joi');
require('dotenv').config();
dotenv.config();
const app = express(); const app = express();
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
const mongoURI = process.env.mongoURI || "mongodb://localhost:27017/";
const database = process.env.database || "knoldus"; // Database name
const secret = process.env.secret || "123-secret-xyz";
/*** Sessions ***/
app.use(session({
secret: secret,
store: MongoStore.create({ mongoUrl: `${mongoURI}${database}`, crypto: { secret: secret } }),
resave: true,
saveUninitialized: false,
cookie: { maxAge: 3600000 },
}));
app.set('view engine', 'ejs'); app.set('view engine', 'ejs');
app.set('views',path.join(__dirname, 'src/views')); app.set('views',path.join(__dirname, 'src/views'));
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use("/static", express.static("./src/public"));
app.use("/images", express.static("./src/public/images"));
app.use(express.json());
/*** Database ***/
const { connectMongo, getCollection } = require("./src/database/connection");
/* let users;
STATIC ROUTINGS async function initDatabase() {
*/ const db = await connectMongo(mongoURI, database);
// For any collection, init here
users = await getCollection(db, "users");
}
app.use("/scripts", express.static("./src/scripts")); /*** ROUTINGS ***/
app.use("/css", express.static("./src/css"));
app.use("/images", express.static("./src/images"));
app.use("/views", express.static("./src/views"));
/*
ROUTINGS
*/
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.render('index'); if (!req.session.errMessage) req.session.errMessage = "";
});
app.get('/landing', (req, res) => {
res.render('landing'); res.render('landing');
return res.status(status.Ok);
}); });
app.get('/signup', (req, res) => { app.get('/signup', (req, res) => {
res.render('signup'); const ignore = ["User not found", "Incorrect password"];
if (ignore.includes(req.session.errMessage)) req.session.errMessage = "";
res.render('signup', { errMessage: req.session.errMessage });
return res.status(status.Ok);
}); });
app.get('/login', (req, res) => { app.get('/login', (req, res) => {
res.render('login'); if (req.session.authenticated) {
}); res.redirect("/home");
return res.status(status.Ok);
app.get('/home', (req, res) => { }
res.render('home'); res.render('login', { errMessage: req.session.errMessage });
}); return res.status(status.Ok);
app.get('/assets', (req, res) => {
res.render('assets');
});
app.get('/plans', (req, res) => {
res.render('plans');
});
app.get('/more', (req, res) => {
res.render('more');
});
app.get('/profile', (req, res) => {
res.render('profiles');
});
app.get('/settings', (req, res) => {
res.render('settings');
}); });
app.get('/aboutUs', (req, res) => { app.get('/aboutUs', (req, res) => {
res.render('aboutUs'); res.render('aboutUs');
return res.status(status.Ok);
}); });
app.post('/api/location', async (req,res) => {
const { latitude, longitude } = req.body;
app.post('/signup', (req, res) => { const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&result_type=country&key=${process.env.geolocation_api}`);
res.status(200);
res.send('200 status code'); const data = await response.json();
res.json(data);
}); });
app.post('/login', (req, res) => { // Initialize database and start app
res.status(200); initDatabase().then(() => {
res.send('200 status code'); console.log("Successfully connected to MongoDB");
});
app.get('/*splat', (req, res) => { // Import authentication handler
res.status(404); app.use(require("./src/auth/authentication")(users));
res.send('404 Not Found');
});
app.listen(port, () => { // Import middleware & apply to user routes
console.log(`Server listening on port ${port}`); const middleware = require("./src/auth/middleware")(users);
}); app.use(require('./src/router/user')(middleware, users));
// 404 handler
app.get('/*splat', (req, res) => {
res.send('404 Not Found');
return res.status(status.NotFound);
});
// Start app
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
});
+841 -2
View File
File diff suppressed because it is too large. Load diff
+9 -4
View File
@@ -2,10 +2,10 @@
"name": "retirementcalculator", "name": "retirementcalculator",
"version": "0.1.0", "version": "0.1.0",
"description": "Retirement Calculator", "description": "Retirement Calculator",
"main": "./src/app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node ./src/app.js", "start": "node app.js",
"dev": "nodemon ./src/app.js" "dev": "nodemon app.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -18,9 +18,14 @@
}, },
"homepage": "https://github.com/JoaquinPar/2800-202510-BBY14#readme", "homepage": "https://github.com/JoaquinPar/2800-202510-BBY14#readme",
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1",
"connect-mongo": "^5.1.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"ejs": "^3.1.10", "ejs": "^3.1.10",
"express": "^5.1.0" "express": "^5.1.0",
"express-session": "^1.18.1",
"joi": "^17.13.3",
"mongodb": "^6.16.0"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^3.1.10" "nodemon": "^3.1.10"
+94
View File
@@ -0,0 +1,94 @@
const status = require("../util/statuses");
const bcrypt = require('bcrypt');
const joi = require("joi");
const salt = 12;
module.exports = (users) => {
const router = require("express").Router();
router.get("/logout", (req, res) => {
req.session.destroy();
return res.redirect('/login');
});
router.post("/login", async (req, res) => {
const credentialSchema = joi.object({
email: joi.string().email().required(),
password: joi.string().max(20).required(),
});
const valid = credentialSchema.validate(req.body);
if (valid.err) {
req.session.errMessage = "Invalid input";
res.status(status.BadRequest);
return res.redirect("/login");
}
users.findOne({ "email": req.body.email }).then((user) => {
if (!user) {
req.session.errMessage = "User not found";
res.status(status.NotFound);
return res.redirect("/login");
}
if (!bcrypt.compareSync(req.body.password, user.password)) {
req.session.errMessage = "Incorrect password";
res.status(status.Unauthorized);
return res.redirect("/login");
}
req.session.authenticated = true;
req.session.email = req.body.email;
req.session.errMessage = "";
res.redirect("/home");
return res.status(status.Ok);
});
});
router.post("/signup", async (req, res) => {
const userSchema = joi.object({
email: joi.string().email().required(),
name: joi.string().alphanum().max(20).required(),
password: joi.string().max(20).min(8).required(),
repassword: joi.string().max(20).min(8).required(),
});
const valid = userSchema.validate(req.body);
if (valid.err) {
req.session.errMessage = "Invalid input",
res.status(status.BadRequest);
return res.redirect("/signup");
}
if (req.body.password != req.body.repassword) {
req.session.errMessage = "Passwords must match";
res.status(status.BadRequest);
return res.redirect("/signup");
}
let hashedPassword = await bcrypt.hashSync(req.body.password, salt);
users.insertOne({
email: req.body.email,
name: req.body.name,
password: hashedPassword,
}).then((results, err) => {
if (err) {
console.error(err);
res.session.errMessage = "Internal server error";
return res.status(status.InternalServerError).redirect("/signup");
}
req.session.authenticated = true;
req.session.email = req.body.email;
req.session.errMessage = "";
return res.status(status.Ok).redirect("/home");
});
});
return router;
}
+30
View File
@@ -0,0 +1,30 @@
const status = require("../util/statuses");
const session = require("express-session");
/**
* createMiddleware returns a middleware function for express.
* @param {MongoClient.collection} users
* @return {async function}
*/
const createMiddleware = (users) => {
return async (req, res, next) => {
if (!req.session.authenticated || !req.session.email) {
req.session.errMessage = "Please login to view that resource";
res.redirect("/login");
return res.status(status.Unauthorized);
}
let user = await users.findOne({ "email": req.session.email }).then((user) => user);
if (!user) {
req.session.errMessage = "User not found";
res.redirect("/login");
return res.status(status.Unauthorized);
}
req.user = user;
next();
};
}
module.exports = createMiddleware;
+28
View File
@@ -0,0 +1,28 @@
const MongoClient = require("mongodb").MongoClient;
/**
* connectMongo returns a database connection to MongoDB
* @param {string} mongoURI
* @param {string} databaseName
* @return {MongoClient}
*/
const connectMongo = async (mongoURI, databaseName) => {
const database = await MongoClient.connect(mongoURI, { connectTimeoutMS: 1000 });
const dbo = database.db(databaseName);
return dbo;
}
/**
* getCollection object to interact with MongoDB
* @param {MongoClient} dbo
* @param {string} collection
* @return {MongoClient.collection}
*/
const getCollection = async (dbo, collection) => {
return await dbo.collection(collection);
}
module.exports = {
connectMongo: connectMongo,
getCollection: getCollection,
}
File renamed without changes.
+24
View File
@@ -0,0 +1,24 @@
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(success, error);
}
}
function success(position) {
const { latitude, longitude } = position.coords;
fetch("api/location" , {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ latitude, longitude })
}).then(res => res.json())
.then(data => {
console.log(data.results[0].formatted_address);
})
}
function error(err) {
console.error("Geolocation error: ", err);
}
+75
View File
@@ -0,0 +1,75 @@
/**
* lockAccount resets inputs and disabled inputs
*/
function lockAccount() {
// Clear unsaved inputs on page load (refresh doesnt clear them)
document.getElementById("account-form").reset();
document.getElementById("save-account").disabled = true;
document.getElementById("email").disabled = true;
document.getElementById("name").disabled = true;
document.getElementById("password").disabled = true;
document.getElementById("repassword").disabled = true;
document.getElementById("save-account").classList.add("cursor-not-allowed");
document.getElementById("edit-account").innerHTML = "Edit";
document.getElementById("edit-account").onclick = unlockAccount;
}
/**
* lockPersonal resets inputs and disabled inputs
*/
function lockPersonal() {
// Clear unsaved inputs on page load (refresh doesnt clear them)
document.getElementById("personal-form").reset();
document.getElementById("save-personal").disabled = true;
document.getElementById("dob").disabled = true;
document.getElementById("education").disabled = true;
document.getElementById("ms-single").disabled = true;
document.getElementById("ms-married").disabled = true;
document.getElementById("ms-divorced").disabled = true;
document.getElementById("ms-widowed").disabled = true;
document.getElementById("save-personal").classList.add("cursor-not-allowed");
document.getElementById("edit-personal").innerHTML = "Edit";
document.getElementById("edit-personal").onclick = unlockPersonal;
}
/**
* unlockAccount removes disabled from inputs and
* allows users to edit their profile.
*/
function unlockAccount() {
document.getElementById("save-account").disabled = false;
// document.getElementById("email").disabled = false;
document.getElementById("name").disabled = false;
document.getElementById("password").disabled = false;
document.getElementById("repassword").disabled = false;
document.getElementById("save-account").classList.remove("cursor-not-allowed");
document.getElementById("edit-account").innerHTML = "Cancel changes";
document.getElementById("edit-account").onclick = lockAccount;
}
/**
* unlocPersonal removes disabled from inputs and
* allows users to edit their personal information.
*/
function unlockPersonal() {
document.getElementById("save-personal").disabled = false;
document.getElementById("dob").disabled = false;
document.getElementById("education").disabled = false;
document.getElementById("ms-single").disabled = false;
document.getElementById("ms-married").disabled = false;
document.getElementById("ms-divorced").disabled = false;
document.getElementById("ms-widowed").disabled = false;
document.getElementById("save-personal").classList.remove("cursor-not-allowed");
document.getElementById("edit-personal").innerHTML = "Cancel changes";
document.getElementById("edit-personal").onclick = lockPersonal;
}
// On page load, ensure forms are locked and reset
lockAccount();
// lockPersonal();
+165
View File
@@ -0,0 +1,165 @@
const status = require("../util/statuses");
const bcrypt = require('bcrypt');
const joi = require("joi");
const salt = 12;
module.exports = (middleware, users) => {
const router = require("express").Router();
router.use(middleware);
router.get('/home', async (req, res) => {
res.render('home', { user: req.user });
return res.status(status.Ok);
});
router.get('/assets', (req, res) => {
res.render('assets', { user: req.user });
return res.status(status.Ok);
});
router.get('/plans', (req, res) => {
res.render('plans', { user: req.user });
return res.status(status.Ok);
});
router.get('/more', (req, res) => {
res.render('more', { user: req.user });
return res.status(status.Ok);
});
router.get('/profile', (req, res) => {
res.render('profile', { user: req.user, errMessage: req.session.errMessage });
return res.status(status.Ok);
});
router.get('/settings', (req, res) => {
res.render('settings', { user: req.user });
return res.status(status.Ok);
});
router.get('/questionnaire', (req, res) => {
const errMessage = req.session.errMessage;
req.session.errMessage = "";
res.render('questionnaire', { user: req.user, errMessage: errMessage });
});
router.post('/questionnaire', (req, res) => {
const questionnaireSchema = joi.object({
dob: joi.date().required(),
education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(),
maritalStatus: joi.string().valid('single', 'married', 'divorced', 'widowed').required(),
income: joi.number().min(0).required(),
expenses: joi.number().min(0).required(),
assets: joi.number().min(0).required(),
liabilities: joi.number().min(0).required(),
retirementAge: joi.number().min(18).max(120).required(),
retirementExpenses: joi.number().min(0).required(),
retirementAssets: joi.number().min(0).required(),
retirementLiabilities: joi.number().min(0).required(),
});
const validationOptions = { convert: true, abortEarly: false };
const { error, value } = questionnaireSchema.validate(req.body, validationOptions);
if (error) {
console.error("Questionnaire validation error:", error.details);
req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', ');
res.status(status.BadRequest).redirect("/questionnaire");
return;
}
users.updateOne(
{ email: req.session.email },
{
$set: {
financialData: true,
dob: value.dob,
education: value.education,
maritalStatus: value.maritalStatus,
income: value.income,
expenses: value.expenses,
assets: value.assets,
liabilities: value.liabilities,
retirementAge: value.retirementAge,
retirementExpenses: value.retirementExpenses,
retirementAssets: value.retirementAssets,
retirementLiabilities: value.retirementLiabilities,
}
}
).then((result) => {
if (result.matchedCount === 0) {
console.log(`User not found during questionnaire update: ${req.session.email}`);
req.session.errMessage = "User session invalid. Please log in again.";
res.status(status.NotFound).redirect("/login");
return;
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`User questionnaire data unchanged (already up-to-date): ${req.session.email}`);
}
req.session.errMessage = "";
res.status(status.Ok).redirect("/home");
}).catch(err => {
console.error("Error updating questionnaire in database:", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
res.status(status.InternalServerError).redirect("/questionnaire");
});
});
router.post("/updateAccount", async (req, res) => {
const accountSchema = joi.object({
email: joi.string().email(),
name: joi.string().alphanum().max(20),
password: joi.string().max(20).min(8),
repassword: joi.string().max(20).min(8),
});
const valid = accountSchema.validate(req.body);
if (valid.err) {
req.session.errMessage = "Invalid input",
res.status(status.BadRequest);
return res.redirect("/profile");
}
let update = {
// email: req.body.email,
name: req.body.name,
};
if ((req.body.password != "") && (req.body.password != req.body.repassword)) {
req.session.errMessage = "New passwords must match";
res.status(status.BadRequest);
return res.redirect("/profile");
} else if (req.body.password != "") {
let hashedPassword = await bcrypt.hashSync(req.body.password, salt);
update.password = hashedPassword;
}
users.updateOne(
{ email: req.session.email },
{ $set: update }
).then((result) => {
if (result.matchedCount === 0) {
console.log(`User not found during account update: ${req.session.email}`);
req.session.errMessage = "User session invalid. Please log in again.";
res.status(status.NotFound).redirect("/login");
return;
}
if (result.modifiedCount === 0 && result.matchedCount === 1) {
console.log(`User account data unchanged (already up-to-date): ${req.session.email}`);
}
req.session.errMessage = "";
return res.status(status.Ok).redirect("/profile");
}).catch(err => {
console.error("Error updating account in database:", err);
req.session.errMessage = "An error occurred while saving your information. Please try again.";
return res.status(status.InternalServerError).redirect("/profile");
});
});
return router;
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
Ok: 200,
BadRequest: 400,
Unauthorized: 401,
NotFound: 404,
InternalServerError: 500,
};
+36 -77
View File
@@ -1,80 +1,39 @@
<html> <%- include("./partials/fileHeader") %>
<head> <%- include("./partials/headerStart") %>
<title>RCalculator</title>
<link rel="icon" href="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-white dark:bg-gray-900">
<header>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="#" class="flex 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" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span>
</a>
<button data-collapse-toggle="navbar-solid-bg" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-solid-bg" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
</svg>
</button>
<div class="hidden w-full md:block md:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-transparent dark:bg-gray-800 md:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/landing" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Home</a>
</li>
<li>
<a href="/aboutUs" class="block py-2 px-3 md:p-0 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:dark:text-blue-500 dark:bg-blue-600 md:dark:bg-transparent" aria-current="page">About Us</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main>
<section class="bg-white dark:bg-gray-900 py-12 md:py-20"> <main>
<div class="max-w-screen-lg mx-auto px-4 text-center"> <section class="bg-white dark:bg-gray-900 py-12 md:py-20">
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-gray-900 md:text-5xl lg:text-6xl dark:text-white">About RCalculator</h1> <div class="max-w-screen-lg mx-auto px-4 text-center">
<p class="mb-12 text-lg font-normal text-gray-500 lg:text-xl sm:px-16 dark:text-gray-400">Welcome to RCalculator, your partner in building a secure and fulfilling financial future.</p> <h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-gray-900 md:text-5xl lg:text-6xl dark:text-white">About RCalculator</h1>
</div> <p class="mb-12 text-lg font-normal text-gray-500 lg:text-xl sm:px-16 dark:text-gray-400">Welcome to RCalculator, your partner in building a secure and fulfilling financial future.</p>
</div>
<div class="max-w-screen-md mx-auto px-4"> <div class="max-w-screen-md mx-auto px-4">
<h2 class="mb-4 text-3xl font-bold tracking-tight text-gray-900 dark:text-white text-center">Our Mission</h2> <h2 class="mb-4 text-3xl font-bold tracking-tight text-gray-900 dark:text-white text-center">Our Mission</h2>
<p class="mb-6 font-normal text-gray-600 dark:text-gray-400 text-lg text-center"> <p class="mb-6 font-normal text-gray-600 dark:text-gray-400 text-lg text-center">
Our mission is to empower you with the tools and insights needed to take control of your long-term financial goals. Our mission is to empower you with the tools and insights needed to take control of your long-term financial goals.
</p> </p>
<div class="prose lg:prose-lg dark:prose-invert mx-auto text-gray-600 dark:text-gray-400"> <div class="prose lg:prose-lg dark:prose-invert mx-auto text-gray-600 dark:text-gray-400">
<p class="mb-4"> <p class="mb-4">
We believe that planning for your desired future, especially retirement, shouldn't be daunting. It doesn't matter what your current age or career status is starting now is what counts. We believe that planning for your desired future, especially retirement, shouldn't be daunting. It doesn't matter what your current age or career status is starting now is what counts.
</p> </p>
<p class="mb-4"> <p class="mb-4">
At RCalculator, we help you trace a personalized plan that suits your unique aspirations. We provide clear, actionable advice and projections to guide you on how to get there, whether you're just starting your career, mid-way through, or nearing retirement. At RCalculator, we help you trace a personalized plan that suits your unique aspirations. We provide clear, actionable advice and projections to guide you on how to get there, whether you're just starting your career, mid-way through, or nearing retirement.
</p> </p>
<p> <p>
Let us help you navigate the path to financial independence and achieve the retirement lifestyle you envision. Let us help you navigate the path to financial independence and achieve the retirement lifestyle you envision.
</p> </p>
</div>
</div>
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<a href="/login" class="inline-flex justify-center items-center mt-4 py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900">
Get started
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</svg>
</a>
</div>
</section>
</main>
<footer class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-6 text-center text-gray-500 dark:text-gray-400 text-sm">
<div class="container mx-auto">
<p>&copy; 2025 RCalculator. All rights reserved.</p>
</div> </div>
</footer> </div>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> <div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script> <a href="/signup" class="inline-flex justify-center items-center mt-4 py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900">
</body> Get started
</html> <svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</svg>
</a>
</div>
</section>
</main>
<%- include("./partials/footer") %>
+9
View File
@@ -0,0 +1,9 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
The Assets Page
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>
+15
View File
@@ -0,0 +1,15 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
<%- include("./partials/header") %>
Welcome: <%= user.email %>
The Dashboard Page
<% if(user.authenticated) { %>
<%= user.email %>
<%= user.errMessage %>
<% } %>
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>
+9 -12
View File
@@ -1,12 +1,9 @@
<!DOCTYPE html> <%- include("./partials/fileHeader") %>
<html> <%- include("./partials/header") %>
<head>
<title></title> <main>
</head> The Index Page
<body> </main>
<h1>Welcome!</h1>
<form method="get" action="/signup"> <%- include("./partials/navBar") %>
<button type="submit">signup</button> <%- include("./partials/footer") %>
</form>
</body>
</html>
+23 -63
View File
@@ -1,64 +1,24 @@
<html> <%- include("./partials/fileHeader") %>
<head> <%- include("./partials/headerStart") %>
<title>RCalculator</title>
<link rel="icon" href="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png"> <main>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.2/dist/tailwind.min.css" rel="stylesheet"> <section class="bg-center bg-no-repeat bg-cover bg-[url('/images/bg1.jpg')] bg-gray-400 bg-blend-multiply">
</head> <div class="px-4 mx-auto max-w-screen-xl text-center py-24 lg:py-56">
<body class="bg-white dark:bg-gray-900"> <h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">Plan now, live better.</h1>
<header> <p class="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 lg:px-48">Planning your retirement is a crucial step towards financial security and a happy future.</p>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"> <div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"> <a href="/signup" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900">
<a href="#" class="flex items-center space-x-3 rtl:space-x-reverse"> Get started
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/rekor.png" class="h-8" alt="Flowbite Logo" /> <svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</a> </svg>
<button data-collapse-toggle="navbar-solid-bg" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-solid-bg" aria-expanded="false"> </a>
<span class="sr-only">Open main menu</span> <a href="/aboutUs" class="inline-flex justify-center hover:text-gray-900 items-center py-3 px-5 sm:ms-4 text-base font-medium text-center text-white rounded-lg border border-white hover:bg-gray-100 focus:ring-4 focus:ring-gray-400">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14"> Learn more
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/> </a>
</svg>
</button>
<div class="hidden w-full md:block md:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-transparent dark:bg-gray-800 md:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/landing" class="block py-2 px-3 md:p-0 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:dark:text-blue-500 dark:bg-blue-600 md:dark:bg-transparent" aria-current="page">Home</a>
</li>
<li>
<a href="/aboutUs" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">About Us</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 md:p-0 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main>
<section class="bg-center bg-no-repeat bg-cover bg-[url('/images/bg1.jpg')] bg-gray-400 bg-blend-multiply">
<div class="px-4 mx-auto max-w-screen-xl text-center py-24 lg:py-56">
<h1 class="mb-4 text-4xl font-extrabold tracking-tight leading-none text-white md:text-5xl lg:text-6xl">Plan now, live better.</h1>
<p class="mb-8 text-lg font-normal text-gray-300 lg:text-xl sm:px-16 lg:px-48">Planning your retirement is a crucial step towards financial security and a happy future.</p>
<div class="flex flex-col space-y-4 sm:flex-row sm:justify-center sm:space-y-0">
<a href="/login" class="inline-flex justify-center items-center py-3 px-5 text-base font-medium text-center text-white rounded-lg bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-900">
Get started
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
</svg>
</a>
<a href="/aboutUs" class="inline-flex justify-center hover:text-gray-900 items-center py-3 px-5 sm:ms-4 text-base font-medium text-center text-white rounded-lg border border-white hover:bg-gray-100 focus:ring-4 focus:ring-gray-400">
Learn more
</a>
</div>
</div>
</section>
</main>
<footer class="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-6 text-center text-gray-500 dark:text-gray-400 text-sm">
<div class="container mx-auto">
<p>&copy; 2025 RCalculator. All rights reserved.</p>
</div> </div>
</footer> </div>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> </section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script> </main>
</body>
</html> <%- include("./partials/footer") %>
+44
View File
@@ -0,0 +1,44 @@
<%- include("./partials/fileHeader") %>
<main>
<div class="flex justify-center items-center h-screen">
<form action="/login" method="POST">
<div class="w-96 p-6 shadow-1g bg-white rounded-md">
<h1 class="text-3xl block text-center font-semibold"> Login </h1>
<hr class="mt-3">
<div class="mt-3">
<label for="email" class="block text-base mb-2">Email</label>
<input type="text" id="email" name="email"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Email" />
</div>
<div class="mt-3">
<label for="password" class="block text-base mb-2">Password</label>
<input type="password" id="password" name="password"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Password" />
</div>
<div class="text-red-800 font-bold" id="forgor"> <%= errMessage %></div>
<div class="mt-3 flex justify-between items-center">
<div>
<input type="checkbox">
<label>Remember me</label>
</div>
<div>
<a href="#" class="text-indigo-600 font-semibold">Forgot Password? </a>
</div>
</div>
<div class="mt-5">
<button type="submit"
class="border-2 border-gray-600 bg-blue-600 text-white py-1 w-full rounded-md hover:bg-transparent hover:text-indigo-700 font-semibold">Login</button>
</div>
<div class="mt-5 block justify-between items-center">
<label>Don't have an account?</label>
<a href="/signup" class="text-indigo-600 font-semibold">Register</a>
</div>
</div>
</form>
</div>
</main>
<%- include("./partials/footer") %>
+10
View File
@@ -0,0 +1,10 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
The More Page
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<title>RCalculator</title>
</head>
<body class="bg-white dark:bg-gray-900">
+11
View File
@@ -0,0 +1,11 @@
<footer>
<div class="fixed bottom-0 left-0 z-50 hidden lg:block w-full h-16 bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 p-6 text-center text-gray-500 dark:text-gray-400 text-sm">
<div class="container mx-auto">
<p>&copy; 2025 RCalculator. All rights reserved.</p>
</div>
</div>
</footer>
<script src="/static/scripts/geolocation.js"></script>
</body>
</html>
+29
View File
@@ -0,0 +1,29 @@
<header>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="/home" class="flex 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" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span>
</a>
<div class="hidden w-full lg:block lg:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 lg:bg-transparent dark:bg-gray-800 lg:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/home" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Dashboard</a>
</li>
<li>
<a href="/assets" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Assets</a>
</li>
<li>
<a href="/plans" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Plans</a>
</li>
<li>
<a href="/more" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">More</a>
</li>
<li>
<a href="/logout" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
+32
View File
@@ -0,0 +1,32 @@
<header>
<nav class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
<a href="/" class="flex 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" />
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">RCalculator</span>
</a>
<button data-collapse-toggle="navbar-solid-bg" type="button"
class="inline-flex items-center justify-center text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
aria-controls="navbar-solid-bg" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="none" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15" />
</svg>
</button>
<div class="hidden w-full lg:block lg:w-auto" id="navbar-solid-bg">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 lg:bg-transparent dark:bg-gray-800 lg:dark:bg-transparent dark:border-gray-700">
<li>
<a href="/" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Home</a>
</li>
<li>
<a href="/login" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Login</a>
</li>
<li>
<a href="mailto:rcalculator@gmail.com" class="block py-2 px-3 lg:p-0 text-gray-900 rounded hover:bg-gray-100 lg:hover:bg-transparent lg:border-0 lg:hover:text-blue-700 dark:text-white lg:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white lg:dark:hover:bg-transparent">Contact us</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
+29
View File
@@ -0,0 +1,29 @@
<div class="fixed bottom-0 left-0 z-50 lg:hidden w-full h-16 bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 text-sm">
<div class="grid h-16 max-w-lg grid-cols-4 mx-auto font-medium">
<a href="/home" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M11.293 3.293a1 1 0 0 1 1.414 0l6 6 2 2a1 1 0 0 1-1.414 1.414L19 12.414V19a2 2 0 0 1-2 2h-3a1 1 0 0 1-1-1v-3h-2v3a1 1 0 0 1-1 1H7a2 2 0 0 1-2-2v-6.586l-.293.293a1 1 0 0 1-1.414-1.414l2-2 6-6Z" clip-rule="evenodd"/>
</svg>
<span class="text-gray-500 dark:text-gray-400 text-sm">Dashboard</span>
</a>
<a href="/assets" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M10.464 8.746c.227-.18.497-.311.786-.394v2.795a2.252 2.252 0 0 1-.786-.393c-.394-.313-.546-.681-.546-1.004 0-.323.152-.691.546-1.004ZM12.75 15.662v-2.824c.347.085.664.228.921.421.427.32.579.686.579.991 0 .305-.152.671-.579.991a2.534 2.534 0 0 1-.921.42Z" />
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25ZM12.75 6a.75.75 0 0 0-1.5 0v.816a3.836 3.836 0 0 0-1.72.756c-.712.566-1.112 1.35-1.112 2.178 0 .829.4 1.612 1.113 2.178.502.4 1.102.647 1.719.756v2.978a2.536 2.536 0 0 1-.921-.421l-.879-.66a.75.75 0 0 0-.9 1.2l.879.66c.533.4 1.169.645 1.821.75V18a.75.75 0 0 0 1.5 0v-.81a4.124 4.124 0 0 0 1.821-.749c.745-.559 1.179-1.344 1.179-2.191 0-.847-.434-1.632-1.179-2.191a4.122 4.122 0 0 0-1.821-.75V8.354c.29.082.559.213.786.393l.415.33a.75.75 0 0 0 .933-1.175l-.415-.33a3.836 3.836 0 0 0-1.719-.755V6Z" clip-rule="evenodd" />
</svg>
<span class="text-gray-500 dark:text-gray-400 text-sm">Assets</span>
</a>
<a href="/plans" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" viewBox="0 0 24 24">
<path d="M13.09 3.294c1.924.95 3.422 1.69 5.472.692a1 1 0 0 1 1.438.9v9.54a1 1 0 0 1-.562.9c-2.981 1.45-5.382.24-7.25-.701a38.739 38.739 0 0 0-.622-.31c-1.033-.497-1.887-.812-2.756-.77-.76.036-1.672.357-2.81 1.396V21a1 1 0 1 1-2 0V4.971a1 1 0 0 1 .297-.71c1.522-1.506 2.967-2.185 4.417-2.255 1.407-.068 2.653.453 3.72.967.225.108.443.216.655.32Z"/>
</svg>
<span class="text-gray-500 dark:text-gray-400 text-sm">Plans</span>
</a>
<a href="/more" class="inline-flex flex-col items-center justify-center px-5 hover:bg-gray-50 dark:hover:bg-gray-800 group">
<svg height="30" width="30" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"></path>
</svg>
<span class="text-gray-500 dark:text-gray-400 text-sm">More</span>
</a>
</div>
</div>
+9
View File
@@ -0,0 +1,9 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
The Plans Page
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>
+90
View File
@@ -0,0 +1,90 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
<h2 class="ml-11 mt-5 mb-6 text-xl font-semibold mb-4 text-white">Welcome <%= user.name %></h2>
<% if (errMessage != "") { %>
<div class="max-w-md mx-auto mb-10 rounded-lg shadow-md text-white font-bold bg-red-400 py-4 text-center"><%= errMessage %></div>
<% } %>
<div class="max-w-md mx-auto bg-white p-8 mb-10 rounded-lg shadow-md">
<div class="flex flex-row justify-between">
<h3 class="pt-2 pr-2 pb-2">Account settings</h3>
<button id="edit-account" onclick="unlockAccount()" class="py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Edit</button>
</div>
<form action="/updateAccount" method="post" class="space-y-4 mt-4" id="account-form">
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input id="email" disabled type="text" name="email" value="<%= user.email %>" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
<input id="name" disabled type="text" name="name" value="<%= user.name %>" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<label for="password" class="block text-sm font-medium text-gray-700">New password</label>
<input id="password" disabled type="password" name="password" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<label for="repassword" class="block text-sm font-medium text-gray-700">Confirm new password</label>
<input id="repassword" disabled type="password" name="repassword" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<div>
<button id="save-account" disabled type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-not-allowed">Save</button>
</div>
</form>
</div>
<!--
<div class="mb-10 max-w-md mx-auto bg-white p-8 rounded-lg shadow-md">
<div class="flex flex-row justify-between">
<h3 class="pt-2 pr-2 pb-2">Personal information</h3>
<button onclick="unlockPersonal()" class="py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Edit</button>
</div>
<form action="/updatePersonal" method="post" class="space-y-4" id="personal-form">
<div>
<label for="dob" class="block text-sm font-medium text-gray-700">Date of Birth</label>
<input id="dob" disabled type="date" name="dob" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
</div>
<div>
<label for="education" class="block text-sm font-medium text-gray-700">Education</label>
<select disabled name="education" id="education" class="mt-1 block w-full px-3 py-2 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<option value="primary">Primary (Elementary)</option>
<option value="secondary">Secondary (High School)</option>
<option value="tertiary">Tertiary (College/University)</option>
<option value="postgraduate">Postgraduate (Master's/PhD)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Marital Status</label>
<div class="mt-1 space-x-4">
<label class="inline-flex items-center">
<input disabled id="ms-single" type="radio" name="maritalStatus" value="single" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<span class="ml-2 text-sm text-gray-700">Single</span>
</label>
<label class="inline-flex items-center">
<input disabled id="ms-married" type="radio" name="maritalStatus" value="married" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<span class="ml-2 text-sm text-gray-700">Married</span>
</label>
<label class="inline-flex items-center">
<input disabled id="ms-divorced" type="radio" name="maritalStatus" value="divorced" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<span class="ml-2 text-sm text-gray-700">Divorced</span>
</label>
<label class="inline-flex items-center">
<input disabled id="ms-widowed" type="radio" name="maritalStatus" value="widowed" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none">
<span class="ml-2 text-sm text-gray-700">Widowed</span>
</label>
</div>
</div>
<div>
<button id="save-personal" disabled type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 cursor-not-allowed">Save</button>
</div>
</form>
</div> -->
<div class="mt-24"></div>
</main>
<script src="/static/scripts/profile.js"></script>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>
+91
View File
@@ -0,0 +1,91 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main class="container mx-auto p-4">
<h2 class="text-xl font-semibold mb-4">Welcome: <%= user.email %></h2>
<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">Financial Questionnaire</h3>
<form action="/questionnaire" method="post" class="space-y-4">
<div>
<label for="dob" class="block text-sm font-medium text-gray-700">Date of Birth</label>
<input type="date" name="dob" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="education" class="block text-sm font-medium text-gray-700">Education</label>
<select name="education" id="education" class="mt-1 block w-full px-3 py-2 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<option value="primary">Primary (Elementary)</option>
<option value="secondary">Secondary (High School)</option>
<option value="tertiary">Tertiary (College/University)</option>
<option value="postgraduate">Postgraduate (Master's/PhD)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Marital Status</label>
<div class="mt-1 space-x-4">
<label class="inline-flex items-center">
<input type="radio" name="maritalStatus" value="single" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">Single</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="maritalStatus" value="married" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">Married</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="maritalStatus" value="divorced" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">Divorced</span>
</label>
<label class="inline-flex items-center">
<input type="radio" name="maritalStatus" value="widowed" class="form-radio h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">Widowed</span>
</label>
</div>
</div>
<div>
<label for="income" class="block text-sm font-medium text-gray-700">Annual Gross Income</label>
<input type="number" name="income" min="0" placeholder="e.g., 50000" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="expenses" class="block text-sm font-medium text-gray-700">Monthly Expenses</label>
<input type="number" name="expenses" min="0" placeholder="e.g., 2000" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="assets" class="block text-sm font-medium text-gray-700">Net Worth</label>
<input type="number" name="assets" min="0" placeholder="Estimated Net Worth" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="liabilities" class="block text-sm font-medium text-gray-700">Liabilities</label>
<input type="number" name="liabilities" min="0" placeholder="Estimated Liabilities" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="retirementAge" class="block text-sm font-medium text-gray-700">Desired Retirement Age</label>
<input type="number" name="retirementAge" placeholder="e.g., 65" min="18" max="120" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="retirementExpenses" class="block text-sm font-medium text-gray-700">Estimated Monthly Retirement Expenses</label>
<input type="number" name="retirementExpenses" min="0" placeholder="e.g., 3000" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="retirementAssets" class="block text-sm font-medium text-gray-700">Estimated Retirement Assets</label>
<input type="number" name="retirementAssets" min="0" placeholder="e.g., 500000" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<label for="retirementLiabilities" class="block text-sm font-medium text-gray-700">Estimated Retirement Liabilities</label>
<input type="number" name="retirementLiabilities" min="0" placeholder="e.g., 50000" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
</div>
<div>
<button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Submit Questionnaire</button>
</div>
</form>
</div>
</main>
<%- include("./partials/footer") %>
+45 -8
View File
@@ -1,9 +1,46 @@
<!DOCTYPE html> <%- include("./partials/fileHeader") %>
<html> <%- include("./partials/headerStart") %>
<head>
</head> <main>
<body> <div class="flex justify-center items-center h-screen">
<h1>SIGNUP</h1> <form action="/signup" method="POST">
</body> <div class="w-96 p-6 shadow-1g bg-white rounded-md">
</html> <h1 class="text-3xl block text-center font-semibold"> Signup </h1>
<hr class="mt-3">
<div class="mt-3">
<label for="email" class="block text-base mb-2">Email</label>
<input type="text" id="email" name="email"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Email" />
</div>
<div class="mt-3">
<label for="name" class="block text-base mb-2">Name</label>
<input type="text" id="name" name="name"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Name" />
</div>
<div class="mt-3">
<label for="password" class="block text-base mb-2">Password</label>
<input type="password" id="password" name="password"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Password" />
<label for="password" class="block text-base mb-2 mt-3">Re-type Password</label>
<input type="password" id="repassword" name="repassword"
class="border focus:border-gray-600 w-full text-base px-2 py-1 focus:outline-none focus:ring-0 "
placeholder="Enter Password" />
</div>
<div class="text-red-800 font-bold " id="forgor"> <%= errMessage %></div>
<div class="mt-5">
<button type="submit"
class="border-2 border-gray-600 bg-blue-600 text-white py-1 w-full rounded-md hover:bg-transparent hover:text-indigo-700 font-semibold">Signup</button>
</div>
<div class="mt-5 block justify-between items-center">
<label>Already have an account?</label>
<a href="/login" class="text-indigo-600 font-semibold">Login</a>
</div>
</div>
</form>
</div>
</main>
<%- include("./partials/footer") %>
+9
View File
@@ -0,0 +1,9 @@
<%- include("./partials/fileHeader") %>
<%- include("./partials/header") %>
<main>
<!-- Your content here -->
</main>
<%- include("./partials/navBar") %>
<%- include("./partials/footer") %>