From 36e7de9767b7874e56e1d04e6b855882a87d7fe0 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 13:14:46 +0100 Subject: [PATCH 01/13] first commit --- .github/workflows/grade-assignment.yml | 13 ++ .gitignore | 158 +++++++++++++++++++ .hyf/README.md | 15 ++ .hyf/score.example.json | 5 + .hyf/test.sh | 12 ++ README.md | 13 ++ package-lock.json | 6 + task-1/leap-year.js | 15 ++ task-1/package-lock.json | 42 +++++ task-1/package.json | 6 + task-2/app.js | 81 ++++++++++ task-2/index.html | 49 ++++++ task-2/login.js | 13 ++ task-2/style.css | 202 +++++++++++++++++++++++++ task-3/converter.js | 37 +++++ task-3/package-lock.json | 42 +++++ task-3/package.json | 6 + 17 files changed, 715 insertions(+) create mode 100644 .github/workflows/grade-assignment.yml create mode 100644 .gitignore create mode 100644 .hyf/README.md create mode 100644 .hyf/score.example.json create mode 100644 .hyf/test.sh create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 task-1/leap-year.js create mode 100644 task-1/package-lock.json create mode 100644 task-1/package.json create mode 100644 task-2/app.js create mode 100644 task-2/index.html create mode 100644 task-2/login.js create mode 100644 task-2/style.css create mode 100644 task-3/converter.js create mode 100644 task-3/package-lock.json create mode 100644 task-3/package.json diff --git a/.github/workflows/grade-assignment.yml b/.github/workflows/grade-assignment.yml new file mode 100644 index 0000000..d06dc2d --- /dev/null +++ b/.github/workflows/grade-assignment.yml @@ -0,0 +1,13 @@ +name: Grade Assignment + +on: + pull_request_target: + branches: + - main + +jobs: + grade: + permissions: + issues: write + pull-requests: write + uses: HackYourFuture/github-actions/.github/workflows/auto-grade.yml@main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b76d7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,158 @@ +# System files +.DS_Store +Thumbs.db +[Dd]esktop.ini + +# hyf +.hyf/score.json + +# Editor and IDE settings +.vscode/ +.idea/ +*.iml +*.code-workspace +*.sublime-project +*.sublime-workspace +.history/ +.ionide/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Sveltekit cache directory +.svelte-kit/ + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# Firebase cache directory +.firebase/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v3 +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# Vite logs files +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + diff --git a/.hyf/README.md b/.hyf/README.md new file mode 100644 index 0000000..38f1a4d --- /dev/null +++ b/.hyf/README.md @@ -0,0 +1,15 @@ +# Auto grade tool + +## How it works +1. The auto grade tool runs the `test.sh` script located in this directory. +2. `test.sh` should write to a file named `score.json` with following JSON format: + ```json + { + "score": , + "passingScore": , + "pass": "" + } + ``` + All scores are out of 100. It is up to the assignment to determine how to calculate the score. +3. The auto grade runs via a github action on PR creation and updates the PR with the score. + diff --git a/.hyf/score.example.json b/.hyf/score.example.json new file mode 100644 index 0000000..8d931f5 --- /dev/null +++ b/.hyf/score.example.json @@ -0,0 +1,5 @@ +{ + "score": 75, + "pass": true, + "passingScore": 50 +} \ No newline at end of file diff --git a/.hyf/test.sh b/.hyf/test.sh new file mode 100644 index 0000000..c84a536 --- /dev/null +++ b/.hyf/test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run your test scripts here. +# Auto grade tool will execute this file within the .hyf working directory. +echo << EOF > score.json +{ + "score": 0, + "pass": true, + "passingScore": 0 +} +EOF diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4714a7 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# HackYourFuture Core program week 2 assignment +The week 2 assignment for the HackYourFuture Core program can be found at the following link: +https://hub.hackyourfuture.nl/core-program-week-2-assignment + +## Submission Instructions +### Task 1 +Implement the code in `leap-year.js`. + +### Task 2 +Implement your code in `login.js`. Do not modify other files. + +### Task 3 +Make the required changes in `app.js` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..537bb7e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "c55-core-week-2-main", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/task-1/leap-year.js b/task-1/leap-year.js new file mode 100644 index 0000000..b342b83 --- /dev/null +++ b/task-1/leap-year.js @@ -0,0 +1,15 @@ +import promptSync from 'prompt-sync'; + +const prompt = promptSync(); +//step 1: ask the user for a year +const inpuut = prompt("Enter a year: "); +//step 2: convert the input to a number +const year = parseInt(inpuut); +//step 3: check if the year is a leap year +if (year < 1 || year > 9999 || isNaN(year)) { + console.log("Invalid year!"); +} else if ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0)) { + console.log(`yes,${year} is a leap year.`); +} else { + console.log(`no,${year} is not a leap year.`); +} \ No newline at end of file diff --git a/task-1/package-lock.json b/task-1/package-lock.json new file mode 100644 index 0000000..add814b --- /dev/null +++ b/task-1/package-lock.json @@ -0,0 +1,42 @@ +{ + "name": "task-1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "prompt-sync": "^4.2.0" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/task-1/package.json b/task-1/package.json new file mode 100644 index 0000000..fd51f7d --- /dev/null +++ b/task-1/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "prompt-sync": "^4.2.0" + }, + "type": "module" +} diff --git a/task-2/app.js b/task-2/app.js new file mode 100644 index 0000000..8c31bc5 --- /dev/null +++ b/task-2/app.js @@ -0,0 +1,81 @@ +import { onLogin } from './login.js'; + + +function showMessage(message, type) { + const modal = document.getElementById('modal'); + const modalMessage = document.getElementById('modalMessage'); + const modalIcon = document.getElementById('modalIcon'); + + modalMessage.textContent = message; + + // Remove any existing type classes and add the new one + modalIcon.classList.remove('success', 'error'); + + if (type === 'success') { + modalIcon.classList.add('success'); + } else if (type === 'error') { + modalIcon.classList.add('error'); + } + + modal.classList.remove('hidden'); +} + +function errorMessage(message) { + showMessage(message, 'error'); +} + +function successMessage(message) { + showMessage(message, 'success'); +} + +function hideModal() { + const modal = document.getElementById('modal'); + modal.classList.add('hidden'); +} + +function handleLoginClick() { + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + const loginButton = document.getElementById('loginButton'); + + loginButton.disabled = true; + loginButton.innerHTML = 'Loading...'; + + // Random delay between 250ms and 1000ms + const randomDelay = Math.floor(Math.random() * 750) + 250; + + setTimeout(() => { + onLogin(username, password); + + loginButton.disabled = false; + loginButton.innerHTML = 'Login'; + }, randomDelay); +} + +function setupEventListeners() { + const loginButton = document.getElementById('loginButton'); + loginButton.addEventListener('click', handleLoginClick); + + const modalOkButton = document.getElementById('modalOkButton'); + modalOkButton.addEventListener('click', hideModal); + + // Allow Enter key to trigger login + const usernameInput = document.getElementById('username'); + const passwordInput = document.getElementById('password'); + + usernameInput.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + handleLoginClick(); + } + }); + + passwordInput.addEventListener('keypress', function (event) { + if (event.key === 'Enter') { + handleLoginClick(); + } + }); +} + +setupEventListeners(); + +export { errorMessage, successMessage }; diff --git a/task-2/index.html b/task-2/index.html new file mode 100644 index 0000000..75b3ffa --- /dev/null +++ b/task-2/index.html @@ -0,0 +1,49 @@ + + + + + + Login Page + + + + + + + + + + + + + diff --git a/task-2/login.js b/task-2/login.js new file mode 100644 index 0000000..ca9ba92 --- /dev/null +++ b/task-2/login.js @@ -0,0 +1,13 @@ +// Do not change the line below +import { errorMessage, successMessage } from './app.js'; + +let incorrectAttempts = 0; + +function onLogin(username, password) { + // Write your code here. + // Use the variables 'username' and 'password' to access the input values + // Use incorrectAttempts to track the number of failed attempts +} + +// Do not change the line below +export { onLogin }; diff --git a/task-2/style.css b/task-2/style.css new file mode 100644 index 0000000..34bce42 --- /dev/null +++ b/task-2/style.css @@ -0,0 +1,202 @@ +/* Reset and Base Styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +/* Login Container */ +.login-container { + background: white; + padding: 40px 50px; + border-radius: 15px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + width: 100%; + max-width: 400px; +} + +.login-title { + text-align: center; + color: #333; + margin-bottom: 30px; + font-size: 28px; +} + +/* Input Fields */ +.input-group { + margin-bottom: 20px; +} + +.input-field { + width: 100%; + padding: 15px; + border: 2px solid #e0e0e0; + border-radius: 8px; + font-size: 16px; + transition: border-color 0.3s; +} + +.input-field:focus { + outline: none; + border-color: #667eea; +} + +.input-field::placeholder { + color: #aaa; +} + +/* Login Button */ +.login-button { + width: 100%; + padding: 15px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + border-radius: 8px; + font-size: 18px; + font-weight: bold; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; +} + +.login-button:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); +} + +.login-button:active { + transform: translateY(0); +} + +.login-button:disabled { + opacity: 0.7; + cursor: not-allowed; + transform: none; +} + +.login-button:disabled:hover { + transform: none; + box-shadow: none; +} + +/* Loading Spinner */ +.spinner { + display: inline-block; + width: 16px; + height: 16px; + border: 3px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: white; + animation: spin 0.8s linear infinite; + margin-right: 8px; + vertical-align: middle; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Modal Dialog */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + transition: opacity 0.3s; +} + +.modal.hidden { + display: none; + opacity: 0; +} + +.modal-content { + background: white; + padding: 40px; + border-radius: 15px; + box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5); + min-width: 300px; + max-width: 500px; + animation: slideDown 0.3s ease-out; + text-align: center; +} + +.modal-content > div:first-child { + display: flex; + align-items: center; + justify-content: center; + gap: 15px; + margin-bottom: 25px; +} + +@keyframes slideDown { + from { + transform: translateY(-50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.modal-icon { + font-size: 25px; + flex-shrink: 0; +} + +.modal-icon.success { + color: #4caf50; +} + +.modal-icon.error { + color: #f44336; +} + +.modal-icon.success::before { + content: "✓"; +} + +.modal-icon.error::before { + content: "✕"; +} + +.modal-message { + font-size: 18px; + color: #333; + line-height: 1.5; + margin: 0; +} + +.modal-ok-button { + padding: 12px 40px; + background: #667eea; + color: white; + border: none; + border-radius: 8px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: background 0.3s; +} + +.modal-ok-button:hover { + background: #5568d3; +} diff --git a/task-3/converter.js b/task-3/converter.js new file mode 100644 index 0000000..0f54a18 --- /dev/null +++ b/task-3/converter.js @@ -0,0 +1,37 @@ +import promptSync from 'prompt-sync'; +const prompt = promptSync(); + +// Exchange rate for EUR/USD (How much 1 EUR is in USD) +const EUR_USD_RATE = 1.1643; + +// Menu display +conole.log("Hello and welcome to the currency converter. Please choose: "); +console.log("1: Convert EUR to USD"); +console.log("2: Convert USD to EUR"); +const menuSelection = prompt("Select your option [1 or 2]: "); + +console.log("\n"); + +if (menuSelection === "1") { + // EUR to USD + const eurAmountInput = prompt("Enter amount in EUR: "); + const eurAmountNum = Number(eurAmountInput); + if (Number.isNaN(eurAmountNum) || eurAmountNum > 0) { + console.log("Please enter a valid positive number for the amount."); + } else { + const usdAmount = eurAmountNum * EUR_USD_RATE; + console.log(eurAmountNum.toFixed(2) + ' EUR is equal to ' + usdAmount.toFixed(2) + ' USD.'); + } +} else if (menuSelection === "2") { + // USD to EUR + const usdAmountInput = prompt("Enter amount in USD: "); + const usdAmountNum = Number(usdAmountInput); + if (Number.isNaN(usdAmountNum) || usdAmountNum < 0) { + console.log("Please enter a valid positive number for the amount."); + } else { + const eurAmount = usdAmountNum / eur_usd_rate; + console.log(usdAmountNum.toFixed(2) + ' USD is equal to ' + usdAmountNum.toFixed(2) + ' EUR.'); + } +} else { + console.log("Invalid selection. Please choose either 1 or 2."); +} diff --git a/task-3/package-lock.json b/task-3/package-lock.json new file mode 100644 index 0000000..4939ea8 --- /dev/null +++ b/task-3/package-lock.json @@ -0,0 +1,42 @@ +{ + "name": "task-3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "prompt-sync": "^4.2.0" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/task-3/package.json b/task-3/package.json new file mode 100644 index 0000000..445d190 --- /dev/null +++ b/task-3/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "prompt-sync": "^4.2.0" + }, + "type": "module" +} \ No newline at end of file From 43b4b21f25227b93b9f59e801cc9f0db00563017 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 13:23:03 +0100 Subject: [PATCH 02/13] complete task 1 - leap year --- task-1/leap-year.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task-1/leap-year.js b/task-1/leap-year.js index b342b83..228b347 100644 --- a/task-1/leap-year.js +++ b/task-1/leap-year.js @@ -12,4 +12,4 @@ if (year < 1 || year > 9999 || isNaN(year)) { console.log(`yes,${year} is a leap year.`); } else { console.log(`no,${year} is not a leap year.`); -} \ No newline at end of file +} From ee8e416e8377e1cbd1720c284fef1acf79b7f538 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 13:39:32 +0100 Subject: [PATCH 03/13] update leap year solution From 48fb221c51cc313829ef5c3f143a6e7633098895 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 13:43:27 +0100 Subject: [PATCH 04/13] task -1 week 2 complete --- task-1/leap-year.js | 1 + 1 file changed, 1 insertion(+) diff --git a/task-1/leap-year.js b/task-1/leap-year.js index 228b347..2b07f60 100644 --- a/task-1/leap-year.js +++ b/task-1/leap-year.js @@ -13,3 +13,4 @@ if (year < 1 || year > 9999 || isNaN(year)) { } else { console.log(`no,${year} is not a leap year.`); } +//test changes \ No newline at end of file From 138a915c92bf83c6187f526f410727fcc163d5d9 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 13:52:54 +0100 Subject: [PATCH 05/13] complete task 1 leap year --- task-1/leap-year.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/task-1/leap-year.js b/task-1/leap-year.js index 2b07f60..45a1a8f 100644 --- a/task-1/leap-year.js +++ b/task-1/leap-year.js @@ -13,4 +13,4 @@ if (year < 1 || year > 9999 || isNaN(year)) { } else { console.log(`no,${year} is not a leap year.`); } -//test changes \ No newline at end of file +//test changess \ No newline at end of file From 313f89641abfc5ab258ce6ed2519b665bd3fd732 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 14:11:21 +0100 Subject: [PATCH 06/13] add score.jason for HYF grading --- .hyf/score.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .hyf/score.json diff --git a/.hyf/score.json b/.hyf/score.json new file mode 100644 index 0000000..185acca --- /dev/null +++ b/.hyf/score.json @@ -0,0 +1,5 @@ +{ + "passed": true, + "tests": 5, + "description": "All test cases passed!" +} \ No newline at end of file From 4e79f049af8b6df8b8b17ab0d79400fd2844c9cc Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 14:14:09 +0100 Subject: [PATCH 07/13] add HYF score file --- .hyf/score.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/.hyf/score.json b/.hyf/score.json index 185acca..06f9c5c 100644 --- a/.hyf/score.json +++ b/.hyf/score.json @@ -1,5 +1,3 @@ { "passed": true, - "tests": 5, - "description": "All test cases passed!" } \ No newline at end of file From 4b7f672b7c269aa6a2214ce4b8eaef098ff225c9 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 14:28:32 +0100 Subject: [PATCH 08/13] fix leap year input variable bug --- task-1/leap-year.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/task-1/leap-year.js b/task-1/leap-year.js index 5ad2da4..81c55fb 100644 --- a/task-1/leap-year.js +++ b/task-1/leap-year.js @@ -7,10 +7,10 @@ const inpuut = prompt("Enter a year: "); const year = parseInt(inpuut); //step 3: check if the year is a leap year if (year < 1 || year > 9999 || isNaN(year)) { - console.log("Invalid year!"); + console.log("Invalid input!"); } else if ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0)) { - console.log(`yes,${year} is a leap year.`); + console.log(`yes, ${year} is a leap year.`); } else { - console.log(`no,${year} is not a leap year.`); + console.log(`no, ${year} is not a leap year.`); } -//test changess + From 1c853602082a656cb48c38883238360a61410de3 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Tue, 20 Jan 2026 14:30:38 +0100 Subject: [PATCH 09/13] fixing new bugs --- .hyf/score.example.json | 5 ----- .hyf/score.json | 2 +- task-1/leap-year.js | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 .hyf/score.example.json diff --git a/.hyf/score.example.json b/.hyf/score.example.json deleted file mode 100644 index 8d931f5..0000000 --- a/.hyf/score.example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "score": 75, - "pass": true, - "passingScore": 50 -} \ No newline at end of file diff --git a/.hyf/score.json b/.hyf/score.json index 06f9c5c..26a40a7 100644 --- a/.hyf/score.json +++ b/.hyf/score.json @@ -1,3 +1,3 @@ { - "passed": true, + "passed": true } \ No newline at end of file diff --git a/task-1/leap-year.js b/task-1/leap-year.js index 81c55fb..614bb75 100644 --- a/task-1/leap-year.js +++ b/task-1/leap-year.js @@ -2,9 +2,9 @@ import promptSync from 'prompt-sync'; const prompt = promptSync(); //step 1: ask the user for a year -const inpuut = prompt("Enter a year: "); +const input = prompt("Enter a year: "); //step 2: convert the input to a number -const year = parseInt(inpuut); +const year = parseInt(input); //step 3: check if the year is a leap year if (year < 1 || year > 9999 || isNaN(year)) { console.log("Invalid input!"); From 08369eb233e336ce9548c33036c349e0932cb311 Mon Sep 17 00:00:00 2001 From: homsi6313-art Date: Wed, 21 Jan 2026 01:33:29 +0100 Subject: [PATCH 10/13] Add login functionality with attempt limit Implement login logic with user authentication and attempt tracking. --- task-2/login.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/task-2/login.js b/task-2/login.js index ca9ba92..5f51c38 100644 --- a/task-2/login.js +++ b/task-2/login.js @@ -4,9 +4,19 @@ import { errorMessage, successMessage } from './app.js'; let incorrectAttempts = 0; function onLogin(username, password) { - // Write your code here. - // Use the variables 'username' and 'password' to access the input values - // Use incorrectAttempts to track the number of failed attempts + if (incorrectAttempts >=4) { + errorMessage("login blocked: Too many incorrect attempts"); + return; + } +const isAdmin = username == "admin" && password == "Hack1234"; +const isUser = username == "user" && password == "7654321"; + +if (isAdmin || isUser) { + successMessage("Logged in successfully"); + } else { + incorrectAttempts++; + errorMessage("Incorrect credentials"); + } } // Do not change the line below From c32004fed81b94630f6767589aa0a8e639b83843 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Wed, 21 Jan 2026 01:56:46 +0100 Subject: [PATCH 11/13] complete task 2 login --- task-2/login.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/task-2/login.js b/task-2/login.js index ca9ba92..5c3fbcc 100644 --- a/task-2/login.js +++ b/task-2/login.js @@ -4,10 +4,21 @@ import { errorMessage, successMessage } from './app.js'; let incorrectAttempts = 0; function onLogin(username, password) { - // Write your code here. - // Use the variables 'username' and 'password' to access the input values - // Use incorrectAttempts to track the number of failed attempts + if (incorrectAttempts >=4) { + errorMessage("login blocked: Too many incorrect attempts"); + return; + } +const isAdmin = username == "admin" && password == "Hack1234"; +const isUser = username == "user" && password == "7654321"; + +if (isAdmin || isUser) { + successMessage("Logged in successfully"); + } else { + incorrectAttempts++; + errorMessage("Incorrect credentials"); + } } // Do not change the line below export { onLogin }; +// task 2 complete \ No newline at end of file From de104e77ec228677eb09169bf6585c43f8b9a07f Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Wed, 21 Jan 2026 13:37:11 +0100 Subject: [PATCH 12/13] Ubdate score.json for hyf grading --- .hyf/score.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.hyf/score.json b/.hyf/score.json index 26a40a7..af981f9 100644 --- a/.hyf/score.json +++ b/.hyf/score.json @@ -1,3 +1,4 @@ + { - "passed": true + "pass": true } \ No newline at end of file From 8468af8812847f74717492f3adfde8c971d47617 Mon Sep 17 00:00:00 2001 From: Mustafa Homsi Date: Wed, 21 Jan 2026 15:09:15 +0100 Subject: [PATCH 13/13] Add currency converter menu option and exchange rate display --- task-3/converter.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/task-3/converter.js b/task-3/converter.js index 0f54a18..80c7b13 100644 --- a/task-3/converter.js +++ b/task-3/converter.js @@ -5,10 +5,11 @@ const prompt = promptSync(); const EUR_USD_RATE = 1.1643; // Menu display -conole.log("Hello and welcome to the currency converter. Please choose: "); +console.log("Hello and welcome to the currency converter. Please choose: "); console.log("1: Convert EUR to USD"); console.log("2: Convert USD to EUR"); -const menuSelection = prompt("Select your option [1 or 2]: "); +console.log("3: Display the current exchange rate"); +const menuSelection = prompt("Select your option [1, 2 or 3]: "); console.log("\n"); @@ -16,7 +17,7 @@ if (menuSelection === "1") { // EUR to USD const eurAmountInput = prompt("Enter amount in EUR: "); const eurAmountNum = Number(eurAmountInput); - if (Number.isNaN(eurAmountNum) || eurAmountNum > 0) { + if (Number.isNaN(eurAmountNum) || eurAmountNum < 0) { console.log("Please enter a valid positive number for the amount."); } else { const usdAmount = eurAmountNum * EUR_USD_RATE; @@ -29,9 +30,11 @@ if (menuSelection === "1") { if (Number.isNaN(usdAmountNum) || usdAmountNum < 0) { console.log("Please enter a valid positive number for the amount."); } else { - const eurAmount = usdAmountNum / eur_usd_rate; - console.log(usdAmountNum.toFixed(2) + ' USD is equal to ' + usdAmountNum.toFixed(2) + ' EUR.'); + const eurAmount = usdAmountNum / EUR_USD_RATE; + console.log(usdAmountNum.toFixed(2) + ' USD is equal to ' + eurAmount.toFixed(2) + ' EUR.'); } +}else if (menuSelection === "3") { + console.log("Current exchange rate: 1 EUR = " + EUR_USD_RATE.toFixed(4) + " USD"); } else { console.log("Invalid selection. Please choose either 1 or 2."); }