add review and read review now done
This commit is contained in:
@@ -37,7 +37,6 @@ exports.HistoryByUserId = async (req, res) => {
|
||||
[id],
|
||||
);
|
||||
|
||||
console.log(data);
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
exports.addToFavorite = async (req, res) => {
|
||||
exports.addFavorite = async (req, res) => {
|
||||
const { userID, productsID } = req.body;
|
||||
|
||||
try {
|
||||
// Use parameterized query to prevent SQL injection
|
||||
const [result] = await db.execute(
|
||||
"INSERT INTO Favorites (UserID, ProductID) VALUES unique(?, ?)",
|
||||
"INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
|
||||
[userID, productsID],
|
||||
);
|
||||
|
||||
@@ -14,13 +14,41 @@ exports.addToFavorite = async (req, res) => {
|
||||
success: true,
|
||||
message: "Product added to favorites successfully",
|
||||
});
|
||||
console.log(result);
|
||||
} catch (error) {
|
||||
console.error("Error adding favorite product:", error);
|
||||
return res.json({ error: "Could not add favorite product" });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getFavorites = async (req, res) => {
|
||||
const { userID } = req.body;
|
||||
|
||||
try {
|
||||
const [favorites] = await db.execute(
|
||||
`
|
||||
SELECT
|
||||
p.*,
|
||||
u.Name AS SellerName,
|
||||
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 = ?
|
||||
`,
|
||||
[userID],
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
favorites: favorites,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error retrieving favorites:", error);
|
||||
res.status(500).json({ error: "Could not retrieve favorite products" });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all products along with their image URLs
|
||||
exports.getAllProducts = async (req, res) => {
|
||||
try {
|
||||
@@ -52,7 +80,6 @@ exports.getAllProducts = async (req, res) => {
|
||||
WHERE RowNum = 1;
|
||||
`);
|
||||
|
||||
console.log(data);
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
@@ -74,7 +101,7 @@ exports.getProductById = async (req, res) => {
|
||||
try {
|
||||
const [data] = await db.execute(
|
||||
`
|
||||
SELECT p.*,U.Name AS SellerName, i.URL AS image_url
|
||||
SELECT p.*,U.Name AS SellerName,U.Email as SellerEmail,U.Phone as SellerPhone, i.URL AS image_url
|
||||
FROM Product p
|
||||
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||
JOIN User U ON p.UserID = U.UserID
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
const db = require("../utils/database");
|
||||
|
||||
exports.getreview = async (req, res) => {
|
||||
/**
|
||||
* Get reviews for a specific product
|
||||
* Returns both reviews for the product and reviews by the product owner for other products
|
||||
*/
|
||||
exports.getReviews = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
console.log("Received Product ID:", id);
|
||||
|
||||
try {
|
||||
const [data] = await db.execute(
|
||||
`
|
||||
SELECT
|
||||
// First query: Get reviews for this specific product
|
||||
const [productReviews] = await db.execute(
|
||||
`SELECT
|
||||
R.ReviewID,
|
||||
R.UserID,
|
||||
R.ProductID,
|
||||
@@ -15,44 +19,49 @@ exports.getreview = async (req, res) => {
|
||||
R.Rating,
|
||||
R.Date AS ReviewDate,
|
||||
U.Name AS ReviewerName,
|
||||
P.Name AS ProductName
|
||||
P.Name AS ProductName,
|
||||
'product' AS ReviewType
|
||||
FROM Review R
|
||||
JOIN User U ON R.UserID = U.UserID
|
||||
JOIN Product P ON R.ProductID = P.ProductID
|
||||
WHERE R.ProductID = ?
|
||||
|
||||
UNION
|
||||
|
||||
SELECT
|
||||
R.ReviewID,
|
||||
R.UserID,
|
||||
R.ProductID,
|
||||
R.Comment,
|
||||
R.Rating,
|
||||
R.Date AS ReviewDate,
|
||||
U.Name AS ReviewerName,
|
||||
P.Name AS ProductName
|
||||
FROM Review R
|
||||
JOIN User U ON R.UserID = U.UserID
|
||||
JOIN Product P ON R.ProductID = P.ProductID
|
||||
WHERE P.UserID = (
|
||||
SELECT UserID
|
||||
FROM Product
|
||||
WHERE ProductID = ?
|
||||
)
|
||||
AND R.UserID != P.UserID;
|
||||
`,
|
||||
[id, id],
|
||||
WHERE R.ProductID = ?`,
|
||||
[id],
|
||||
);
|
||||
|
||||
// Log raw data for debugging
|
||||
console.log("Raw Database Result:", data);
|
||||
// // Second query: Get reviews written by the product owner for other products
|
||||
// const [sellerReviews] = await db.execute(
|
||||
// `SELECT
|
||||
// R.ReviewID,
|
||||
// R.UserID,
|
||||
// R.ProductID,
|
||||
// R.Comment,
|
||||
// R.Rating,
|
||||
// R.Date AS ReviewDate,
|
||||
// U.Name AS ReviewerName,
|
||||
// P.Name AS ProductName,
|
||||
// 'seller' AS ReviewType
|
||||
// FROM Review R
|
||||
// JOIN User U ON R.UserID = U.UserID
|
||||
// JOIN Product P ON R.ProductID = P.ProductID
|
||||
// WHERE R.UserID = (
|
||||
// SELECT UserID
|
||||
// FROM Product
|
||||
// WHERE ProductID = ?
|
||||
// )
|
||||
// AND R.ProductID != ?`,
|
||||
// [id, id],
|
||||
// );
|
||||
|
||||
// Combine the results
|
||||
const combinedReviews = [...productReviews];
|
||||
|
||||
// Log data for debugging
|
||||
console.log("Combined Reviews:", combinedReviews);
|
||||
|
||||
console.log(data);
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Products fetched successfully",
|
||||
data,
|
||||
message: "Reviews fetched successfully",
|
||||
data: combinedReviews,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Full Error Details:", error);
|
||||
@@ -64,7 +73,9 @@ exports.getreview = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Add this to your existing controller file
|
||||
/**
|
||||
* Submit a new review for a product
|
||||
*/
|
||||
exports.submitReview = async (req, res) => {
|
||||
const { productId, userId, rating, comment } = req.body;
|
||||
|
||||
@@ -85,16 +96,41 @@ exports.submitReview = async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if user has already reviewed this product
|
||||
const [existingReview] = await db.execute(
|
||||
`SELECT ReviewID FROM Review WHERE ProductID = ? AND UserID = ?`,
|
||||
[productId, userId],
|
||||
);
|
||||
|
||||
if (existingReview.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "You have already reviewed this product",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user is trying to review their own product
|
||||
const [productOwner] = await db.execute(
|
||||
`SELECT UserID FROM Product WHERE ProductID = ?`,
|
||||
[productId],
|
||||
);
|
||||
|
||||
if (productOwner.length > 0 && productOwner[0].UserID === userId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "You cannot review your own product",
|
||||
});
|
||||
}
|
||||
|
||||
// Insert the review into the database
|
||||
const [result] = await db.execute(
|
||||
`
|
||||
INSERT INTO Review (
|
||||
`INSERT INTO Review (
|
||||
ProductID,
|
||||
UserID,
|
||||
Rating,
|
||||
Comment
|
||||
) VALUES (?, ?, ?, ?)
|
||||
`,
|
||||
Comment,
|
||||
Date
|
||||
) VALUES (?, ?, ?, ?, NOW())`,
|
||||
[productId, userId, rating, comment],
|
||||
);
|
||||
|
||||
@@ -103,22 +139,24 @@ exports.submitReview = async (req, res) => {
|
||||
|
||||
// Fetch the newly created review to return to client
|
||||
const [newReview] = await db.execute(
|
||||
`
|
||||
SELECT
|
||||
ReviewID as id,
|
||||
ProductID,
|
||||
UserID,
|
||||
Rating,
|
||||
Comment,
|
||||
Date as ReviewDate
|
||||
FROM Review
|
||||
WHERE ReviewID = ?
|
||||
`,
|
||||
`SELECT
|
||||
R.ReviewID,
|
||||
R.ProductID,
|
||||
R.UserID,
|
||||
R.Rating,
|
||||
R.Comment,
|
||||
R.Date AS ReviewDate,
|
||||
U.Name AS ReviewerName,
|
||||
P.Name AS ProductName
|
||||
FROM Review R
|
||||
JOIN User U ON R.UserID = U.UserID
|
||||
JOIN Product P ON R.ProductID = P.ProductID
|
||||
WHERE R.ReviewID = ?`,
|
||||
[reviewId],
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
success: false,
|
||||
success: true, // Fixed from false to true
|
||||
message: "Review submitted successfully",
|
||||
data: newReview[0],
|
||||
});
|
||||
@@ -131,3 +169,134 @@ exports.submitReview = async (req, res) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// /**
|
||||
// * Update an existing review
|
||||
// */
|
||||
// exports.updateReview = async (req, res) => {
|
||||
// const { reviewId } = req.params;
|
||||
// const { rating, comment } = req.body;
|
||||
// const userId = req.body.userId; // Assuming you have middleware that validates the user
|
||||
|
||||
// // Validate required fields
|
||||
// if (!reviewId || !rating || !comment) {
|
||||
// return res.status(400).json({
|
||||
// success: false,
|
||||
// message: "Missing required fields",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Validate rating is between 1 and 5
|
||||
// if (rating < 1 || rating > 5) {
|
||||
// return res.status(400).json({
|
||||
// success: false,
|
||||
// message: "Rating must be between 1 and 5",
|
||||
// });
|
||||
// }
|
||||
|
||||
// try {
|
||||
// // Check if review exists and belongs to the user
|
||||
// const [existingReview] = await db.execute(
|
||||
// `SELECT ReviewID, UserID FROM Review WHERE ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// if (existingReview.length === 0) {
|
||||
// return res.status(404).json({
|
||||
// success: false,
|
||||
// message: "Review not found",
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (existingReview[0].UserID !== userId) {
|
||||
// return res.status(403).json({
|
||||
// success: false,
|
||||
// message: "You can only update your own reviews",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Update the review
|
||||
// await db.execute(
|
||||
// `UPDATE Review
|
||||
// SET Rating = ?, Comment = ?, Date = NOW()
|
||||
// WHERE ReviewID = ?`,
|
||||
// [rating, comment, reviewId],
|
||||
// );
|
||||
|
||||
// // Fetch the updated review
|
||||
// const [updatedReview] = await db.execute(
|
||||
// `SELECT
|
||||
// R.ReviewID,
|
||||
// R.ProductID,
|
||||
// R.UserID,
|
||||
// R.Rating,
|
||||
// R.Comment,
|
||||
// R.Date AS ReviewDate,
|
||||
// U.Name AS ReviewerName,
|
||||
// P.Name AS ProductName
|
||||
// FROM Review R
|
||||
// JOIN User U ON R.UserID = U.UserID
|
||||
// JOIN Product P ON R.ProductID = P.ProductID
|
||||
// WHERE R.ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Review updated successfully",
|
||||
// data: updatedReview[0],
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error updating review:", error);
|
||||
// return res.status(500).json({
|
||||
// success: false,
|
||||
// message: "Database error occurred",
|
||||
// error: error.message,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * Delete a review
|
||||
// */
|
||||
// exports.deleteReview = async (req, res) => {
|
||||
// const { reviewId } = req.params;
|
||||
// const userId = req.body.userId; // Assuming you have middleware that validates the user
|
||||
|
||||
// try {
|
||||
// // Check if review exists and belongs to the user
|
||||
// const [existingReview] = await db.execute(
|
||||
// `SELECT ReviewID, UserID FROM Review WHERE ReviewID = ?`,
|
||||
// [reviewId],
|
||||
// );
|
||||
|
||||
// if (existingReview.length === 0) {
|
||||
// return res.status(404).json({
|
||||
// success: false,
|
||||
// message: "Review not found",
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (existingReview[0].UserID !== userId) {
|
||||
// return res.status(403).json({
|
||||
// success: false,
|
||||
// message: "You can only delete your own reviews",
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Delete the review
|
||||
// await db.execute(`DELETE FROM Review WHERE ReviewID = ?`, [reviewId]);
|
||||
|
||||
// res.json({
|
||||
// success: true,
|
||||
// message: "Review deleted successfully",
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error deleting review:", error);
|
||||
// return res.status(500).json({
|
||||
// success: false,
|
||||
// message: "Database error occurred",
|
||||
// error: error.message,
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
@@ -39,9 +39,9 @@ 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_products", searchRouter); //prefix with /api/product
|
||||
app.use("/api/Engine", recommendedRouter); //prefix with /api/
|
||||
app.use("/api/get", history); //prefix with /api/
|
||||
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/
|
||||
|
||||
// Set up a scheduler to run cleanup every hour
|
||||
|
||||
@@ -3,6 +3,6 @@ const express = require("express");
|
||||
const { HistoryByUserId } = require("../controllers/history");
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/history", HistoryByUserId);
|
||||
router.post("/getHistory", HistoryByUserId);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const {
|
||||
addToFavorite,
|
||||
addFavorite,
|
||||
getFavorites,
|
||||
getAllProducts,
|
||||
getProductById,
|
||||
} = require("../controllers/product");
|
||||
@@ -13,8 +14,10 @@ router.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
router.post("/add_fav_product", addToFavorite);
|
||||
router.get("/get_product", getAllProducts);
|
||||
router.post("/addFavorite", addFavorite);
|
||||
router.post("/getFavorites", getFavorites);
|
||||
|
||||
router.get("/getProduct", getAllProducts);
|
||||
router.get("/:id", getProductById); // Simplified route
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// routes/product.js
|
||||
const express = require("express");
|
||||
const { getreview, submitReview } = require("../controllers/review");
|
||||
const { getReviews, submitReview } = require("../controllers/review");
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/:id", getreview);
|
||||
router.get("/:id", getReviews);
|
||||
router.post("/add", submitReview);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -9,6 +9,6 @@ router.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
router.get("/search", searchProductsByName);
|
||||
router.get("/getProduct", searchProductsByName);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -11,9 +11,8 @@ const Home = () => {
|
||||
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||
|
||||
const handleLinkClick = async (id) => {
|
||||
// Example: append to localStorage or call analytics
|
||||
const response = await fetch(
|
||||
"http://localhost:3030/api/product/add_fav_product",
|
||||
"http://localhost:3030/api/product/addFavorites",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -32,6 +31,16 @@ const Home = () => {
|
||||
console.log(`Add Product -> History: ${id}`);
|
||||
};
|
||||
|
||||
function reloadPage() {
|
||||
var doctTimestamp = new Date(performance.timing.domLoading).getTime();
|
||||
var now = Date.now();
|
||||
var tenSec = 10 * 1000;
|
||||
if (now > doctTimestamp + tenSec) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
reloadPage();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchrecomProducts = async () => {
|
||||
// Get the user's data from localStorage
|
||||
@@ -76,13 +85,15 @@ const Home = () => {
|
||||
}
|
||||
};
|
||||
fetchrecomProducts();
|
||||
//reloadPage();
|
||||
}, []);
|
||||
reloadPage();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
"http://localhost:3030/api/product/get_product",
|
||||
"http://localhost:3030/api/product/getProduct",
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to fetch products");
|
||||
|
||||
@@ -118,15 +129,18 @@ const Home = () => {
|
||||
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||
console.log(storedUser);
|
||||
try {
|
||||
const response = await fetch("http://localhost:3030/api/get/history", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
const response = await fetch(
|
||||
"http://localhost:3030/api/history/getHistory",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: storedUser.ID,
|
||||
}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: storedUser.ID,
|
||||
}),
|
||||
});
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to fetch products");
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { useState, useEffect, setErrors } from "react";
|
||||
import { useParams, Link, isSession } from "react-router-dom";
|
||||
import { Heart, ArrowLeft, Tag, User, Calendar, Star } from "lucide-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import {
|
||||
Heart,
|
||||
ArrowLeft,
|
||||
Tag,
|
||||
User,
|
||||
Calendar,
|
||||
Star,
|
||||
Phone,
|
||||
Mail,
|
||||
} from "lucide-react";
|
||||
|
||||
const ProductDetail = () => {
|
||||
const { id } = useParams();
|
||||
@@ -8,16 +17,19 @@ const ProductDetail = () => {
|
||||
const [loading, setLoading] = useState({
|
||||
product: true,
|
||||
reviews: true,
|
||||
submitting: false,
|
||||
});
|
||||
const [error, setError] = useState({
|
||||
product: null,
|
||||
reviews: null,
|
||||
submit: null,
|
||||
});
|
||||
const [isFavorite, setIsFavorite] = useState(false);
|
||||
const [contactForm, showContactForm, setShowContactForm] = useState(false);
|
||||
const [showContactOptions, setShowContactOptions] = useState(false);
|
||||
const [currentImage, setCurrentImage] = useState(0);
|
||||
const [reviews, setReviews] = useState([]);
|
||||
const [showReviewForm, setShowReviewForm] = useState(false);
|
||||
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||
|
||||
const [reviewForm, setReviewForm] = useState({
|
||||
rating: 3,
|
||||
@@ -42,39 +54,70 @@ const ProductDetail = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmitReview = async () => {
|
||||
try {
|
||||
// Ensure userId is present
|
||||
if (!userData.userId) {
|
||||
throw new Error("User ID is missing. Unable to update profile.");
|
||||
}
|
||||
const handleSubmitReview = async (e) => {
|
||||
e.preventDefault(); // Prevent form default behavior
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
setLoading((prev) => ({ ...prev, submitting: true }));
|
||||
setError((prev) => ({ ...prev, submit: null }));
|
||||
|
||||
const reviewData = {
|
||||
productId: id,
|
||||
rating: reviewForm.rating,
|
||||
comment: reviewForm.comment,
|
||||
userId: storedUser.ID,
|
||||
};
|
||||
|
||||
const response = await fetch(`http://localhost:3030/api/review/add`, {
|
||||
method: "POST", // or "PUT" if your backend supports it
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(userData),
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(reviewData),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || "Failed to update profile");
|
||||
// Check if API returned an error message even with 200 status
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || "Failed to submit review");
|
||||
}
|
||||
|
||||
console.log("Profile updated successfully:", result);
|
||||
alert("Profile updated successfully!");
|
||||
alert("Review submitted successfully!");
|
||||
|
||||
setReviewForm({
|
||||
rating: 3,
|
||||
comment: "",
|
||||
name: "",
|
||||
});
|
||||
setShowReviewForm(false);
|
||||
|
||||
try {
|
||||
setLoading((prev) => ({ ...prev, reviews: true }));
|
||||
const reviewsResponse = await fetch(
|
||||
`http://localhost:3030/api/review/${id}`,
|
||||
);
|
||||
const reviewsResult = await reviewsResponse.json();
|
||||
|
||||
if (reviewsResult.success) {
|
||||
setReviews(reviewsResult.data || []);
|
||||
setError((prev) => ({ ...prev, reviews: null }));
|
||||
} else {
|
||||
throw new Error(reviewsResult.message || "Error fetching reviews");
|
||||
}
|
||||
} catch (reviewsError) {
|
||||
console.error("Error fetching reviews:", reviewsError);
|
||||
setError((prev) => ({ ...prev, reviews: reviewsError.message }));
|
||||
} finally {
|
||||
setLoading((prev) => ({ ...prev, reviews: false }));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating profile:", error);
|
||||
setError(
|
||||
error.message || "An error occurred while updating your profile.",
|
||||
);
|
||||
console.error("Error submitting review:", error);
|
||||
alert(`Error: ${error.message}`);
|
||||
setError((prev) => ({
|
||||
...prev,
|
||||
submit: error.message,
|
||||
}));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setLoading((prev) => ({ ...prev, submitting: false }));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -172,48 +215,6 @@ const ProductDetail = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Handle form input changes
|
||||
const handleContactInputChange = (e) => {
|
||||
const { id, value } = e.target;
|
||||
setContactForm((prev) => ({
|
||||
...prev,
|
||||
[id]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
// Handle message submission with improved validation
|
||||
const handleSendMessage = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Basic validation
|
||||
if (!contactForm.email || !contactForm.phone) {
|
||||
alert("Please fill in all required fields");
|
||||
return;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(contactForm.email)) {
|
||||
alert("Please enter a valid email address");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement actual message sending logic
|
||||
try {
|
||||
// Mock API call
|
||||
console.log("Message sent:", contactForm);
|
||||
setContactForm({
|
||||
email: "",
|
||||
phone: "",
|
||||
message: "Hi, is this item still available?",
|
||||
});
|
||||
setShowContactForm(false);
|
||||
alert("Message sent to seller!");
|
||||
} catch (error) {
|
||||
alert(`Failed to send message: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Image navigation
|
||||
const nextImage = () => {
|
||||
if (product?.images?.length > 0) {
|
||||
@@ -417,12 +418,38 @@ const ProductDetail = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* <button
|
||||
onClick={() => setShowContactForm(!showContactForm)}
|
||||
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
||||
>
|
||||
Contact Seller
|
||||
</button> */}
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setShowContactOptions(!showContactOptions)}
|
||||
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
||||
>
|
||||
Contact Seller
|
||||
</button>
|
||||
|
||||
{showContactOptions && (
|
||||
<div className="absolute z-10 w-full bg-white border border-gray-200 shadow-md">
|
||||
{product.SellerPhone && (
|
||||
<a
|
||||
href={`tel:${product.SellerPhone}`}
|
||||
className="flex items-center gap-2 p-3 hover:bg-gray-50 border-b border-gray-100"
|
||||
>
|
||||
<Phone className="h-5 w-5 text-green-500" />
|
||||
<span>Call Seller</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{product.SellerEmail && (
|
||||
<a
|
||||
href={`mailto:${product.SellerEmail}`}
|
||||
className="flex items-center gap-2 p-3 hover:bg-gray-50"
|
||||
>
|
||||
<Mail className="h-5 w-5 text-green-500" />
|
||||
<span>Email Seller</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="pt-4 border-t border-gray-200">
|
||||
{/* Seller Info */}
|
||||
@@ -441,29 +468,6 @@ const ProductDetail = () => {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Options */}
|
||||
{showContactForm && (
|
||||
<div className="mt-4 border border-gray-200 p-4">
|
||||
<h3 className="font-medium text-gray-800 mb-2">
|
||||
Contact Seller
|
||||
</h3>
|
||||
<div className="flex flex-col sm:flex-row gap-3">
|
||||
<a
|
||||
href={`tel:${contactForm.phone}`}
|
||||
className="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 text-center "
|
||||
>
|
||||
Call Seller
|
||||
</a>
|
||||
<a
|
||||
href={`mailto:${contactForm.email}`}
|
||||
className="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 text-center "
|
||||
>
|
||||
Email Seller
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -495,7 +499,7 @@ const ProductDetail = () => {
|
||||
>
|
||||
<div className="flex justify-between mb-2">
|
||||
<div className="font-medium text-gray-800">
|
||||
{review.ReviewerName || "Anonymous"}
|
||||
{review.ReviewerName}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{review.ReviewDate
|
||||
@@ -546,20 +550,6 @@ const ProductDetail = () => {
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmitReview}>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="name" className="block text-gray-700 mb-1">
|
||||
Your Name <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={reviewForm.name}
|
||||
onChange={handleReviewInputChange}
|
||||
className="w-full p-3 border border-gray-300 rounded focus:outline-none focus:border-green-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label className="block text-gray-700 mb-1">
|
||||
Rating <span className="text-red-500">*</span>
|
||||
@@ -612,8 +602,9 @@ const ProductDetail = () => {
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
||||
disabled={loading.submitting}
|
||||
>
|
||||
Submit Review
|
||||
{loading.submitting ? "Submitting..." : "Submit Review"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -27,7 +27,7 @@ const SearchPage = () => {
|
||||
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`http://localhost:3030/api/search_products/search`,
|
||||
`http://localhost:3030/api/search/getProduct`,
|
||||
{
|
||||
params: { name: query },
|
||||
},
|
||||
|
||||
@@ -101,7 +101,8 @@ CREATE TABLE Favorites (
|
||||
UserID INT,
|
||||
ProductID INT,
|
||||
FOREIGN KEY (UserID) REFERENCES User (UserID),
|
||||
FOREIGN KEY (ProductID) REFERENCES Product (ProductID)
|
||||
FOREIGN KEY (ProductID) REFERENCES Product (ProductID),
|
||||
UNIQUE (UserID, ProductID) -- Prevents duplicate favorites
|
||||
);
|
||||
|
||||
-- Product-Category Junction Table (Many-to-Many)
|
||||
|
||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Campus-Plug",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
Binary file not shown.
@@ -108,6 +108,10 @@ def get_user_history(user_id):
|
||||
return final
|
||||
|
||||
|
||||
def delete_user_recommendations(userId):
|
||||
pass
|
||||
|
||||
|
||||
def get_recommendations(user_id, top_n=10):
|
||||
try:
|
||||
# Get all products and user history with their category vectors
|
||||
|
||||
@@ -6,8 +6,6 @@ from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
from app import get_recommendations
|
||||
|
||||
#import time
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app) # Enable CORS for all routes
|
||||
|
||||
Reference in New Issue
Block a user