Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions backend/src/controllers/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const upload = multer({
*/
export const getProducts = async (req: AuthenticatedRequest, res: Response) => {
try {
const products = await ProductModel.find();
const products = await ProductModel.find({ isMarkedSold: { $in: [false, null] } });
res.status(200).json(products);
} catch (error) {
res.status(500).json({ message: "Error fetching products", error });
Expand Down Expand Up @@ -52,7 +52,10 @@ export const getProductById = async (req: AuthenticatedRequest, res: Response) =
export const getProductsByName = async (req: AuthenticatedRequest, res: Response) => {
try {
const query = req.params.query;
const products = await ProductModel.find({ name: { $regex: query, $options: "i" } });
const products = await ProductModel.find({
name: { $regex: query, $options: "i" },
isMarkedSold: { $in: [false, null] },
});
if (!products) {
return res.status(404).json({ message: "Product not found" });
}
Expand Down Expand Up @@ -191,6 +194,7 @@ export const updateProductById = [
description: req.body.description,
images: finalImages,
timeUpdated: new Date(),
isMarkedSold: req.body.isMarkedSold ?? false,
},
{ new: true },
);
Expand Down
5 changes: 5 additions & 0 deletions backend/src/models/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const productSchema = new Schema({
type: String,
required: true,
},
isMarkedSold: {
type: Boolean,
required: true,
default: false,
},
images: [{ type: String }],
});

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/pages/EditProduct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function EditProduct() {
images: string[];
userEmail: string;
description: string;
isMarkedSold: boolean;
}>();

const productName = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -84,6 +85,7 @@ export function EditProduct() {
body.append("price", productPrice.current.value);
body.append("description", productDescription.current.value);
body.append("userEmail", user.email || "");
body.append("isMarkedSold", String(product?.isMarkedSold));

// append existing image URLs
existingImages.forEach((url) => body.append("existingImages", url));
Expand Down
58 changes: 56 additions & 2 deletions frontend/src/pages/Individual-product-page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { faPenToSquare } from "@fortawesome/free-solid-svg-icons";
import { faPenToSquare, faCheck, faArrowUp } 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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useContext, useEffect, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { get, post } from "src/api/requests";
import { get, patch, post } from "src/api/requests";
import { FirebaseContext } from "src/utils/FirebaseProvider";
import EmblaCarousel from "src/components/EmblaCarousel";
import { EmblaOptionsType } from "embla-carousel";
Expand All @@ -20,6 +20,7 @@ export function IndividualProductPage() {
images: string[];
userEmail: string;
description: string;
isMarkedSold: boolean;
}>();
const [error, setError] = useState<string>();
const [hasPermissions, setHasPermissions] = useState<boolean>(false);
Expand Down Expand Up @@ -122,6 +123,33 @@ export function IndividualProductPage() {
setIsSubmitting(false);
}
};

const handleMarkSold = async () => {
if (!product) return;
const body = new FormData();
body.append("name", product.name);
body.append("price", product.price.toString());
body.append("description", product.description);
body.append("userEmail", product.userEmail);
product.images.forEach((url) => body.append("existingImages", url));
body.append("isMarkedSold", String(!product.isMarkedSold));

await patch(`/api/products/${id}`, body)
.then(async (res) => {
const response = await res.json();
if (res.ok) {
setProduct(response.updatedProduct);
navigate(`/products/${id}`);
} else {
alert("Failed to update product");
console.log(response);
}
})
.catch((e) => {
console.log(e);
});
};

const isCooling = Boolean(cooldownEnd && Date.now() < cooldownEnd);
// const secondsLeft = isCooling ? Math.ceil((cooldownEnd! - Date.now()) / 1000) : 0;
const msLeft = isCooling ? cooldownEnd! - Date.now() : 0;
Expand Down Expand Up @@ -234,9 +262,35 @@ export function IndividualProductPage() {

<hr className="my-6 w-full mx-auto h-0 border-[1px] border-solid border-gray-300" />

{hasPermissions &&
(product?.isMarkedSold ? (
<button
className="text-lg font-inter py-4 mb-6 font-bold border border-ucsd-blue text-ucsd-blue rounded-md"
onClick={handleMarkSold}
>
Renew on Marketplace <FontAwesomeIcon icon={faArrowUp} />
</button>
) : (
<button
className="text-lg font-inter py-4 mb-6 font-bold bg-ucsd-blue hover:bg-ucsd-darkblue text-white rounded-md transition-colors"
onClick={handleMarkSold}
>
Mark as Sold <FontAwesomeIcon icon={faCheck} />
</button>
))}

<h2 className="font-inter text-[#35393C] text-base md:text-xl font-normal pb-6">
USD ${product?.price?.toFixed(2)}
</h2>
{product?.isMarkedSold && (
<div className="bg-red-100 p-5 mb-6">
<p className="font-inter text-black text-base md:text-xl font-normal break-words">
{hasPermissions
? "This product has been marked as sold. It will not appear on the marketplace, but others can still be find it under your profile."
: "This product is no longer available."}
</p>
</div>
)}
{product?.description && (
<div className="bg-[#F5F0E6] p-5 mb-6">
<p className="font-inter text-black text-base md:text-xl font-normal break-words">
Expand Down