diff --git a/4-bank-project/bank-solution/app.js b/4-bank-project/bank-solution/app.js new file mode 100644 index 0000000..9e9424a --- /dev/null +++ b/4-bank-project/bank-solution/app.js @@ -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"; + } +} + + + diff --git a/4-bank-project/bank-solution/index.html b/4-bank-project/bank-solution/index.html new file mode 100644 index 0000000..02b036e --- /dev/null +++ b/4-bank-project/bank-solution/index.html @@ -0,0 +1,93 @@ + + + + + + + + Bank App + + +

Bank App

+
Loading...
+ + + + +
+
+ × +
+
DATE
+ + +
OBJECT
+ + +
AMOUNT (USE NEGATIVE VALUE FOR DEBIT)
+ + + +
+
+
+ + \ No newline at end of file diff --git a/4-bank-project/bank-solution/solution.md b/4-bank-project/bank-solution/solution.md new file mode 100644 index 0000000..b4c8874 --- /dev/null +++ b/4-bank-project/bank-solution/solution.md @@ -0,0 +1,70 @@ +# 1. Subtask-1 + +## Challenge +We can do this by adding `/credits':{templateId: 'credits'}` in our `routes` + +![Credits](../images/credits.png) + +## Assignment +For this assignment, we need to modify `route` and the function `updateRoute()`. We add +`document.title = route.title;` in `updateRoute()` to update our window title. + +```javascript +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 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(); + } +} +``` + +# 2.Subtask-2 + +## Challenge +The following code in `register()` will display the error message: + +```javascript + if (result.error) { + return updateElement('registerError', result.error); + } +``` +![Error](../images/user_exists.png) + +## Assignment +Created a css file named `styles.css` and linked it to `index.html` + +# 3.Subtask-3 + +## Challenge +Styled the dashboard page and used media queries to create a responsive design + +## Assignments +- Extracted the server base API URL + +- Reorganized the code and added comments at relevant locations + +- Created a new function `sendRequest` to send a HTTP request to the URL instead of using the same code repeatedly + +# 4.Subtask-4 + +## Challenge +Everytime the dashboard is loaded, the account data is reloaded. Hence, we don't need to store all the account details since they can be fetched from the server. First, I modified `updateState()` to only save the current user. In the `init()` fuction, we called the `updateAccountData()` + + + diff --git a/4-bank-project/bank-solution/styles.css b/4-bank-project/bank-solution/styles.css new file mode 100644 index 0000000..4371e77 --- /dev/null +++ b/4-bank-project/bank-solution/styles.css @@ -0,0 +1,202 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Times New Roman', Times, serif; +} + +#heading{ + position: absolute; + top: 10px; + left: 50%; + font-size: 80px; + transform: translateX(-50%); + color: white; + z-index: -1; +} +body { + background: linear-gradient(to bottom, #008CFF, #50BFFF); + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +#app{ + background: #fff; + padding: 30px; + border-radius: 10px; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.2); + text-align: left; + width: 800px; +} + +#registerError{ + color: red; + font-size: 30px; +} + +h1 { + color: #007BFF; + margin-bottom: 20px; + text-align: center; +} +h2 { + color: #007BFF; + margin-bottom: 20px; +} +input { + width: 100%; + padding: 10px; + margin: 10px 0; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 20px; +} +.button { + width: 100%; + padding: 10px; + background: #007BFF; + color: #fff; + border: none; + border-radius: 5px; + cursor: pointer; + margin-top: 10px; + font-size: 20px; +} +.button:hover { + background: #0056b3; +} +.divider { + margin: 20px 0; + color: #aaa; + font-size: 14px; +} +table, tr, th,td{ + border:2px solid black; + border-collapse: collapse; + text-align: center; +} + +body {font-family: Arial, Helvetica, sans-serif;} + +/* The Modal (background) */ +.addtransactions { + display: none; + position: fixed; + z-index: 1; + padding-top: 100px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0,0,0); + background-color: rgba(0,0,0,0.4); +} + +/* Modal Content */ +#tcontent { + background-color: #fefefe; + margin: auto; + padding: 20px; + border: 1px solid #888; + width: 80%; +} + +/* The Close Button */ +#close { + color: #aaaaaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +#close:hover, +#close:focus { + color: #000; + text-decoration: none; + cursor: pointer; +} + +#showbalance{ + font-size: 50px; + font-weight: bolder; + color: #007BFF; + margin-top: 20px; + margin-bottom: 20px; + text-align: center; +} + +table{ + width: 100%; + font-size: 20px; +} + +th{ + background-color: rgb(180, 180, 180); + height:25px; +} + +#transactions tr:nth-child(odd){ + background-color: #4896e9; + height:25px; +} +#transactions tr:nth-child(even){ + background-color: rgb(180, 180, 180); + height:25px; +} +#tbutton{ + width:20%; + position: relative; + left: 65%; +} + +#logout{ + width:10%; + position: relative; +} + +#submit{ + position: relative; + text-align: center; +} + +#creditsbutton { + width: 100px; +} + +#back{ + width: 100px; + padding: 5px; +} + +@media (max-width: 480px) { + #tbutton #logout { + width: 30%; + position: relative; + left: 40%; + } + } + + +@media (max-height:480px){ + #logout { + margin-top:0.75rem; + position:fixed; + top:0.75rem; + left:0.75rem; + } + + #tbutton{ + margin-top:0.75rem; + position: fixed; + top: 0.75rem; + right:0.75rem; + } + + #app{ + height:41rem; + } +} + diff --git a/4-bank-project/images/add_transaction.png b/4-bank-project/images/add_transaction.png new file mode 100644 index 0000000..02e9dc3 Binary files /dev/null and b/4-bank-project/images/add_transaction.png differ diff --git a/4-bank-project/images/credits.png b/4-bank-project/images/credits.png new file mode 100644 index 0000000..dd359c4 Binary files /dev/null and b/4-bank-project/images/credits.png differ diff --git a/4-bank-project/images/dashboard.png b/4-bank-project/images/dashboard.png new file mode 100644 index 0000000..33b22e2 Binary files /dev/null and b/4-bank-project/images/dashboard.png differ diff --git a/4-bank-project/images/transaction_added.png b/4-bank-project/images/transaction_added.png new file mode 100644 index 0000000..bf60f6e Binary files /dev/null and b/4-bank-project/images/transaction_added.png differ diff --git a/4-bank-project/images/user_exists.png b/4-bank-project/images/user_exists.png new file mode 100644 index 0000000..3daf6b8 Binary files /dev/null and b/4-bank-project/images/user_exists.png differ