fav product from prodDetail page

This commit is contained in:
Mann Patel
2025-04-15 00:18:19 -06:00
parent fdf63f4e6a
commit 06e045fbff
6 changed files with 91 additions and 82 deletions

View File

@@ -47,14 +47,29 @@ exports.getFavorites = async (req, res) => {
const [favorites] = await db.execute(
`
SELECT
p.*,
p.ProductID,
p.Name,
p.Description,
p.Price,
p.CategoryID,
p.UserID,
p.Date,
u.Name AS SellerName,
i.URL AS image_url
MIN(i.URL) AS image_url
FROM Favorites f
JOIN Product p ON f.ProductID = p.ProductID
JOIN User u ON p.UserID = u.UserID
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
WHERE f.UserID = ?
GROUP BY
p.ProductID,
p.Name,
p.Description,
p.Price,
p.CategoryID,
p.UserID,
p.Date,
u.Name;
`,
[userID],
);
@@ -73,31 +88,25 @@ exports.getFavorites = async (req, res) => {
exports.getAllProducts = async (req, res) => {
try {
const [data, fields] = await db.execute(`
WITH RankedImages AS (
SELECT
P.ProductID,
P.Name AS ProductName,
P.Price,
P.Date AS DateUploaded,
U.Name AS SellerName,
I.URL AS ProductImage,
C.Name AS Category,
ROW_NUMBER() OVER (PARTITION BY P.ProductID ORDER BY I.URL) AS RowNum
FROM Product P
JOIN Image_URL I ON P.ProductID = I.ProductID
JOIN User U ON P.UserID = U.UserID
JOIN Category C ON P.CategoryID = C.CategoryID
)
SELECT
ProductID,
ProductName,
Price,
DateUploaded,
SellerName,
ProductImage,
Category
FROM RankedImages
WHERE RowNum = 1;
MIN(I.URL) AS ProductImage,
C.Name AS Category
FROM Product P
JOIN Image_URL I ON P.ProductID = I.ProductID
JOIN User U ON P.UserID = U.UserID
JOIN Category C ON P.CategoryID = C.CategoryID
GROUP BY
P.ProductID,
P.Name,
P.Price,
P.Date,
U.Name,
C.Name;
`);
res.json({

View File

@@ -1,6 +1,6 @@
const express = require("express");
const cors = require("cors");
//Get the db connection
const db = require("./utils/database");
const userRouter = require("./routes/user");
@@ -33,19 +33,20 @@ transporter
console.error("Email connection failed:", error);
});
//Check database connection
checkDatabaseConnection(db);
//Routes
app.use("/api/user", userRouter); //prefix with /api/user
app.use("/api/product", productRouter); //prefix with /api/product
app.use("/api/search", searchRouter); //prefix with /api/product
app.use("/api/engine", recommendedRouter); //prefix with /api/
app.use("/api/history", history); //prefix with /api/
app.use("/api/review", review); //prefix with /api/
app.use("/api/user", userRouter);
app.use("/api/product", productRouter);
app.use("/api/search", searchRouter);
app.use("/api/engine", recommendedRouter);
app.use("/api/history", history);
app.use("/api/review", review);
// Set up a scheduler to run cleanup every hour
setInterval(cleanupExpiredCodes, 60 * 60 * 1000);
clean_up_time = 30*60*1000;
setInterval(cleanupExpiredCodes, clean_up_time);
app.listen(3030, () => {
console.log(`Running Backend on http://localhost:3030/`);

View File

@@ -47,8 +47,8 @@ const Navbar = ({ onLogout, userName }) => {
<div className="relative">
<input
type="text"
placeholder="Search for books, electronics, furniture..."
className="w-full p-2 pl-10 pr-4 border border-gray-300 rounded-md focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
placeholder="Search for anything..."
className="w-full p-2 pl-10 pr-4 border border-gray-300 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500"
value={searchQuery}
onChange={handleSearchChange}
/>

View File

@@ -34,7 +34,6 @@ const Home = () => {
if (data.success) {
setShowAlert(true);
}
console.log(response);
console.log(`Add Product -> History: ${id}`);
};
@@ -82,7 +81,6 @@ const Home = () => {
if (!response.ok) throw new Error("Failed to fetch products");
const data = await response.json();
console.log(data);
if (data.success) {
setRecommended(
data.data.map((product) => ({
@@ -145,7 +143,6 @@ const Home = () => {
useEffect(() => {
const fetchrecomProducts = async () => {
// Get the user's data from localStorage
console.log(storedUser);
try {
const response = await fetch(
"http://localhost:3030/api/history/getHistory",
@@ -162,7 +159,6 @@ const Home = () => {
if (!response.ok) throw new Error("Failed to fetch products");
const data = await response.json();
console.log(data);
if (data.success) {
sethistory(
data.data.map((product) => ({

View File

@@ -10,6 +10,8 @@ import {
Phone,
Mail,
} from "lucide-react";
import FloatingAlert from "../components/FloatingAlert"; // adjust path if needed
const ProductDetail = () => {
const { id } = useParams();
@@ -29,8 +31,32 @@ const ProductDetail = () => {
const [currentImage, setCurrentImage] = useState(0);
const [reviews, setReviews] = useState([]);
const [showReviewForm, setShowReviewForm] = useState(false);
const [showAlert, setShowAlert] = useState(false);
const storedUser = JSON.parse(sessionStorage.getItem("user"));
const toggleFavorite = async (id) => {
const response = await fetch(
"http://localhost:3030/api/product/addFavorite",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userID: storedUser.ID,
productID: id,
}),
},
);
const data = await response.json();
if (data.success) {
setShowAlert(true);
}
console.log(`Add Product -> History: ${id}`);
};
const [reviewForm, setReviewForm] = useState({
rating: 3,
comment: "",
@@ -68,7 +94,7 @@ const ProductDetail = () => {
userId: storedUser.ID,
};
const response = await fetch(`http://localhost:3030/api/review/add`, {
const response = await fetch(`http://localhost:3030/api/review/addReview`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(reviewData),
@@ -182,38 +208,6 @@ const ProductDetail = () => {
fetchReviews();
}, [id]);
// Handle favorite toggle with error handling
const toggleFavorite = async () => {
try {
const response = await fetch(
"http://localhost:3030/api/product/add_to_favorite",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userID: 1, // Replace with actual user ID
productsID: id,
}),
},
);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
setIsFavorite(!isFavorite);
} else {
throw new Error(result.message || "Failed to toggle favorite");
}
} catch (error) {
console.error("Error toggling favorite:", error);
alert(`Failed to add to favorites: ${error.message}`);
}
};
// Image navigation
const nextImage = () => {
@@ -296,6 +290,7 @@ const ProductDetail = () => {
// Render product details
return (
<div className="max-w-6xl mx-auto px-4 py-8">
<div className="mb-6">
<Link
@@ -306,6 +301,12 @@ const ProductDetail = () => {
<span>Back</span>
</Link>
</div>
{showAlert && (
<FloatingAlert
message="Product added to favorites!"
onClose={() => setShowAlert(false)}
/>
)}
<div className="flex flex-col md:flex-row gap-8">
<div className="md:w-3/5">
@@ -370,7 +371,6 @@ const ProductDetail = () => {
</div>
)}
</div>
<div className="md:w-2/5">
<div className="bg-white border border-gray-200 p-6 mb-6">
<div className="flex justify-between items-start mb-4">
@@ -378,7 +378,7 @@ const ProductDetail = () => {
{product.Name || "Unnamed Product"}
</h1>
<button
onClick={toggleFavorite}
onClick={() => toggleFavorite(product.ProductID)}
className="p-2 hover:bg-gray-100 rounded-full"
aria-label={
isFavorite ? "Remove from favorites" : "Add to favorites"

View File

@@ -17,6 +17,9 @@ const SearchPage = () => {
const [priceRange, setPriceRange] = useState({ min: 0, max: 1000 });
const [isFilterOpen, setIsFilterOpen] = useState(false);
useEffect(() => {
fetchProducts(initialSearchQuery);
}, [initialSearchQuery]);