Merge branch 'mannBranch' into aaqil
This commit is contained in:
@@ -7,7 +7,7 @@ exports.addToFavorite = async (req, res) => {
|
|||||||
// Use parameterized query to prevent SQL injection
|
// Use parameterized query to prevent SQL injection
|
||||||
const [result] = await db.execute(
|
const [result] = await db.execute(
|
||||||
"INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
|
"INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
|
||||||
[userID, productsID]
|
[userID, productsID],
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({
|
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) => {
|
exports.getAllProducts = async (req, res) => {
|
||||||
try {
|
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({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: "Product added to favorites successfully",
|
message: "Products fetched successfully",
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error finding user:", error);
|
console.error("Error finding products:", error);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
found: false,
|
found: false,
|
||||||
error: "Database error occurred",
|
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(
|
// db_con.query(
|
||||||
// "SELECT ProductID FROM product WHERE ProductID = ?",
|
// "SELECT ProductID FROM product WHERE ProductID = ?",
|
||||||
// [productID],
|
// [productID],
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
func cosine(x, y []int) float64 {
|
|
||||||
var dotProduct, normX, normY float64
|
|
||||||
for i := 1; i < len(x); i++ {
|
|
||||||
dotProduct += float64(x[i] * y[i])
|
|
||||||
normX += float64(x[i] * x[i])
|
|
||||||
normY += float64(y[i] * y[i])
|
|
||||||
}
|
|
||||||
return dotProduct / (math.Sqrt(normX) * math.Sqrt(normY))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
history := [][]int{
|
|
||||||
{1, 0, 1, 0},
|
|
||||||
{0, 1, 0, 1},
|
|
||||||
{1, 0, 1, 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
productCategory := [][]int{
|
|
||||||
{1, 1, 0, 0},
|
|
||||||
{0, 0, 0, 1},
|
|
||||||
{0, 0, 1, 0},
|
|
||||||
{0, 0, 1, 1},
|
|
||||||
{0, 1, 0, 0},
|
|
||||||
|
|
||||||
{0, 1, 1, 1},
|
|
||||||
{1, 1, 1, 0},
|
|
||||||
{0, 0, 0, 1},
|
|
||||||
{1, 1, 1, 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate similarity between first search and each product
|
|
||||||
for i, product := range productCategory {
|
|
||||||
sim := cosine(history[0], product)
|
|
||||||
fmt.Printf("Similarity with product %d: %f\n", i, sim)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const { addToFavorite, getAllProducts } = require("../controllers/product");
|
const {
|
||||||
|
addToFavorite,
|
||||||
|
getAllProducts,
|
||||||
|
getProductById,
|
||||||
|
} = require("../controllers/product");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -7,4 +11,6 @@ router.post("/add_fav_product", addToFavorite);
|
|||||||
|
|
||||||
router.get("/get_product", getAllProducts);
|
router.get("/get_product", getAllProducts);
|
||||||
|
|
||||||
|
router.post("/get_productID", getProductById);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ const pool = mysql.createPool({
|
|||||||
host: "localhost",
|
host: "localhost",
|
||||||
user: "root",
|
user: "root",
|
||||||
database: "marketplace",
|
database: "marketplace",
|
||||||
//password: "12345678",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Export a promise for promise-based query
|
//Export a promise for promise-based query
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ function App() {
|
|||||||
email: userData.email,
|
email: userData.email,
|
||||||
// Add any other required fields
|
// Add any other required fields
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -119,7 +119,7 @@ function App() {
|
|||||||
email: tempUserData.email,
|
email: tempUserData.email,
|
||||||
code: code,
|
code: code,
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -163,7 +163,7 @@ function App() {
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(userData),
|
body: JSON.stringify(userData),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -222,11 +222,12 @@ function App() {
|
|||||||
setError("Email and password are required");
|
setError("Email and password are required");
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
return;
|
return;
|
||||||
} else if (!formValues.email.endsWith("@ucalgary.ca")) {
|
|
||||||
setError("Please use your UCalgary email address (@ucalgary.ca)");
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// else if (!formValues.email.endsWith("@ucalgary.ca")) {
|
||||||
|
// setError("Please use your UCalgary email address (@ucalgary.ca)");
|
||||||
|
// setIsLoading(false);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
try {
|
try {
|
||||||
if (isSignUp) {
|
if (isSignUp) {
|
||||||
// Handle Sign Up with verification
|
// Handle Sign Up with verification
|
||||||
@@ -265,7 +266,7 @@ function App() {
|
|||||||
email: formValues.email,
|
email: formValues.email,
|
||||||
password: formValues.password,
|
password: formValues.password,
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -509,8 +510,8 @@ function App() {
|
|||||||
{isLoading
|
{isLoading
|
||||||
? "Please wait..."
|
? "Please wait..."
|
||||||
: isSignUp
|
: isSignUp
|
||||||
? "Create Account"
|
? "Create Account"
|
||||||
: "Sign In"}
|
: "Sign In"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const Home = () => {
|
|||||||
const fetchProducts = async () => {
|
const fetchProducts = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
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");
|
if (!response.ok) throw new Error("Failed to fetch products");
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ const Home = () => {
|
|||||||
title: product.Name,
|
title: product.Name,
|
||||||
price: product.Price,
|
price: product.Price,
|
||||||
category: product.CategoryID,
|
category: product.CategoryID,
|
||||||
image: product.ImageURL,
|
image: product.URL,
|
||||||
condition: "New", // Modify based on actual data
|
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",
|
datePosted: "Just now",
|
||||||
isFavorite: false,
|
isFavorite: false,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(data.message || "Error fetching products");
|
throw new Error(data.message || "Error fetching products");
|
||||||
@@ -50,8 +50,8 @@ const Home = () => {
|
|||||||
prevListings.map((listing) =>
|
prevListings.map((listing) =>
|
||||||
listing.id === id
|
listing.id === id
|
||||||
? { ...listing, isFavorite: !listing.isFavorite }
|
? { ...listing, isFavorite: !listing.isFavorite }
|
||||||
: listing
|
: listing,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +1,54 @@
|
|||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams, Link } from "react-router-dom";
|
import { useParams, Link } from "react-router-dom";
|
||||||
import {
|
import { Heart, ArrowLeft, Tag, User, Calendar } from "lucide-react";
|
||||||
Heart,
|
|
||||||
ArrowLeft,
|
|
||||||
Tag,
|
|
||||||
User,
|
|
||||||
Calendar,
|
|
||||||
Share,
|
|
||||||
Flag,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
const ProductDetail = () => {
|
const ProductDetail = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const [product, setProduct] = useState(null);
|
||||||
const [isFavorite, setIsFavorite] = useState(false);
|
const [isFavorite, setIsFavorite] = useState(false);
|
||||||
const [showContactForm, setShowContactForm] = useState(false);
|
const [showContactForm, setShowContactForm] = useState(false);
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
const [currentImage, setCurrentImage] = useState(0);
|
const [currentImage, setCurrentImage] = useState(0);
|
||||||
|
|
||||||
// Sample data for demonstration
|
// Fetch product details
|
||||||
const product = [
|
useEffect(() => {
|
||||||
{
|
const fetchProduct = async () => {
|
||||||
id: 0,
|
try {
|
||||||
title: "Dell XPS 13 Laptop - 2023 Model",
|
const response = await fetch(
|
||||||
price: 850,
|
`http://localhost:3030/api/product/get_productID/${id}`,
|
||||||
shortDescription:
|
);
|
||||||
"Dell XPS 13 laptop in excellent condition. Intel Core i7, 16GB RAM, 512GB SSD. Includes charger and original box.",
|
if (!response.ok) throw new Error("Failed to fetch product");
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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 = () => {
|
const toggleFavorite = () => {
|
||||||
setIsFavorite(!isFavorite);
|
setIsFavorite(!isFavorite);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle message submission
|
||||||
const handleSendMessage = (e) => {
|
const handleSendMessage = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// TODO: this would send the message to the seller
|
// Handle message logic here (send to seller)
|
||||||
console.log("Message sent:", message);
|
console.log("Message sent:", message);
|
||||||
setMessage("");
|
setMessage("");
|
||||||
setShowContactForm(false);
|
setShowContactForm(false);
|
||||||
// Show confirmation or success message
|
|
||||||
alert("Message sent to seller!");
|
alert("Message sent to seller!");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to split description into paragraphs
|
// Image navigation
|
||||||
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
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
setCurrentImage((prev) =>
|
setCurrentImage((prev) =>
|
||||||
prev === product.images.length - 1 ? 0 : prev + 1,
|
prev === product.images.length - 1 ? 0 : prev + 1,
|
||||||
@@ -94,9 +65,10 @@ const ProductDetail = () => {
|
|||||||
setCurrentImage(index);
|
setCurrentImage(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!product) return <div>Loading...</div>; // Handle loading state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-6xl mx-auto px-4 py-8">
|
<div className="max-w-6xl mx-auto px-4 py-8">
|
||||||
{/* Breadcrumb & Back Link */}
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
@@ -108,22 +80,19 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row gap-8">
|
<div className="flex flex-col md:flex-row gap-8">
|
||||||
{/* Left Column - Images */}
|
|
||||||
<div className="md:w-3/5">
|
<div className="md:w-3/5">
|
||||||
{/* Main Image */}
|
|
||||||
<div className="bg-white border border-gray-200 mb-4 relative">
|
<div className="bg-white border border-gray-200 mb-4 relative">
|
||||||
<img
|
<img
|
||||||
src={product[id].images[currentImage]}
|
src={product.images[currentImage]}
|
||||||
alt={product[id].title}
|
alt={product.title}
|
||||||
className="w-full h-auto object-contain cursor-pointer"
|
className="w-full h-auto object-contain cursor-pointer"
|
||||||
onClick={nextImage}
|
onClick={nextImage}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Thumbnail Images */}
|
{product.images.length > 1 && (
|
||||||
{product[id].images.length > 1 && (
|
|
||||||
<div className="flex gap-2 overflow-x-auto pb-2">
|
<div className="flex gap-2 overflow-x-auto pb-2">
|
||||||
{product[id].images.map((image, index) => (
|
{product.images.map((image, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`bg-white border ${currentImage === index ? "border-green-500" : "border-gray-200"} min-w-[100px] cursor-pointer`}
|
className={`bg-white border ${currentImage === index ? "border-green-500" : "border-gray-200"} min-w-[100px] cursor-pointer`}
|
||||||
@@ -131,7 +100,7 @@ const ProductDetail = () => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={image}
|
src={image}
|
||||||
alt={`${product[id].title} - view ${index + 1}`}
|
alt={`${product.title} - view ${index + 1}`}
|
||||||
className="w-full h-auto object-cover"
|
className="w-full h-auto object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,13 +109,11 @@ const ProductDetail = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - Details */}
|
|
||||||
<div className="md:w-2/5">
|
<div className="md:w-2/5">
|
||||||
{/* Product Info Card */}
|
|
||||||
<div className="bg-white border border-gray-200 p-6 mb-6">
|
<div className="bg-white border border-gray-200 p-6 mb-6">
|
||||||
<div className="flex justify-between items-start mb-4">
|
<div className="flex justify-between items-start mb-4">
|
||||||
<h1 className="text-2xl font-bold text-gray-800">
|
<h1 className="text-2xl font-bold text-gray-800">
|
||||||
{product[id].title}
|
{product.title}
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
onClick={toggleFavorite}
|
onClick={toggleFavorite}
|
||||||
@@ -159,30 +126,27 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-2xl font-bold text-green-600 mb-4">
|
<div className="text-2xl font-bold text-green-600 mb-4">
|
||||||
${product[id].price}
|
${product.price}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-2 mb-6 text-sm">
|
<div className="flex flex-wrap gap-x-4 gap-y-2 mb-6 text-sm">
|
||||||
<div className="flex items-center text-gray-600">
|
<div className="flex items-center text-gray-600">
|
||||||
<Tag className="h-4 w-4 mr-1" />
|
<Tag className="h-4 w-4 mr-1" />
|
||||||
<span>{product[id].category}</span>
|
<span>{product.category}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-gray-600">
|
<div className="flex items-center text-gray-600">
|
||||||
<span className="font-medium">Condition:</span>
|
<span className="font-medium">Condition:</span>
|
||||||
<span className="ml-1">{product[id].condition}</span>
|
<span className="ml-1">{product.condition}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center text-gray-600">
|
<div className="flex items-center text-gray-600">
|
||||||
<Calendar className="h-4 w-4 mr-1" />
|
<Calendar className="h-4 w-4 mr-1" />
|
||||||
<span>Posted on {product[id].datePosted}</span>
|
<span>Posted on {product.datePosted}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Short Description */}
|
|
||||||
<div className="bg-gray-50 p-4 mb-6 border border-gray-200">
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Contact Button */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowContactForm(!showContactForm)}
|
onClick={() => setShowContactForm(!showContactForm)}
|
||||||
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
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
|
Contact Seller
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* TODO:Contact Form */}
|
|
||||||
{showContactForm && (
|
{showContactForm && (
|
||||||
<div className="border border-gray-200 p-4 mb-4">
|
<div className="border border-gray-200 p-4 mb-4">
|
||||||
<h3 className="font-medium text-gray-800 mb-2">
|
<h3 className="font-medium text-gray-800 mb-2">
|
||||||
@@ -204,8 +167,8 @@ const ProductDetail = () => {
|
|||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
value={product[id].User.email}
|
value={message}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -217,8 +180,6 @@ const ProductDetail = () => {
|
|||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
id="phone"
|
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"
|
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -233,8 +194,8 @@ const ProductDetail = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="contactMessage"
|
id="contactMessage"
|
||||||
value={contactMessage}
|
value={message}
|
||||||
onChange={(e) => setContactMessage(e.target.value)}
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
placeholder="Hi, is this item still available?"
|
placeholder="Hi, is this item still available?"
|
||||||
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
className="w-full p-3 border border-gray-300 focus:outline-none focus:border-green-500"
|
||||||
/>
|
/>
|
||||||
@@ -249,13 +210,12 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Seller Info */}
|
|
||||||
<div className="pt-4 border-t border-gray-200">
|
<div className="pt-4 border-t border-gray-200">
|
||||||
<div className="flex items-center mb-3">
|
<div className="flex items-center mb-3">
|
||||||
<div className="mr-3">
|
<div className="mr-3">
|
||||||
{product[id].seller.avatar ? (
|
{product.seller.avatar ? (
|
||||||
<img
|
<img
|
||||||
src={product[id].seller.avatar}
|
src={product.seller.avatar}
|
||||||
alt="Seller"
|
alt="Seller"
|
||||||
className="h-12 w-12 rounded-full"
|
className="h-12 w-12 rounded-full"
|
||||||
/>
|
/>
|
||||||
@@ -267,17 +227,17 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-gray-800">
|
<h3 className="font-medium text-gray-800">
|
||||||
{product[id].seller.name}
|
{product.seller.name}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Member since {product[id].seller.memberSince}
|
Member since {product.seller.memberSince}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-600">
|
<div className="text-sm text-gray-600">
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">Rating:</span>{" "}
|
<span className="font-medium">Rating:</span>{" "}
|
||||||
{product[id].seller.rating}/5
|
{product.seller.rating}/5
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -285,13 +245,10 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description Section */}
|
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<h2 className="text-xl font-bold text-gray-800 mb-4">Description</h2>
|
<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="bg-white border border-gray-200 p-6">
|
||||||
<div className="text-gray-700">
|
<div className="text-gray-700">{product.description}</div>
|
||||||
{formatDescription(product[id].description)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
24
recommondation-engine/server1.py
Normal file
24
recommondation-engine/server1.py
Normal 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())
|
||||||
Reference in New Issue
Block a user