Files
group-2-threaded-project-pe…/web/components/ProductCard.js
augmentedpotato 077d147498 Styling refactor
2026-04-18 16:22:38 -06:00

83 lines
3.5 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";
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";
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);
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>
);
}