Bug fix: Products No image

This commit is contained in:
Mann Patel
2025-03-24 23:04:12 -06:00
parent fc125cc76a
commit e7a6e1dd8b
5 changed files with 139 additions and 106 deletions

View File

@@ -7,7 +7,7 @@ exports.addToFavorite = async (req, res) => {
// Use parameterized query to prevent SQL injection
const [result] = await db.execute(
"INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
[userID, productsID]
[userID, productsID],
);
res.json({
@@ -20,18 +20,22 @@ exports.addToFavorite = async (req, res) => {
}
};
//Get all products
// Get all products along with their image URLs
exports.getAllProducts = async (req, res) => {
try {
const [data, fields] = await db.execute("SELECT * FROM Product");
const [data, fields] = await db.execute(`
SELECT p.*, i.URL
FROM Product p
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
`);
res.json({
success: true,
message: "Product added to favorites successfully",
message: "Products fetched successfully",
data,
});
} catch (error) {
console.error("Error finding user:", error);
console.error("Error finding products:", error);
return res.status(500).json({
found: false,
error: "Database error occurred",
@@ -39,6 +43,48 @@ exports.getAllProducts = async (req, res) => {
}
};
// Get a single product by ID along with image URLs
exports.getProductById = async (req, res) => {
const { id } = req.params;
console.log(id);
try {
const [data] = await db.execute(
`
SELECT p.*, i.URL AS image_url
FROM Product p
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
WHERE p.ProductID = ?
`,
[id],
);
if (data.length === 0) {
return res.status(404).json({
success: false,
message: "Product not found",
});
}
// Assuming that `data` contains product information and the image URLs
const product = {
...data[0], // First product found in the query
images: data.map((image) => image.image_url), // Collect all image URLs into an array
};
res.json({
success: true,
message: "Product fetched successfully",
data: product,
});
} catch (error) {
console.error("Error fetching product:", error);
return res.status(500).json({
success: false,
error: "Database error occurred",
});
}
};
// db_con.query(
// "SELECT ProductID FROM product WHERE ProductID = ?",
// [productID],

View File

@@ -1,5 +1,9 @@
const express = require("express");
const { addToFavorite, getAllProducts } = require("../controllers/product");
const {
addToFavorite,
getAllProducts,
getProductById,
} = require("../controllers/product");
const router = express.Router();
@@ -7,4 +11,6 @@ router.post("/add_fav_product", addToFavorite);
router.get("/get_product", getAllProducts);
router.post("/get_productID", getProductById);
module.exports = router;

View File

@@ -11,7 +11,7 @@ const Home = () => {
const fetchProducts = async () => {
try {
const response = await fetch(
"http://localhost:3030/api/product/get_product"
"http://localhost:3030/api/product/get_product",
);
if (!response.ok) throw new Error("Failed to fetch products");
@@ -24,12 +24,12 @@ const Home = () => {
title: product.Name,
price: product.Price,
category: product.CategoryID,
image: product.ImageURL,
image: product.URL,
condition: "New", // Modify based on actual data
seller: "Unknown", // Modify if seller info is available
seller: product.UserID, // Modify if seller info is available
datePosted: "Just now",
isFavorite: false,
}))
})),
);
} else {
throw new Error(data.message || "Error fetching products");
@@ -50,8 +50,8 @@ const Home = () => {
prevListings.map((listing) =>
listing.id === id
? { ...listing, isFavorite: !listing.isFavorite }
: listing
)
: listing,
),
);
};

View File

