diff --git a/3-typing-game/Assignment/index.html b/3-typing-game/Assignment/index.html new file mode 100644 index 0000000..9439ad2 --- /dev/null +++ b/3-typing-game/Assignment/index.html @@ -0,0 +1,46 @@ + + + + Guess the Text + + + + + + +
    + +

    -

    +
    + +

    -

    +
    + +

    -

    +
    + +

    -

    +
    +
+
+

How to Play:

+

How to Play: + In this game, your objective is to find the hidden number based on the clues provided for your previous guess.
+ There are three types of clues:
+ Red digits indicate the number of digits in your guess that are in the correct position and have the correct value.
+ Yellow digits show how many digits are present in the hidden number but are in the wrong position.
+ Green digits represent the number of digits in your guess that are repeated in the hidden number.
+ Use these clues to narrow down your guesses and discover the hidden number!
+

+
+

Your Guess:

+ + +

Previous Guesses:

+
    + +
+ + + + \ No newline at end of file diff --git a/3-typing-game/Assignment/script.js b/3-typing-game/Assignment/script.js new file mode 100644 index 0000000..cd376b0 --- /dev/null +++ b/3-typing-game/Assignment/script.js @@ -0,0 +1,139 @@ +const Num1 = document.getElementById('Num1'); +const Num2 = document.getElementById('Num2'); +const Num3 = document.getElementById('Num3'); +const Num4 = document.getElementById('Num4'); +const Prev_Guess = document.getElementById('ps'); +const Prev_Guess_Header = document.getElementById('psh'); +const inputbox = document.getElementById('guess'); +const RestartButton = document.getElementById('rs'); + +let GuessNum = 0; + +let GuessAmt = 0; + + + +function UpdateNum(k){ + Num1.innerText=k[0]; + Num2.innerText=k[1]; + Num3.innerText=k[2]; + Num4.innerText=k[3]; +} + +RestartButton.addEventListener('click',()=>{ + RestartButton.visibility=''; + GuessNum=getRandomInt(1000,9999); + GuessAmt=0; + inputbox.value=''; + UpdateNum('----'); + while (Prev_Guess.firstChild) { + Prev_Guess.removeChild(Prev_Guess.lastChild); + } + Prev_Guess_Header.innerText = "Previous Guesses:"; + Prev_Guess.style.visibility='visible'; +}) + + +inputbox.addEventListener("input",()=>{ + let val=inputbox.value; + + + + if(val.length > 3){ + GuessAmt++; + //Do PrevGuess + inputbox.value=""; + let ele =document.createElement("ls"); + let ele2=document.createElement("ol"); + ele2.className = "Numbox"; + h1=document.createElement("h1"); + h2=document.createElement("h1"); + h3=document.createElement("h1"); + h4=document.createElement("h1"); + ele2.appendChild(h1); + ele2.appendChild(h2); + ele2.appendChild(h3); + ele2.appendChild(h4); + + h2.className = "red"; + h3.className="yellow"; + h4.className="green"; + + ele.appendChild(ele2) + Prev_Guess.appendChild(ele); + + //Set val of h1 + GuessNum=GuessNum.toString(); + let y=0; + let r=0; + let m=Num1.innerText+Num2.innerText+Num3.innerText+Num4.innerText; + let k='' + //Count a number repeating + let multi_occurence='' + let ml=0 + console.log(GuessNum); + for(let i=0;i1){ + ml++; + } + } + } + + } + } + console.log(k); + UpdateNum(k); + + if(k === GuessNum){ + Prev_Guess.style.visibility = 'hidden'; + + let hs = localStorage.getItem('hs'); + RestartButton.style.visibility = 'visible'; + if(hs === null || hs > GuessAmt){ + Prev_Guess_Header.innerText = `You Won with ${GuessAmt} guesses.That is a new HIGHSCORE!`; + localStorage.setItem('hs',GuessAmt); + } + else{ + Prev_Guess_Header.innerText = `You Won with ${GuessAmt} guesses.The High Score is ${hs}`; + } + } + //Setup Text + h1.innerText=val; + h2.innerText = r; + h3.innerText= y; + h4.innerText=ml; + + } +}) + + + + + +function count(str,count_term){ + let c =0; + for(let j=0;jelapsedTime / 1000 || HighScore == null){ + message = `CONGRATULATIONS! You finished with a new Highscore of ${elapsedTime / 1000} seconds.`; + localStorage.setItem("hs",elapsedTime/1000); + } +Part three was done by a setting a divs style hidden and visible in which both highscore and congrats message is displayed + +Here is the entire code: + +HTML: + + + + Typing game + + + +

Typing game!

+

Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!

+

+

+
+ + +
+ + + + + + + + +CSS: +.highlight { + background-color: yellow; + } + + .error { + background-color: lightcoral; + border: red; + } + .sep{ + margin-top: 20px; + margin-bottom: 30px; + width: 500px; + border: 1px solid black; + } + .box { + width: 500px; + background-color: lightgreen; + height: 170px; + border: 1px solid black; + padding: 10px; + text-align: center; + margin: 10px; + } + .x{ + margin-left: 480px; + } + Java: + const quotes = [ + + 'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.', + 'There is nothing more deceptive than an obvious fact.', + 'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.', + 'I never make exceptions. An exception disproves the rule.', + 'What one man can invent another can discover.', + 'Nothing clears up a case so much as stating it to another person.', + 'Education never ends, Watson. It is a series of lessons, with the greatest for the last.', +]; +// store the list of words and the index of the word the player is currently typing +let words = []; +let wordIndex = 0; +// the starting time +let startTime = Date.now(); +// page elements +const quoteElement = document.getElementById('quote'); +const messageElement = document.getElementById('message'); +const typedValueElement = document.getElementById('typed-value'); +const WinBox=document.getElementById('winbox'); +const close = document.getElementById("close"); + +let k=false; + + +close.addEventListener('click',() => { + WinBox.style.visibility = 'hidden'; +}) + +document.getElementById('start').addEventListener('click', () => { + // get a quote + const quoteIndex = Math.floor(Math.random() * quotes.length); + const quote = quotes[quoteIndex]; + // Put the quote into an array of words + words = quote.split(' '); + // reset the word index for tracking + wordIndex = 0; + WinBox.style.visibility = 'hidden'; + // UI updates + // Create an array of span elements so we can set a class + const spanWords = words.map(function(word) { return `${word} `}); + // Convert into string and set as innerHTML on quote display + quoteElement.innerHTML = spanWords.join(''); + // Highlight the first word + quoteElement.childNodes[0].className = 'highlight'; + // Clear any prior messages + messageElement.innerText = ''; + typedValueElement.disabled = false; + // Clear the textbox + typedValueElement.value = ''; + // set focus + typedValueElement.focus(); + // set the event handler + // Start the timer + startTime = new Date().getTime(); + }); + + + // at the end of script.js +typedValueElement.addEventListener('input', () => { + // Get the current word + const currentWord = words[wordIndex]; + // get the current value + const typedValue = typedValueElement.value; + + if (typedValue === currentWord && wordIndex === words.length - 1) { + // end of sentence + // Display success + const elapsedTime = new Date().getTime() - startTime; + // Setup the textbox + const HighScore = localStorage.getItem("hs"); + let message = ``; + if(HighScore != null){ + message= `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.Current highscore is ${HighScore}`; + } + if(HighScore>elapsedTime / 1000 || HighScore == null){ + message = `CONGRATULATIONS! You finished with a new Highscore of ${elapsedTime / 1000} seconds.`; + localStorage.setItem("hs",elapsedTime/1000); + } + messageElement.innerText = message; + WinBox.style.visibility = 'visible'; + //Challenges + typedValueElement.disabled = true; + typedValueElement.removeEventListener('input'); + + + + } else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) { + // end of word + // clear the typedValueElement for the new word + typedValueElement.value = ''; + // move to the next word + wordIndex++; + // reset the class name for all elements in quote + for (const wordElement of quoteElement.childNodes) { + wordElement.className = ''; + } + // highlight the new word + quoteElement.childNodes[wordIndex].className = 'highlight'; + } else if (currentWord.startsWith(typedValue)) { + // currently correct + // highlight the next word + typedValueElement.className = ''; + } else { + // error state + typedValueElement.className = 'error'; + } + }); + + + + + + Assignment: + + For the Assignment I made a game where your objective is to find the hidden number based on the clues provided for your previous guess. + There are three types of clues: + Red digits indicate the number of digits in your guess that are in the correct position and have the correct value. + Yellow digits show how many digits are present in the hidden number but are in the wrong position + Green digits represent the number of digits in your guess that are repeated in the hidden number. + Use these clues to narrow down guesses and discover the hidden number!Sc + +ScreenShots: +![image](https://github.com/user-attachments/assets/0118b3eb-1446-4e61-87d8-a9d96e898ada) +![image](https://github.com/user-attachments/assets/7f57c03e-3cb1-484b-a967-0a0ef01508d0) + + +Code: +HTML + + + + Guess the Text + + + + + + +
    + +

    -

    +
    + +

    -

    +
    + +

    -

    +
    + +

    -

    +
    +
+
+

How to Play:

+

How to Play: + In this game, your objective is to find the hidden number based on the clues provided for your previous guess.
+ There are three types of clues:
+ Red digits indicate the number of digits in your guess that are in the correct position and have the correct value.
+ Yellow digits show how many digits are present in the hidden number but are in the wrong position.
+ Green digits represent the number of digits in your guess that are repeated in the hidden number.
+ Use these clues to narrow down your guesses and discover the hidden number!
+

+
+

Your Guess:

+ + +

Previous Guesses:

+
    + +
+ + + + + +JAVA: +const Num1 = document.getElementById('Num1'); +const Num2 = document.getElementById('Num2'); +const Num3 = document.getElementById('Num3'); +const Num4 = document.getElementById('Num4'); +const Prev_Guess = document.getElementById('ps'); +const Prev_Guess_Header = document.getElementById('psh'); +const inputbox = document.getElementById('guess'); +const RestartButton = document.getElementById('rs'); + +let GuessNum = 0; + +let GuessAmt = 0; + + + +function UpdateNum(k){ + Num1.innerText=k[0]; + Num2.innerText=k[1]; + Num3.innerText=k[2]; + Num4.innerText=k[3]; +} + +RestartButton.addEventListener('click',()=>{ + RestartButton.visibility=''; + GuessNum=getRandomInt(1000,9999); + GuessAmt=0; + inputbox.value=''; + UpdateNum('----'); + while (Prev_Guess.firstChild) { + Prev_Guess.removeChild(Prev_Guess.lastChild); + } + Prev_Guess_Header.innerText = "Previous Guesses:"; + Prev_Guess.style.visibility='visible'; +}) + + +inputbox.addEventListener("input",()=>{ + let val=inputbox.value; + + + + if(val.length > 3){ + GuessAmt++; + //Do PrevGuess + inputbox.value=""; + let ele =document.createElement("ls"); + let ele2=document.createElement("ol"); + ele2.className = "Numbox"; + h1=document.createElement("h1"); + h2=document.createElement("h1"); + h3=document.createElement("h1"); + h4=document.createElement("h1"); + ele2.appendChild(h1); + ele2.appendChild(h2); + ele2.appendChild(h3); + ele2.appendChild(h4); + + h2.className = "red"; + h3.className="yellow"; + h4.className="green"; + + ele.appendChild(ele2) + Prev_Guess.appendChild(ele); + + //Set val of h1 + GuessNum=GuessNum.toString(); + let y=0; + let r=0; + let m=Num1.innerText+Num2.innerText+Num3.innerText+Num4.innerText; + let k='' + //Count a number repeating + let multi_occurence='' + let ml=0 + console.log(GuessNum); + for(let i=0;i1){ + ml++; + } + } + } + + } + } + console.log(k); + UpdateNum(k); + + if(k === GuessNum){ + Prev_Guess.style.visibility = 'hidden'; + + let hs = localStorage.getItem('hs'); + RestartButton.style.visibility = 'visible'; + if(hs === null || hs > GuessAmt){ + Prev_Guess_Header.innerText = `You Won with ${GuessAmt} guesses.That is a new HIGHSCORE!`; + localStorage.setItem('hs',GuessAmt); + } + else{ + Prev_Guess_Header.innerText = `You Won with ${GuessAmt} guesses.The High Score is ${hs}`; + } + } + //Setup Text + h1.innerText=val; + h2.innerText = r; + h3.innerText= y; + h4.innerText=ml; + + } +}) + + + + + +function count(str,count_term){ + let c =0; + for(let j=0;j + + + Typing game + + + + + +

Typing game!

+

Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!

+

+

+
+ + +
+ + + + + + + \ No newline at end of file diff --git a/3-typing-game/script.js b/3-typing-game/script.js new file mode 100644 index 0000000..1b79c54 --- /dev/null +++ b/3-typing-game/script.js @@ -0,0 +1,108 @@ +const quotes = [ + + 'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.', + 'There is nothing more deceptive than an obvious fact.', + 'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.', + 'I never make exceptions. An exception disproves the rule.', + 'What one man can invent another can discover.', + 'Nothing clears up a case so much as stating it to another person.', + 'Education never ends, Watson. It is a series of lessons, with the greatest for the last.', +]; +// store the list of words and the index of the word the player is currently typing +let words = []; +let wordIndex = 0; +// the starting time +let startTime = Date.now(); +// page elements +const quoteElement = document.getElementById('quote'); +const messageElement = document.getElementById('message'); +const typedValueElement = document.getElementById('typed-value'); +const WinBox=document.getElementById('winbox'); +const close = document.getElementById("close"); + +let k=false; + + +close.addEventListener('click',() => { + WinBox.style.visibility = 'hidden'; +}) + +document.getElementById('start').addEventListener('click', () => { + // get a quote + const quoteIndex = Math.floor(Math.random() * quotes.length); + const quote = quotes[quoteIndex]; + // Put the quote into an array of words + words = quote.split(' '); + // reset the word index for tracking + wordIndex = 0; + WinBox.style.visibility = 'hidden'; + // UI updates + // Create an array of span elements so we can set a class + const spanWords = words.map(function(word) { return `${word} `}); + // Convert into string and set as innerHTML on quote display + quoteElement.innerHTML = spanWords.join(''); + // Highlight the first word + quoteElement.childNodes[0].className = 'highlight'; + // Clear any prior messages + messageElement.innerText = ''; + typedValueElement.disabled = false; + // Clear the textbox + typedValueElement.value = ''; + // set focus + typedValueElement.focus(); + // set the event handler + // Start the timer + startTime = new Date().getTime(); + }); + + + // at the end of script.js +typedValueElement.addEventListener('input', () => { + // Get the current word + const currentWord = words[wordIndex]; + // get the current value + const typedValue = typedValueElement.value; + + if (typedValue === currentWord && wordIndex === words.length - 1) { + // end of sentence + // Display success + const elapsedTime = new Date().getTime() - startTime; + // Setup the textbox + const HighScore = localStorage.getItem("hs"); + let message = ``; + if(HighScore != null){ + message= `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.Current highscore is ${HighScore}`; + } + if(HighScore>elapsedTime / 1000 || HighScore == null){ + message = `CONGRATULATIONS! You finished with a new Highscore of ${elapsedTime / 1000} seconds.`; + localStorage.setItem("hs",elapsedTime/1000); + } + messageElement.innerText = message; + WinBox.style.visibility = 'visible'; + //Challenges + typedValueElement.disabled = true; + typedValueElement.removeEventListener('input'); + + + + } else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) { + // end of word + // clear the typedValueElement for the new word + typedValueElement.value = ''; + // move to the next word + wordIndex++; + // reset the class name for all elements in quote + for (const wordElement of quoteElement.childNodes) { + wordElement.className = ''; + } + // highlight the new word + quoteElement.childNodes[wordIndex].className = 'highlight'; + } else if (currentWord.startsWith(typedValue)) { + // currently correct + // highlight the next word + typedValueElement.className = ''; + } else { + // error state + typedValueElement.className = 'error'; + } + }); \ No newline at end of file diff --git a/3-typing-game/style.css b/3-typing-game/style.css new file mode 100644 index 0000000..c69b562 --- /dev/null +++ b/3-typing-game/style.css @@ -0,0 +1,27 @@ +.highlight { + background-color: yellow; + } + + .error { + background-color: lightcoral; + border: red; + } + .sep{ + margin-top: 20px; + margin-bottom: 30px; + width: 500px; + border: 1px solid black; + } + .box { + width: 500px; + background-color: lightgreen; + height: 170px; + border: 1px solid black; + padding: 10px; + text-align: center; + margin: 10px; + } + .x{ + margin-left: 480px; + } + \ No newline at end of file diff --git a/4-bank-project/Solution.md b/4-bank-project/Solution.md new file mode 100644 index 0000000..fabbabe --- /dev/null +++ b/4-bank-project/Solution.md @@ -0,0 +1,147 @@ +``` +This is the Documentation for Bank Project + + +1-template-route +Challenge:Add a new template and route for a third page that shows the credits for this app. + +This Challenge was pretty straight forward and simple i created the following template for the credits + +Then I added a button to the dashboard to display this template + + + +Assignment: +The routes declaration contains currently only the template ID to use. But when displaying a new page, a bit more is needed sometimes. Let's improve our routing implementation with two additional features: +1.Give titles to each template and update the window title with this new title when the template changes. +2.Add an option to run some code after the template changes. +We want to print 'Dashboard is shown' in the developer console every time the dashboard page is displayed. + +For Giving the titles to each page i stored the titles in routes as +const routes = { + '/login': { templateId: 'login', title: 'Login Page' }, + '/dashboard': { templateId: 'dashboard', title: 'Dashboard', init: refresh }, + '/credits': { templateId: 'credits', title: 'Credits' }, +}; +Then I set the title elements text to this using the following piece of code in updateroute() +document.getElementById('title').innerText = route.title; + +2-forms +Challenge: +Show an error message in the HTML if the user already exists. + +Assignment: +Create a new styles.css file and add a link to it in your current index.html file. In the CSS file you just created add some styling to make the Login and Dashboard page looks nice and tidy. Try to create a color theme to give your app its own branding. + +For this task i have styled both the dashboard and the login page with css and partially with html I have attached both these files in solution folder of this PR + + +3-data +Challenge: +Work together to make the dashboard page look like a real banking app. If you already styled your app, try to use media queries to create a responsive design working nicely on both desktop and mobile devices. + +For this task i have styled the tables inside dashboard where it shows transaction and made nice looking for both pc and mobile with css and partially with html I have attached both these files in solution folder of this PR + + +Assignment: +As your codebase grows, it's important to refactor your code frequently to keep it readable and maintainable over time. Add comments and refactor your app.js to improve the code quality: + + Extract constants, like the server API base URL + Factorize similar code: for example you can create a sendRequest() function to regroup the code used in both createAccount() and getAccount() + Reorganize the code to make it easier to read, and add comments + +For this assignment I have refactored the code and added comments for easier reading and all over organized the entire code. The code file attached in solution folder has been done in the same way. + + +4-state-management +Challenge: +Now that we reload the account data every time the dashboard is loaded, do you think we still need to persist all the account data? +Try working together to change what is saved and loaded from local Storage to only include what is absolutely required for the app to work. + + +For this task I changed the code to only store the name of currently logged in user by updating the following: + +function updateState(property, newData) { + state = Object.freeze({ + ...state, + [property]: newData + }); + user = state.account.user; // Update the global `user` variable + localStorage.setItem(storageKey, state.account.user); // Save to LocalStorage +} + + +Then when the tab is reloaded or opened I set the user this variable using the following: + +async function init() { + const savedAccount = localStorage.getItem(storageKey); + const data = await getAccount(savedAccount); + if (data.error) { + return logout(); + } + updateState('account', data); + + // Setup navigation handling + window.onpopstate = () => updateRoute(); + updateRoute(); +} + + +Assignment: +Our bank app is still missing one important feature: the possibility to enter new transactions. Using everything that you've learnt in the four previous lessons, implement an "Add transaction" dialog: + + Add an "Add transaction" button in the dashboard page + Either create a new page with an HTML template, or use JavaScript to show/hide the dialog HTML without leaving the dashboard page (you can use hidden property for that, or CSS classes) + Make sure you handle keyboard and screen reader accessibility for the dialog + Implement an HTML form to receive input data + Create JSON data from the form data and send it to the API + Update the dashboard page with the new data + + + +Soln: +I have added a trasaction box for new transaction inside of bank app I have also styled this box to fit in with reset of the page here is the code: + +Code: +async function createTransaction(account) { + try { + const response = await fetch('http://localhost:5000/api/accounts/' + user + '/transactions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: account + }); + return await response.json(); + } catch (error) { + return { error: error.message || 'Unknown error' }; + } +} + + +function AddNewTransaction(event) { + event.preventDefault(); + const registerForm = document.getElementById('popup-box'); + const formData = new FormData(registerForm); // Collect form data + const jsonData = JSON.stringify(Object.fromEntries(formData)); // Convert to JSON + createTransaction(jsonData); // Send transaction to the server + navigate('/dashboard'); // Redirect to dashboard +} + +// ----------------------------- Popup Handling ----------------------------- +//Toggles the visibility of the popup box that is the new transaction box. + +function TogglePopup() { + const popup = document.getElementById("popup-box"); + if (popup.style.visibility === "hidden" || !popup.style.visibility) { + popup.style.visibility = "visible"; // Show popup + } else { + popup.style.visibility = "hidden"; // Hide popup + } +}``` diff --git a/4-bank-project/bank-project-solution/app.js b/4-bank-project/bank-project-solution/app.js new file mode 100644 index 0000000..838bcba --- /dev/null +++ b/4-bank-project/bank-project-solution/app.js @@ -0,0 +1,189 @@ +// ----------------------------- Routes ----------------------------- +const routes = { + '/login': { templateId: 'login', title: 'Login Page' }, + '/dashboard': { templateId: 'dashboard', title: 'Dashboard', init: refresh }, + '/credits': { templateId: 'credits', title: 'Credits' }, +}; + +// ----------------------------- Global Variables ----------------------------- +let user = ""; +const storageKey = 'savedAccount'; + +let state = Object.freeze({ + account: null // Stores the current user's account data +}); + +// ----------------------------- State Management ----------------------------- + +function updateState(property, newData) { + state = Object.freeze({ + ...state, + [property]: newData + }); + user = state.account.user; // Update the global `user` variable + localStorage.setItem(storageKey, state.account.user); // Save to LocalStorage +} + +// ----------------------------- Route Management ----------------------------- +function updateRoute() { + const path = window.location.pathname; // Get the current URL path + const route = routes[path]; + + if (!route) { + return navigate('/dashboard'); // Redirect to dashboard if route not found + } + + document.getElementById('title').innerText = route.title; // Update the page title + + // Call route's initialization function if defined + if (typeof route.init === 'function') { + route.init(); + } + + // Load the appropriate template into the app container + const template = document.getElementById(route.templateId); + const view = template.content.cloneNode(true); + const app = document.getElementById('app'); + app.innerHTML = ''; // Clear previous content + app.appendChild(view); + + if (typeof route.init === 'function') { + route.init(); // Ensure init function is called again + } + + dashboardLoaded = true; // Update dashboard state (if used) +} + +// ----------------------------- Transactions ----------------------------- + +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; // Set date + tr.children[1].textContent = transaction.object; // Set object + tr.children[2].textContent = transaction.amount.toFixed(2); // Set amount + return transactionRow; +} + + +async function createTransaction(account) { + try { + const response = await fetch('http://localhost:5000/api/accounts/' + user + '/transactions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: account + }); + return await response.json(); + } catch (error) { + return { error: error.message || 'Unknown error' }; + } +} + + +function AddNewTransaction(event) { + event.preventDefault(); + const registerForm = document.getElementById('popup-box'); + const formData = new FormData(registerForm); // Collect form data + const jsonData = JSON.stringify(Object.fromEntries(formData)); // Convert to JSON + createTransaction(jsonData); // Send transaction to the server + navigate('/dashboard'); // Redirect to dashboard +} + +// ----------------------------- Popup Handling ----------------------------- +//Toggles the visibility of the popup box that is the new transaction box. + +function TogglePopup() { + const popup = document.getElementById("popup-box"); + if (popup.style.visibility === "hidden" || !popup.style.visibility) { + popup.style.visibility = "visible"; // Show popup + } else { + popup.style.visibility = "hidden"; // Hide popup + } +} + +// ----------------------------- Authentication ----------------------------- + +//Logs the user out and redirects to the login page. + +function logout() { + updateState('account', null); // Clear account data + navigate('/login'); +} + + +//Logs the user in by verifying credentials + +async function login() { + const loginForm = document.getElementById('loginForm'); + const user = loginForm.user.value; + const data = await getAccount(user); // Fetch account data + + if (data.error) { + return updateElement('loginError', data.error); + } + + updateState('account', data); // Update state with account data + navigate('/dashboard'); // Redirect to dashboard +} + + +async function getAccount(user) { + try { + const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user)); + return await response.json(); + } catch (error) { + return { error: error.message || 'Unknown error' }; + } +} + +// ----------------------------- Dashboard Updates ----------------------------- +function updateDashboard() { + const account = state.account; + if (!account) { + return logout(); // Logout if no account data + } + + // Update UI elements with account details + updateElement('description', account.description); + updateElement('balance', account.balance.toFixed(2)); + updateElement('currency', account.currency); + + // Builds transactions table + const transactionsRows = document.createDocumentFragment(); + for (const transaction of account.transactions) { + const transactionRow = createTransactionRow(transaction); + transactionsRows.appendChild(transactionRow); + } + updateElement('transactions', transactionsRows); +} + + +//Refreshes the account data and updates the dashboard. + +async function refresh() { + await updateAccountData(); + updateDashboard(); +} + +// ----------------------------- Utility Functions ----------------------------- +function updateElement(id, textOrNode) { + const element = document.getElementById(id); + element.textContent = ''; + element.append(textOrNode); +} + +// ----------------------------- Initialization ----------------------------- +async function init() { + const savedAccount = localStorage.getItem(storageKey); + const data = await getAccount(savedAccount); + if (data.error) { + return logout(); + } + updateState('account', data); + + // Setup navigation handling + window.onpopstate = () => updateRoute(); + updateRoute(); +} +init(); diff --git a/4-bank-project/bank-project-solution/index.html b/4-bank-project/bank-project-solution/index.html new file mode 100644 index 0000000..dfb4300 --- /dev/null +++ b/4-bank-project/bank-project-solution/index.html @@ -0,0 +1,131 @@ + + + + + + + + Bank App + + + + + + + +
Loading...
+ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/4-bank-project/bank-project-solution/logo.png b/4-bank-project/bank-project-solution/logo.png new file mode 100644 index 0000000..38e1d03 Binary files /dev/null and b/4-bank-project/bank-project-solution/logo.png differ diff --git a/4-bank-project/bank-project-solution/style.css b/4-bank-project/bank-project-solution/style.css new file mode 100644 index 0000000..d546a37 --- /dev/null +++ b/4-bank-project/bank-project-solution/style.css @@ -0,0 +1,106 @@ +html { + height: 100%; +} + +body { + height: 100%; + margin: 0; + background-repeat: no-repeat; + background-attachment: fixed; + background-image: linear-gradient(to bottom, rgb(255, 255, 255), rgb(196, 196, 196)); +} + +.modal{ + background-color: white; +} + + +#app{ + height: 100%; + width: 100%; + display: flex; /* Enables flexbox on body */ + justify-content: center; /* Centers horizontally */ + align-items: center; /* Centers vertically */ + text-align: center; +} + +#H1 { + color: white; +} + +.inputLogin { + align-self: center; + width: 400px; + height: 25px; +} + +#LoginMenu{ + background-repeat: no-repeat; + background-attachment: fixed; + background-image: linear-gradient(to bottom, rgb(0, 204, 255), rgb(4, 168, 173)); + width: 30%; + min-height: 80%; + border-radius: 3%; +} + + +#DashboardMenu { + background-repeat: no-repeat; + background-attachment: fixed; + background-image: linear-gradient(to bottom, rgb(0, 204, 255), rgb(4, 168, 173)); + width: 90%; + min-height: 90%; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + justify-content: space-between; + color: white; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + color: white; +} + +header a { + text-decoration: none; + color: white; + margin: 0 10px; + font-weight: bold; +} + +section { + background: rgba(255, 255, 255, 0.8); + padding: 15px; + color: black; +} + +section h2 { + margin-top: 0; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; +} + +table th, table td { + padding: 10px; + border: 1px solid #ccc; + text-align: left; +} + +table th { + background-color: rgba(0, 204, 255, 0.7); + color: white; +} + + +#registerForm { + display: flex; + flex-direction: column; +} diff --git a/4-bank-project/index.html b/4-bank-project/index.html new file mode 100644 index 0000000..5ade482 --- /dev/null +++ b/4-bank-project/index.html @@ -0,0 +1,11 @@ + + + + + + Bank App + + + + +