diff --git a/4-bank-project/1-template-route/SOLUTION.md b/4-bank-project/1-template-route/SOLUTION.md
new file mode 100644
index 0000000..d853b33
--- /dev/null
+++ b/4-bank-project/1-template-route/SOLUTION.md
@@ -0,0 +1,17 @@
+## Assignment
+
+```javascript
+document.title = route.title;
+```
+This code in the updateRoute function sets the title of the window when template changes.
+In the same function we can write code to do the necessary template specific functions.
+We can also set a specific function to the route by passing the function pointer (without parenthesis) and call the function here if it exists.
+```
+if (route.templateId == 'dashboard') {
+ console.log('Dashboard is shown');
+}
+```
+
+## Challenge
+
+Using a new template I was able to easily show the credit page.
\ No newline at end of file
diff --git a/4-bank-project/1-template-route/app.js b/4-bank-project/1-template-route/app.js
new file mode 100644
index 0000000..6507841
--- /dev/null
+++ b/4-bank-project/1-template-route/app.js
@@ -0,0 +1,43 @@
+
+const routes = {
+ '/login': { templateId: 'login', title: 'Login' },
+ '/dashboard': { templateId: 'dashboard', title: 'Dashboard' },
+ '/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;
+
+ if (route.templateId == 'dashboard') {
+ console.log('Dashboard is shown');
+ }
+
+ const template = document.getElementById(route.templateId);
+ const view = template.content.cloneNode(true);
+ const app = document.getElementById('app');
+ app.innerHTML = '';
+ app.appendChild(view);
+}
+
+function navigate(path) {
+ window.history.pushState({}, path, path);
+
+ updateRoute();
+}
+
+function onLinkClick(event) {
+ event.preventDefault();
+ navigate(event.target.href);
+}
+
+window.onpopstate = () => updateRoute();
+updateRoute();
+
+// updateRoute('login');
\ No newline at end of file
diff --git a/4-bank-project/1-template-route/index.html b/4-bank-project/1-template-route/index.html
new file mode 100644
index 0000000..9d9dc76
--- /dev/null
+++ b/4-bank-project/1-template-route/index.html
@@ -0,0 +1,164 @@
+
+
+
+
+
+ Bank App
+
+
+
+ Loading...
+
+ Bank App
+
+
+
+
+ Credits
+
+
+ Welcome
+
+ Myscript2010
+
+ LEARN EDITING SCRIPT
+
+ WITH CODE
+
+ CSS HTML EFFECT
+
+ SOURCES CODE
+
+ CODEPEN GITHUB
+
+ SOURCE NEXT
+
+ ALL SOURCES CODE
+
+ CODE EFFECT
+
+ EFFECT SHINE
+
+ CODE EFFECT
+
+ EFFECT ROTATE
+
+ CODE EFFECT
+
+ EFFECT SLIDE
+
+ CODE EFFECT
+
+ EFFECT HOVER
+
+ CODE EFFECT
+
+ EFFECT TRANSISI
+
+ CODE EFFECT
+
+ EFFECT FLIP
+
+ CODE EFFECT
+
+ EFFECT TOOLTIP
+
+ CODE EFFECT
+
+ EFFECT RADIAL
+
+ CODE EFFECT
+
+ EFFECT MULTIPLE
+
+ CODE EFFECT
+
+ EFFECT ANIMATED
+
+ CODE EFFECT
+
+ EFFECT EXPANDING
+
+ CODE EFFECT
+
+ EFFECT TRANSFORMS
+
+ CODE EFFECT
+
+ EFFECT OVERLAY
+
+ CODE EFFECT
+
+ EFFECT LOADING
+
+ CODE EFFECT
+
+ EFFECT CIRCLE
+
+ CODE EFFECT
+
+ EFFECT NAPIGATION
+
+ CODE EFFECT
+
+ EFFECT DROPDOWN
+
+ CODE EFFECT
+
+ EFFECT LIGHTING
+
+ CODE EFFECT
+
+ EFFECT ZOOM
+
+ CODE EFFECT
+
+ EFFECT SCALE
+
+ CODE EFFECT
+
+ EFFECT OPACITY
+
+ CODE EFFECT
+
+ EFFECT FLASHING
+
+ CODE EFFECT
+
+ EFFCET SMOKE
+
+ CODE EDITED BY.
+
+ MYSCRIPT2010
+
+
+
+
+
+
+
+
+ Transactions
+
+
+
+ Date
+ Object
+ Amount
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/4-bank-project/2-forms/SOLUTION.md b/4-bank-project/2-forms/SOLUTION.md
new file mode 100644
index 0000000..e4e0afe
--- /dev/null
+++ b/4-bank-project/2-forms/SOLUTION.md
@@ -0,0 +1,8 @@
+## Assignment
+
+I first added the css file for each page in routes so that when document loads, the javascript will just change the current href of the link tag.
+I was able to make a movie like credits scene from a project in codepen.
+
+## Challenge
+
+I created a div which would be initially hidden and when the user enters an existing username, I check the error in result of register function.
\ No newline at end of file
diff --git a/4-bank-project/2-forms/app.js b/4-bank-project/2-forms/app.js
new file mode 100644
index 0000000..0f0486e
--- /dev/null
+++ b/4-bank-project/2-forms/app.js
@@ -0,0 +1,75 @@
+
+const routes = {
+ '/login': { templateId: 'login', title: 'Login', style: 'style.css' },
+ '/dashboard': { templateId: 'dashboard', title: 'Dashboard' },
+ '/credits': { templateId: 'credits', title: 'Credits', style: 'credits.css' },
+};
+
+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 == "User already exists")
+ {
+ document.getElementById('userError').hidden = false;
+ } else {
+ console.log('Account created!', result);
+ }
+}
+
+async function createAccount(account) {
+ try {
+ const response = await fetch('//localhost:5000/api/accounts', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: account
+ });
+ return await response.json();
+ } catch (error) {
+ return { error: error.message || 'Unknown error' };
+ }
+}
+
+function updateRoute() {
+ const path = window.location.pathname;
+ const route = routes[path];
+
+ if (!route) {
+ return navigate('/login');
+ }
+
+ if (route.style) {
+ const style = document.getElementById('stylesheet');
+ style.href = route.style;
+ }
+
+ document.title = route.title;
+
+ if (route.templateId == 'dashboard') {
+ console.log('Dashboard is shown');
+ }
+
+ const template = document.getElementById(route.templateId);
+ const view = template.content.cloneNode(true);
+ const app = document.getElementById('app');
+ app.innerHTML = '';
+ app.appendChild(view);
+}
+
+function navigate(path) {
+ window.history.pushState({}, path, path);
+
+ updateRoute();
+}
+
+function onLinkClick(event) {
+ event.preventDefault();
+ navigate(event.target.href);
+}
+
+window.onpopstate = () => updateRoute();
+updateRoute();
+
+// updateRoute('login');
\ No newline at end of file
diff --git a/4-bank-project/2-forms/credits.css b/4-bank-project/2-forms/credits.css
new file mode 100644
index 0000000..c724f00
--- /dev/null
+++ b/4-bank-project/2-forms/credits.css
@@ -0,0 +1,52 @@
+.wrapper {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ width: 400px;
+ margin-left: -200px;
+ text-align: center;
+ text-transform: uppercase;
+ color: white;
+ animation: 60s credits linear infinite;
+}
+
+h1 {
+ color: white;
+}
+
+* {
+ font: 300 30px/1 'Open Sans Condensed', sans-serif;
+}
+
+.movie {
+ margin-bottom: 50px;
+ font-size: 50px;
+}
+
+.job {
+ margin-bottom: 5px;
+ font-size: 18px;
+}
+
+.name {
+ margin-bottom: 50px;
+ font-size: 35px;
+}
+
+@keyframes credits {
+ 0% {
+ top: 100%;
+ }
+ 100% {
+ top: -500%;
+ }
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ background: radial-gradient(ellipse at top left, #334455 0%, #112233 100%);
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/4-bank-project/2-forms/index.html b/4-bank-project/2-forms/index.html
new file mode 100644
index 0000000..1c437b6
--- /dev/null
+++ b/4-bank-project/2-forms/index.html
@@ -0,0 +1,189 @@
+
+
+
+
+
+ Bank App
+
+
+
+
+ Loading...
+
+
+
Bank App
+
+
+
LOGIN
+
+
OR
+
REGISTER
+
+
+
+
+
+
+
+
+
+
+ Transactions
+
+
+
+ Date
+ Object
+ Amount
+
+
+
+
+
+
+
+
+ Credits
+
+
+ Welcome
+
+ My script
+
+ LEARN EDITING SCRIPT
+
+ WITH CODE
+
+ CSS HTML EFFECT
+
+ SOURCES CODE
+
+ CODEPEN GITHUB
+
+ SOURCE NEXT
+
+ ALL SOURCES CODE
+
+ CODE EFFECT
+
+ EFFECT SHINE
+
+ CODE EFFECT
+
+ EFFECT ROTATE
+
+ CODE EFFECT
+
+ EFFECT SLIDE
+
+ CODE EFFECT
+
+ EFFECT HOVER
+
+ CODE EFFECT
+
+ EFFECT TRANSISI
+
+ CODE EFFECT
+
+ EFFECT FLIP
+
+ CODE EFFECT
+
+ EFFECT TOOLTIP
+
+ CODE EFFECT
+
+ EFFECT RADIAL
+
+ CODE EFFECT
+
+ EFFECT MULTIPLE
+
+ CODE EFFECT
+
+ EFFECT ANIMATED
+
+ CODE EFFECT
+
+ EFFECT EXPANDING
+
+ CODE EFFECT
+
+ EFFECT TRANSFORMS
+
+ CODE EFFECT
+
+ EFFECT OVERLAY
+
+ CODE EFFECT
+
+ EFFECT LOADING
+
+ CODE EFFECT
+
+ EFFECT CIRCLE
+
+ CODE EFFECT
+
+ EFFECT NAPIGATION
+
+ CODE EFFECT
+
+ EFFECT DROPDOWN
+
+ CODE EFFECT
+
+ EFFECT LIGHTING
+
+ CODE EFFECT
+
+ EFFECT ZOOM
+
+ CODE EFFECT
+
+ EFFECT SCALE
+
+ CODE EFFECT
+
+ EFFECT OPACITY
+
+ CODE EFFECT
+
+ EFFECT FLASHING
+
+ CODE EFFECT
+
+ EFFCET SMOKE
+
+ CODE EDITED BY.
+
+ MYSCRIPT2010
+
+
+
+
+
+
\ No newline at end of file
diff --git a/4-bank-project/2-forms/style.css b/4-bank-project/2-forms/style.css
new file mode 100644
index 0000000..10f83f2
--- /dev/null
+++ b/4-bank-project/2-forms/style.css
@@ -0,0 +1,128 @@
+.full {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+#container {
+ background-color:aliceblue;
+ border-radius: 10px;
+ padding: 30px;
+ width: 50%;
+ height: fit-content;
+ min-width: 200px;
+ max-width: 400px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+
+hr {
+ width: 50%;
+ border: none;
+ border-top: 2px solid #ccc;
+ margin: 20px 0;
+}
+
+#userError {
+ color: red;
+}
+.or {
+ text-align: center;
+ font-weight: bold;
+ margin: 20px 0;
+ position: relative;
+ color: #666;
+}
+
+.or::before,
+.or::after {
+ content: "";
+ display: block;
+ width: 44%;
+ height: 1px;
+ background-color: #000000;
+ position: absolute;
+}
+
+.or::before {
+ left: 0%;
+}
+
+.or::after {
+ right: 0%;
+}
+
+label {
+ width: 100%;
+ display: block;
+ text-align: left;
+ margin-top: 16px;
+ margin-bottom: 5px;
+}
+
+input {
+ border-radius: 10px;
+ margin: auto;
+ padding: 10px;
+ width: 100%;
+}
+
+h1 {
+ color: #3584c9;
+}
+
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+}
+
+button {
+ background-color: #1f91ea;
+ color: white;
+ padding: 10px;
+ border-radius: 10px;
+ margin-top: 16px;
+ border: none;
+ cursor: pointer;
+}
+
+body {
+ background-color: #38a3e7;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ overflow: hidden;
+}
+
+input, button {
+ width: 100%;
+ box-sizing: border-box;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
+
+footer {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ width: 100%;
+ text-align: left;
+}
+
+footer>a {
+ color: white;
+ text-decoration: none;
+ border: none;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/4-bank-project/3-data/SOLUTION.md b/4-bank-project/3-data/SOLUTION.md
new file mode 100644
index 0000000..45641a9
--- /dev/null
+++ b/4-bank-project/3-data/SOLUTION.md
@@ -0,0 +1,7 @@
+## Challenge
+
+I used min function to set the width of the container dynamically if the window resizes. I also aligned the credits under the bank app header because if the window is small, the credits would display over the container.
+
+## Assignment
+
+I declared the base url global and replaced the complete url with string concatenation. To create a common function for both, I analyzed the parameters and made a params for methods, headers and body.
\ No newline at end of file
diff --git a/4-bank-project/3-data/app.js b/4-bank-project/3-data/app.js
new file mode 100644
index 0000000..288e975
--- /dev/null
+++ b/4-bank-project/3-data/app.js
@@ -0,0 +1,163 @@
+let account = null;
+const baseURL = '//localhost:5000';
+const accountURL = 'api/accounts';
+
+const routes = {
+ '/login': { templateId: 'login', title: 'Login', style: 'login.css' },
+ '/dashboard': { templateId: 'dashboard', title: 'Dashboard', style: 'dashboard.css', init: updateDashboard },
+ '/credits': { templateId: 'credits', title: 'Credits', style: 'credits.css' },
+};
+
+
+// Helper function to get element by id and update its text content
+function updateElement(id, textOrNode)
+{
+ const element = document.getElementById(id);
+ element.textContent = '';
+ element.hidden = false;
+ element.append(textOrNode);
+}
+
+
+// Gets login form data and sends a request to the server
+async function login()
+{
+ const loginForm = document.getElementById('loginForm')
+ const user = loginForm.user.value;
+ const data = await sendRequest('/api/accounts/' + encodeURIComponent(user));
+
+ if (data.error) {
+ updateElement('loginError', data.error);
+ return console.log('loginError', data.error);
+ }
+
+ account = data;
+ navigate('/dashboard');
+}
+
+
+// Gets register form data and sends a POST request to the server
+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 sendRequest('/api/accounts', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: jsonData
+ });
+
+ if (result.error)
+ {
+ updateElement('userError', result.error);
+
+ } else {
+ console.log('Account created!', result);
+ account = result;
+ navigate('/dashboard');
+ }
+}
+
+
+// Helper function to send a request to the server
+async function sendRequest(url, params = {}) {
+ try {
+ console.log('Sending request to:', baseURL + url);
+ const response = await fetch(baseURL + url, params);
+ return await response.json();
+ } catch (error) {
+ return { error: error.message || 'Unknown error' };
+ }
+}
+
+
+// Clones the transaction template and fills it with data
+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;
+}
+
+
+// Updates the dashboard with the account data
+function updateDashboard()
+{
+ if (!account) {
+ return navigate('/login');
+ }
+
+ 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);
+}
+
+
+// On navigation, update the route and the view using templates
+function updateRoute()
+{
+ const path = window.location.pathname;
+ const route = routes[path];
+
+ // When invalid path or home page
+ if (!route) {
+ return navigate('/login');
+ }
+
+ document.title = route.title;
+
+ if (route.templateId == 'dashboard') {
+ console.log('Dashboard is shown');
+ }
+
+ const template = document.getElementById(route.templateId);
+ const view = template.content.cloneNode(true);
+ const app = document.getElementById('app');
+ app.innerHTML = '';
+ app.appendChild(view);
+
+ if (route.style) {
+ const style = document.getElementById('stylesheet');
+ style.href = route.style;
+ }
+
+ if (typeof route.init === 'function') {
+ route.init();
+ }
+}
+
+
+// Navigate to the given path
+function navigate(path)
+{
+ window.history.pushState({}, path, path);
+ updateRoute();
+}
+
+
+// Prevent default behavior and navigate to the href
+function onLinkClick(event)
+{
+ event.preventDefault();
+ navigate(event.target.href);
+}
+
+
+// Event listener for back or forward buttons
+window.onpopstate = () => updateRoute();
+updateRoute();
\ No newline at end of file
diff --git a/4-bank-project/3-data/credits.css b/4-bank-project/3-data/credits.css
new file mode 100644
index 0000000..c724f00
--- /dev/null
+++ b/4-bank-project/3-data/credits.css
@@ -0,0 +1,52 @@
+.wrapper {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ width: 400px;
+ margin-left: -200px;
+ text-align: center;
+ text-transform: uppercase;
+ color: white;
+ animation: 60s credits linear infinite;
+}
+
+h1 {
+ color: white;
+}
+
+* {
+ font: 300 30px/1 'Open Sans Condensed', sans-serif;
+}
+
+.movie {
+ margin-bottom: 50px;
+ font-size: 50px;
+}
+
+.job {
+ margin-bottom: 5px;
+ font-size: 18px;
+}
+
+.name {
+ margin-bottom: 50px;
+ font-size: 35px;
+}
+
+@keyframes credits {
+ 0% {
+ top: 100%;
+ }
+ 100% {
+ top: -500%;
+ }
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ background: radial-gradient(ellipse at top left, #334455 0%, #112233 100%);
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/4-bank-project/3-data/dashboard.css b/4-bank-project/3-data/dashboard.css
new file mode 100644
index 0000000..25f3e89
--- /dev/null
+++ b/4-bank-project/3-data/dashboard.css
@@ -0,0 +1,150 @@
+header {
+ background-color: #434454;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+#app {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+
+}
+
+#description {
+ color: #59727e;
+}
+
+#transaction-header {
+ background-color: azure;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+#transactions-section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: #effefe;
+ padding: 15px 0 25px 0;
+ width: 100%;
+}
+
+#dashboard-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 50%;
+ background-color: #effefe;
+ padding: 0px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+}
+
+h1 {
+ margin: 0%;
+ color: white;
+}
+
+td, th {
+ padding: 10px;
+}
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+}
+
+a {
+ background-color: transparent;
+ color: white;
+ font-weight: bold;
+ border: 1px solid white;
+ padding: 10px;
+ border-radius: 10px;
+ text-decoration: none;
+}
+
+#add-transaction {
+ background-color: #258ad9;
+ color: white;
+ font-weight: bold;
+ border: 1px solid white;
+ padding: 10px;
+ border-radius: 10px;
+ cursor: pointer;
+}
+
+table {
+ background-color: #f5f5f5;
+ border-collapse: collapse;
+ border-radius: 10px;
+ width: 80%;
+ padding: 10px;
+}
+
+
+
+tr:nth-child(even):not(:has(hr)) {
+ background-color: #cfe5f2;
+}
+
+body {
+ display: flex;
+ height: 100vh;
+ margin: 0;
+ justify-content: center;
+ align-items: center;
+ background-color: #38a3e7;
+ overflow: hidden;
+}
+
+section {
+ text-align: center;
+}
+
+#account {
+ background-image: linear-gradient(to right, #79bce7, #2193eb, #79bce7);
+ padding: 10px;
+ width: 100%;
+ color: white;
+ box-sizing: border-box;
+}
+
+#currency {
+ font-size: xx-large;
+}
+
+#balance {
+ font-size: 4rem;
+ font-weight: bold;
+}
+
+footer {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ width: 100%;
+ text-align: left;
+}
+
+footer>a {
+ color: white;
+ text-decoration: none;
+ border: none;
+ padding: 0%;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
\ No newline at end of file
diff --git a/4-bank-project/3-data/index.html b/4-bank-project/3-data/index.html
new file mode 100644
index 0000000..420b1ba
--- /dev/null
+++ b/4-bank-project/3-data/index.html
@@ -0,0 +1,207 @@
+
+
+
+
+
+ Bank App
+
+
+
+
+ Loading...
+
+ Bank App
+
+
+
+
LOGIN
+
+
OR
+
REGISTER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Date
+ Object
+ Amount
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Credits
+
+
+ Welcome
+
+ My script
+
+ LEARN EDITING SCRIPT
+
+ WITH CODE
+
+ CSS HTML EFFECT
+
+ SOURCES CODE
+
+ CODEPEN GITHUB
+
+ SOURCE NEXT
+
+ ALL SOURCES CODE
+
+ CODE EFFECT
+
+ EFFECT SHINE
+
+ CODE EFFECT
+
+ EFFECT ROTATE
+
+ CODE EFFECT
+
+ EFFECT SLIDE
+
+ CODE EFFECT
+
+ EFFECT HOVER
+
+ CODE EFFECT
+
+ EFFECT TRANSISI
+
+ CODE EFFECT
+
+ EFFECT FLIP
+
+ CODE EFFECT
+
+ EFFECT TOOLTIP
+
+ CODE EFFECT
+
+ EFFECT RADIAL
+
+ CODE EFFECT
+
+ EFFECT MULTIPLE
+
+ CODE EFFECT
+
+ EFFECT ANIMATED
+
+ CODE EFFECT
+
+ EFFECT EXPANDING
+
+ CODE EFFECT
+
+ EFFECT TRANSFORMS
+
+ CODE EFFECT
+
+ EFFECT OVERLAY
+
+ CODE EFFECT
+
+ EFFECT LOADING
+
+ CODE EFFECT
+
+ EFFECT CIRCLE
+
+ CODE EFFECT
+
+ EFFECT NAPIGATION
+
+ CODE EFFECT
+
+ EFFECT DROPDOWN
+
+ CODE EFFECT
+
+ EFFECT LIGHTING
+
+ CODE EFFECT
+
+ EFFECT ZOOM
+
+ CODE EFFECT
+
+ EFFECT SCALE
+
+ CODE EFFECT
+
+ EFFECT OPACITY
+
+ CODE EFFECT
+
+ EFFECT FLASHING
+
+ CODE EFFECT
+
+ EFFCET SMOKE
+
+ CODE EDITED BY.
+
+ MYSCRIPT2010
+
+
+
+
+
+
\ No newline at end of file
diff --git a/4-bank-project/3-data/login.css b/4-bank-project/3-data/login.css
new file mode 100644
index 0000000..11d2303
--- /dev/null
+++ b/4-bank-project/3-data/login.css
@@ -0,0 +1,123 @@
+#login-section, #loginForm, #registerForm, .or, #app {
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+#container {
+ background-color:aliceblue;
+ border-radius: 10px;
+ padding: 30px;
+ width: min(60%, 400px);;
+ height: fit-content;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+
+hr {
+ width: 50%;
+ border: none;
+ border-top: 2px solid #ccc;
+ margin: 20px 0;
+}
+
+#userError, #loginError {
+ color: red;
+}
+.or {
+ text-align: center;
+ font-weight: bold;
+ margin: 20px 0;
+ position: relative;
+ color: #666;
+}
+
+.or::before,
+.or::after {
+ content: "";
+ display: block;
+ width: 44%;
+ height: 1px;
+ background-color: #000000;
+ position: absolute;
+}
+
+.or::before {
+ left: 0%;
+}
+
+.or::after {
+ right: 0%;
+}
+
+label {
+ width: 100%;
+ display: block;
+ text-align: left;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+input {
+ border-radius: 10px;
+ margin: auto;
+ width: 100%;
+}
+
+h1 {
+ color: #3584c9;
+ margin: 0%;
+}
+
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+}
+
+footer {
+ width: 100%;
+ padding-bottom: 20px;
+ text-align: center;
+}
+
+footer>a {
+ color: #3584c9;
+ text-decoration: none;
+ border: none;
+ font-weight: bold;
+}
+
+button {
+ background-color: #1f91ea;
+ color: white;
+ border-radius: 10px;
+ border: none;
+ cursor: pointer;
+}
+
+body {
+ background-color: #38a3e7;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ overflow: hidden;
+}
+
+input, button {
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/SOLUTION.md b/4-bank-project/4-state-management/SOLUTION.md
new file mode 100644
index 0000000..d2f59d7
--- /dev/null
+++ b/4-bank-project/4-state-management/SOLUTION.md
@@ -0,0 +1,7 @@
+## Assignment
+
+Using a new transaction template, I was able to make entry for new transactions. Then I used the helper function to forward the form to /api/accounts/"user"/transactions. After that i navigated to the dashboard for the results.
+
+## Challenge
+
+I removed storing all the attributes other than username because for login we only needed username. I also fixed an bug where if we reload the page when the state.account is null but account exist in local storage, then the user will get logged out.
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/app.js b/4-bank-project/4-state-management/app.js
new file mode 100644
index 0000000..8a7ac99
--- /dev/null
+++ b/4-bank-project/4-state-management/app.js
@@ -0,0 +1,231 @@
+let state = Object.freeze({
+ account: null
+});
+const baseURL = '//localhost:5000';
+const accountURL = 'api/accounts';
+const storageKey = 'savedAccount';
+
+const routes = {
+ '/login': { templateId: 'login', title: 'Login', style: 'login.css' },
+ '/dashboard': { templateId: 'dashboard', title: 'Dashboard', style: 'dashboard.css', init: refresh },
+ '/transactions': { templateId: 'transaction-template', title: 'Transactions', style: 'transaction.css' },
+ '/credits': { templateId: 'credits', title: 'Credits', style: 'credits.css' },
+};
+
+
+// Helper function to get element by id and update its text content
+function updateElement(id, textOrNode)
+{
+ const element = document.getElementById(id);
+ element.textContent = '';
+ element.hidden = false;
+ element.append(textOrNode);
+}
+
+
+// Gets login form data and sends a request to the server
+async function login()
+{
+ const loginForm = document.getElementById('loginForm')
+ const user = loginForm.user.value;
+ const data = await sendRequest('/api/accounts/' + encodeURIComponent(user));
+
+ if (data.error) {
+ updateElement('loginError', data.error);
+ return console.log('loginError', data.error);
+ }
+
+ // account = data;
+ localStorage.setItem(storageKey, user);
+ updateState('account', data);
+ navigate('/dashboard');
+}
+
+function logout() {
+ localStorage.removeItem(storageKey);
+ updateState('account', null);
+ navigate('/login');
+}
+
+// Gets register form data and sends a POST request to the server
+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 sendRequest('/api/accounts', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: jsonData
+ });
+
+ if (result.error)
+ {
+ updateElement('userError', result.error);
+
+ } else {
+ console.log('Account created!', result);
+ // account = result;
+ localStorage.setItem(storageKey, result.user);
+ updateState('account', result);
+ navigate('/dashboard');
+ }
+}
+
+// Helper function to send a request to the server
+async function sendRequest(endpoint, options = {}) {
+ try {
+ console.log('Sending request to:', baseURL + endpoint);
+ const response = await fetch(baseURL + endpoint, options);
+ return await response.json();
+ } catch (error) {
+ return { error: error.message || 'Unknown error' };
+ }
+}
+
+
+function newTransaction()
+{
+ const transactionForm = document.getElementById('transaction-form');
+ const formData = new FormData(transactionForm);
+ const data = Object.fromEntries(formData);
+ const jsonData = JSON.stringify(data);
+ const result = sendRequest('/api/accounts/' + encodeURIComponent(state.account.user) + '/transactions', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: jsonData
+ });
+ if (result.error)
+ {
+ console.log('Error creating transaction', result);
+ }
+ refresh();
+ navigate('/dashboard');
+}
+
+// Clones the transaction template and fills it with data
+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;
+}
+
+
+// Updates the dashboard with the account data
+function updateDashboard()
+{
+ const account = state.account;
+ if (!account) {
+ return navigate('/login');
+ }
+
+ 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);
+}
+
+async function updateAccountData() {
+ const account = state.account;
+ if (!account) {
+ return logout();
+ }
+
+ const data = await sendRequest('/api/accounts/' + encodeURIComponent(account.user));
+ if (data.error) {
+ return logout();
+ }
+
+ updateState('account', data);
+}
+
+function updateState(property, newData) {
+ state = Object.freeze({
+ ...state,
+ [property]: newData
+ });
+ localStorage.setItem(storageKey, JSON.stringify(state.account));
+}
+
+// On navigation, update the route and the view using templates
+function updateRoute()
+{
+ const path = window.location.pathname;
+ const route = routes[path];
+
+ // When invalid path or home page
+ if (!route) {
+ return navigate('/dashboard');
+ }
+
+ document.title = route.title;
+
+ if (route.templateId == 'dashboard') {
+ console.log('Dashboard is shown');
+ }
+
+ const template = document.getElementById(route.templateId);
+ const view = template.content.cloneNode(true);
+ const app = document.getElementById('app');
+ app.innerHTML = '';
+ app.appendChild(view);
+
+ if (route.style) {
+ const style = document.getElementById('stylesheet');
+ style.href = route.style;
+ }
+
+ if (typeof route.init === 'function') {
+ route.init();
+ }
+}
+
+async function refresh() {
+ await updateAccountData();
+ updateDashboard();
+}
+
+// Navigate to the given path
+function navigate(path)
+{
+ window.history.pushState({}, path, path);
+ updateRoute();
+}
+
+
+// Prevent default behavior and navigate to the href
+function onLinkClick(event)
+{
+ event.preventDefault();
+ navigate(event.target.href);
+}
+
+
+// Checks localStorage for an account
+async function init() {
+ const savedAccount = localStorage.getItem(storageKey);
+ if (savedAccount) {
+ // local storage account exists but is not in state
+ // This allows to reload page and still be logged in
+ updateState('account', JSON.parse(savedAccount));
+ await updateAccountData();
+ }
+ window.onpopstate = () => updateRoute();
+ updateRoute();
+}
+
+init();
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/credits.css b/4-bank-project/4-state-management/credits.css
new file mode 100644
index 0000000..c724f00
--- /dev/null
+++ b/4-bank-project/4-state-management/credits.css
@@ -0,0 +1,52 @@
+.wrapper {
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ width: 400px;
+ margin-left: -200px;
+ text-align: center;
+ text-transform: uppercase;
+ color: white;
+ animation: 60s credits linear infinite;
+}
+
+h1 {
+ color: white;
+}
+
+* {
+ font: 300 30px/1 'Open Sans Condensed', sans-serif;
+}
+
+.movie {
+ margin-bottom: 50px;
+ font-size: 50px;
+}
+
+.job {
+ margin-bottom: 5px;
+ font-size: 18px;
+}
+
+.name {
+ margin-bottom: 50px;
+ font-size: 35px;
+}
+
+@keyframes credits {
+ 0% {
+ top: 100%;
+ }
+ 100% {
+ top: -500%;
+ }
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ background: radial-gradient(ellipse at top left, #334455 0%, #112233 100%);
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/dashboard.css b/4-bank-project/4-state-management/dashboard.css
new file mode 100644
index 0000000..f70de6f
--- /dev/null
+++ b/4-bank-project/4-state-management/dashboard.css
@@ -0,0 +1,156 @@
+header {
+ background-color: #434454;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+#app {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+#description {
+ color: #59727e;
+}
+
+#transaction-header {
+ background-color: azure;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+#transactions-section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background-color: #effefe;
+ padding: 15px 0 25px 0;
+ width: 100%;
+ overflow-y: scroll;
+ max-height: 100%;
+}
+
+#dashboard-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: min(70%, 500px);
+ height: 80%;
+ min-width: 300px;
+ background-color: #effefe;
+ padding: 0px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ box-sizing: border-box;
+}
+
+h1 {
+ margin: 0%;
+ color: white;
+}
+
+td, th {
+ padding: 10px;
+}
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+}
+
+a {
+ background-color: transparent;
+ color: white;
+ font-weight: bold;
+ border: 1px solid white;
+ padding: 10px;
+ border-radius: 10px;
+ text-decoration: none;
+}
+
+#add-transaction {
+ background-color: #258ad9;
+ color: white;
+ font-weight: bold;
+ border: 1px solid white;
+ padding: 10px;
+ border-radius: 10px;
+ cursor: pointer;
+}
+
+table {
+ background-color: #f5f5f5;
+ border-collapse: collapse;
+ border-radius: 10px;
+ width: 80%;
+ padding: 10px;
+}
+
+
+
+tr:nth-child(even):not(:has(hr)) {
+ background-color: #cfe5f2;
+}
+
+body {
+ display: flex;
+ height: 100vh;
+ margin: 0;
+ justify-content: center;
+ align-items: center;
+ background-color: #38a3e7;
+ overflow: hidden;
+}
+
+section {
+ text-align: center;
+}
+
+#account {
+ background-image: linear-gradient(to right, #79bce7, #2193eb, #79bce7);
+ padding: 10px;
+ width: 100%;
+ color: white;
+ box-sizing: border-box;
+}
+
+#currency {
+ font-size: xx-large;
+}
+
+#balance {
+ font-size: 4rem;
+ font-weight: bold;
+}
+
+footer {
+ position: absolute;
+ bottom: 10px;
+ left: 10px;
+ width: 100%;
+ text-align: left;
+}
+
+footer>a {
+ color: white;
+ text-decoration: none;
+ border: none;
+ padding: 0%;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/index.html b/4-bank-project/4-state-management/index.html
new file mode 100644
index 0000000..132f18d
--- /dev/null
+++ b/4-bank-project/4-state-management/index.html
@@ -0,0 +1,226 @@
+
+
+
+
+
+ Bank App
+
+
+
+
+ Loading...
+
+
+ Bank App
+
+
+
LOGIN
+
+
OR
+
REGISTER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Date
+ Object
+ Amount
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Credits
+
+
+ Welcome
+
+ My script
+
+ LEARN EDITING SCRIPT
+
+ WITH CODE
+
+ CSS HTML EFFECT
+
+ SOURCES CODE
+
+ CODEPEN GITHUB
+
+ SOURCE NEXT
+
+ ALL SOURCES CODE
+
+ CODE EFFECT
+
+ EFFECT SHINE
+
+ CODE EFFECT
+
+ EFFECT ROTATE
+
+ CODE EFFECT
+
+ EFFECT SLIDE
+
+ CODE EFFECT
+
+ EFFECT HOVER
+
+ CODE EFFECT
+
+ EFFECT TRANSISI
+
+ CODE EFFECT
+
+ EFFECT FLIP
+
+ CODE EFFECT
+
+ EFFECT TOOLTIP
+
+ CODE EFFECT
+
+ EFFECT RADIAL
+
+ CODE EFFECT
+
+ EFFECT MULTIPLE
+
+ CODE EFFECT
+
+ EFFECT ANIMATED
+
+ CODE EFFECT
+
+ EFFECT EXPANDING
+
+ CODE EFFECT
+
+ EFFECT TRANSFORMS
+
+ CODE EFFECT
+
+ EFFECT OVERLAY
+
+ CODE EFFECT
+
+ EFFECT LOADING
+
+ CODE EFFECT
+
+ EFFECT CIRCLE
+
+ CODE EFFECT
+
+ EFFECT NAPIGATION
+
+ CODE EFFECT
+
+ EFFECT DROPDOWN
+
+ CODE EFFECT
+
+ EFFECT LIGHTING
+
+ CODE EFFECT
+
+ EFFECT ZOOM
+
+ CODE EFFECT
+
+ EFFECT SCALE
+
+ CODE EFFECT
+
+ EFFECT OPACITY
+
+ CODE EFFECT
+
+ EFFECT FLASHING
+
+ CODE EFFECT
+
+ EFFCET SMOKE
+
+ CODE EDITED BY.
+
+ MYSCRIPT2010
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/login.css b/4-bank-project/4-state-management/login.css
new file mode 100644
index 0000000..3ec8105
--- /dev/null
+++ b/4-bank-project/4-state-management/login.css
@@ -0,0 +1,121 @@
+#app, #login-section, #login-container, #loginForm , #registerForm, .or{
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+#login-container {
+ background-color:aliceblue;
+ border-radius: 10px;
+ padding: 30px;
+ height: fit-content;
+ width: min(60%, 400px);;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+
+hr {
+ width: 50%;
+ border: none;
+ border-top: 2px solid #ccc;
+ margin: 20px 0;
+}
+
+#userError, #loginError {
+ color: red;
+}
+.or {
+ text-align: center;
+ font-weight: bold;
+ margin: 20px 0;
+ position: relative;
+ color: #666;
+}
+
+.or::before, .or::after {
+ content: "";
+ display: block;
+ width: 44%;
+ height: 1px;
+ background-color: #000000;
+ position: absolute;
+}
+
+.or::before {
+ left: 0%;
+}
+
+.or::after {
+ right: 0%;
+}
+
+label {
+ width: 100%;
+ display: block;
+ text-align: left;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+input {
+ border-radius: 10px;
+ margin: auto;
+ border-width: 1px;
+}
+
+h1 {
+ color: #3584c9;
+ margin: 0%;
+}
+
+footer {
+ width: 100%;
+ padding-bottom: 20px;
+ text-align: center;
+}
+
+footer>a {
+ color: #3584c9;
+ text-decoration: none;
+ border: none;
+ font-weight: bold;
+}
+
+button {
+ background-color: #1f91ea;
+ color: white;
+ border-radius: 10px;
+ border: none;
+ cursor: pointer;
+}
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+}
+
+body {
+ overflow: hidden;
+ background-color: #38a3e7;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+}
+
+input, button {
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
\ No newline at end of file
diff --git a/4-bank-project/4-state-management/transaction.css b/4-bank-project/4-state-management/transaction.css
new file mode 100644
index 0000000..d4e4fad
--- /dev/null
+++ b/4-bank-project/4-state-management/transaction.css
@@ -0,0 +1,77 @@
+
+#transaction-container {
+ width: 60%;
+ vertical-align: middle;
+ height: min-content;
+ display: flex;
+ flex-direction: column;
+ min-width: 250px;
+ background-color: #effefe;
+ border-radius: 10px;
+}
+
+#transaction-container {
+ padding: 30px;
+}
+
+label {
+ width: 100%;
+ display: block;
+ text-align: left;
+ margin-top: 18px;
+ margin-bottom: 5px;
+ font-size: small;
+}
+
+input, a{
+ border-radius: 10px;
+ margin: auto;
+ padding: 10px;
+ width: 100%;
+ box-sizing: border-box;
+ border-width: 1px;
+}
+
+#button-container {
+ display: flex;
+ flex-direction: row;
+}
+
+a {
+ text-decoration: none;
+ box-sizing:content-box;
+ text-align: center;
+ background-color: #1f91ea;
+ color: white;
+ width: 20%;
+ border: none;
+ font-weight: bold;
+ margin-top: 18px;
+ cursor: pointer;
+}
+
+#transaction-cancel {
+ background-color: #effefe;
+ color: #1f91ea;
+}
+
+h2 {
+ padding: 0%;
+ margin: 0%;
+ color: #3584c9;
+ font-weight: bold;
+ text-align: center;
+}
+
+body {
+ display: flex;
+ height: 100vh;
+ margin: 0;
+ justify-content: center;
+ align-items: center;
+ background-color: #38a3e7;
+}
+
+* {
+ font-family: 'Arial', sans-serif;
+}
\ No newline at end of file