From 554c3823c2a2ef79f3c48b38dd9894bbbb5b2946 Mon Sep 17 00:00:00 2001 From: Ruben Alvarez-Gutierrez Date: Mon, 8 Sep 2025 20:07:42 -0700 Subject: [PATCH 01/34] chessClient instruction update --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ad533605..8ef4ce73 100644 --- a/README.md +++ b/README.md @@ -155,14 +155,13 @@ This service usually runs on port 8080. #### Chess Client (Testing Interface) -Provides different views of the chess board for development: - ```bash cd chessClient npm install ``` -You can use the **Live Server extension in VS Code** to open the HTML files: +Run the http server using on port 80: npx http-server -p 80 to ensure that the chess board frame loads during any session. +You can use the **Live Server extension in VS Code** to open the HTML files for local testing of the chess board: * **Board only:** Right-click `index.html` → "Open with Live Server" * **Board with controls:** Right-click `parent.html` → "Open with Live Server" From 6e240fc92158abcaba62f02d9f437b5c639a3978 Mon Sep 17 00:00:00 2001 From: Ruben Alvarez-Gutierrez Date: Mon, 8 Sep 2025 20:12:44 -0700 Subject: [PATCH 02/34] chessClient instruction update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ef4ce73..a8226bec 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ cd chessClient npm install ``` -Run the http server using on port 80: npx http-server -p 80 to ensure that the chess board frame loads during any session. +Run the http server on port 80 using: `npx http-server -p 80` to ensure that the chess board frame loads during any session. You can use the **Live Server extension in VS Code** to open the HTML files for local testing of the chess board: * **Board only:** Right-click `index.html` → "Open with Live Server" From fc9d76fc825c3814b2c4eea260902e2dc127e406 Mon Sep 17 00:00:00 2001 From: Ruben Alvarez-Gutierrez Date: Tue, 9 Sep 2025 13:40:38 -0700 Subject: [PATCH 03/34] new node instructions --- README.md | 64 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index a8226bec..8467500c 100644 --- a/README.md +++ b/README.md @@ -6,32 +6,58 @@ This is an educational platform that combines chess instruction with STEM learni ## Development Environment Setup -To run the platform, you’ll need **Node.js v14**, **nodemon**, and **Apache** installed. +To run the platform, you’ll need **Node.js v18**, **nodemon**, and **Apache** installed. ### NodeJS -This project requires NodeJS version 14. +This project requires **Node.js v18.20.8**. -**Linux:** - -```bash -curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - -sudo apt-get install -y nodejs -``` - -**macOS:** - -```bash -brew install node@14 -``` - -Or download from [nodejs.org](https://nodejs.org/) - -**Windows:** -Download and install from [nodejs.org](https://nodejs.org/) +We recommend using a version manager so everyone runs the same Node.js version consistently. --- +### Option 1: Using [Volta](https://volta.sh) (recommended) + +Volta automatically uses the Node version pinned in this project’s `package.json`. + +**Install Volta:** + +- **Linux / macOS:** + ```bash + curl https://get.volta.sh | bash + source ~/.bashrc # or ~/.zshrc +- **Windows:** +Download and run the installer from [volta.sh](https://volta.sh) + +Now when you cd into this project, Volta will automatically install and use **Node.js v18.20.8**. + +### Option 2: Using [nvm](https://github.com/nvm-sh/nvm) + +If you prefer nvm: + +- **Linux / macOS:** + ```bash + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + source ~/.nvm/nvm.sh + ``` + Then: + ```bash + nvm install 18.20.8 + nvm use 18.20.8 + ``` + (Optional) Set it as default: + ```bash + nvm alias default 18.20.8 + ``` +- **Windows:** + Use [nvm-windows](https://github.com/coreybutler/nvm-windows): + + Download and run the installer from the [releases page](https://github.com/coreybutler/nvm-windows/releases). + Then install and use Node: + ```powershell + nvm install 18.20.8 + nvm use 18.20.8 + ### Nodemon Nodemon automatically restarts your server whenever code changes are detected. From 6c97dfe3dda7bc606c38ddadfeefcc95fd7e8bef Mon Sep 17 00:00:00 2001 From: Brandon Yu Date: Wed, 10 Sep 2025 20:12:41 -0700 Subject: [PATCH 04/34] added module export to resolve middleware bug --- middlewareNode/routes/lessons.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/middlewareNode/routes/lessons.js b/middlewareNode/routes/lessons.js index dfdf5fcc..eac07ddb 100644 --- a/middlewareNode/routes/lessons.js +++ b/middlewareNode/routes/lessons.js @@ -884,4 +884,6 @@ list = [ ] } -] \ No newline at end of file +] + +module.exports = router; \ No newline at end of file From d35dfab972abe175c073b85e2aa72231877fc96c Mon Sep 17 00:00:00 2001 From: Brandon Yu Date: Thu, 11 Sep 2025 21:36:01 -0700 Subject: [PATCH 05/34] added querying for user activities --- middlewareNode/routes/activities.js | 48 +++++++++++++++++++++++++++++ middlewareNode/server.js | 1 + 2 files changed, 49 insertions(+) create mode 100644 middlewareNode/routes/activities.js diff --git a/middlewareNode/routes/activities.js b/middlewareNode/routes/activities.js new file mode 100644 index 00000000..b9a54123 --- /dev/null +++ b/middlewareNode/routes/activities.js @@ -0,0 +1,48 @@ +const config = require("config"); +const express = require('express'); +const passport = require("passport"); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { MongoClient, ObjectId } = require('mongodb'); +require('dotenv').config(); + +let cachedClient = null; // cache db client to prevent repeated connections + +// get db client +async function getDb() { + if (!cachedClient) { // if not cached, connect + cachedClient = new MongoClient(config.get("mongoURI")); + await cachedClient.connect(); + } + return cachedClient.db("ystem"); // returned cached client +} + +router.get("/:username", async (req, res) => { + try { + const db = await getDb(); + const username = req.params.username; + + if(!username) { + res.status(401).json({error:'Authentication required'}) + } + const users = db.collection("users"); + const currentUser = await users.findOne( + { username }, + ); + if(!currentUser) { + res.status(404).json({error: "User not found!"}); + } + const userId = currentUser._id; + const activities = db.collection("activities"); + const userActivities = await activities.find( + { userId }, + ); + res.status(200).json({activities: userActivities}); + + } catch (err) { + console.error('Error fetching activities: ', err); + res.status(500).json({error: 'Server error'}); + } +}) + +module.exports = router; \ No newline at end of file diff --git a/middlewareNode/server.js b/middlewareNode/server.js index 07b718fa..ef78583a 100644 --- a/middlewareNode/server.js +++ b/middlewareNode/server.js @@ -38,6 +38,7 @@ app.use("/auth", require("./routes/auth")); app.use("/timeTracking", require("./routes/timeTracking")); app.use("/puzzles", require("./routes/puzzles")); app.use("/lessons", require("./routes/lessons")); +app.use("/activity", require("./routes/activities")); const PORT = process.env.PORT || 8000; From e6091e9ea05a6c987fb019bb7c229271e569ab3b Mon Sep 17 00:00:00 2001 From: Brandon Yu Date: Tue, 16 Sep 2025 22:41:52 -0700 Subject: [PATCH 06/34] added backend routes for updating activities --- middlewareNode/routes/activities.js | 64 ++++++++++++++++++++++++++++- middlewareNode/server.js | 2 +- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/middlewareNode/routes/activities.js b/middlewareNode/routes/activities.js index b9a54123..34bac48f 100644 --- a/middlewareNode/routes/activities.js +++ b/middlewareNode/routes/activities.js @@ -17,13 +17,13 @@ async function getDb() { return cachedClient.db("ystem"); // returned cached client } -router.get("/:username", async (req, res) => { +router.get("/", async (req, res) => { try { const db = await getDb(); const username = req.params.username; if(!username) { - res.status(401).json({error:'Authentication required'}) + res.status(401).json({error:'Authentication required'}); } const users = db.collection("users"); const currentUser = await users.findOne( @@ -45,4 +45,64 @@ router.get("/:username", async (req, res) => { } }) + +router.put("/activity/:activityName", async (req, res) => { + try { + const db = await getDb(); + const { username, activityName } = req.params; + if(!username) { + res.status(401).json({error:'Authentication required'}); + } + const users = db.collection("users"); + const currentUser = await users.findOne( + { username }, + ); + if(!currentUser) { + res.status(404).json({error:"User not found!"}); + } + const userId = currentUser._id; + //use userId as parameter to select document to update + //pass activity name in reqeust to update to completed + const activities = db.collection("activities"); + /*await activities.updateOne( + { userId, "activities.name": activityName }, + { $set: { "activities.$.completed": true } } + );*/ + res.status(200); + } catch (err) { + console.error('Error updating activities: ', err); + res.status(500).json({error: 'Server error'}); + } +}) + +router.post("/activity/complete", async (req, res) => { + try { + const db = await getDb(); + const { username } = req.params; + if(!username) { + res.status(401).json({error:'Authentication required'}); + } + const users = db.collection("users"); + const currentUser = await users.findOne( + { username }, + ); + if(!currentUser) { + res.status(404).json({error:"User not found!"}); + } + const userId = currentUser._id; + //use userId as parameter to select document to update + //pass activity name in reqeust to update to completed + const activities = db.collection("activities"); + /*await activities.updateOne( + { userId }, + { $push: { lastCompleted: new Date() } } + );*/ + res.status(200); + } catch (err) { + console.error('Error updating activity completion: ', err); + res.status(500).json({error: 'Server error'}); + } +}) + + module.exports = router; \ No newline at end of file diff --git a/middlewareNode/server.js b/middlewareNode/server.js index ef78583a..3393124f 100644 --- a/middlewareNode/server.js +++ b/middlewareNode/server.js @@ -38,7 +38,7 @@ app.use("/auth", require("./routes/auth")); app.use("/timeTracking", require("./routes/timeTracking")); app.use("/puzzles", require("./routes/puzzles")); app.use("/lessons", require("./routes/lessons")); -app.use("/activity", require("./routes/activities")); +app.use("/activity", require("./routes/activities/:username")); const PORT = process.env.PORT || 8000; From b16dcdac5c699550f37286bdae8902026f6c153f Mon Sep 17 00:00:00 2001 From: Brandon Yu Date: Fri, 19 Sep 2025 12:12:41 -0700 Subject: [PATCH 07/34] added activity fetching to modal and fixed bug with backend route --- middlewareNode/routes/activities.js | 43 ++++++++----------- middlewareNode/server.js | 2 +- .../Modals/ActivitiesModal.tsx | 35 ++++++++++++++- .../NewStudentProfile/NewStudentProfile.tsx | 2 +- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/middlewareNode/routes/activities.js b/middlewareNode/routes/activities.js index 34bac48f..9249d935 100644 --- a/middlewareNode/routes/activities.js +++ b/middlewareNode/routes/activities.js @@ -1,7 +1,7 @@ const config = require("config"); const express = require('express'); const passport = require("passport"); -const router = express.Router(); +const router = express.Router({mergeParams: true}); const jwt = require('jsonwebtoken'); const { MongoClient, ObjectId } = require('mongodb'); require('dotenv').config(); @@ -20,28 +20,27 @@ async function getDb() { router.get("/", async (req, res) => { try { const db = await getDb(); - const username = req.params.username; - + const { username } = req.params; if(!username) { - res.status(401).json({error:'Authentication required'}); + return res.status(401).json({error:'Authentication required'}); } const users = db.collection("users"); const currentUser = await users.findOne( { username }, ); if(!currentUser) { - res.status(404).json({error: "User not found!"}); + return res.status(404).json({error: "User not found!"}); } const userId = currentUser._id; const activities = db.collection("activities"); - const userActivities = await activities.find( - { userId }, + const userActivities = await activities.findOne( + { userId }, { projection: {activities: 1, _id: 0}} ); - res.status(200).json({activities: userActivities}); + return res.status(200).json({activities: userActivities}); } catch (err) { console.error('Error fetching activities: ', err); - res.status(500).json({error: 'Server error'}); + return res.status(500).json({error: 'Server error'}); } }) @@ -51,27 +50,25 @@ router.put("/activity/:activityName", async (req, res) => { const db = await getDb(); const { username, activityName } = req.params; if(!username) { - res.status(401).json({error:'Authentication required'}); + return res.status(401).json({error:'Authentication required'}); } const users = db.collection("users"); const currentUser = await users.findOne( { username }, ); if(!currentUser) { - res.status(404).json({error:"User not found!"}); + return res.status(404).json({error:"User not found!"}); } const userId = currentUser._id; - //use userId as parameter to select document to update - //pass activity name in reqeust to update to completed const activities = db.collection("activities"); - /*await activities.updateOne( + await activities.updateOne( { userId, "activities.name": activityName }, { $set: { "activities.$.completed": true } } - );*/ - res.status(200); + ); + return res.status(200); } catch (err) { console.error('Error updating activities: ', err); - res.status(500).json({error: 'Server error'}); + return res.status(500).json({error: 'Server error'}); } }) @@ -80,24 +77,22 @@ router.post("/activity/complete", async (req, res) => { const db = await getDb(); const { username } = req.params; if(!username) { - res.status(401).json({error:'Authentication required'}); + return res.status(401).json({error:'Authentication required'}); } const users = db.collection("users"); const currentUser = await users.findOne( { username }, ); if(!currentUser) { - res.status(404).json({error:"User not found!"}); + return res.status(404).json({error:"User not found!"}); } const userId = currentUser._id; - //use userId as parameter to select document to update - //pass activity name in reqeust to update to completed const activities = db.collection("activities"); - /*await activities.updateOne( + await activities.updateOne( { userId }, { $push: { lastCompleted: new Date() } } - );*/ - res.status(200); + ); + return res.status(200); } catch (err) { console.error('Error updating activity completion: ', err); res.status(500).json({error: 'Server error'}); diff --git a/middlewareNode/server.js b/middlewareNode/server.js index 3393124f..2bbb6a5f 100644 --- a/middlewareNode/server.js +++ b/middlewareNode/server.js @@ -38,7 +38,7 @@ app.use("/auth", require("./routes/auth")); app.use("/timeTracking", require("./routes/timeTracking")); app.use("/puzzles", require("./routes/puzzles")); app.use("/lessons", require("./routes/lessons")); -app.use("/activity", require("./routes/activities/:username")); +app.use("/activities/:username", require("./routes/activities")); const PORT = process.env.PORT || 8000; diff --git a/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx b/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx index 8c6bfa03..6c899350 100644 --- a/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx +++ b/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import "./ActivitiesModal.scss"; import { ReactComponent as GrowthBox } from "../../../images/ActivitiesAssets/growth_box.svg"; import { ReactComponent as WaterMeter } from "../../../images/ActivitiesAssets/water_meter.svg"; @@ -9,14 +9,45 @@ import { ReactComponent as TopicBag } from "../../../images/ActivitiesAssets/top import { ReactComponent as ShortBottomVine} from "../../../images/ActivitiesAssets/short_bottom_vine.svg"; import { ReactComponent as BottomVine} from "../../../images/ActivitiesAssets/bottom_vine.svg"; import { ReactComponent as Stemmy} from "../../../images/ActivitiesAssets/stemmy.svg"; +import { environment } from "../../../environments/environment"; +import { useCookies } from "react-cookie"; -const ActivitiesModal = ({ onClose }: { onClose: () => void }) => { +const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: string }) => { // Close modal only when clicking the background overlay (not child elements) const handleOverlayClick = (e: React.MouseEvent) => { if (e.target === e.currentTarget) { onClose(); } }; + + const [cookies] = useCookies(['login']); + + + const fetchActivities = async () => { + try { + const url = `${environment.urls.middlewareURL}/activities/${username}`; + const response = await fetch(url, { + method: "GET", + headers: { + 'Authorization': `Bearer ${cookies.login}`, + 'Content-Type': 'application/json', + }, + credentials: 'include' + }) + const activities = await response.json(); + console.log(activities); + } catch (err) { + console.error("Error fetching activities:", err); + } + + } + useEffect(() => { + try { + fetchActivities(); + } catch (err) { + console.log(err); + } + }, []); return (
diff --git a/react-ystemandchess/src/Pages/NewStudentProfile/NewStudentProfile.tsx b/react-ystemandchess/src/Pages/NewStudentProfile/NewStudentProfile.tsx index 7c36d6a0..6f33c561 100644 --- a/react-ystemandchess/src/Pages/NewStudentProfile/NewStudentProfile.tsx +++ b/react-ystemandchess/src/Pages/NewStudentProfile/NewStudentProfile.tsx @@ -435,7 +435,7 @@ const NewStudentProfile = ({ userPortraitSrc }: any) => { {/* Render modals conditionally */} {activeModal === "streak" && setActiveModal(null)} />} - {activeModal === "activities" && setActiveModal(null)} />} + {activeModal === "activities" && setActiveModal(null)} username={username}/>} {activeModal === "badges" && setActiveModal(null)} />} {activeModal === "leaderboard" && setActiveModal(null)} />} From 7c2802ea8f2c4751cec04411a7a9613d424ea0b0 Mon Sep 17 00:00:00 2001 From: Noah Date: Fri, 19 Sep 2025 14:46:17 -0700 Subject: [PATCH 08/34] Readme updated, will be reviewing if any code needs fixing due to the node update, but everything seems good so far. --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad533605..abbbd5e2 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ This is an educational platform that combines chess instruction with STEM learni ## Development Environment Setup -To run the platform, you’ll need **Node.js v14**, **nodemon**, and **Apache** installed. +To run the platform, you’ll need **Node.js v14.21.3**, **nodemon**, and **Apache** installed. ### NodeJS -This project requires NodeJS version 14. +This project requires NodeJS version 14.21.3. **Linux:** @@ -109,6 +109,16 @@ npm start # Start the API server The server typically runs on port 8000. You should see "MongoDB Connected..." when it starts successfully. +If you wish to log into the user pages with test accounts, you may use the following usernames and passwords: + +**Mentor Userpage:** + +mentor 123123123 + +**Student Userpage:** + +student 123123123 + --- #### Main React Application (Frontend) From 26ec48317e64464c7d0566fd741ae75c965d96bb Mon Sep 17 00:00:00 2001 From: Brandon Yu Date: Fri, 19 Sep 2025 19:20:26 -0700 Subject: [PATCH 09/34] updated activities modal to use activities fetched from backend --- .../Modals/ActivitiesModal.tsx | 22 ++++++++++--------- .../src/utils/activityNames.ts | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 react-ystemandchess/src/utils/activityNames.ts diff --git a/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx b/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx index 6c899350..ff05812a 100644 --- a/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx +++ b/react-ystemandchess/src/Pages/NewStudentProfile/Modals/ActivitiesModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import "./ActivitiesModal.scss"; import { ReactComponent as GrowthBox } from "../../../images/ActivitiesAssets/growth_box.svg"; import { ReactComponent as WaterMeter } from "../../../images/ActivitiesAssets/water_meter.svg"; @@ -11,6 +11,7 @@ import { ReactComponent as BottomVine} from "../../../images/ActivitiesAssets/bo import { ReactComponent as Stemmy} from "../../../images/ActivitiesAssets/stemmy.svg"; import { environment } from "../../../environments/environment"; import { useCookies } from "react-cookie"; +import { parseActivities } from "../../../utils/activityNames"; const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: string }) => { // Close modal only when clicking the background overlay (not child elements) @@ -21,6 +22,8 @@ const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: }; const [cookies] = useCookies(['login']); + const [activities, setActivities] = useState(null); + const [loading, setLoading] = useState(true); const fetchActivities = async () => { @@ -34,8 +37,11 @@ const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: }, credentials: 'include' }) - const activities = await response.json(); - console.log(activities); + const json = await response.json(); + const data = json.activities.activities; + const activityNames = parseActivities(data); + setActivities(activityNames); + setLoading(false); } catch (err) { console.error("Error fetching activities:", err); } @@ -49,7 +55,8 @@ const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: } }, []); - return ( + return ( + !loading &&
{/* Close (X) button */} @@ -83,12 +90,7 @@ const ActivitiesModal = ({ onClose, username }: { onClose: () => void; username: {/* Stack of daily activity buttons. Activities are hard coded for now.*/}
- {[ - 'Attend a Session', - 'Perform Castle', - 'Play Chess Against a Bot', - 'Perform Castle', - ].map((activity, idx) => ( + {activities.map((activity, idx) => (