add review and read review now done
This commit is contained in:
@@ -37,7 +37,6 @@ exports.HistoryByUserId = async (req, res) => {
|
|||||||
[id],
|
[id],
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: "Products fetched successfully",
|
message: "Products fetched successfully",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
const db = require("../utils/database");
|
const db = require("../utils/database");
|
||||||
|
|
||||||
exports.addToFavorite = async (req, res) => {
|
exports.addFavorite = async (req, res) => {
|
||||||
const { userID, productsID } = req.body;
|
const { userID, productsID } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 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 unique(?, ?)",
|
"INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
|
||||||
[userID, productsID],
|
[userID, productsID],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -14,13 +14,41 @@ exports.addToFavorite = async (req, res) => {
|
|||||||
success: true,
|
success: true,
|
||||||
message: "Product added to favorites successfully",
|
message: "Product added to favorites successfully",
|
||||||
});
|
});
|
||||||
console.log(result);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding favorite product:", error);
|
console.error("Error adding favorite product:", error);
|
||||||
return res.json({ error: "Could not add favorite product" });
|
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
|
// Get all products along with their image URLs
|
||||||
exports.getAllProducts = async (req, res) => {
|
exports.getAllProducts = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@@ -52,7 +80,6 @@ exports.getAllProducts = async (req, res) => {
|
|||||||
WHERE RowNum = 1;
|
WHERE RowNum = 1;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: "Products fetched successfully",
|
message: "Products fetched successfully",
|
||||||
@@ -74,7 +101,7 @@ exports.getProductById = async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const [data] = await db.execute(
|
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
|
FROM Product p
|
||||||
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
LEFT JOIN Image_URL i ON p.ProductID = i.ProductID
|
||||||
JOIN User U ON p.UserID = U.UserID
|
JOIN User U ON p.UserID = U.UserID
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
const db = require("../utils/database");
|
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;
|
const { id } = req.params;
|
||||||
console.log("Received Product ID:", id);
|
console.log("Received Product ID:", id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [data] = await db.execute(
|
// First query: Get reviews for this specific product
|
||||||
`
|
const [productReviews] = await db.execute(
|
||||||
SELECT
|
`SELECT
|
||||||
R.ReviewID,
|
R.ReviewID,
|
||||||
R.UserID,
|
R.UserID,
|
||||||
R.ProductID,
|
R.ProductID,
|
||||||
@@ -15,44 +19,49 @@ exports.getreview = async (req, res) => {
|
|||||||
R.Rating,
|
R.Rating,
|
||||||
R.Date AS ReviewDate,
|
R.Date AS ReviewDate,
|
||||||
U.Name AS ReviewerName,
|
U.Name AS ReviewerName,
|
||||||
P.Name AS ProductName
|
P.Name AS ProductName,
|
||||||
|
'product' AS ReviewType
|
||||||
FROM Review R
|
FROM Review R
|
||||||
JOIN User U ON R.UserID = U.UserID
|
JOIN User U ON R.UserID = U.UserID
|
||||||
JOIN Product P ON R.ProductID = P.ProductID
|
JOIN Product P ON R.ProductID = P.ProductID
|
||||||
WHERE R.ProductID = ?
|
WHERE R.ProductID = ?`,
|
||||||
|
[id],
|
||||||
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],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Log raw data for debugging
|
// // Second query: Get reviews written by the product owner for other products
|
||||||
console.log("Raw Database Result:", data);
|
// 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({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: "Products fetched successfully",
|
message: "Reviews fetched successfully",
|
||||||
data,
|
data: combinedReviews,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Full Error Details:", 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) => {
|
exports.submitReview = async (req, res) => {
|
||||||
const { productId, userId, rating, comment } = req.body;
|
const { productId, userId, rating, comment } = req.body;
|
||||||
|
|
||||||
@@ -85,16 +96,41 @@ exports.submitReview = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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
|
// Insert the review into the database
|
||||||
const [result] = await db.execute(
|
const [result] = await db.execute(
|
||||||
`
|
`INSERT INTO Review (
|
||||||
INSERT INTO Review (
|
|
||||||
ProductID,
|
ProductID,
|
||||||
UserID,
|
UserID,
|
||||||
Rating,
|
Rating,
|
||||||
Comment
|
Comment,
|
||||||
) VALUES (?, ?, ?, ?)
|
Date
|
||||||
`,
|
) VALUES (?, ?, ?, ?, NOW())`,
|
||||||
[productId, userId, rating, comment],
|
[productId, userId, rating, comment],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,22 +139,24 @@ exports.submitReview = async (req, res) => {
|
|||||||
|
|
||||||
// Fetch the newly created review to return to client
|
// Fetch the newly created review to return to client
|
||||||
const [newReview] = await db.execute(
|
const [newReview] = await db.execute(
|
||||||
`
|
`SELECT
|
||||||
SELECT
|
R.ReviewID,
|
||||||
ReviewID as id,
|
R.ProductID,
|
||||||
ProductID,
|
R.UserID,
|
||||||
UserID,
|
R.Rating,
|
||||||
Rating,
|
R.Comment,
|
||||||
Comment,
|
R.Date AS ReviewDate,
|
||||||
Date as ReviewDate
|
U.Name AS ReviewerName,
|
||||||
FROM Review
|
P.Name AS ProductName
|
||||||
WHERE ReviewID = ?
|
FROM Review R
|
||||||
`,
|
JOIN User U ON R.UserID = U.UserID
|
||||||
|
JOIN Product P ON R.ProductID = P.ProductID
|
||||||
|
WHERE R.ReviewID = ?`,
|
||||||
[reviewId],
|
[reviewId],
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
success: false,
|
success: true, // Fixed from false to true
|
||||||
message: "Review submitted successfully",
|
message: "Review submitted successfully",
|
||||||
data: newReview[0],
|
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
|
//Routes
|
||||||
app.use("/api/user", userRouter); //prefix with /api/user
|
app.use("/api/user", userRouter); //prefix with /api/user
|
||||||
app.use("/api/product", productRouter); //prefix with /api/product
|
app.use("/api/product", productRouter); //prefix with /api/product
|
||||||
app.use("/api/search_products", searchRouter); //prefix with /api/product
|
app.use("/api/search", searchRouter); //prefix with /api/product
|
||||||
app.use("/api/Engine", recommendedRouter); //prefix with /api/
|
app.use("/api/engine", recommendedRouter); //prefix with /api/
|
||||||
app.use("/api/get", history); //prefix with /api/
|
app.use("/api/history", history); //prefix with /api/
|
||||||
app.use("/api/review", review); //prefix with /api/
|
app.use("/api/review", review); //prefix with /api/
|
||||||
|
|
||||||
// Set up a scheduler to run cleanup every hour
|
// Set up a scheduler to run cleanup every hour
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ const express = require("express");
|
|||||||
const { HistoryByUserId } = require("../controllers/history");
|
const { HistoryByUserId } = require("../controllers/history");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post("/history", HistoryByUserId);
|
router.post("/getHistory", HistoryByUserId);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// routes/product.js
|
// routes/product.js
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const {
|
const {
|
||||||
addToFavorite,
|
addFavorite,
|
||||||
|
getFavorites,
|
||||||
getAllProducts,
|
getAllProducts,
|
||||||
getProductById,
|
getProductById,
|
||||||
} = require("../controllers/product");
|
} = require("../controllers/product");
|
||||||
@@ -13,8 +14,10 @@ router.use((req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/add_fav_product", addToFavorite);
|
router.post("/addFavorite", addFavorite);
|
||||||
router.get("/get_product", getAllProducts);
|
router.post("/getFavorites", getFavorites);
|
||||||
|
|
||||||
|
router.get("/getProduct", getAllProducts);
|
||||||
router.get("/:id", getProductById); // Simplified route
|
router.get("/:id", getProductById); // Simplified route
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// routes/product.js
|
// routes/product.js
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const { getreview, submitReview } = require("../controllers/review");
|
const { getReviews, submitReview } = require("../controllers/review");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get("/:id", getreview);
|
router.get("/:id", getReviews);
|
||||||
router.post("/add", submitReview);
|
router.post("/add", submitReview);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ router.use((req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/search", searchProductsByName);
|
router.get("/getProduct", searchProductsByName);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ const Home = () => {
|
|||||||
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||||
|
|
||||||
const handleLinkClick = async (id) => {
|
const handleLinkClick = async (id) => {
|
||||||
// Example: append to localStorage or call analytics
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"http://localhost:3030/api/product/add_fav_product",
|
"http://localhost:3030/api/product/addFavorites",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -32,6 +31,16 @@ const Home = () => {
|
|||||||
console.log(`Add Product -> History: ${id}`);
|
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(() => {
|
useEffect(() => {
|
||||||
const fetchrecomProducts = async () => {
|
const fetchrecomProducts = async () => {
|
||||||
// Get the user's data from localStorage
|
// Get the user's data from localStorage
|
||||||
@@ -76,13 +85,15 @@ const Home = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchrecomProducts();
|
fetchrecomProducts();
|
||||||
|
//reloadPage();
|
||||||
}, []);
|
}, []);
|
||||||
|
reloadPage();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
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/getProduct",
|
||||||
);
|
);
|
||||||
if (!response.ok) throw new Error("Failed to fetch products");
|
if (!response.ok) throw new Error("Failed to fetch products");
|
||||||
|
|
||||||
@@ -118,15 +129,18 @@ const Home = () => {
|
|||||||
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||||
console.log(storedUser);
|
console.log(storedUser);
|
||||||
try {
|
try {
|
||||||
const response = await fetch("http://localhost:3030/api/get/history", {
|
const response = await fetch(
|
||||||
method: "POST",
|
"http://localhost:3030/api/history/getHistory",
|
||||||
headers: {
|
{
|
||||||
"Content-Type": "application/json",
|
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");
|
if (!response.ok) throw new Error("Failed to fetch products");
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { useState, useEffect, setErrors } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams, Link, isSession } from "react-router-dom";
|
import { useParams, Link } from "react-router-dom";
|
||||||
import { Heart, ArrowLeft, Tag, User, Calendar, Star } from "lucide-react";
|
import {
|
||||||
|
Heart,
|
||||||
|
ArrowLeft,
|
||||||
|
Tag,
|
||||||
|
User,
|
||||||
|
Calendar,
|
||||||
|
Star,
|
||||||
|
Phone,
|
||||||
|
Mail,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
const ProductDetail = () => {
|
const ProductDetail = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
@@ -8,16 +17,19 @@ const ProductDetail = () => {
|
|||||||
const [loading, setLoading] = useState({
|
const [loading, setLoading] = useState({
|
||||||
product: true,
|
product: true,
|
||||||
reviews: true,
|
reviews: true,
|
||||||
|
submitting: false,
|
||||||
});
|
});
|
||||||
const [error, setError] = useState({
|
const [error, setError] = useState({
|
||||||
product: null,
|
product: null,
|
||||||
reviews: null,
|
reviews: null,
|
||||||
|
submit: null,
|
||||||
});
|
});
|
||||||
const [isFavorite, setIsFavorite] = useState(false);
|
const [isFavorite, setIsFavorite] = useState(false);
|
||||||
const [contactForm, showContactForm, setShowContactForm] = useState(false);
|
const [showContactOptions, setShowContactOptions] = useState(false);
|
||||||
const [currentImage, setCurrentImage] = useState(0);
|
const [currentImage, setCurrentImage] = useState(0);
|
||||||
const [reviews, setReviews] = useState([]);
|
const [reviews, setReviews] = useState([]);
|
||||||
const [showReviewForm, setShowReviewForm] = useState(false);
|
const [showReviewForm, setShowReviewForm] = useState(false);
|
||||||
|
const storedUser = JSON.parse(sessionStorage.getItem("user"));
|
||||||
|
|
||||||
const [reviewForm, setReviewForm] = useState({
|
const [reviewForm, setReviewForm] = useState({
|
||||||
rating: 3,
|
rating: 3,
|
||||||
@@ -42,39 +54,70 @@ const ProductDetail = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitReview = async () => {
|
const handleSubmitReview = async (e) => {
|
||||||
try {
|
e.preventDefault(); // Prevent form default behavior
|
||||||
// Ensure userId is present
|
|
||||||
if (!userData.userId) {
|
|
||||||
throw new Error("User ID is missing. Unable to update profile.");
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true);
|
try {
|
||||||
setError(null);
|
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`, {
|
const response = await fetch(`http://localhost:3030/api/review/add`, {
|
||||||
method: "POST", // or "PUT" if your backend supports it
|
method: "POST",
|
||||||
headers: {
|
headers: { "Content-Type": "application/json" },
|
||||||
"Content-Type": "application/json",
|
body: JSON.stringify(reviewData),
|
||||||
},
|
|
||||||
body: JSON.stringify(userData),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
// Check if API returned an error message even with 200 status
|
||||||
throw new Error(result.error || "Failed to update profile");
|
if (!result.success) {
|
||||||
|
throw new Error(result.message || "Failed to submit review");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Profile updated successfully:", result);
|
alert("Review submitted successfully!");
|
||||||
alert("Profile updated 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) {
|
} catch (error) {
|
||||||
console.error("Error updating profile:", error);
|
console.error("Error submitting review:", error);
|
||||||
setError(
|
alert(`Error: ${error.message}`);
|
||||||
error.message || "An error occurred while updating your profile.",
|
setError((prev) => ({
|
||||||
);
|
...prev,
|
||||||
|
submit: error.message,
|
||||||
|
}));
|
||||||
} finally {
|
} 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
|
// Image navigation
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
if (product?.images?.length > 0) {
|
if (product?.images?.length > 0) {
|
||||||
@@ -417,12 +418,38 @@ const ProductDetail = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <button
|
<div className="relative">
|
||||||
onClick={() => setShowContactForm(!showContactForm)}
|
<button
|
||||||
className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-3 px-4 mb-3"
|
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> */}
|
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">
|
<div className="pt-4 border-t border-gray-200">
|
||||||
{/* Seller Info */}
|
{/* Seller Info */}
|
||||||
@@ -441,29 +468,6 @@ const ProductDetail = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -495,7 +499,7 @@ const ProductDetail = () => {
|
|||||||
>
|
>
|
||||||
<div className="flex justify-between mb-2">
|
<div className="flex justify-between mb-2">
|
||||||
<div className="font-medium text-gray-800">
|
<div className="font-medium text-gray-800">
|
||||||
{review.ReviewerName || "Anonymous"}
|
{review.ReviewerName}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
{review.ReviewDate
|
{review.ReviewDate
|
||||||
@@ -546,20 +550,6 @@ const ProductDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmitReview}>
|
<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">
|
<div className="mb-4">
|
||||||
<label className="block text-gray-700 mb-1">
|
<label className="block text-gray-700 mb-1">
|
||||||
Rating <span className="text-red-500">*</span>
|
Rating <span className="text-red-500">*</span>
|
||||||
@@ -612,8 +602,9 @@ const ProductDetail = () => {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const SearchPage = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`http://localhost:3030/api/search_products/search`,
|
`http://localhost:3030/api/search/getProduct`,
|
||||||
{
|
{
|
||||||
params: { name: query },
|
params: { name: query },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -101,7 +101,8 @@ CREATE TABLE Favorites (
|
|||||||
UserID INT,
|
UserID INT,
|
||||||
ProductID INT,
|
ProductID INT,
|
||||||
FOREIGN KEY (UserID) REFERENCES User (UserID),
|
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)
|
-- 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
|
return final
|
||||||
|
|
||||||
|
|
||||||
|
def delete_user_recommendations(userId):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_recommendations(user_id, top_n=10):
|
def get_recommendations(user_id, top_n=10):
|
||||||
try:
|
try:
|
||||||
# Get all products and user history with their category vectors
|
# 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 flask_cors import CORS
|
||||||
from app import get_recommendations
|
from app import get_recommendations
|
||||||
|
|
||||||
#import time
|
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
CORS(app) # Enable CORS for all routes
|
CORS(app) # Enable CORS for all routes
|
||||||
|
|||||||
Reference in New Issue
Block a user