Skip to content

This PR is the solution to the 'js-basics #4 #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions 4-bank-project/bank-solution/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
const URL='//localhost:5000/api/accounts';
let currentUser;

const routes = {
'/login': { templateId: 'login', title:"Login"},
'/dashboard': { templateId: 'dashboard', title:"Dashboard",init: refresh, onShow:()=>console.log("Dashboard is shown") },
'/credits':{templateId: 'credits',title:"Credits"}
};

function showCredits(){
navigate('/credits');
}

function backToDashboard(){
navigate('/dashboard');
}

let state = Object.freeze({
account: null
});
const storageKey = 'savedAccount';

function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
document.title = route.title;
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);

if (typeof route.init === 'function') {
route.init();
}

}


function navigate(path) {
window.history.pushState({}, path, path);
updateRoute();
}

function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}



// Take the data from registration form and creates a new account
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
const result = await createAccount(jsonData);
if (result.error) {
return updateElement('registerError', result.error);
}
currentUser = data.user;
console.log('Account created!', result);
updateState('account', result);
navigate('/dashboard');
console.log("Dashboard is shown")
}

// To add a transaction
async function addTransactions(event){
console.log(`${currentUser}`)
const content = document.getElementById('content');
const contentFormData = new FormData(content);
const transactionData = Object.fromEntries(contentFormData);
const transactionJsonData = JSON.stringify(transactionData);
event.preventDefault();
const apiURL=`${URL}/${currentUser}/transactions`;
const result = await sendRequest(apiURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: transactionJsonData
});

if (!result.error) {
await refresh();
}

return result;
}


async function sendRequest(url, options = {}) {
try {
const response = await fetch(url, options);
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}


// Sends a POST request to create a new account
async function createAccount(account) {
return sendRequest(URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
}

//Retrieves account data
async function getAccount(user) {
return sendRequest(`${URL}/${user}`);
}


window.onpopstate = () => updateRoute();
updateRoute();

async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
currentUser=user;
const data = await getAccount(user);

if (data.error)
{
return updateElement('loginError', data.error);
}

updateState('account', data);
navigate('/dashboard');
console.log('Dashboard is shown');
}


function updateElement(id, textOrNode) {
const element = document.getElementById(id);
element.textContent = '';
element.append(textOrNode);
}


function updateDashboard() {
const account = state.account;
if (!account) {
return logout();
}
updateElement('showuser',currentUser)
updateElement('description', account.description);
updateElement('balance', account.balance.toFixed(2));
updateElement('currency', account.currency);
const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions)
{
const transactionRow = createTransactionRow(transaction);
transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);
}

function createTransactionRow(transaction) {
const template = document.getElementById('transaction');
const transactionRow = template.content.cloneNode(true);
const tr = transactionRow.querySelector('tr');
tr.children[0].textContent = transaction.date;
tr.children[1].textContent = transaction.object;
tr.children[2].textContent = transaction.amount.toFixed(2);
return transactionRow;
}

function updateState(property, newData) {
state = Object.freeze({
...state,
[property]: newData
});
if (property === 'account' && newData)
{
localStorage.setItem(storageKey, JSON.stringify({ user: newData.user }));
} else if (property === 'account' && !newData)
{
localStorage.removeItem(storageKey);
}
}
function logout() {
updateState('account', null);
currentUser=null;
navigate('/login');
}

function init() {
const savedAccount = localStorage.getItem(storageKey);
if (savedAccount) {
const { user } = JSON.parse(savedAccount);
currentUser = user;
updateAccountData(); // Fetch fresh account data from the server
}
window.onpopstate = () => updateRoute();
updateRoute();
}

init();


async function updateAccountData() {
const account = state.account;
if (!account)
{
return logout();
}

const data = await getAccount(account.user);
if (data.error) {
return logout();
}

updateState('account', data);
}

async function refresh() {
await updateAccountData();
updateDashboard();
}


// To open and close the 'Add Transactions' dialogue
const transactionBox = document.getElementById("tbox");
const transactionButton = document.getElementById("tbutton");
const span = document.getElementById("close");
function openBox() {
transactionBox.style.display = "block";
}
function closeBox(){
transactionBox.style.display = "none";
}
window.onclick = function(event) {
if (event.target == transactionBox) {
transactionBox.style.display = "none";
}
}



93 changes: 93 additions & 0 deletions 4-bank-project/bank-solution/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
<title>Bank App</title>
</head>
<body>
<h1 id="heading">Bank App</h1>
<div id="app">Loading...</div>
<template id="login">
<section>
<h2>LOGIN</h2>

<form id="loginForm" action="javascript:login()">
<label for="username">Username (required)</label>
<input id="username" name="user" type="text" maxlength="20" required>
<div id="loginError" role="alert"></div>
<button class="button">Login</button>
</form>
<hr>
<h2>REGISTER</h2>
<form id="registerForm" action="javascript:register()">
<label for="user">Username</label>
<input id="user" name="user" type="text">
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" value="$" maxlength="5">
<label for="description">Description</label>
<input id="description" name="description" type="text" maxlength="100">
<label for="balance">Current balance</label>
<input id="balance" name="balance" type="number" value="0">
<div id="registerError" role="alert"></div>
<button class="button">Register</button>
</form>
</section>
</template>
<template id="dashboard" >
<header>
<h1 id="showuser"></h1>
<button id="logout" class="button" onclick="logout()">Logout</button>
<button id="tbutton" class="button" onclick="openBox()">Add Transactions</button>
</header>
<section id="showbalance" >
Balance: <span id="balance"></span><span id="currency"></span>
</section>
<h2 id="description"></h2>
<section>
<h2>Transactions</h2>
<table id="transactiontable">
<thead>
<tr>
<th>Date</th>
<th>Object</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="transactions"></tbody>
</table>
</section>
<button id="creditsbutton" class="button" onclick="showCredits()">Credits</button>
</template>
<template id="transaction">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</template>
<template id="credits">
<button id="back" class="button" onclick="backToDashboard()">Back to account</button>
<h1>Made by: Anandajith S</h1>
</template>
<div id="tbox" class="addtransactions">
<div id="tcontent">
<span id="close" onclick="closeBox()">&times;</span>
<form id="content">
<h5>DATE</h5>
<label for="date"></label>
<input type="date" id="date" name="date">
<h5>OBJECT</h5>
<label for="object"></label>
<input type="text" id="object" name="object">
<h5>AMOUNT (USE NEGATIVE VALUE FOR DEBIT)</h5>
<label for="amount"></label>
<input type="number" id="amount" name="amount">
<button id="submit" class="button" onclick="addTransactions(event);closeBox();">Submit</button>
</form>
</div>
</div>
</body>
</html>
Loading