From 229af0b73a91fd39c7147b9b0859bacf29a21b83 Mon Sep 17 00:00:00 2001 From: Abas Mohamed Date: Wed, 21 Jan 2026 22:36:26 -0800 Subject: [PATCH 1/4] Add/edit products with the new properties is working --- backend/src/controllers/products.ts | 10 +- backend/src/models/product.ts | 16 +++ frontend/src/components/Product.tsx | 4 + frontend/src/pages/AddProduct.tsx | 94 ++++++++++++++- frontend/src/pages/EditProduct.tsx | 109 +++++++++++++++++- .../src/pages/Individual-product-page.tsx | 19 +++ frontend/src/pages/SavedProducts.tsx | 8 ++ 7 files changed, 255 insertions(+), 5 deletions(-) diff --git a/backend/src/controllers/products.ts b/backend/src/controllers/products.ts index 3c00ae0..c97cb44 100644 --- a/backend/src/controllers/products.ts +++ b/backend/src/controllers/products.ts @@ -69,12 +69,12 @@ export const addProduct = [ upload, async (req: AuthenticatedRequest, res: Response) => { try { - const { name, price, description } = req.body; + const { name, price, description, year, category, condition, location} = req.body; if (!req.user) return res.status(404).json({ message: "User not found" }); const userId = req.user._id; const userEmail = req.user.userEmail; - if (!name || !price || !userEmail) { - return res.status(400).json({ message: "Name, price, and userEmail are required." }); + if (!name || !price || !userEmail || !year || !category || !condition || !location) { + return res.status(400).json({ message: "Name, price, userEmail, year, category, condition, and location are required." }); } const images: string[] = []; @@ -101,6 +101,10 @@ export const addProduct = [ description, userEmail, images, + year, + category, + condition, + location, timeCreated: new Date(), timeUpdated: new Date(), }); diff --git a/backend/src/models/product.ts b/backend/src/models/product.ts index ca7386a..d303661 100644 --- a/backend/src/models/product.ts +++ b/backend/src/models/product.ts @@ -12,6 +12,22 @@ const productSchema = new Schema({ description: { type: String, }, + year: { + type: Number, + required: true, + }, + category: { + type: String, + required: true, + }, + condition: { + type: String, + required: true, + }, + location: { + type: String, + required: true, + }, timeCreated: { type: Date, required: true, diff --git a/frontend/src/components/Product.tsx b/frontend/src/components/Product.tsx index 4a5b4b5..a7b6bc2 100644 --- a/frontend/src/components/Product.tsx +++ b/frontend/src/components/Product.tsx @@ -10,6 +10,10 @@ interface Props { productImages: string[]; productName: string; productPrice: number; + productYear: number; + productCategory: string; + productCondition: string; + productLocation: string; isSaved?: boolean; onSaveToggle?: (productId: string, newSavedStatus: boolean) => void; } diff --git a/frontend/src/pages/AddProduct.tsx b/frontend/src/pages/AddProduct.tsx index bf68a9b..f28da10 100644 --- a/frontend/src/pages/AddProduct.tsx +++ b/frontend/src/pages/AddProduct.tsx @@ -10,8 +10,21 @@ export function AddProduct() { const productName = useRef(null); const productPrice = useRef(null); const productDescription = useRef(null); + const productYear = useRef(null); + const productCategory = useRef(null); + const productCondition = useRef(null); + const productLocation = useRef(null); const productImages = useRef(null); + + const currentYear = new Date().getFullYear(); + const years = Array.from({ length: currentYear - 1950 }, (_, i) => currentYear - i); + + // Placeholder categories (letters for now) + const categories = ["A", "B", "C", "D", "E", "F"]; + + const conditions = ["New", "Used"]; + const { user } = useContext(FirebaseContext); const [error, setError] = useState(false); const [fileError, setFileError] = useState(null); @@ -56,7 +69,7 @@ export function AddProduct() { setIsSubmitting(true); e.preventDefault(); try { - if (productName.current && productPrice.current && productDescription.current && user) { + if (productName.current && productPrice.current && productDescription.current && productYear.current && productCategory.current && productCondition.current && productLocation.current && user) { let images; if (productImages.current && productImages.current.files) { images = productImages.current.files[0]; @@ -66,6 +79,10 @@ export function AddProduct() { body.append("name", productName.current.value); body.append("price", productPrice.current.value); body.append("description", productDescription.current.value); + body.append("year", productYear.current.value); + body.append("category", productCategory.current.value); + body.append("condition", productCondition.current.value); + body.append("location", productLocation.current.value); if (user.email) body.append("userEmail", user.email); if (productImages.current && productImages.current.files) { @@ -146,6 +163,81 @@ export function AddProduct() { /> + {/* Year */} +
+ + +
+ + {/* Category */} +
+ + +
+ + {/* Condition */} +
+ + +
+ + {/* Location */} +
+ + +
+ {/* Images */}
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
)} +
+

+ {product?.year} +

+
+
+

+ {product?.category} +

+
+
+

+ {product?.condition} +

+
{!hasPermissions && (
setIsHovered(true)} diff --git a/frontend/src/pages/SavedProducts.tsx b/frontend/src/pages/SavedProducts.tsx index 9a722dd..3ac0bb0 100644 --- a/frontend/src/pages/SavedProducts.tsx +++ b/frontend/src/pages/SavedProducts.tsx @@ -11,6 +11,10 @@ export function SavedProducts() { _id: string; name: string; price: number; + year: number; + category: string; + condition: string; + location: string; images: string[]; }> >([]); @@ -93,6 +97,10 @@ export function SavedProducts() { productId={product._id} productName={product.name} productPrice={product.price} + productYear={product.year} + productCategory={product.category} + productCondition={product.condition} + productLocation={product.location} productImages={ product.images && product.images.length > 0 ? product.images From a3743bbb7d9f5be193103e34d1351a01fd3cb4ca Mon Sep 17 00:00:00 2001 From: Abas Mohamed Date: Fri, 23 Jan 2026 19:27:48 -0800 Subject: [PATCH 2/4] Fixed edit product functionality and updated the ui for product display --- backend/src/controllers/products.ts | 4 + frontend/src/pages/AddProduct.tsx | 4 +- frontend/src/pages/EditProduct.tsx | 3 +- .../src/pages/Individual-product-page.tsx | 75 +++++++++++++------ 4 files changed, 60 insertions(+), 26 deletions(-) diff --git a/backend/src/controllers/products.ts b/backend/src/controllers/products.ts index c97cb44..6a3ef2c 100644 --- a/backend/src/controllers/products.ts +++ b/backend/src/controllers/products.ts @@ -193,6 +193,10 @@ export const updateProductById = [ name: req.body.name, price: req.body.price, description: req.body.description, + year: req.body.year, + category: req.body.category, + condition: req.body.condition, + location: req.body.location, images: finalImages, timeUpdated: new Date(), }, diff --git a/frontend/src/pages/AddProduct.tsx b/frontend/src/pages/AddProduct.tsx index f28da10..3e2f299 100644 --- a/frontend/src/pages/AddProduct.tsx +++ b/frontend/src/pages/AddProduct.tsx @@ -20,8 +20,8 @@ export function AddProduct() { const currentYear = new Date().getFullYear(); const years = Array.from({ length: currentYear - 1950 }, (_, i) => currentYear - i); - // Placeholder categories (letters for now) - const categories = ["A", "B", "C", "D", "E", "F"]; + + const categories = ["Electronics", "School Supplies", "Dorm", "Furniture", "Clothes"]; const conditions = ["New", "Used"]; diff --git a/frontend/src/pages/EditProduct.tsx b/frontend/src/pages/EditProduct.tsx index 98e3961..2b24bbe 100644 --- a/frontend/src/pages/EditProduct.tsx +++ b/frontend/src/pages/EditProduct.tsx @@ -32,8 +32,7 @@ export function EditProduct() { const currentYear = new Date().getFullYear(); const years = Array.from({ length: currentYear - 1950 }, (_, i) => currentYear - i); - // Placeholder categories (letters for now) - const categories = ["A", "B", "C", "D", "E", "F"]; + const categories = ["Electronics", "School Supplies", "Dorm", "Furniture", "Clothes"]; const conditions = ["New", "Used"]; diff --git a/frontend/src/pages/Individual-product-page.tsx b/frontend/src/pages/Individual-product-page.tsx index da2201b..f2fbf8c 100644 --- a/frontend/src/pages/Individual-product-page.tsx +++ b/frontend/src/pages/Individual-product-page.tsx @@ -1,6 +1,7 @@ import { faPenToSquare } from "@fortawesome/free-solid-svg-icons"; import { faHeart as faHeartSolid } from "@fortawesome/free-solid-svg-icons"; import { faHeart as faHeartRegular } from "@fortawesome/free-regular-svg-icons"; +import { faCalendar, faTag, faCheckCircle, faMapMarkerAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useContext, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; @@ -232,37 +233,67 @@ export function IndividualProductPage() { {/* Info Section */}
-

+

{product?.name}

-
+ {/* Price - Prominent Display */} +
+

+ ${product?.price?.toFixed(2)} +

+

USD

+
+ + {/* Product Details Grid */} +
+
+
+ + Year +
+

{product?.year}

+
+ +
+
+ + Category +
+

{product?.category}

+
+ +
+
+ + Condition +
+

{product?.condition}

+
-

- USD ${product?.price?.toFixed(2)} -

+ {product?.location && ( +
+
+ + Location +
+

{product.location}

+
+ )} +
+ + {/* Description */} {product?.description && ( -
-

+

+

+ Description +

+

{product.description}

)} -
-

- {product?.year} -

-
-
-

- {product?.category} -

-
-
-

- {product?.condition} -

-
+ {!hasPermissions && (
setIsHovered(true)} From 606d9ef599fc6db506b7d0f100744eabf21011e8 Mon Sep 17 00:00:00 2001 From: Abas Mohamed Date: Tue, 27 Jan 2026 18:19:18 -0800 Subject: [PATCH 3/4] Removed location property, updated image upload ui --- backend/src/controllers/products.ts | 8 +- backend/src/models/product.ts | 4 - frontend/src/pages/AddProduct.tsx | 123 ++++++++++++----------- frontend/src/pages/EditProduct.tsx | 147 +++++++++++++++------------- 4 files changed, 151 insertions(+), 131 deletions(-) diff --git a/backend/src/controllers/products.ts b/backend/src/controllers/products.ts index 6a3ef2c..4d200a9 100644 --- a/backend/src/controllers/products.ts +++ b/backend/src/controllers/products.ts @@ -69,12 +69,12 @@ export const addProduct = [ upload, async (req: AuthenticatedRequest, res: Response) => { try { - const { name, price, description, year, category, condition, location} = req.body; + const { name, price, description, year, category, condition} = req.body; if (!req.user) return res.status(404).json({ message: "User not found" }); const userId = req.user._id; const userEmail = req.user.userEmail; - if (!name || !price || !userEmail || !year || !category || !condition || !location) { - return res.status(400).json({ message: "Name, price, userEmail, year, category, condition, and location are required." }); + if (!name || !price || !userEmail || !year || !category || !condition) { + return res.status(400).json({ message: "Name, price, userEmail, year, category, condition, are required." }); } const images: string[] = []; @@ -104,7 +104,6 @@ export const addProduct = [ year, category, condition, - location, timeCreated: new Date(), timeUpdated: new Date(), }); @@ -196,7 +195,6 @@ export const updateProductById = [ year: req.body.year, category: req.body.category, condition: req.body.condition, - location: req.body.location, images: finalImages, timeUpdated: new Date(), }, diff --git a/backend/src/models/product.ts b/backend/src/models/product.ts index d303661..973f962 100644 --- a/backend/src/models/product.ts +++ b/backend/src/models/product.ts @@ -24,10 +24,6 @@ const productSchema = new Schema({ type: String, required: true, }, - location: { - type: String, - required: true, - }, timeCreated: { type: Date, required: true, diff --git a/frontend/src/pages/AddProduct.tsx b/frontend/src/pages/AddProduct.tsx index 3e2f299..0f8d594 100644 --- a/frontend/src/pages/AddProduct.tsx +++ b/frontend/src/pages/AddProduct.tsx @@ -13,7 +13,6 @@ export function AddProduct() { const productYear = useRef(null); const productCategory = useRef(null); const productCondition = useRef(null); - const productLocation = useRef(null); const productImages = useRef(null); @@ -21,7 +20,13 @@ export function AddProduct() { const years = Array.from({ length: currentYear - 1950 }, (_, i) => currentYear - i); - const categories = ["Electronics", "School Supplies", "Dorm", "Furniture", "Clothes"]; + const categories = [ + 'Electronics', + 'School Supplies', + 'Dorm Essentials', + 'Furniture', + 'Clothes', + 'Miscellaneous']; const conditions = ["New", "Used"]; @@ -69,7 +74,7 @@ export function AddProduct() { setIsSubmitting(true); e.preventDefault(); try { - if (productName.current && productPrice.current && productDescription.current && productYear.current && productCategory.current && productCondition.current && productLocation.current && user) { + if (productName.current && productPrice.current && productDescription.current && productYear.current && productCategory.current && productCondition.current && user) { let images; if (productImages.current && productImages.current.files) { images = productImages.current.files[0]; @@ -82,7 +87,6 @@ export function AddProduct() { body.append("year", productYear.current.value); body.append("category", productCategory.current.value); body.append("condition", productCondition.current.value); - body.append("location", productLocation.current.value); if (user.email) body.append("userEmail", user.email); if (productImages.current && productImages.current.files) { @@ -113,6 +117,66 @@ export function AddProduct() {

Add Product

+ {/* Images */} +
+ +

Upload up to 10 photos

+ + {newPreviews.length > 0 && ( +
+
+ {newPreviews.map((src, idx) => ( +
+ + +
+ ))} +
+
+ )} + + +
{/* Name */}
- {/* Location */} -
- - -
- - {/* Images */} -
- - - {newPreviews.length > 0 && ( -
-
- {newPreviews.map((src, idx) => ( -
- - -
- ))} -
-
- )} - - -
+ {/* Images */} +
+ +

Upload up to 10 photos

+ + {(newPreviews.length > 0 || existingImages.length > 0) && ( +
+ {existingImages.map((url) => ( +
+ + +
+ ))} + + {newPreviews.map((src, idx) => ( +
+ + +
+ ))} +
+ )} + + +
+
-
- - -
- -
- - - {(newPreviews.length > 0 || existingImages.length > 0) && ( -
- {existingImages.map((url) => ( -
- - -
- ))} - - {newPreviews.map((src, idx) => ( -
- - -
- ))} -
- )} - - -
-
+ {newPreviews.length > 0 && ( +
+
+ {newPreviews.map((src, idx) => ( +
+ + +
+ ))}
- ))} -
-
- )} +
+ )} - + {fileError &&

{fileError}

}
- - - - {/* Name */} -
- - -
- {/* Price */} -
- - -
- - {/* Description */} -
- -