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;