diff --git a/Sprint-3/alarmclock/alarmclock.js b/Sprint-3/alarmclock/alarmclock.js index 6ca81cd3b..cf5e43935 100644 --- a/Sprint-3/alarmclock/alarmclock.js +++ b/Sprint-3/alarmclock/alarmclock.js @@ -1,4 +1,73 @@ -function setAlarm() {} +// Constants for time conversion +const SECONDS_PER_MINUTE = 60; +const MILLISECONDS_PER_SECOND = 1000; + +let intervalId; + +/** + * Formats time in seconds to MM:SS format + */ +function formatTimeDisplay(totalSeconds) { + const minutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE); + const seconds = totalSeconds % SECONDS_PER_MINUTE; + + return ( + "Time Remaining: " + + String(minutes).padStart(2, "0") + + ":" + + String(seconds).padStart(2, "0") + ); +} + +function setAlarm() { + const input = document.getElementById("alarmSet"); + const heading = document.getElementById("timeRemaining"); + const errorMsg = document.getElementById("alarmError"); + + // Reset alarm sound + flashing background before new countdown + pauseAlarm(); + + let raw = input.value.trim(); + + // Clear previous error + errorMsg.textContent = ""; + + // STRONG VALIDATION: must be digits only + if (!/^\d+$/.test(raw)) { + heading.innerText = "Time Remaining: 00:00"; + errorMsg.textContent = + "Invalid input. Please enter a whole number of seconds (e.g., 10, 30, 120). Decimals and text are not allowed."; + return; + } + + let totalSeconds = Number(raw); + + // Prevent extremely large or zero values + if (totalSeconds === 0 || totalSeconds > 86400) { + heading.innerText = "Time Remaining: 00:00"; + errorMsg.textContent = + "Please enter a value between 1 and 86,400 seconds. Examples: 10, 45, 300."; + return; + } + + // Reset any existing countdown + clearInterval(intervalId); + + heading.innerText = formatTimeDisplay(totalSeconds); + + intervalId = setInterval(() => { + totalSeconds--; + + if (totalSeconds <= 0) { + heading.innerText = formatTimeDisplay(0); + clearInterval(intervalId); + playAlarm(); + return; + } + + heading.innerText = formatTimeDisplay(totalSeconds); + }, MILLISECONDS_PER_SECOND); +} // DO NOT EDIT BELOW HERE @@ -12,14 +81,21 @@ function setup() { document.getElementById("stop").addEventListener("click", () => { pauseAlarm(); }); + + // Allow Enter key to trigger alarm + document.getElementById("alarmSet").addEventListener("keyup", (e) => { + if (e.key === "Enter") setAlarm(); + }); } function playAlarm() { audio.play(); + document.body.classList.add("alarm-active"); } function pauseAlarm() { audio.pause(); + document.body.classList.remove("alarm-active"); } window.onload = setup; diff --git a/Sprint-3/alarmclock/index.html b/Sprint-3/alarmclock/index.html index 48e2e80d9..6f0c4797a 100644 --- a/Sprint-3/alarmclock/index.html +++ b/Sprint-3/alarmclock/index.html @@ -4,17 +4,42 @@ - Title here + Alarm clock app + -
-

Time Remaining: 00:00

- - +
+

+ Time Remaining: 00:00 +

+ + + + + + +

+ + + + +
- - -
diff --git a/Sprint-3/alarmclock/style.css b/Sprint-3/alarmclock/style.css index 0c72de38b..a8388f90c 100644 --- a/Sprint-3/alarmclock/style.css +++ b/Sprint-3/alarmclock/style.css @@ -2,8 +2,8 @@ position: fixed; top: 50%; left: 50%; - -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); + text-align: center; } #alarmSet { @@ -13,3 +13,33 @@ h1 { text-align: center; } + +/* Visible, helpful error message */ +.error-message { + color: #b00020; + font-size: 0.9rem; + margin-top: -10px; + margin-bottom: 20px; + min-height: 1.2rem; /* Prevent layout shift */ +} + +/* Accessible focus outline */ +button:focus, +input:focus { + outline: 3px solid #005fcc; + outline-offset: 3px; +} + +/* Flashing background when alarm is active */ +.alarm-active { + animation: flash 1s infinite alternate; +} + +@keyframes flash { + from { + background-color: #fff; + } + to { + background-color: #ffcccc; + } +} diff --git a/Sprint-3/package.json b/Sprint-3/package.json index 711a5390f..c58a56a6b 100644 --- a/Sprint-3/package.json +++ b/Sprint-3/package.json @@ -26,7 +26,7 @@ "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", - "jest": "^30.0.4", + "jest": "^30.2.0", "jest-environment-jsdom": "^30.0.4" } }