diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..50d065b Binary files /dev/null and b/.DS_Store differ diff --git a/1-js-basics/Assignment_subtask2.html b/1-js-basics/Assignment_subtask2.html new file mode 100644 index 0000000..8072c34 --- /dev/null +++ b/1-js-basics/Assignment_subtask2.html @@ -0,0 +1,33 @@ + + + + + + Personal Website + + + +
+

Home

+

This is my personal Website.

+
+
+

About Me

+

Hi, I'm Naveen

+
+
+

Contact

+

Phone: 1234567890

+

Social

+
+ + \ No newline at end of file diff --git a/1-js-basics/subtask1.md b/1-js-basics/subtask1.md new file mode 100644 index 0000000..91a3a8b --- /dev/null +++ b/1-js-basics/subtask1.md @@ -0,0 +1,123 @@ +1. Subtopic data-types + + Challenge: + The gotchas I found are: + - Difference between == and ===: '==' only checks if it's the same value and doesn't check the data type so int and str can be the same according to it but '===' checks both the value and the data type + - Using parseInt: Parsing strings normally converts them to their numbers but if the string starts with 0x then it takes 16 as the base when the base is generally 10 + + Assignment: + - We will need an **array** to store each iem inside the cart + - We would need an **object** (key-value pair) to store the details of each item +2. Subtopic functions-methods + + Challenge: + A **method** is a **function** that is defined inside an object as a property + + Assignment: + functions that don't return anything: +``` +function HelloWorld() { + console.log("Hello, World!"); +}; +HelloWorld() +``` + functions that return something: +``` +function add(a, b) { + return a+b; +}; +sum=add(5, 5); +console.log(sum); +``` + A function with undefined variables +``` +function Intro(name,age=17, country='India') { + console.log(`Hi, I am ${name}. I am ${age} years old and from ${country}`) +}; +Intro ("Naveen","18","USA"); +Intro("Naveen"); +``` + In the first call the age country are defined so it prints them but in the second call they are not defined so it prints the default values. + +3. Subtopic 3-making-decisions + + Challenge: + A function using logic operators is: +``` +let num=parseInt(prompt("Enter a number: ")); +if (num>0){ + console.log("Positive") +} +else if (num<0){ + console.log("Negative") +} +else{ + console.log("Zero") +} +``` + A function using ternary operators is: +``` +let num=parseInt(prompt("Enter a number: ")); +if (num>0){ + console.log("Positive") +} +else if (num<0){ + console.log("Negative") +} +else{ + console.log("Zero") +} +``` + Assignment: +``` +let allStudents=['A','B-',1,4,5,2] +let studentswhoPassed=[]; +for (i=0;i=3){ + studentswhoPassed.push(allStudents[i]); + } + } + else if (allStudents[i]!='C-') { + studentswhoPassed.push(allStudents[i]); + } +} +console.log(studentswhoPassed) +``` +4. Subtopic arrays-loops + + Challenge: + For loop: +``` +let num=10; +for (i=1;i i + 1); +numbers.forEach(i=>{ + console.log(i); +``` + for-of loop: +``` +let num=10; +const numbers = Array.from({ length: num }, (_, i) => i + 1); +for (const i of numbers) { + console.log(i); +} +``` + map: +``` +let num=10; +const numbers=Array.from({length: num }, (_, i) => i + 1); +numbers.map(i=>console.log(i)); +``` + Assignment: +``` +for (i = 3; i <= 20; i +=3) { + console.log(i) +} +``` \ No newline at end of file diff --git a/2-terrarium/.DS_Store b/2-terrarium/.DS_Store new file mode 100644 index 0000000..447c446 Binary files /dev/null and b/2-terrarium/.DS_Store differ diff --git a/2-terrarium/terrarium-solution/.DS_Store b/2-terrarium/terrarium-solution/.DS_Store new file mode 100644 index 0000000..49e94e4 Binary files /dev/null and b/2-terrarium/terrarium-solution/.DS_Store differ diff --git a/2-terrarium/terrarium-solution/3rd-assignment.md b/2-terrarium/terrarium-solution/3rd-assignment.md new file mode 100644 index 0000000..c608b4d --- /dev/null +++ b/2-terrarium/terrarium-solution/3rd-assignment.md @@ -0,0 +1,24 @@ +# NodeLists + +## What are NodeLists + +- NodeLists are a type of collection in JavaScript that is similar to arrays, but it is a collection of html elements by class, id +- It can be created using queryselectorAll() +- It does not map, filter, reduce methods like arrays +- NodeLists also won't automatically update when the DOM changes so we have to manually update it + +## Uses + +- We can use NodeList to dynamically add ,remove or update elements from the DOM +- We can use NodeLists to loop through elements +- We can create event handlers for each element in the NodeList + +## Applications + +NodeLists are used in various websites like Google and Amazon to interact with **results of a search** or **details of a product** + +### In Google Search +When we search for an item in Google: +1. Each result is a DOM element with a unique tag +2. Using document.querySelectorAll, Google collects all search result elements as a NodeList +3. Google then loops through the NodeList to display each result \ No newline at end of file diff --git a/2-terrarium/terrarium-solution/images/plant1.png b/2-terrarium/terrarium-solution/images/plant1.png new file mode 100644 index 0000000..9baee27 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant1.png differ diff --git a/2-terrarium/terrarium-solution/images/plant10.png b/2-terrarium/terrarium-solution/images/plant10.png new file mode 100644 index 0000000..4b5136d Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant10.png differ diff --git a/2-terrarium/terrarium-solution/images/plant11.png b/2-terrarium/terrarium-solution/images/plant11.png new file mode 100644 index 0000000..3530fe5 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant11.png differ diff --git a/2-terrarium/terrarium-solution/images/plant12.png b/2-terrarium/terrarium-solution/images/plant12.png new file mode 100644 index 0000000..b7f6dfd Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant12.png differ diff --git a/2-terrarium/terrarium-solution/images/plant13.png b/2-terrarium/terrarium-solution/images/plant13.png new file mode 100644 index 0000000..18938b7 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant13.png differ diff --git a/2-terrarium/terrarium-solution/images/plant14.png b/2-terrarium/terrarium-solution/images/plant14.png new file mode 100644 index 0000000..87ccb62 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant14.png differ diff --git a/2-terrarium/terrarium-solution/images/plant2.png b/2-terrarium/terrarium-solution/images/plant2.png new file mode 100644 index 0000000..b90853f Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant2.png differ diff --git a/2-terrarium/terrarium-solution/images/plant3.png b/2-terrarium/terrarium-solution/images/plant3.png new file mode 100644 index 0000000..17e10d1 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant3.png differ diff --git a/2-terrarium/terrarium-solution/images/plant4.png b/2-terrarium/terrarium-solution/images/plant4.png new file mode 100644 index 0000000..4bbafad Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant4.png differ diff --git a/2-terrarium/terrarium-solution/images/plant5.png b/2-terrarium/terrarium-solution/images/plant5.png new file mode 100644 index 0000000..d303d40 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant5.png differ diff --git a/2-terrarium/terrarium-solution/images/plant6.png b/2-terrarium/terrarium-solution/images/plant6.png new file mode 100644 index 0000000..823eeed Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant6.png differ diff --git a/2-terrarium/terrarium-solution/images/plant7.png b/2-terrarium/terrarium-solution/images/plant7.png new file mode 100644 index 0000000..fceb7f2 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant7.png differ diff --git a/2-terrarium/terrarium-solution/images/plant8.png b/2-terrarium/terrarium-solution/images/plant8.png new file mode 100644 index 0000000..40b4f15 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant8.png differ diff --git a/2-terrarium/terrarium-solution/images/plant9.png b/2-terrarium/terrarium-solution/images/plant9.png new file mode 100644 index 0000000..17a3435 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/plant9.png differ diff --git a/2-terrarium/terrarium-solution/images/screenshot_gray.png b/2-terrarium/terrarium-solution/images/screenshot_gray.png new file mode 100644 index 0000000..5d930e1 Binary files /dev/null and b/2-terrarium/terrarium-solution/images/screenshot_gray.png differ diff --git a/2-terrarium/terrarium-solution/index.html b/2-terrarium/terrarium-solution/index.html new file mode 100644 index 0000000..f911577 --- /dev/null +++ b/2-terrarium/terrarium-solution/index.html @@ -0,0 +1,70 @@ + + + + + + Terrarium + + + + +

