From 6dd31b9a321e31416a5d15d4c4e122d104d76db3 Mon Sep 17 00:00:00 2001 From: Alfredo <193377562+A-Galicia@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:23:03 -0700 Subject: [PATCH 1/5] fix: browser title --- packages/frontend/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/index.html b/packages/frontend/index.html index fd561d0..e181b90 100644 --- a/packages/frontend/index.html +++ b/packages/frontend/index.html @@ -10,7 +10,7 @@ href="https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,500;1,600;1,700;1,800&display=swap" rel="stylesheet" /> - frontend + GOAT Timer
From 53230ef9b97b4b2b8c2bcd520b4b43e726322c91 Mon Sep 17 00:00:00 2001 From: Alfredo <193377562+A-Galicia@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:26:48 -0700 Subject: [PATCH 2/5] delete: schedual was not implemented --- packages/backend/models/user.js | 53 +-------------------------------- 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/packages/backend/models/user.js b/packages/backend/models/user.js index bb6a2ed..e071bff 100644 --- a/packages/backend/models/user.js +++ b/packages/backend/models/user.js @@ -1,4 +1,4 @@ -//schema for users: auth fields, study goals, schedule, hours, password hashing +//schema for users: auth fields, study goals, hours, password hashing import mongoose from "mongoose"; import bcrypt from "bcryptjs"; @@ -10,52 +10,6 @@ function isPasswordHash(password) { return typeof password === "string" && BCRYPT_HASH_PATTERN.test(password); } -const dayNames = [ - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", - "sunday", -]; - -//user's weekly availability, including days, start time, end time, and timezone -const scheduleSchema = new mongoose.Schema( - { - days: { - type: [ - { - type: String, - enum: dayNames, - lowercase: true, - trim: true, - }, - ], - default: [], - }, - - startTime: { - type: String, - default: "", - trim: true, - }, - - endTime: { - type: String, - default: "", - trim: true, - }, - - timezone: { - type: String, - default: "UTC", - trim: true, - }, - }, - { _id: false }, -); - //user model fields, ensure password not present when returning data const userSchema = new mongoose.Schema( { @@ -113,11 +67,6 @@ const userSchema = new mongoose.Schema( min: [0, "goal cannot be negative"], }, - schedule: { - type: scheduleSchema, - default: () => ({}), - }, - totalHours: { type: Number, default: 0, From 1ac08d0dc784594bf972a6e7283cb9c9274101d9 Mon Sep 17 00:00:00 2001 From: Alfredo <193377562+A-Galicia@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:52:46 -0700 Subject: [PATCH 3/5] feat: mouse lock resize --- .../frontend/src/components/MouseLock.jsx | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/components/MouseLock.jsx b/packages/frontend/src/components/MouseLock.jsx index 83d3bb7..770a830 100644 --- a/packages/frontend/src/components/MouseLock.jsx +++ b/packages/frontend/src/components/MouseLock.jsx @@ -1,14 +1,65 @@ //trap mouse movement within canvas and show the mouse/ball position when active -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import styles from "./Timer.module.css"; const RADIUS = 20; const CANVAS_WIDTH = 500; const CANVAS_HEIGHT = 300; +const ASPECT_RATIO = CANVAS_WIDTH / CANVAS_HEIGHT; +const MIN_CANVAS_WIDTH = 280; +const MAX_CANVAS_WIDTH = 300; function MouseLock({ canvasRef, onLockChange }) { const animationRef = useRef(null); const positionRef = useRef({ x: 250, y: 150 }); + const [canvasSize, setCanvasSize] = useState({ + width: CANVAS_WIDTH, + height: CANVAS_HEIGHT, + }); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + function calculateCanvasSize() { + const parentWidth = + canvas.parentElement && canvas.parentElement.clientWidth + ? canvas.parentElement.clientWidth + : window.innerWidth; + const width = Math.min( + MAX_CANVAS_WIDTH, + Math.max(MIN_CANVAS_WIDTH, Math.floor(parentWidth * 0.95)), + ); + const height = Math.round(width / ASPECT_RATIO); + return { width, height }; + } + + function resizeCanvas() { + setCanvasSize((currentSize) => { + const nextSize = calculateCanvasSize(); + if ( + nextSize.width === currentSize.width && + nextSize.height === currentSize.height + ) { + return currentSize; + } + + positionRef.current = { + x: Math.min(positionRef.current.x, nextSize.width), + y: Math.min(positionRef.current.y, nextSize.height), + }; + + return nextSize; + }); + } + + resizeCanvas(); + window.addEventListener("resize", resizeCanvas); + + return () => { + window.removeEventListener("resize", resizeCanvas); + }; + }, [canvasRef]); useEffect(() => { const canvas = canvasRef.current; @@ -17,6 +68,16 @@ function MouseLock({ canvasRef, onLockChange }) { const ctx = canvas.getContext("2d"); if (!ctx) return; + const displayWidth = canvasSize.width; + const displayHeight = canvasSize.height; + const dpr = window.devicePixelRatio; + + canvas.width = Math.round(displayWidth * dpr); + canvas.height = Math.round(displayHeight * dpr); + canvas.style.width = `${displayWidth}px`; + canvas.style.height = `${displayHeight}px`; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + function findSecondCenter(pos, max) { if (pos < RADIUS) return pos + max; if (pos + RADIUS > max) return pos - max; @@ -31,15 +92,13 @@ function MouseLock({ canvasRef, onLockChange }) { function drawCanvas() { const { x, y } = positionRef.current; - - const x2 = findSecondCenter(x, canvas.width); - const y2 = findSecondCenter(y, canvas.height); + const x2 = findSecondCenter(x, displayWidth); + const y2 = findSecondCenter(y, displayHeight); ctx.fillStyle = "black"; - ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(0, 0, displayWidth, displayHeight); ctx.fillStyle = "#f00"; - drawBall(x, y); if (x2 !== null) drawBall(x2, y); @@ -62,8 +121,8 @@ function MouseLock({ canvasRef, onLockChange }) { const current = positionRef.current; positionRef.current = { - x: updateCoord(current.x, event.movementX, canvas.width), - y: updateCoord(current.y, event.movementY, canvas.height), + x: updateCoord(current.x, event.movementX, displayWidth), + y: updateCoord(current.y, event.movementY, displayHeight), }; if (!animationRef.current) { @@ -105,13 +164,13 @@ function MouseLock({ canvasRef, onLockChange }) { document.exitPointerLock(); } }; - }, [canvasRef, onLockChange]); + }, [canvasRef, canvasSize, onLockChange]); return ( ); From 717878882a5856b667ae56d0de896bfa91942a4b Mon Sep 17 00:00:00 2001 From: Alfredo <193377562+A-Galicia@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:06:44 -0700 Subject: [PATCH 4/5] delete: unused files --- packages/frontend/src/components/Groups.css | 141 ------------------ packages/frontend/src/components/Groups.jsx | 108 -------------- .../frontend/src/components/UserNavbar.jsx | 42 ------ 3 files changed, 291 deletions(-) delete mode 100644 packages/frontend/src/components/Groups.css delete mode 100644 packages/frontend/src/components/Groups.jsx delete mode 100644 packages/frontend/src/components/UserNavbar.jsx diff --git a/packages/frontend/src/components/Groups.css b/packages/frontend/src/components/Groups.css deleted file mode 100644 index 9e1477d..0000000 --- a/packages/frontend/src/components/Groups.css +++ /dev/null @@ -1,141 +0,0 @@ -.groupsPage { - min-height: calc(100vh - 86px); - background: var(--bg, #faf9ff); - padding: 50px; - font-family: "EB Garamond", Georgia, serif; -} - -.groupsContainer { - width: min(1100px, 94vw); - margin: 0 auto; -} - -.groupsHeader { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 32px; - gap: 20px; -} - -.groupsTitle { - font-size: 3.5rem; - font-style: italic; - margin: 0; -} - -.groupsSubtitle { - color: var(--text-gray, #555555); - margin: 8px 0 0; -} - -.groupsButton { - background: var(--brand-black, #000000); - color: var(--white, #ffffff); - border: none; - border-radius: 999px; - padding: 10px 22px; - font-family: "EB Garamond", Georgia, serif; - font-size: 1rem; - font-weight: 700; - cursor: pointer; -} - -.groupsError { - color: #c62828; -} - -.groupsGrid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 24px; -} - -.groupCardLink { - color: inherit; - text-decoration: none; -} - -.groupCard { - background: var(--bg, #faf9ff); - border: 1px solid var(--border-color, #d8d8d8); - border-radius: 30px; - padding: 28px 32px; - transition: - transform 0.18s ease, - box-shadow 0.18s ease; -} - -.groupCard:hover { - transform: translateY(-2px); - box-shadow: 0 8px 18px rgba(0, 0, 0, 0.08); -} - -.groupCardHeader { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: center; - margin-bottom: 8px; -} - -.groupCardHeader h2 { - margin: 0; - font-size: 1.5rem; -} - -.groupCardHeader span, -.groupOwner { - color: var(--text-gray, #555555); -} - -.groupOwner { - margin: 0 0 22px; -} - -.groupProgressHeader { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; -} - -.groupProgressTrack { - width: 100%; - height: 12px; - background: var(--accent-light, #d1d2e8); - border-radius: 10px; - position: relative; - overflow: hidden; -} - -.groupProgressFill { - position: absolute; - top: 0; - left: 0; - height: 100%; - background: var(--accent-purple, #9b9ece); - border-radius: 10px; -} - -.groupProgressPercent { - text-align: right; - font-weight: bold; - font-size: 1.2rem; - margin-top: 10px; -} - -@media (max-width: 700px) { - .groupsPage { - padding: 28px 16px; - } - - .groupsHeader { - flex-direction: column; - align-items: flex-start; - } - - .groupsTitle { - font-size: 2.6rem; - } -} \ No newline at end of file diff --git a/packages/frontend/src/components/Groups.jsx b/packages/frontend/src/components/Groups.jsx deleted file mode 100644 index 8f76c06..0000000 --- a/packages/frontend/src/components/Groups.jsx +++ /dev/null @@ -1,108 +0,0 @@ -//displays all groups as cards with owner, member count, and progress information -import { useEffect, useState } from "react"; -import { Link, useNavigate } from "react-router"; -import "./Groups.css"; - -const AZURE_URL = - "https://goattimer-hgh5bxcub9hrdgha.centralus-01.azurewebsites.net"; - -function getProgress(group) { - const hours = Number(group.hours || 0); - const goal = Number(group.groupGoal || 0); - - if (goal <= 0) { - return 0; - } - - return Math.min(Math.round((hours / goal) * 100), 100); -} - -function Groups() { - const [groups, setGroups] = useState([]); - const [error, setError] = useState(""); - const navigate = useNavigate(); - - useEffect(() => { - async function loadGroups() { - try { - const res = await fetch(`${AZURE_URL}/api/groups`, { - credentials: "include", - }); - - if (!res.ok) { - throw new Error("Could not load groups"); - } - - const data = await res.json(); - setGroups(Array.isArray(data) ? data : []); - } catch (err) { - setError(err.message); - } - } - - loadGroups(); - }, []); - - return ( -
-
-
-
-