@@ -1,83 +1,54 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { useParams, Link } from "react-router-dom";
import {
Heart,
ArrowLeft,
Tag,
User,
Calendar,
Share,
Flag,
} from "lucide-react";
import { Heart, ArrowLeft, Tag, User, Calendar } from "lucide-react";
const ProductDetail = () => {
const { id } = useParams();
const [product, setProduct] = useState(null);
const [isFavorite, setIsFavorite] = useState(false);
const [showContactForm, setShowContactForm] = useState(false);
const [message, setMessage] = useState("");
const [currentImage, setCurrentImage] = useState(0);
// Sample data for demonstration
const product = [
{
id: 0,
title: "Dell XPS 13 Laptop - 2023 Model",
price: 850,
shortDescription:
"Dell XPS 13 laptop in excellent condition. Intel Core i7, 16GB RAM, 512GB SSD. Includes charger and original box.",
description:
"Selling my Dell XPS 13 laptop. Only 6 months old and in excellent condition. Intel Core i7 processor, 16GB RAM, 512GB SSD. Battery life is still excellent (around 10 hours of regular use). Comes with original charger and box. Selling because I'm upgrading to a MacBook for design work.\n\nSpecs:\n- Intel Core i7 11th Gen\n- 16GB RAM\n- 512GB NVMe SSD\n- 13.4\" FHD+ Display (1920x1200)\n- Windows 11 Pro\n- Backlit Keyboard\n- Thunderbolt 4 ports",
condition: "Like New",
category:
"Electronics, Electronics, Electronics, Electronics , Electronics , Electronics, Electronicss",
datePosted: "2023-03-02",
images: [
"/image1.avif",
"/image2.avif",
"/image3.avif",
"/image3.avif",
"/image3.avif",
],
seller: {
name: "Michael T.",
rating: 4.8,
memberSince: "January 2022",
avatar: "/Profile.jpg",
},
},
];
// Fetch product details
useEffect(() => {
const fetchProduct = async () => {
try {
const response = await fetch(
`http://localhost:3030/api/product/get_productID/${id}`,
);
if (!response.ok) throw new Error("Failed to fetch product");
console.log(product[id]);
const data = await response.json();
if (data.success) {
setProduct(data.data); // Update the state with product details
} else {
throw new Error(data.message || "Error fetching product");
}
} catch (error) {
console.error("Error fetching product:", error);
}
};
fetchProduct();
}, [id]);
// Handle favorite toggle
const toggleFavorite = () => {
setIsFavorite(!isFavorite);
};
// Handle message submission
const handleSendMessage = (e) => {
e.preventDefault();
// TODO: this would send the message to the seller
// Handle message logic here (send to seller)
console.log("Message sent:", message);
setMessage("");
setShowContactForm(false);
// Show confirmation or success message
alert("Message sent to seller!");
};
// Function to split description into paragraphs
const formatDescription = (text) => {
return text.split("\n\n").map((paragraph, index) => (
<p key={index} className="mb-4">
{paragraph.split("\n").map((line, i) => (
<span key={i}>
{line}
{i < paragraph.split("\n").length - 1 && <br />}
</span>
))}
</p>
));
};
// image navigation
// Image navigation
const nextImage = () => {
setCurrentImage((prev) =>
prev === product.images.length - 1 ? 0 : prev + 1,
@@ -94,9 +65,10 @@ const ProductDetail = () => {
setCurrentImage(index);
};
if (!product) return <div>Loading...</div>; // Handle loading state
return (
<div className="max-w-6xl mx-auto px-4 py-8">
{/* Breadcrumb & Back Link */}
<div className="mb-6">
<Link
to="/"
@@ -108,22 +80,19 @@ const ProductDetail = () => {
</div>
<div className="flex flex-col md:flex-row gap-8">
{/* Left Column - Images */}
<div className="md:w-3/5">
{/* Main Image */}
<div className="bg-white border border-gray-200 mb-4 relative">
<img
src={product[id].images[currentImage]}
alt={product[id].title}
src={product.images[currentImage]}
alt={product.title}
className="w-full h-auto object-contain cursor-pointer"
onClick={nextImage}
/>
</div>
{/* Thumbnail Images */}
{product[id].images.length > 1 && (
{product.images.length > 1 && (
<div className="flex gap-2 overflow-x-auto pb-2">
{product[id].images.map((image, index) => (
{product.images.map((image, index) => (
<div
key={index}
className={`bg-white border ${currentImage === index ? "border-green-500" : "border-gray-200"} min-w-[100px] cursor-pointer`}
@@ -131,7 +100,7 @@ const ProductDetail = () => {
>
<img
src={image}
alt={`${product[id].title} - view ${index + 1}`}
alt={`${product.title} - view ${index + 1}`}
className="w-full h-auto object-cover"
/>
</div>
@@ -140,13 +109,11 @@ const ProductDetail = () => {
)}
</div>
{/* Right Column - Details */}
<div className="md:w-2/5">
{/* Product Info Card */}
<div className="bg-white border border-gray-200 p-6 mb-6">
<div className="flex justify-between items-start mb-4">
<h1 className="text-2xl font-bold text-gray-800">
{product[id].title}
{product.title}
</h1>
<button
onClick={toggleFavorite}
@@ -159,30 +126,27 @@ const ProductDetail = () => {
</div>
<div className="text-2xl font-bold text-green-600 mb-4">
${product[id].price}
${product.price}
</div>
<div className="flex flex-wrap gap-x-4 gap-y-2 mb-6 text-sm">
<div className="flex items-center text-gray-600">
<Tag className="h-4 w-4 mr-1" />
<span>{product[id].category}</span>
<span>{product.category}</span>
</div>
<div className="flex items-center text-gray-600">
<span className="font-medium">Condition:</span>
<span className="ml-1">{product[id].condition}</span>
<span className="ml-1">{product.condition}</span>
</div>
<div className="flex items-center text-gray-600">
<Calendar className="h-4 w-4 mr-1" />
<span>Posted on {product[id].datePosted}</span>
<span>Posted on {product.datePosted}</span>
</div>
</div>
{/* Short Description */}
<div className="bg-gray-50 p-4 mb-6 border border-gray-200">
<p className="text-gray-700">{product[id].shortDescription}</p>
<p className="text-gray-700">{product.shortDescription}</p>
</div>
{/* Contact Button */}
<button
onClick={() => setShowContactForm(!showContactForm)}
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
@@ -190,7 +154,6 @@ const ProductDetail = () => {
Contact Seller
</button>
{/* TODO:Contact Form */}
{showContactForm && (
<div className="border border-gray-200 p-4 mb-4">
<h3 className="font-medium text-gray-800 mb-2">
@@ -204,8 +167,8 @@ const ProductDetail = () => {
<input
type="email"
id="email"
value={product[id].User.email}
onChange={(e) => setEmail(e.target.value)}
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
required
/>
@@ -217,8 +180,6 @@ const ProductDetail = () => {
<input
type="tel"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
required
/>
@@ -233,8 +194,8 @@ const ProductDetail = () => {
<input
type="text"
id="contactMessage"
value={contactMessage}
onChange={(e) => setContactMessage(e.target.value)}
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Hi, is this item still available?"
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
/>
@@ -249,13 +210,12 @@ const ProductDetail = () => {
</div>
)}
{/* Seller Info */}
<div className="pt-4 border-t border-gray-200">
<div className="flex items-center mb-3">
<div className="mr-3">
{product[id].seller.avatar ? (
{product.seller.avatar ? (
<img
src={product[id].seller.avatar}
src={product.seller.avatar}
alt="Seller"
className="h-12 w-12 rounded-full"
/>
@@ -267,17 +227,17 @@ const ProductDetail = () => {
</div>
<div>
<h3 className="font-medium text-gray-800">
{product[id].seller.name}
{product.seller.name}
</h3>
<p className="text-sm text-gray-500">
Member since {product[id].seller.memberSince}
Member since {product.seller.memberSince}
</p>
</div>
</div>
<div className="text-sm text-gray-600">
<div>
<span className="font-medium">Rating:</span>{" "}
{product[id].seller.rating}/5
{product.seller.rating}/5
</div>
</div>
</div>
@@ -285,13 +245,10 @@ const ProductDetail = () => {
</div>
</div>
{/* Description Section */}
<div className="mt-8">
<h2 className="text-xl font-bold text-gray-800 mb-4">Description</h2>
<div className="bg-white border border-gray-200 p-6">
<div className="text-gray-700">
{formatDescription(product[id].description)}
</div>
<div className="text-gray-700">{product.description}</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,24 @@
import asyncio
import websockets
async def handle_client(websocket, path):
try:
# Receive user ID
user_id = await websocket.recv()
print(f"Received user ID: {user_id}")
# Optional: You can add more logic here if needed
except websockets.exceptions.ConnectionClosed:
print("Client disconnected")
except Exception as e:
print(f"Error processing request: {str(e)}")
async def start_server():
server = await websockets.serve(handle_client, "localhost", 5555)
print("WebSocket server started")
await server.wait_closed()
# Run the server
if __name__ == "__main__":
asyncio.run(start_server())