My Terrarium

+
+
+
+
+
+
+
+
+
+
+
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+ plant +
+
+
+ + \ No newline at end of file diff --git a/2-terrarium/terrarium-solution/script.js b/2-terrarium/terrarium-solution/script.js new file mode 100644 index 0000000..c2cb3d8 --- /dev/null +++ b/2-terrarium/terrarium-solution/script.js @@ -0,0 +1,56 @@ +dragElement(document.getElementById('plant1')); +dragElement(document.getElementById('plant2')); +dragElement(document.getElementById('plant3')); +dragElement(document.getElementById('plant4')); +dragElement(document.getElementById('plant5')); +dragElement(document.getElementById('plant6')); +dragElement(document.getElementById('plant7')); +dragElement(document.getElementById('plant8')); +dragElement(document.getElementById('plant9')); +dragElement(document.getElementById('plant10')); +dragElement(document.getElementById('plant11')); +dragElement(document.getElementById('plant12')); +dragElement(document.getElementById('plant13')); +dragElement(document.getElementById('plant14')); + +function dragElement(terrariumElement) { + let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + + terrariumElement.onpointerdown = function (e) { + e.preventDefault(); + pos3 = e.clientX; + pos4 = e.clientY; + document.onpointermove = elementDrag; + document.onpointerup = stopElementDrag; + }; + + function elementDrag(e) { + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + + terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px'; + terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px'; + } + + function stopElementDrag() { + document.onpointermove = null; + document.onpointerup = null; + } + + terrariumElement.addEventListener('mouseenter', () => { + terrariumElement.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)'; + terrariumElement.style.transform = 'scale(1.1)'; // Slightly enlarge the element. + }); + + terrariumElement.addEventListener('mouseleave', () => { + terrariumElement.style.boxShadow = 'none'; + terrariumElement.style.transform = 'scale(1)'; // Restore to original size. + }); +} + +for (let i = 1; i <= 14; i++) { + const plant = document.getElementById(`plant${i}`); + if (plant) dragElement(plant); +} \ No newline at end of file diff --git a/2-terrarium/terrarium-solution/style.css b/2-terrarium/terrarium-solution/style.css new file mode 100644 index 0000000..9bc0f8b --- /dev/null +++ b/2-terrarium/terrarium-solution/style.css @@ -0,0 +1,111 @@ +h1{ + color: red; + text-align: center; +} + +body { + font-family: helvetica, arial, sans-serif; +} +#left-container { + background-color: #eee; + width: 15%; + left: 0px; + top: 0px; + position: absolute; + height: 100%; + padding: 10px; +} + +#right-container { + background-color: #eee; + width: 20%; + left: 80%; + right: 0px; + top: 0px; + position: absolute; + height: 100%; + padding: 10px; +} +.plant-holder { + position: relative; + width: auto; + height: 14%; + display: flex; + justify-content: space-around; + align-items: center; +} + +.plant { + position: absolute; + max-width: 50%; + height: auto; + z-index: 2; +} + +.jar-walls { + height: 80%; + width: 50%; + background: #d1e1df; + border-radius: 5rem; + position: absolute; + bottom: 0.5%; + left: 20%; + opacity: 0.5; + z-index: 1; + margin-left: 4rem; +} + +.jar-top { + width: 40%; + height: 5%; + position: absolute; + background: #d1e1df; + bottom: 80.5%; + left: 25%; + opacity: 0.7; + z-index: 1; + margin-left: 4%; +} + +.jar-bottom { + width: 43%; + height: 1%; + background: #d1e1df; + position: absolute; + bottom: 0%; + left: 25%; + opacity: 0.7; + margin-left: 3%; +} + +.dirt { + width: 46.5%; + height: 5%; + background: #2f1d18; + position: absolute; + border-radius: 0 0 5rem 5rem; + bottom: 1%; + left: 21%; + opacity: 0.7; + z-index: -1; + margin-left: 5.3%; +} + +.jar-glossy-long, .jar-glossy-short { + position: absolute; + width: 2%; + background: #ffffff; + border-radius: 50px; + opacity: 0.5; + left: 2%; +} + +.jar-glossy-long { + height: 30%; + bottom: 15%; +} + +.jar-glossy-short { + height: 7%; + bottom: 50%; +} \ No newline at end of file diff --git a/3-typing-game/.DS_Store b/3-typing-game/.DS_Store new file mode 100644 index 0000000..bec0415 Binary files /dev/null and b/3-typing-game/.DS_Store differ diff --git a/3-typing-game/typing-game-solution/.DS_Store b/3-typing-game/typing-game-solution/.DS_Store new file mode 100644 index 0000000..8155516 Binary files /dev/null and b/3-typing-game/typing-game-solution/.DS_Store differ diff --git a/3-typing-game/typing-game-solution/assignment-typing-game/index.html b/3-typing-game/typing-game-solution/assignment-typing-game/index.html new file mode 100644 index 0000000..ffd1d5a --- /dev/null +++ b/3-typing-game/typing-game-solution/assignment-typing-game/index.html @@ -0,0 +1,19 @@ + + + + + + Typing Game + + + +
+

Typing Game

+

Press "Start Game" to see your quote!

+ + +

Time: 0s

+
+ + + \ No newline at end of file diff --git a/3-typing-game/typing-game-solution/assignment-typing-game/script.js b/3-typing-game/typing-game-solution/assignment-typing-game/script.js new file mode 100644 index 0000000..5ca51a4 --- /dev/null +++ b/3-typing-game/typing-game-solution/assignment-typing-game/script.js @@ -0,0 +1,62 @@ +const quotes = [ + "There is nothing more deceptive than an obvious fact", + "You see, but you do not observe", + "I am a brain, Watson. The rest of me is a mere appendix", + "The game is afoot!", + "Education never ends, Watson. It is a series of lessons with the greatest for the last" +]; + +const startBtn = document.getElementById("start-btn"); +const quoteElement = document.getElementById("quote"); +const inputElement = document.getElementById("input"); +const timerElement = document.getElementById("timer"); + +let startTime, interval; + +function getRandomQuote() { + const randomIndex = Math.floor(Math.random() * quotes.length); + return quotes[randomIndex]; +} + +function startGame() { + const randomQuote = getRandomQuote(); + quoteElement.textContent = randomQuote; + inputElement.value = ""; + inputElement.disabled = false; + inputElement.focus(); + startBtn.disabled = true; + + startTime = new Date(); + timerElement.textContent = "Time: 0s"; + + interval = setInterval(() => { + const elapsedTime = Math.floor((new Date() - startTime) / 1000); + timerElement.textContent = `Time: ${elapsedTime}s`; + }, 1000); +} + +function calculateWPM(quote, timeInSeconds) { + const words = quote.split(" ").length; // Count the number of words in the quote + const timeInMinutes = timeInSeconds / 60; // Convert time to minutes + return Math.round(words / timeInMinutes); // Calculate WPM +} + +function endGame() { + clearInterval(interval); + const elapsedTime = Math.floor((new Date() - startTime) / 1000); + const wpm = calculateWPM(quoteElement.textContent, elapsedTime); + alert(`You completed the game in ${elapsedTime} seconds! Your WPM is ${wpm}.`); + inputElement.disabled = true; + startBtn.disabled = false; +} + +inputElement.addEventListener("input", () => { + const quoteText = quoteElement.textContent; + const userInput = inputElement.value; + + if (userInput === quoteText) { + endGame(); + } +}); + +startBtn.addEventListener("click", startGame); \ No newline at end of file diff --git a/3-typing-game/typing-game-solution/assignment-typing-game/style.css b/3-typing-game/typing-game-solution/assignment-typing-game/style.css new file mode 100644 index 0000000..8e275a9 --- /dev/null +++ b/3-typing-game/typing-game-solution/assignment-typing-game/style.css @@ -0,0 +1,48 @@ +body { + font-family: Arial, sans-serif; + background-color: #f4f4f9; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; +} + +.container { + text-align: center; + background: #fff; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +textarea { + width: 80%; + height: 100px; + margin-top: 10px; + border: 2px solid #ddd; + border-radius: 5px; + padding: 10px; + font-size: 16px; + resize: none; +} + +button { + margin-top: 10px; + padding: 10px 20px; + background: #007bff; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +button:hover { + background: #0056b3; +} + +#timer { + margin-top: 10px; + font-size: 20px; + font-weight: bold; +} \ No newline at end of file diff --git a/3-typing-game/typing-game-solution/challenge/index.html b/3-typing-game/typing-game-solution/challenge/index.html new file mode 100644 index 0000000..3210abe --- /dev/null +++ b/3-typing-game/typing-game-solution/challenge/index.html @@ -0,0 +1,20 @@ + + + + + + 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/typing-game-solution/challenge/script.js b/3-typing-game/typing-game-solution/challenge/script.js new file mode 100644 index 0000000..39ac62f --- /dev/null +++ b/3-typing-game/typing-game-solution/challenge/script.js @@ -0,0 +1,51 @@ +// inside script.js +// all of our quotes +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'); + +// at the end of script.js +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; + + // 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 = ''; + + // Setup the textbox + // Clear the textbox + typedValueElement.value = ''; + // set focus + typedValueElement.focus(); + // set the event handler + + // Start the timer + startTime = new Date().getTime(); +}); \ No newline at end of file diff --git a/3-typing-game/typing-game-solution/challenge/style.css b/3-typing-game/typing-game-solution/challenge/style.css new file mode 100644 index 0000000..5cd1796 --- /dev/null +++ b/3-typing-game/typing-game-solution/challenge/style.css @@ -0,0 +1,9 @@ +/* inside style.css */ +.highlight { + background-color: yellow; +} + +.error { + background-color: lightcoral; + border: red; +} \ No newline at end of file diff --git a/4-bank-project/.DS_Store b/4-bank-project/.DS_Store new file mode 100644 index 0000000..3dce8f9 Binary files /dev/null and b/4-bank-project/.DS_Store differ diff --git a/4-bank-project/bank-project/Index.html b/4-bank-project/bank-project/Index.html new file mode 100644 index 0000000..b9e5602 --- /dev/null +++ b/4-bank-project/bank-project/Index.html @@ -0,0 +1,101 @@ + + + + + + Bank App + + + + +
Loading...
+ + + + + + diff --git a/4-bank-project/bank-project/app.js b/4-bank-project/bank-project/app.js new file mode 100644 index 0000000..9a930b3 --- /dev/null +++ b/4-bank-project/bank-project/app.js @@ -0,0 +1,239 @@ +let state = Object.freeze({ + account: null +}); + +const storageKey = 'savedAccount'; +const routes = { + '/login': { templateId: 'login' }, + '/dashboard': { templateId: 'dashboard', init: refresh }, + '/send-money': { templateId: 'send-money' }, +}; + +function onLinkClick(event) { + event.preventDefault(); + const path = event.target.getAttribute('href'); + navigate(path); +}; + +function navigate(path) { + window.history.pushState({}, path, path); + updateRoute(); +}; + +function updateRoute() { + const path = window.location.pathname; + const route = routes[path]; + + if (!route) { + return navigate('/login'); + } + + const template = document.getElementById(route.templateId); + const view = template.content.cloneNode(true); + const app = document.getElementById('app'); + app.innerHTML = ''; + app.appendChild(view); + + if (route.init) { + route.init(); + } +}; + +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); + updateState('account', result); + + if (result.error) { + if (result.error == 'User already exists') { + return console.log('This one already exists'); + } + return console.log('An error occurred:', result.error); + } + console.log('Account created!', result); + navigate('/dashboard'); +}; + +async function createAccount(account) { + try { + const response = await fetch('//localhost:8080/api/accounts', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: account + }); + return await response.json(); + } catch (error) { + return { error: error.message || 'Unknown error' }; + } +} + +async function getAccount(user) { + try { + const response = await fetch('//localhost:8080/api/accounts/' + encodeURIComponent(user)); + return await response.json(); + } catch (error) { + return { error: error.message || 'Unknown error' }; + } +} + +async function login() { + const loginForm = document.getElementById('loginForm'); + const user = loginForm.user.value; + const data = await getAccount(user); + + if (data.error) { + return console.log('loginError', data.error); + } + + updateState('account', data); + navigate('/dashboard'); +} + +function updateElement(id, textOrNode) { + const element = document.getElementById(id); + if (!element) { + console.warn(`Element with id '${id}' not found`); + return; + } + element.textContent = ''; + element.append(textOrNode); +} + +function updateDashboard() { + const account = state.account; + + if (!account) { + return navigate('/login'); + } + + const elements = { + description: document.getElementById('description'), + name: document.getElementById('name'), + balance: document.getElementById('balance'), + currency: document.getElementById('currency'), + transactions: document.getElementById('transactions') + }; + + if (elements.description) elements.description.textContent = account.description || ''; + if (elements.name) elements.name.textContent = account.user || ''; + if (elements.balance) elements.balance.textContent = account.balance.toFixed(2); + if (elements.currency) elements.currency.textContent = account.currency; + + const transactionsBody = document.querySelector('#transactions tbody'); + if (transactionsBody) { + transactionsBody.innerHTML = ''; + const transactionsRows = document.createDocumentFragment(); + for (const transaction of account.transactions) { + const transactionRow = createTransactionRow(transaction); + transactionsRows.appendChild(transactionRow); + } + transactionsBody.appendChild(transactionsRows); + } +} + +function createTransactionRow(transaction) { + const template = document.getElementById('transaction'); + if (!template) { + console.warn('Transaction template not found'); + return document.createElement('tr'); + } + + const transactionRow = template.content.cloneNode(true); + const tr = transactionRow.querySelector('tr'); + if (!tr) { + console.warn('Transaction row structure not found'); + return document.createElement('tr'); + } + + const dateCell = tr.querySelector('.date'); + const objectCell = tr.querySelector('.object'); + const amountCell = tr.querySelector('.amount'); + + if (dateCell) dateCell.textContent = transaction.date; + if (objectCell) objectCell.textContent = transaction.object; + if (amountCell) amountCell.textContent = transaction.amount.toFixed(2); + + return transactionRow; +} + +function updateState(property, newData) { + state = Object.freeze({ + ...state, + [property]: newData + }); + localStorage.setItem(storageKey, JSON.stringify(state.account)); +} + +function logout() { + updateState('account', null); + navigate('/login'); +} + +function init() { + const savedAccount = localStorage.getItem(storageKey); + if (savedAccount) { + updateState('account', JSON.parse(savedAccount)); + } + + window.onpopstate = () => updateRoute(); + updateRoute(); +} + +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(); +} + +async function addTransaction() { + const transactionForm = document.getElementById('transactionForm'); + const formData = new FormData(transactionForm); + const data = Object.fromEntries(formData); + const jsonData = JSON.stringify({ + date: data.date, + object: data.object, + amount: parseFloat(data.amount) + }); + + const account = state.account; + + try { + const response = await fetch(`//localhost:8080/api/accounts/${encodeURIComponent(account.user)}/transactions`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: jsonData + }); + + const result = await response.json(); + if (result.error) { + return console.log('Error adding transaction:', result.error); + } + await refresh(); + console.log('Transaction added:', result); + + account.balance += result.amount; + account.transactions.push(result); + updateState('account', account); + updateDashboard(); + } catch (error) { + console.log('Error:', error.message); + } +} + +init(); \ No newline at end of file diff --git a/4-bank-project/bank-project/bsconfig.json b/4-bank-project/bank-project/bsconfig.json new file mode 100644 index 0000000..b0062f1 --- /dev/null +++ b/4-bank-project/bank-project/bsconfig.json @@ -0,0 +1,5 @@ +{ + "port": 8080, + "files": ["./**/*.{html,htm,css,js}"], + "server": { "baseDir": "./" } +} \ No newline at end of file diff --git a/4-bank-project/bank-project/package.json b/4-bank-project/bank-project/package.json new file mode 100644 index 0000000..f9e14dc --- /dev/null +++ b/4-bank-project/bank-project/package.json @@ -0,0 +1,12 @@ +{ + "name": "bank-project", + "version": "1.0.0", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "" +} diff --git a/4-bank-project/bank-project/style.css b/4-bank-project/bank-project/style.css new file mode 100644 index 0000000..d459f70 --- /dev/null +++ b/4-bank-project/bank-project/style.css @@ -0,0 +1,123 @@ +h1{ + color: rgb(255, 255, 255); + display: flex; + justify-content: center; +} +body{ + background-color: rgb(0, 115, 207); +} +header{ + display: flex; + flex-direction: row; + align-items: center; + padding: 10px; + justify-content: space-between; +} +.header-buttons{ + display: flex; + gap: 20px; +} +#Login-Register-Forms{ + background-color: rgb(255, 255, 255); + border-radius: 20px; + padding: 30px; + width: 23%; + margin-left: 37%; +} +section{ + background-color: rgb(255, 255, 255); + border-radius: 20px; + padding: 10px; + width: 100%; + margin-top: 20px; +} +h2{ + color: rgb(0, 115, 207); +} +button{ + background-color: rgb(0, 0, 0); + color: white; + border: none; + border-radius: 10px; + height: 40px; + width: 80px; + margin-top: 20px; + +} +#loginForm button , #registerForm button{ + background-color: rgb(0, 115, 207); + color: white; + border: none; + border-radius: 10px; + height: 40px; + width: 80px; + margin-top: 20px; +} +button:hover{ + cursor: pointer; + border-width: 3px; + border-color: black; + border-style: solid; + transition: 1s; +} +form{ + max-width: 200px; +} +label{ + color: black; + font-size: large; +} +input{ + border-radius: 10px; + border-width: 1px; + height: 40px; + width: 300px; + font-size: medium; +} +input:hover{ + border-width: 2px; + border-color: black; + border-style: solid; + transition: 1s; +} +input:focus{ + outline: none; +} +#loginError{ + display: none; + color: red; + font-size: larger; + margin-top: 20px; + height: 1.5rem; +} +navbar{ + background-color: (101, 60, 117); +} + +table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; + font-size: 18px; + text-align: left; +} + +thead tr { + background-color: #f2f2f2; + border-bottom: 2px solid #ddd; +} + +th, td { + padding: 12px 15px; + border: 1px solid #ddd; +} + +tbody tr:hover { + background-color: #f9f9f9; +} + +#balance, #currency { + font-weight: bold; + font-size: 20px; + color: #333; +} \ No newline at end of file