Files
group-2-threaded-project-pe…/web/components/ProductCard.js
2026-04-20 19:19:30 -06:00

86 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Link from "next/link";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/context/AuthContext";
import { useCart } from "@/context/CartContext";
//Shared CSS class for the quantity plus and minus buttons
const qtyBtnCls = "w-7 h-7 border border-[#ddd] rounded-md bg-white text-base cursor-pointer flex items-center justify-center transition-colors hover:border-[#e68672] disabled:opacity-50";
//Card shown in the products grid, includes quantity selector and add to cart button
export default function ProductCard({ prodId, prodName, categoryName, prodPrice, imageUrl }) {
const { user } = useAuth();
const { addItem, selectedStoreId } = useCart();
const router = useRouter();
const [quantity, setQuantity] = useState(1);
const [adding, setAdding] = useState(false);
const [feedback, setFeedback] = useState(null);
//Adds the selected quantity to the cart, redirects to login if not logged in
async function handleAddToCart(e) {
e.preventDefault();
if (!user) { router.push("/login"); return; }
if (!selectedStoreId) {
setFeedback("Please select a store first");
setTimeout(() => setFeedback(null), 2500);
return;
}
setAdding(true);
setFeedback(null);
try {
await addItem(prodId, quantity);
setFeedback("Added!");
setTimeout(() => setFeedback(null), 1500);
} catch (err) {
setFeedback(err.message || "Failed to add");
setTimeout(() => setFeedback(null), 2500);
} finally {
setAdding(false);
}
}
return (
<div className="flex flex-col no-underline rounded-2xl overflow-hidden shadow-[0_4px_12px_rgba(0,0,0,0.08)] transition-all duration-300 hover:-translate-y-1.5 hover:shadow-[0_8px_24px_rgba(0,0,0,0.13)] bg-white">
<Link href={`/products/${prodId}`} className="no-underline text-inherit flex-1">
<div className="bg-[#fff8ee] flex items-center justify-center aspect-square">
<img
src={imageUrl || "/images/pet-placeholder.png"}
alt={prodName}
className="w-full h-full object-cover"
onError={(e) => {
e.currentTarget.onerror = null;
e.currentTarget.src = "/images/pet-placeholder.png";
}}
/>
</div>
<div className="px-3 pt-[0.6rem] pb-3 flex flex-col gap-[0.2rem]">
<h3 className="text-[0.95rem] font-bold text-[#222] m-0 truncate">{prodName}</h3>
<p className="text-[0.8rem] text-[#666] m-0">{categoryName}</p>
{prodPrice != null && (
<span className="inline-block mt-1 text-[1.05rem] font-bold text-[#1a7a3c]">${parseFloat(prodPrice).toFixed(2)}</span>
)}
</div>
</Link>
<div className="px-3 pb-3 flex flex-col gap-1.5">
<div className="flex items-center gap-2">
<button className={qtyBtnCls} type="button" onClick={() => setQuantity((q) => Math.max(1, q - 1))} disabled={adding}></button>
<span className="min-w-6 text-center font-semibold text-[0.9rem]">{quantity}</span>
<button className={qtyBtnCls} type="button" onClick={() => setQuantity((q) => q + 1)} disabled={adding}>+</button>
</div>
<button
className="w-full py-[0.45rem] bg-[#e68672] text-white border-none rounded-lg text-[0.88rem] font-bold cursor-pointer transition-colors hover:bg-[#d4705e] disabled:opacity-60 disabled:cursor-not-allowed"
type="button"
onClick={handleAddToCart}
disabled={adding}
>
{adding ? "Adding…" : "Add to Cart"}
</button>
{feedback && <p className="text-[0.78rem] text-[#2e7d32] m-0 text-center">{feedback}</p>}
</div>
</div>
);
}