diff --git a/backend/auth.ts b/backend/auth.ts index 90be965..bea411b 100644 --- a/backend/auth.ts +++ b/backend/auth.ts @@ -10,6 +10,7 @@ dotenv.config(); type User = { username: string; hashedPassword: string }; const creds = [User]; // username, hashedPassword +// Middleware to authenticate user using JWT export function authenticateUser(req: Request, res: Response, next: any) { const authHeader = req.headers["authorization"]; //Getting the 2nd part of the auth header (the token) @@ -34,6 +35,7 @@ export function authenticateUser(req: Request, res: Response, next: any) { } } +// Controller function to handle user login export const loginUser = async (req: Request, res: Response) => { connectDB(); const { username, password } = req.body; // from form @@ -71,6 +73,7 @@ export const loginUser = async (req: Request, res: Response) => { } }; +// Function to generate a JWT access token function generateAccessToken(username: any) { return new Promise((resolve, reject) => { jwt.sign( diff --git a/backend/models/basketSchema.ts b/backend/models/basketSchema.ts index dba8eb3..345f689 100644 --- a/backend/models/basketSchema.ts +++ b/backend/models/basketSchema.ts @@ -1,14 +1,16 @@ import mongoose, { Schema } from "mongoose"; +// Defining the type for a basket object, specifying structure of basket data. export type IBasket = { - _id: Schema.Types.ObjectId; + _id: Schema.Types.ObjectId; // Unique identifier for the basket, using MongoDB ObjectId. basketName: string; description: string; - members: Schema.Types.ObjectId[]; - items: Schema.Types.ObjectId[]; - created: Date; + members: Schema.Types.ObjectId[]; // Array of ObjectIds referencing the members of the basket. + items: Schema.Types.ObjectId[]; // Array of ObjectIds referencing the items of the basket. + created: Date; // Timestamp for when the basket was creaed }; +// Defining the data types and requirements for each field in our basket schema const BasketSchema = new Schema({ basketName: { type: String, required: true }, description: { type: String, required: true }, @@ -17,6 +19,7 @@ const BasketSchema = new Schema({ created: { type: Date, required: true, default: Date.now }, }); +// Create a model for the basket schema, using the existing "basket" model if it exists, otherwise creating a new one. const Basket = mongoose.models["basket"] || mongoose.model("basket", BasketSchema); diff --git a/backend/models/groupSchema.ts b/backend/models/groupSchema.ts index 936b52b..1eba5cd 100644 --- a/backend/models/groupSchema.ts +++ b/backend/models/groupSchema.ts @@ -1,15 +1,17 @@ import mongoose, { Schema } from "mongoose"; +// Defining the type for a group object, specifying structure of group data. export type IGroup = { _id: Schema.Types.ObjectId; groupName: string; privateGroup: boolean; description: string; - members: Schema.Types.ObjectId[]; - baskets: Schema.Types.ObjectId[]; + members: Schema.Types.ObjectId[]; // Array of ObjectIds referencing the members of the group. + baskets: Schema.Types.ObjectId[]; // Array of ObjectIds referencing the baskets of the group. created: Date; }; +// Defining the data types and requirements for each field in our group schema const GroupSchema = new Schema({ groupName: { type: String, required: true }, privateGroup: { type: Boolean, required: true }, @@ -19,6 +21,7 @@ const GroupSchema = new Schema({ created: { type: Date, required: true, default: Date.now }, }); +// Create a model for the group schema, using the existing "group" model if it exists, otherwise creating a new one. const Group = mongoose.models["groups"] || mongoose.model("groups", GroupSchema); diff --git a/backend/models/itemSchema.ts b/backend/models/itemSchema.ts index 4746786..d578aa5 100644 --- a/backend/models/itemSchema.ts +++ b/backend/models/itemSchema.ts @@ -1,20 +1,21 @@ import mongoose, { Schema } from "mongoose"; +// Defining the type for an item object, specifying the structure of the item data. export type IItem = { _id: Schema.Types.ObjectId; name: string; toShare: boolean; isPrivate: boolean; type: string; - basket: Schema.Types.ObjectId; + basket: Schema.Types.ObjectId; // ObjectId referencing the basket to which the item belongs. notes: string; price: number; quantity: number; - created: Date; - lastModified: Date; + created: Date; // Timestamp for when the item was created. + lastModified: Date; // Timestamp for when the item was last modified. }; -// Mongoose schema +// Defining the data types and requirements for each field in our item schema. const itemSchema = new Schema({ name: { type: String, required: true }, toShare: { type: Boolean, required: true }, @@ -28,6 +29,7 @@ const itemSchema = new Schema({ lastModified: { type: Date, required: true, default: Date.now }, }); +// Create a model for the item schema, using the existing "items" model if it exists, otherwise creating a new one. const Event = mongoose.models["items"] || mongoose.model("items", itemSchema); export default Event; diff --git a/backend/models/userSchema.ts b/backend/models/userSchema.ts index 9753ef7..db6846a 100644 --- a/backend/models/userSchema.ts +++ b/backend/models/userSchema.ts @@ -1,5 +1,6 @@ import mongoose, { Schema } from "mongoose"; +// Defining the type for a user object, specifying the structure of the user data. export type IUser = { _id: Schema.Types.ObjectId; username: string; @@ -13,9 +14,7 @@ export type IUser = { joined: Date; }; -//groupId and digitalWaiver seem to require a schema -//currently there is no schema for them so I am leaving them as null for now -//can groupId just be a string and digitalWaiver be a boolean? +// Defining the data types and requirements for each field in our user schema. const UserSchema = new Schema({ username: { type: String, required: true }, email: { type: String, required: true }, @@ -28,6 +27,7 @@ const UserSchema = new Schema({ joined: { type: Date, required: true, default: Date.now }, }); +// Create a model for the user schema, using the existing "users" model if it exists, otherwise creating a new one. const User = mongoose.models["users"] || mongoose.model("users", UserSchema); export default User; diff --git a/backend/routes/basketRoutes.ts b/backend/routes/basketRoutes.ts index 4f1c7bf..016bc1a 100644 --- a/backend/routes/basketRoutes.ts +++ b/backend/routes/basketRoutes.ts @@ -6,6 +6,7 @@ import { authenticateUser } from "../auth.js"; const router = express.Router(); +// Route to get all baskets router.get("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { @@ -20,6 +21,7 @@ router.get("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to get a specific basket by ID or name router.get( "/:basketid", authenticateUser, @@ -56,23 +58,19 @@ router.get( }, ); +// Route to create a new basket router.post("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { console.log("Creating a new basket with data:", req.body); - //Create new basket to add + // Create new basket to add const { basketName, description, members, items } = req.body; if (!basketName || !description) { console.error("Missing required fields", req.body); return res.status(400).send("Missing required fields"); } - const basketToAdd = new Basket({ - basketName, - description, - members, - items, - }); + const basketToAdd = new Basket({basketName, description, members, items,}); const newBasket = await basketToAdd.save(); console.log("New basket created:", newBasket); @@ -83,6 +81,7 @@ router.post("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to update a basket by ID router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { // Get basket ID from URL const { id } = req.params; @@ -106,6 +105,7 @@ router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to remove an item from a basket router.patch( "/:bid/removeitem", authenticateUser, @@ -135,6 +135,7 @@ router.patch( }, ); +// Route to delete a basket by ID router.delete("/:id", authenticateUser, async (req: Request, res: Response) => { connectDB(); const { id } = req.params; diff --git a/backend/routes/groupRoutes.ts b/backend/routes/groupRoutes.ts index 67ea2f4..76a96b7 100644 --- a/backend/routes/groupRoutes.ts +++ b/backend/routes/groupRoutes.ts @@ -6,6 +6,7 @@ import connectDB from "../connection.js"; const router = express.Router(); +// Route to get all groups router.get("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { @@ -20,6 +21,7 @@ router.get("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to get a specific group by ID or name router.get( "/:groupid", authenticateUser, @@ -61,26 +63,29 @@ router.get( }, ); +// Route to create a new group router.post("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { console.log("Creating a new group with data:", req.body); //Create new group to add - const { groupName, privateGroup, description, members, baskets } = req.body; - //*assuming groupname and privateGroup is required fields need to add a default description ("No description given") etc. - //*ALSO do we want the baskets to be a list of baskets or just one basket (what we have) something to think - //about because arent there going to be multiple baskets per group + const { groupName, + privateGroup, + description, + members, + baskets + } = req.body; if (!groupName || privateGroup == null || !description) { console.error("Missing required fields", req.body); return res.status(400).send("Missing required fields"); } const GroupToAdd = new Group({ - groupName, - privateGroup, - description, - members, - baskets, + groupName, + privateGroup, + description, + members, + baskets }); const newGroup = await GroupToAdd.save(); @@ -92,6 +97,7 @@ router.post("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to update a group by ID router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { // Get user ID from URL const { id } = req.params; @@ -115,6 +121,7 @@ router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to delete a group by ID router.delete("/:id", authenticateUser, async (req: Request, res: Response) => { connectDB(); const { id } = req.params; diff --git a/backend/routes/itemRoutes.ts b/backend/routes/itemRoutes.ts index dbd5205..f4133fe 100644 --- a/backend/routes/itemRoutes.ts +++ b/backend/routes/itemRoutes.ts @@ -6,6 +6,7 @@ import connectDB from "../connection.js"; const router = express.Router(); +// Route to get all items router.get("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { @@ -20,6 +21,7 @@ router.get("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to get a specific item by ID or name router.get( "/:itemid", authenticateUser, @@ -57,6 +59,7 @@ router.get( }, ); +// Route to create a new item router.post("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); try { @@ -95,6 +98,7 @@ router.post("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to update an item by ID router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { // Get user ID from URL const { id } = req.params; @@ -118,6 +122,7 @@ router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to delete an item by ID router.delete("/:id", authenticateUser, async (req: Request, res: Response) => { connectDB(); const { id } = req.params; diff --git a/backend/routes/userRoutes.ts b/backend/routes/userRoutes.ts index 1072755..7eb9f6e 100644 --- a/backend/routes/userRoutes.ts +++ b/backend/routes/userRoutes.ts @@ -7,6 +7,7 @@ import bcrypt from "bcrypt"; import mongoose from "mongoose"; const router = express.Router(); +// Route to get all users router.get("/", authenticateUser, async (req: Request, res: Response) => { connectDB(); @@ -22,6 +23,7 @@ router.get("/", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to get a specific user by ID router.get( "/:userid", authenticateUser, @@ -33,7 +35,7 @@ router.get( // Use findById correctly with the id parameter from the request const user = await User.findById(req.params.userid); - // Check if group is null or undefined + // Check if user is null or undefined if (!user) { return res.status(404).send("No users found"); // Use return to exit the function after sending the response } @@ -46,7 +48,7 @@ router.get( // Use findById correctly with the id parameter from the request const user = await User.findOne({ username: req.params.userid }); - // Check if group is null or undefined + // Check if user is null or undefined if (!user) { return res.status(404).send("No users found"); // Use return to exit the function after sending the response } @@ -62,6 +64,7 @@ router.get( }, ); +// Route to get a specific user by username router.get( "/username/:username", authenticateUser, @@ -82,6 +85,7 @@ router.get( }, ); +// Route to register a new user router.post("/", async (req: Request, res: Response) => { connectDB(); let { username, email, password, firstName, lastName } = req.body; @@ -132,6 +136,7 @@ router.post("/", async (req: Request, res: Response) => { } }); +// Route to update a user by ID router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { connectDB(); // Get user ID from URL @@ -155,6 +160,7 @@ router.patch("/:id", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to delete a user by ID router.delete("/:id", authenticateUser, async (req: Request, res: Response) => { connectDB(); const { id } = req.params; @@ -173,6 +179,7 @@ router.delete("/:id", authenticateUser, async (req: Request, res: Response) => { } }); +// Route to remove a friend from user's friends list router.delete( "/:id/remove-friend", authenticateUser, diff --git a/frontend/lib/deletes.tsx b/frontend/lib/deletes.tsx index 59a1072..2c5779c 100644 --- a/frontend/lib/deletes.tsx +++ b/frontend/lib/deletes.tsx @@ -1,12 +1,12 @@ import { ObjectId } from "mongoose"; import { IBasket } from "../../backend/models/basketSchema"; import { IItem } from "../../backend/models/itemSchema"; -import { fetchGroup } from "./fetches"; +import { fetchGroupById } from "./fetches"; -// const vite_backend_url = import.meta.env.VITE_BACKEND_URL as string; const vite_backend_url = "https://gather-app-307.azurewebsites.net"; const token = localStorage.getItem("token"); +// Function to delete a group export const handleDeleteGroup = async (groupId: string) => { try { const response = await fetch(`${vite_backend_url}/groups/${groupId}`, { @@ -24,9 +24,10 @@ export const handleDeleteGroup = async (groupId: string) => { } }; +// Function to delete all baskets and items in a group export const handleDeleteAllBasketsAndItems = async (groupId: string) => { try { - const data = await fetchGroup(groupId); + const data = await fetchGroupById(groupId); if (data) { const group = await data.json(); const groupBaskets = group.baskets; @@ -46,6 +47,7 @@ export const handleDeleteAllBasketsAndItems = async (groupId: string) => { } }; +// Function to delete an item export const handleDeleteItem = async (itemId: string) => { try { const response = await fetch(`${vite_backend_url}/items/${itemId}`, { @@ -63,6 +65,7 @@ export const handleDeleteItem = async (itemId: string) => { } }; +// Function to delete a basket export const handleDeleteBasket = async (basketId: string) => { try { const response = await fetch(`${vite_backend_url}/baskets/${basketId}`, { @@ -79,6 +82,8 @@ export const handleDeleteBasket = async (basketId: string) => { console.error("There was an error deleting the basket", error); } }; + +// Function to remove a group from users' group lists export const handleDeleteGroupFromUsers = async ( groupId: string, userIds: string[], @@ -294,6 +299,7 @@ export const handleRemoveUserFromEachBasket = async ( } }; +// Function to remove a basket from a group export const handleDeleteBasketFromGroup = async ( groupId: string, basketId: string, @@ -342,6 +348,7 @@ export const handleDeleteBasketFromGroup = async ( } }; +// Function to delete all items in a basket export const handleDeleteAllItemsInBasket = async (basketId: string) => { try { // Fetch all items in the basket @@ -368,6 +375,7 @@ export const handleDeleteAllItemsInBasket = async (basketId: string) => { } }; +// Function to remove a friend from a user export const removeFriendFromUserByFriendId = async ( friendId: string, userId: string, @@ -393,6 +401,7 @@ export const removeFriendFromUserByFriendId = async ( } }; +// Function to remove an item from a basket and delete the item export const removeItemFromBasketAndDelete = async ( baskets: IBasket[], item: IItem, @@ -428,6 +437,7 @@ export const removeItemFromBasketAndDelete = async ( } }; +// Function to delete an item from a basket by basket ID export const deleteItemWithBasketString = (item: IItem, bid: string = "") => { if (!item.basket && bid === "") throw "Missing basket id."; fetch( diff --git a/frontend/lib/edits.tsx b/frontend/lib/edits.tsx index 8125a6e..f7599ab 100644 --- a/frontend/lib/edits.tsx +++ b/frontend/lib/edits.tsx @@ -5,8 +5,9 @@ import { IUser } from "../../backend/models/userSchema"; import { IGroup } from "../../backend/models/groupSchema"; import { fetchBasket } from "./fetches"; -// const vite_backend_url = import.meta.env.VITE_BACKEND_URL as string; const vite_backend_url = "https://gather-app-307.azurewebsites.net"; + +// Types for updating data type updatedGroup = { groupName: string; description: string; @@ -29,6 +30,7 @@ type updatedItem = { const token = localStorage.getItem("token"); +// Add a group to a user export const addGroupToUser = async (user: IUser, groups: string[]) => { return fetch(`${vite_backend_url}/users/${user._id}`, { method: "PATCH", @@ -40,6 +42,7 @@ export const addGroupToUser = async (user: IUser, groups: string[]) => { }); }; +// Edit group details export const editGroup = async (groupId: string, groupData: updatedGroup) => { return fetch(`${vite_backend_url}/groups/${groupId}`, { method: "PATCH", @@ -51,6 +54,7 @@ export const editGroup = async (groupId: string, groupData: updatedGroup) => { }); }; +// Edit basket details export const editBasket = async ( basketId: string, basketData: updatedBasket, @@ -65,6 +69,7 @@ export const editBasket = async ( }); }; +// Add items to a basket export const addItemToBasket = async ( basketId: ObjectId, basketItems: ObjectId[], @@ -79,6 +84,7 @@ export const addItemToBasket = async ( }); }; +// Edit item details export const editItem = async (itemId: string, itemData: updatedItem) => { return fetch(`${vite_backend_url}/items/${itemId}`, { method: "PATCH", @@ -90,6 +96,7 @@ export const editItem = async (itemId: string, itemData: updatedItem) => { }); }; +// Move item from one basket to another export const moveItem = async ( userBaskets: IBasket[], newBasket: IBasket, @@ -112,6 +119,8 @@ export const moveItem = async ( (i) => i !== item._id, ); console.log(newBasketsItems); + + // Remove item from current basket const removeItemFromBasket = await fetch( `${vite_backend_url}/baskets/${item.basket}`, { @@ -130,6 +139,8 @@ export const moveItem = async ( } else { console.error("Failed to remove item"); } + + // Update item with new basket const updatedItem = await fetch(`${vite_backend_url}/items/${item._id}`, { method: "PATCH", headers: { @@ -143,6 +154,8 @@ export const moveItem = async ( } else { console.error("Failed to update item"); } + + // Update new basket with the item const updatedBasket = await fetch( `${vite_backend_url}/baskets/${newBasket._id}`, { @@ -165,6 +178,7 @@ export const moveItem = async ( } }; +// Edit user details export const editUser = async ( userId: string, userData: { firstName: string; lastName: string }, @@ -179,6 +193,7 @@ export const editUser = async ( }); }; +// Add baskets to a group export const addBasketToGroup = async (group: IGroup, baskets: ObjectId[]) => { return fetch(`${vite_backend_url}/groups/${group._id}`, { method: "PATCH", @@ -190,6 +205,7 @@ export const addBasketToGroup = async (group: IGroup, baskets: ObjectId[]) => { }); }; +// Add users to a group export const addUserToGroup = async (group: IGroup, users: ObjectId[]) => { return fetch(`${vite_backend_url}/groups/${group._id}`, { method: "PATCH", @@ -201,6 +217,7 @@ export const addUserToGroup = async (group: IGroup, users: ObjectId[]) => { }); }; +// Add friends to a user export const addFriendToUser = async ( user: IUser, updatedFriends: ObjectId[], @@ -215,6 +232,7 @@ export const addFriendToUser = async ( }); }; +// Remove a basket from a group export const removeBasketFromGroup = async (group: IGroup, bid: string) => { fetch(`${vite_backend_url}/groups/removebasket/${group._id}&${bid}`, { method: "PATCH", diff --git a/frontend/lib/fetches.tsx b/frontend/lib/fetches.tsx index b56055b..e20e72e 100644 --- a/frontend/lib/fetches.tsx +++ b/frontend/lib/fetches.tsx @@ -4,9 +4,9 @@ import { IBasket } from "../../backend/models/basketSchema"; import { ObjectId } from "mongoose"; import { addUserToGroup, addGroupToUser } from "./edits"; -//const vite_backend_url = import.meta.env.VITE_BACKEND_URL as string; const vite_backend_url = "https://gather-app-307.azurewebsites.net"; +// Fetch a basket by ID export const fetchBasket = async (basketId: string) => { return fetch(`${vite_backend_url}/baskets/${basketId}`, { headers: { @@ -15,6 +15,7 @@ export const fetchBasket = async (basketId: string) => { }); }; +// Fetch an item by ID export const fetchItem = async (itemId: string) => { return fetch(`${vite_backend_url}/items/${itemId}`, { headers: { @@ -23,6 +24,7 @@ export const fetchItem = async (itemId: string) => { }); }; +// Fetch a group by ID export const fetchGroupById = async (groupId: string) => { try { const res = await fetch(`${vite_backend_url}/groups/${groupId}`, { @@ -40,14 +42,7 @@ export const fetchGroupById = async (groupId: string) => { } }; -export const fetchGroup = async (groupId: string) => { - return fetch(`${vite_backend_url}/groups/${groupId}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("token")}`, - }, - }); -}; - +// Fetch a user by ID export const fetchUser = async (userId: ObjectId) => { return fetch(`${vite_backend_url}/users/${userId}`, { headers: { @@ -56,6 +51,7 @@ export const fetchUser = async (userId: ObjectId) => { }); }; +// Fetch a user by string ID export const fetchUserWithString = async (userId: string) => { return fetch(`${vite_backend_url}/users/${userId}`, { headers: { @@ -64,6 +60,7 @@ export const fetchUserWithString = async (userId: string) => { }); }; +// Fetch groups for a user export const fetchUserGroupsByUser = async (user: IUser) => { const groupPromises = user.groups.map(async (group: ObjectId) => { const res = await fetch(`${vite_backend_url}/groups/${group}`, { @@ -81,6 +78,7 @@ export const fetchUserGroupsByUser = async (user: IUser) => { return tempGroupList; }; +// Fetch friends for a user export const fetchUserFriendsByUser = async (user: IUser) => { const friendPromises = user.friends.map(async (friend: ObjectId) => { const res = await fetch(`${vite_backend_url}/users/${friend}`, { @@ -98,6 +96,7 @@ export const fetchUserFriendsByUser = async (user: IUser) => { return tempFriendList; }; +// Add a friend to a group export const addFriendToGroup = async (friendId: ObjectId, groupId: string) => { try { const group = await fetchGroupById(groupId); @@ -138,6 +137,7 @@ export const addFriendToGroup = async (friendId: ObjectId, groupId: string) => { } }; +// Fetch baskets for a group export const fetchGroupBaskets = async (group: IGroup) => { const basketPromises = group.baskets.map(async (basket) => { const res = await fetch(`${vite_backend_url}/baskets/${basket}`, { @@ -157,6 +157,7 @@ export const fetchGroupBaskets = async (group: IGroup) => { return tempBaskets; }; +// Fetch items for a basket export const fetchBasketItems = async (basket: IBasket) => { if (basket.items.length === 0) { return []; @@ -177,6 +178,7 @@ export const fetchBasketItems = async (basket: IBasket) => { return tempItems; }; +// Fetch baskets for a user export const fetchUserBaskets = async (userId: string) => { const res = await fetch(`${vite_backend_url}/baskets`, { headers: { @@ -196,6 +198,7 @@ export const fetchUserBaskets = async (userId: string) => { return []; }; +// Fetch groups for a list of user group IDs export const fetchGroups = async (userGroups: ObjectId[]) => { const groupPromises = userGroups.map(async (group) => { const res = await fetch(`${vite_backend_url}/groups/${group}`, { @@ -213,6 +216,7 @@ export const fetchGroups = async (userGroups: ObjectId[]) => { return tempGroupList.filter((group) => group !== undefined); }; +// Fetch members by their IDs export const fetchMembers = async (memberIds: ObjectId[]) => { try { const fetchedMembers = await Promise.all( @@ -236,6 +240,7 @@ export const fetchMembers = async (memberIds: ObjectId[]) => { } }; +// Log in a user export const loginUser = async (credentials: { username: string; password: string; @@ -252,6 +257,7 @@ export const loginUser = async (credentials: { return res; }; +// Fetch a user by username export const fetchUserByUsername = async (username: string) => { console.log(localStorage.getItem("token")); return fetch(`${vite_backend_url}/users/username/${username}`, { diff --git a/frontend/lib/posts.tsx b/frontend/lib/posts.tsx index ff25361..cea3470 100644 --- a/frontend/lib/posts.tsx +++ b/frontend/lib/posts.tsx @@ -1,9 +1,9 @@ import { ObjectId } from "mongoose"; -// const vite_backend_url = import.meta.env.VITE_BACKEND_URL as string; const vite_backend_url = "https://gather-app-307.azurewebsites.net"; console.log("Backend URL:", vite_backend_url); +// Type definitions for new user, new items, credentials, and basket data type newUser = { username: string; email: string; @@ -34,6 +34,7 @@ type basketData = { members: ObjectId[]; }; +// Function to create a new user export const createUser = async (user: newUser) => { return fetch(`${vite_backend_url}/users`, { method: "POST", @@ -41,10 +42,11 @@ export const createUser = async (user: newUser) => { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token")}`, }, - body: JSON.stringify(user), + body: JSON.stringify(user), // Sending the item data as JSON in the request body }); }; +// Function to create a new item export const createNewItem = async (itemData: newItems) => { return fetch(`${vite_backend_url}/items`, { method: "POST", @@ -52,10 +54,11 @@ export const createNewItem = async (itemData: newItems) => { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token")}`, }, - body: JSON.stringify(itemData), + body: JSON.stringify(itemData), }); }; +// Function to log in a user export const loginUser = async (credentials: credentials) => { return fetch(`${vite_backend_url}/login`, { method: "POST", @@ -63,10 +66,11 @@ export const loginUser = async (credentials: credentials) => { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token")}`, }, - body: JSON.stringify(credentials), + body: JSON.stringify(credentials), }); }; +// Function to create a new group export const createNewGroup = async (groupData: any) => { return fetch(`${vite_backend_url}/groups/`, { method: "POST", @@ -78,6 +82,7 @@ export const createNewGroup = async (groupData: any) => { }); }; +// Function to create a new basket export const createNewBasket = async (basketData: basketData) => { return fetch(`${vite_backend_url}/baskets/`, { method: "POST", diff --git a/frontend/src/components/AddFriendToBasket.tsx b/frontend/src/components/AddFriendToBasket.tsx index 299a8ed..14e6ff1 100644 --- a/frontend/src/components/AddFriendToBasket.tsx +++ b/frontend/src/components/AddFriendToBasket.tsx @@ -15,6 +15,7 @@ import { fetchBasket } from "../../lib/fetches"; import { editBasket } from "../../lib/edits"; import { ObjectId } from "mongoose"; +// Defining the props for the component interface Props { basketId: string; groupMembers: IUser[]; @@ -22,7 +23,7 @@ interface Props { currentUserId: string | undefined; // Add a prop for the current user's ID } -// Uses member ids that are passed in from basket.tsx +// Uses member ids that are passed in from basket.tsx to add friends to a basket const AddFriendToBasket: React.FC = ({ basketId, groupMembers, @@ -34,6 +35,7 @@ const AddFriendToBasket: React.FC = ({ groupMembers.filter((member) => member._id.toString() !== currentUserId), ); + // Effect to update the members state when groupMembers or currentUserId change useEffect(() => { // This effect runs when memberid prop changes setMembers( @@ -41,12 +43,14 @@ const AddFriendToBasket: React.FC = ({ ); }, [groupMembers, currentUserId]); + // Function to add a member to the basket const AddToBasket = async (basketId: string, friendId: string) => { try { const res = await fetchBasket(basketId); let basket; if (res.ok) { basket = await res.json(); + // Check if the friend is already in the basket if (!basket.members.includes(friendId)) { basket.members.push(friendId); console.log("Pushed friend ID to basket's member list"); diff --git a/frontend/src/components/Basket.tsx b/frontend/src/components/Basket.tsx index b684f60..5191305 100644 --- a/frontend/src/components/Basket.tsx +++ b/frontend/src/components/Basket.tsx @@ -18,6 +18,7 @@ import { IBasket } from "../../../backend/models/basketSchema"; import { IUser } from "../../../backend/models/userSchema"; import { ObjectId } from "mongoose"; +// Define the props for the component interface Props { basketId: string; groupMembers: IUser[]; @@ -25,19 +26,24 @@ interface Props { groupId: string; } +// Basket component to display and manage basket details const BasketComp = ({ basketId, groupMembers, LoggedInUser, groupId, }: Props) => { + // State to store basket details const [basketObj, setBasket] = useState({} as IBasket); + // State to handle errors const [error, setError] = useState({ msg: "", isErrored: false, }); + // State to store member names const [memberNames, setMemberNames] = useState([]); + // Effect to fetch basket details and member names useEffect(() => { fetchBasket(basketId) .then((res) => @@ -82,12 +88,15 @@ const BasketComp = ({ // we designed our database, it was nearly impossible to fix this design flaw by the time we realized (we just didn't have enough time // to change the rest of our project since it was so late in the train). We acknowledge that this is a privacy problem and we would have // liked to fix it but because of time constraints we were unable to. + + // Determine if the logged-in user is a member of the basket const isMemberOfBasket = LoggedInUser && basketObj && basketObj.members && basketObj.members.includes(LoggedInUser?._id); + // Determine if the logged-in user is the owner of the basket const isOwnerOfBasket = LoggedInUser && basketObj && diff --git a/frontend/src/components/BasketItem.tsx b/frontend/src/components/BasketItem.tsx index 03ea4e5..616e695 100644 --- a/frontend/src/components/BasketItem.tsx +++ b/frontend/src/components/BasketItem.tsx @@ -21,14 +21,19 @@ interface Props { basketMemberView: boolean; } +// Component to display and manage a basket item const BasketItem = ({ itemId, bid, basketMemberView }: Props) => { + // State to store item details const [item, setItem] = useState(); + // State to handle loading state const [loading, setLoading] = useState(true); + // State to handle errors const [error, setError] = useState({ msg: "", isErrored: false, }); + // Effect to fetch item details useEffect(() => { setLoading(true); fetchItem(itemId) @@ -51,12 +56,14 @@ const BasketItem = ({ itemId, bid, basketMemberView }: Props) => { }); }, [itemId]); + // Function to remove an item from the basket const removeItem = async (item: IItem) => { console.log(item); deleteItemWithBasketString(item, bid); window.location.reload(); }; + // If the item is private and the user is not a basket member, do not render the item if (!basketMemberView && item?.isPrivate) { return; } diff --git a/frontend/src/components/CompactGroup.tsx b/frontend/src/components/CompactGroup.tsx index ba9f591..0936c6c 100644 --- a/frontend/src/components/CompactGroup.tsx +++ b/frontend/src/components/CompactGroup.tsx @@ -5,6 +5,7 @@ import ConstrainedText from "./ConstrainedText"; import { useEffect, useState } from "react"; import { fetchMembers } from "../../lib/fetches"; +// Define the props for the component interface Props { group: IGroup; width: string; @@ -12,14 +13,17 @@ interface Props { corners?: boolean[]; } +// CompactGroupV1 component to display a compact view of a group const CompactGroupV1 = ({ group, width, height, corners = [false, false, false, false], }: Props) => { + // State to store member names const [memberNames, setMemberNames] = useState([]); + // Effect to fetch member names useEffect(() => { fetchMembers(group.members) .then((res) => { diff --git a/frontend/src/components/EditBasket.tsx b/frontend/src/components/EditBasket.tsx index 263b8df..e0dea00 100644 --- a/frontend/src/components/EditBasket.tsx +++ b/frontend/src/components/EditBasket.tsx @@ -26,23 +26,28 @@ import { editBasket } from "../../lib/edits"; //Add Radio for boolean //Number input for number type - interface Props { basketId: string; groupId: string; } +// Component to edit and manage basket details const EditBasket: React.FC = ({ basketId, groupId }) => { // Note: Colors not added yet, just basic structure + // State to handle editing mode const [isEditing, setIsEditing] = useState(false); + // State to store edited basket name const [editedName, setEditedName] = useState(""); + // State to store edited basket description const [editedDesc, setEditedDesc] = useState(""); + // State to store basket data const [BasketData, setBasketData] = useState({ basketId: "", basketName: "", basketDesc: "", }); + // Effect to fetch basket data useEffect(() => { const fetchBasketData = async () => { try { @@ -67,6 +72,7 @@ const EditBasket: React.FC = ({ basketId, groupId }) => { fetchBasketData(); }, [basketId]); + // Function to handle deletion of basket and its items const handleDelete = async (groupId: string, basketId: string) => { console.log("got here"); console.log(groupId); @@ -86,6 +92,7 @@ const EditBasket: React.FC = ({ basketId, groupId }) => { } }; + // Function to handle saving changes to the basket const handleSaveChanges = async () => { try { const updatedBasket = { diff --git a/frontend/src/components/EditGroup.tsx b/frontend/src/components/EditGroup.tsx index 6b01ade..97056a9 100644 --- a/frontend/src/components/EditGroup.tsx +++ b/frontend/src/components/EditGroup.tsx @@ -28,7 +28,6 @@ import { useNavigate } from "react-router-dom"; //Add Radio for boolean //Number input for number type - interface Props { GroupId: string; members: string[] | []; @@ -36,6 +35,7 @@ interface Props { setUser: any; } +// Component to edit and manage group details const Editgroup: React.FC = ({ GroupId, members, @@ -48,10 +48,15 @@ const Editgroup: React.FC = ({ setUser: any; }) => { // Note: Colors not added yet, just basic structure + // State to handle editing mode const [isEditing, setIsEditing] = useState(false); + // State to store edited group name const [editedName, setEditedName] = useState(""); + // State to store edited group description const [editedDesc, setEditedDesc] = useState(""); + // State to store edited group visibility const [editedPub, setEditedPub] = useState(""); + // State to store group data const [groupData, setgroupData] = useState({ GroupId: "", groupName: "", @@ -60,6 +65,7 @@ const Editgroup: React.FC = ({ }); const navigate = useNavigate(); + // Effect to fetch group data useEffect(() => { const fetchgroupData = async () => { try { @@ -82,6 +88,7 @@ const Editgroup: React.FC = ({ fetchgroupData(); }, [GroupId]); + // Function to handle deletion of group and its associated items and members const handleDelete = async (groupId: string, userIds: string[]) => { console.log("here"); console.log(userIds); @@ -101,6 +108,7 @@ const Editgroup: React.FC = ({ } }; + // Function to handle saving changes to the group const handleSaveChanges = async () => { try { const updatedgroup = { diff --git a/frontend/src/components/EditItem.tsx b/frontend/src/components/EditItem.tsx index e488022..d8deb2b 100644 --- a/frontend/src/components/EditItem.tsx +++ b/frontend/src/components/EditItem.tsx @@ -38,15 +38,24 @@ interface Props { editable?: boolean; } +// Component to edit and manage item details const EditItem: React.FC = ({ itemId, editable = true }) => { // Note: Colors not added yet, just basic structure + // State to handle editing mode const [isEditing, setIsEditing] = useState(false); + // State to handle editing name const [editedName, setEditedName] = useState(""); + // State to store edited item descriptions const [editedDesc, setEditedDesc] = useState(""); + // State to store edited item quantity const [editedQuant, setEditedQuant] = useState(""); + // State to store edited item price const [editedPrice, setEditedPrice] = useState(""); + // State to store edited item visibility const [editedPub, setEditedPub] = useState(false); + // State to store edited item shareability const [editedSharable, setEditedSharable] = useState(""); + // State to store item data const [ItemData, setItemData] = useState({ itemId: "", itemName: "", @@ -57,6 +66,7 @@ const EditItem: React.FC = ({ itemId, editable = true }) => { itemSharable: "", }); + // Effect to fetch item data useEffect(() => { const fetchItemData = async () => { try { @@ -89,9 +99,11 @@ const EditItem: React.FC = ({ itemId, editable = true }) => { fetchItemData(); }, [itemId]); + // Format and parse functions for price input const format = (val: any) => `$` + val; const parse = (val: any) => val.replace(/^\$/, ""); + // Function to handle saving changes to the item const handleSaveChanges = async () => { try { const updatedItem = { diff --git a/frontend/src/components/Friends_List_Component.tsx b/frontend/src/components/Friends_List_Component.tsx index 2e2d088..49bb0f4 100644 --- a/frontend/src/components/Friends_List_Component.tsx +++ b/frontend/src/components/Friends_List_Component.tsx @@ -40,16 +40,21 @@ type Props = { LoggedInUser: any; }; +// Component to manage and display friends list const Friends_List: React.FC = ({ initialUserId = "", LoggedInUser, }) => { + // State to store the groups the user is part of const [groups, setGroups] = useState([]); + // State to store the user's friends const [friends, setFriends] = useState([]); + // State to store the user ID (or username) entered in the input field const [userId, setUserId] = useState(initialUserId); const [errorMessage, setErrorMessage] = useState(""); const [loading, setLoading] = useState(true); + // Effect to fetch friends and groups data when the component mounts or when the LoggedInUser changes useEffect(() => { const fetchFriendsAndGroups = async () => { console.log(LoggedInUser); @@ -58,9 +63,11 @@ const Friends_List: React.FC = ({ if (response.ok) { const user = await response.json(); + // Fetch the groups the user is part of const groupsList = await fetchUserGroupsByUser(user); setGroups(groupsList); - + + // Fetch the user's friends const friendsData = await fetchUserFriendsByUser(user); setFriends(friendsData); } else { @@ -78,6 +85,7 @@ const Friends_List: React.FC = ({ } }, [LoggedInUser]); + // Function to remove a friend from the user's friends list const removeFriend = async (friendId: string) => { try { console.log(friendId); @@ -90,6 +98,7 @@ const Friends_List: React.FC = ({ } }; + // Function to add a friend to the user's friends list const addFriend = async (username: string) => { try { console.log(username); @@ -132,6 +141,7 @@ const Friends_List: React.FC = ({ } }; + // Function to handle adding a friend when the "Add Friend" button is clicked const handleClick: React.MouseEventHandler = async ( event, ) => { @@ -149,6 +159,7 @@ const Friends_List: React.FC = ({ } }; + // Function to handle adding a friend to a group const handleGroupClick = async (groupId: string, friendId: ObjectId) => { try { console.log(`Group ID: ${groupId} clicked`); diff --git a/frontend/src/components/ItemGroup.tsx b/frontend/src/components/ItemGroup.tsx index 72b7b5a..72d9668 100644 --- a/frontend/src/components/ItemGroup.tsx +++ b/frontend/src/components/ItemGroup.tsx @@ -40,6 +40,7 @@ type Props = { stateVariable: any; }; +// Component to display and manage items in a group const ItemGroup: React.FC = ({ group, stateVariable, @@ -54,6 +55,7 @@ const ItemGroup: React.FC = ({ const [loading, setLoading] = React.useState(true); const category = group.groupName; + // Fetch all data related to the group and user when the component mounts or stateVariable.user changes useEffect(() => { const fetchAllData = async () => { if (stateVariable.user) { @@ -62,10 +64,12 @@ const ItemGroup: React.FC = ({ setBasket(fetchedBaskets[0]); const tempItems: IItem[] = []; + // Fetch items from all baskets in the group for (const basket of fetchedBaskets) { const fetchedItems = await fetchBasketItems(basket); tempItems.push(...fetchedItems); } + // Fetch baskets that belong to the user const userBaskets = await fetchUserBaskets(stateVariable.user._id); setUserBaskets(userBaskets as IBasket[] | []); console.log("userBaskets: ", userBaskets); @@ -80,12 +84,14 @@ const ItemGroup: React.FC = ({ }); }, [stateVariable.user]); + // Update the baskets state when the selected basket changes useEffect(() => { if (basket) { setBaskets([basket, ...baskets.slice(1)]); } }, [basket]); + // Function to remove an item from the basket and delete it const removeItem = async (item: IItem) => { const newItems = items.filter((i) => i._id !== item._id); setItems(newItems); @@ -97,6 +103,7 @@ const ItemGroup: React.FC = ({ }); }; + // Function to handle moving an item to a different basket const handleMove = async (basket: IBasket, item: IItem) => { try { console.log(`Basket ID: ${basket._id} clicked`); diff --git a/frontend/src/components/NavbarSignedIn.tsx b/frontend/src/components/NavbarSignedIn.tsx index b78f73b..089047f 100644 --- a/frontend/src/components/NavbarSignedIn.tsx +++ b/frontend/src/components/NavbarSignedIn.tsx @@ -17,7 +17,6 @@ import logo from "../../public/TheLeaf.png"; import { ReactNode } from "react"; import { useNavigate, Link as ReactLink } from "react-router-dom"; -// const vite_backend_url = import.meta.env.VITE_BACKEND_URL as string; const vite_backend_url = "https://gather-app-307.azurewebsites.net"; const NavLink = ({ @@ -45,13 +44,15 @@ const NavLink = ({ ); interface Props { - stateVariable: any; - updateState: any; + stateVariable: any; // Contains user state information + updateState: any; // Function to update state } +// NavbarSignedIn component const NavbarSignedIn = ({ stateVariable, updateState }: Props) => { const navigate = useNavigate(); + // Handle logout action const handleLogout = () => { updateState.setToken(""); updateState.setUser(""); @@ -59,14 +60,17 @@ const NavbarSignedIn = ({ stateVariable, updateState }: Props) => { navigate("/"); }; + // Navigate to profile page const handleProfileClick = () => { navigate("/profile"); }; + // Navigate to groups page const handleGroupsClick = () => { navigate("/groups"); }; + // Navigate to items page const handleItemsClick = () => { navigate("/items"); }; diff --git a/frontend/src/components/NavbarSignedOut.tsx b/frontend/src/components/NavbarSignedOut.tsx index 48283ed..9db7593 100644 --- a/frontend/src/components/NavbarSignedOut.tsx +++ b/frontend/src/components/NavbarSignedOut.tsx @@ -2,6 +2,7 @@ import { Link } from "react-router-dom"; import { Box, Flex, Text, Button, HStack, Image } from "@chakra-ui/react"; import logo from "../../public/TheLeaf.png"; +// Component for the navigation bar when the user is signed out const NavbarSignedOut = () => { return ( diff --git a/frontend/src/components/NewBasketOptions.tsx b/frontend/src/components/NewBasketOptions.tsx index 8cdec89..9b0f23f 100644 --- a/frontend/src/components/NewBasketOptions.tsx +++ b/frontend/src/components/NewBasketOptions.tsx @@ -18,6 +18,7 @@ import { IGroup } from "../../../backend/models/groupSchema"; import { addBasketToGroup } from "../../lib/edits"; import { createNewBasket } from "../../lib/posts"; +// Component for displaying options to create a new basket const NewBasketOptions = ({ user, group, @@ -27,26 +28,24 @@ const NewBasketOptions = ({ group: IGroup; updateGroup: any; }) => { - //Backend notes: If possible, - // 1) automatically provide default description if none given - // 2) automatically create a basket for the user rather than having no baskets created upon group creation - //Frontend notes: - // 1) When added, update "owner" keyword to be automatically the id of the logged in user - // 2) If no user logged in, impossible to create group + // Function to handle the creation of a new basket const createBasket = async (basketName: string, description: string) => { if (user === null) { console.error("No user logged in"); return; } + // Prepare basket data const basketData = { basketName: basketName, description: description, members: [user._id], }; + // Create new basket const promise = await createNewBasket(basketData); if (promise.status === 201) { const data = await promise.json(); console.log("Basket created successfully", data); + // Add new basket to the group const newData = [...group.baskets, data._id]; console.log(newData); const groupPromise = await addBasketToGroup(group, newData); @@ -57,6 +56,7 @@ const NewBasketOptions = ({ console.log("Group updated successfully"); } } + // Reload the page window.location.reload(); }; @@ -76,6 +76,7 @@ interface CreateProps { postBasket: (name: string, description: string) => void; } +// Component for creating a new group const CreateGroup = ({ postBasket }: CreateProps) => { const [basket, setBasket] = useState({ name: "", @@ -84,6 +85,7 @@ const CreateGroup = ({ postBasket }: CreateProps) => { const [errored, setError] = useState({ state: false, msg: "" }); const { onOpen, onClose, isOpen } = useDisclosure(); + // Handle input change const handleChange = (event: FormEvent) => { const { name, value } = event.currentTarget; if (name === "name") { @@ -93,6 +95,7 @@ const CreateGroup = ({ postBasket }: CreateProps) => { } }; + // Handle form submission const handleSubmit = () => { postBasket( basket.name, diff --git a/frontend/src/components/NewGroupOptions.tsx b/frontend/src/components/NewGroupOptions.tsx index 93b253c..875fc95 100644 --- a/frontend/src/components/NewGroupOptions.tsx +++ b/frontend/src/components/NewGroupOptions.tsx @@ -16,6 +16,7 @@ import { IUser } from "../../../backend/models/userSchema"; import { createNewGroup, createNewBasket } from "../../lib/posts"; import { addGroupToUser } from "../../lib/edits"; +// Component for creating new group options const NewGroupOptions = ({ user, updateUser, @@ -23,12 +24,7 @@ const NewGroupOptions = ({ user: IUser; updateUser: any; }) => { - //Backend notes: If possible, - // 1) automatically provide default description if none given - // 2) automatically create a basket for the user rather than having no baskets created upon group creation - //Frontend notes: - // 1) When added, update "owner" keyword to be automatically the id of the logged in user - // 2) If no user logged in, impossible to create group + // Function to create a new group const createGroup = async ( groupName: string, privateGroup: boolean, @@ -58,7 +54,8 @@ const NewGroupOptions = ({ if (promise.status === 201) { const data = await promise.json(); console.log("Group created successfully", data); - + + // Add new group to user's list of groups const newData = [...user.groups, data._id]; const userPromise = await addGroupToUser(user, newData); @@ -86,6 +83,7 @@ interface CreateProps { postGroup: (name: string, isPublic: boolean, description: string) => void; } +// Component for creating a new group const CreateGroup = ({ postGroup }: CreateProps) => { const [group, setGroup] = useState({ name: "", @@ -95,6 +93,7 @@ const CreateGroup = ({ postGroup }: CreateProps) => { const [errored, setError] = useState({ state: false, msg: "" }); const { onOpen, onClose, isOpen } = useDisclosure(); + // Handle input change const handleChange = (event: FormEvent) => { const { name, value } = event.currentTarget; console.log("Edited ", name, " value", value); @@ -107,6 +106,7 @@ const CreateGroup = ({ postGroup }: CreateProps) => { } }; + // Handle form submission const handleSubmit = () => { postGroup( group.name, diff --git a/frontend/src/components/NewItemOptions.tsx b/frontend/src/components/NewItemOptions.tsx index 45c0471..69ce81d 100644 --- a/frontend/src/components/NewItemOptions.tsx +++ b/frontend/src/components/NewItemOptions.tsx @@ -26,6 +26,7 @@ import { addItemToBasket } from "../../lib/edits"; import { ObjectId } from "mongoose"; import { IBasket } from "../../../backend/models/basketSchema"; +// Component for displaying options to add a new item const NewItemOptions = ({ basket, updateBasket, @@ -35,6 +36,7 @@ const NewItemOptions = ({ updateBasket: any; display?: string; }) => { + // Function to create a new item const createItem = async ( name: string, toShare: boolean, @@ -97,6 +99,7 @@ interface CreateProps { ) => void; } +// Component for creating a new item const CreateItem = ({ postItem }: CreateProps) => { const [item, setItem] = useState({ name: "", @@ -110,6 +113,7 @@ const CreateItem = ({ postItem }: CreateProps) => { const [errored, setError] = useState({ state: false, msg: "" }); const { onOpen, onClose, isOpen } = useDisclosure(); + // Handle input change const handleChange = (event: FormEvent) => { const { name, value, type, checked } = event.currentTarget; @@ -119,6 +123,7 @@ const CreateItem = ({ postItem }: CreateProps) => { })); }; + // Handle form submission const handleSubmit = () => { console.log(item); postItem( @@ -141,12 +146,14 @@ const CreateItem = ({ postItem }: CreateProps) => { }); }; + // Handle price input change const handleNumberInputChangePrice = (valueAsString: string) => { // Convert string to float because the input will provide a formatted string with a dollar sign const valueAsNumber = parseFloat(valueAsString.replace(/^\$/, "")); setItem((prevItem) => ({ ...prevItem, price: valueAsNumber })); }; + // Handle quantity input change const handleNumberInputChangeQuantity = ( valueAsString: string, valueAsNumber: number, diff --git a/frontend/src/components/PageNumberButton.tsx b/frontend/src/components/PageNumberButton.tsx index 787a188..4cc2bf7 100644 --- a/frontend/src/components/PageNumberButton.tsx +++ b/frontend/src/components/PageNumberButton.tsx @@ -1,11 +1,12 @@ import { Button } from "@chakra-ui/react"; interface Props { - displayValue: number; // Pass <0 for ... - selectedValue?: number; - onSelect?: (x: number) => void; + displayValue: number; // The value to display on the button. Pass a value < 0 to display "..." + selectedValue?: number; // The currently selected value + onSelect?: (x: number) => void; // Function to call when the button is clicked } +// Component for rendering a page number button const PageNumberButton = ({ displayValue, selectedValue = -1, diff --git a/frontend/src/components/PageSelector.tsx b/frontend/src/components/PageSelector.tsx index e4bd7b7..fe6e6f0 100644 --- a/frontend/src/components/PageSelector.tsx +++ b/frontend/src/components/PageSelector.tsx @@ -5,12 +5,13 @@ import PageNumberButton from "./PageNumberButton"; interface Props { range: number; // Creates page selectors from 1:range inclusive limit: number; // Limit how many page numbers are displayed at one time - selected: number; - onSelect: (x: number) => void; - minimal?: boolean; + selected: number; // Currently selected page + onSelect: (x: number) => void; // Function to handle page selection + minimal?: boolean; // Whether to display minimal UI } // Page selector creates 1...range cell boxes that are selectable +// Creates page number buttons and navigation controls const PageSelector = ({ range, limit, @@ -18,6 +19,7 @@ const PageSelector = ({ onSelect, minimal = true, }: Props) => { + // Ensure the limit is at least 5 if (limit < 5) { console.log( "Error: limit for PageSelector must be at most 5. Supplied: ", @@ -27,7 +29,7 @@ const PageSelector = ({ } const cells: JSX.Element[] = []; //Holds a list of elements to render - // Push a left arrow + // Add a left arrow button cells.push(