Groups

-

View group members and goals.

-
- - -
- - {error &&

{error}

} - -
- {groups.map((group) => { - const progress = getProgress(group); - - return ( - -
-
-

{group.name || `Group #${group._id.slice(-4)}`}

- {group.users?.length || 0} members -
- -

- Owner:{" "} - {group.owner?.name || group.owner?.email || "Unknown"} -

- -
- Progress - - {group.hours || 0}/{group.groupGoal || 0} hours - -
- -
-
-
- -
{progress}%
-
- - ); - })} -
-
-
- ); -} - -export default Groups; diff --git a/packages/frontend/src/components/UserNavbar.jsx b/packages/frontend/src/components/UserNavbar.jsx deleted file mode 100644 index 24b5da8..0000000 --- a/packages/frontend/src/components/UserNavbar.jsx +++ /dev/null @@ -1,42 +0,0 @@ -//navbar for logged in user -import React from "react"; -import { Link, useLocation } from "react-router"; -import "./UserHome.css"; - -function UserNavbar() { - const location = useLocation(); - - return ( - - ); -} - -export default UserNavbar; From 72bf4b88e0e6a058028fe819d62cc0e351de54c8 Mon Sep 17 00:00:00 2001 From: Alfredo <193377562+A-Galicia@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:44:50 -0700 Subject: [PATCH 5/5] fix: rounding --- packages/frontend/src/components/Dashboard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/Dashboard.jsx b/packages/frontend/src/components/Dashboard.jsx index 3808410..f3a926d 100644 --- a/packages/frontend/src/components/Dashboard.jsx +++ b/packages/frontend/src/components/Dashboard.jsx @@ -78,7 +78,7 @@ const Dashboard = () => { } ${now.getDate()}`; const weeklyGoal = user?.goal || 15; - const hoursStudied = user?.weeklyHours || 0; + const hoursStudied = user?.weeklyHours?.toFixed(3) || 0; const progress = weeklyGoal > 0 ? Math.round((hoursStudied / weeklyGoal) * 100) : 0;