diff --git a/backend/controllers/product.js b/backend/controllers/product.js
index bbe4f1f..f385270 100644
--- a/backend/controllers/product.js
+++ b/backend/controllers/product.js
@@ -32,11 +32,49 @@ exports.addProduct = async (req, res) => {
}
};
+exports.removeProduct = async (req, res) => {
+ const { userID, productID } = req.body;
+ console.log(userID);
+
+ try {
+ // First delete images
+ await db.execute(`DELETE FROM Image_URL WHERE ProductID = ?`, [productID]);
+ await db.execute(`DELETE FROM History WHERE ProductID = ?`, [productID]);
+ await db.execute(`DELETE FROM Favorites WHERE ProductID = ?`, [productID]);
+ await db.execute(`DELETE FROM Product_Category WHERE ProductID = ?`, [
+ productID,
+ ]);
+ await db.execute(`DELETE FROM Product_Category WHERE ProductID = ?`, [
+ productID,
+ ]);
+ await db.execute(`DELETE FROM Transaction WHERE ProductID = ?`, [
+ productID,
+ ]);
+ await db.execute(
+ `DELETE FROM Recommendation WHERE RecommendedProductID = ?`,
+ [productID],
+ );
+
+ // Then delete the product
+ await db.execute(`DELETE FROM Product WHERE UserID = ? AND ProductID = ?`, [
+ userID,
+ productID,
+ ]);
+
+ res.json({
+ success: true,
+ message: "Product removed successfully",
+ });
+ } catch (error) {
+ console.error("Error removing product:", error);
+ return res.json({ error: "Could not remove product" });
+ }
+};
+
exports.addFavorite = async (req, res) => {
const { userID, productID } = req.body;
console.log(userID);
try {
- // Use parameterized query to prevent SQL injection
const [result] = await db.execute(
`INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)`,
[userID, productID],
@@ -72,6 +110,60 @@ exports.removeFavorite = async (req, res) => {
}
};
+exports.updateProduct = async (req, res) => {
+ const { productId } = req.params;
+ const { name, description, price, category, images } = req.body;
+
+ console.log(productId);
+
+ const connection = await db.getConnection();
+ try {
+ await connection.beginTransaction();
+
+ // Step 1: Check if the product exists
+ const [checkProduct] = await connection.execute(
+ "SELECT * FROM Product WHERE ProductID = ?",
+ [productId],
+ );
+ if (checkProduct.length === 0) {
+ await connection.rollback();
+ return res.status(404).json({ error: "Product not found" });
+ }
+
+ // Step 2: Update the product
+ await connection.execute(
+ `
+ UPDATE Product
+ SET Name = ?, Description = ?, Price = ?, CategoryID = ?
+ WHERE ProductID = ?
+ `,
+ [name, description, price, category, productId],
+ );
+
+ // Step 3: Delete existing images
+ await connection.execute(`DELETE FROM Image_URL WHERE ProductID = ?`, [
+ productId,
+ ]);
+
+ // Step 4: Insert new image URLs
+ for (const imageUrl of images) {
+ await connection.execute(
+ `INSERT INTO Image_URL (ProductID, URL) VALUES (?, ?)`,
+ [productId, imageUrl],
+ );
+ }
+
+ await connection.commit();
+ res.json({ success: true, message: "Product updated successfully" });
+ } catch (error) {
+ await connection.rollback();
+ console.error("Update product error:", error);
+ res.status(500).json({ error: "Failed to update product" });
+ } finally {
+ connection.release();
+ }
+};
+
exports.myProduct = async (req, res) => {
const { userID } = req.body;
@@ -253,33 +345,3 @@ exports.getProductById = async (req, res) => {
});
}
};
-
-// db_con.query(
-// "SELECT ProductID FROM product WHERE ProductID = ?",
-// [productID],
-// (err, results) => {
-// if (err) {
-// console.error("Error checking product:", err);
-// return res.json({ error: "Database error" });
-// }
-
-// if (results.length === 0) {
-// return res.json({ error: "Product does not exist" });
-// }
-// },
-// );
-
-// db_con.query(
-// "INSERT INTO Favorites (UserID, ProductID) VALUES (?, ?)",
-// [userID, productID],
-// (err, result) => {
-// if (err) {
-// console.error("Error adding favorite product:", err);
-// return res.json({ error: "Could not add favorite product" });
-// }
-// res.json({
-// success: true,
-// message: "Product added to favorites successfully",
-// });
-// },
-// );
diff --git a/backend/index.js b/backend/index.js
index 04c623e..3080f33 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -16,7 +16,6 @@ const {
cleanupExpiredCodes,
checkDatabaseConnection,
} = require("./utils/helper");
-const { getAllCategory } = require("./controllers/category");
const app = express();
diff --git a/backend/routes/product.js b/backend/routes/product.js
index 8343405..a3c75ba 100644
--- a/backend/routes/product.js
+++ b/backend/routes/product.js
@@ -8,6 +8,8 @@ const {
getProductById,
addProduct,
myProduct,
+ removeProduct,
+ updateProduct,
} = require("../controllers/product");
const router = express.Router();
@@ -21,9 +23,12 @@ router.post("/addFavorite", addFavorite);
router.post("/getFavorites", getFavorites);
router.post("/delFavorite", removeFavorite);
+router.post("/delProduct", removeProduct);
router.post("/myProduct", myProduct);
router.post("/addProduct", addProduct);
router.get("/getProduct", getAllProducts);
router.get("/:id", getProductById); // Simplified route
+router.put("/update/:productId", updateProduct);
+
module.exports = router;
diff --git a/frontend/src/components/ProductForm.jsx b/frontend/src/components/ProductForm.jsx
deleted file mode 100644
index 1461c1b..0000000
--- a/frontend/src/components/ProductForm.jsx
+++ /dev/null
@@ -1,403 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { X, ChevronLeft, Plus, Trash2 } from "lucide-react";
-
-const ProductForm = ({
- editingProduct,
- setEditingProduct,
- onSave,
- onCancel,
-}) => {
- const [selectedCategory, setSelectedCategory] = useState("");
- const [categories, setCategories] = useState([]);
- const [categoryMapping, setCategoryMapping] = useState({});
- const storedUser = JSON.parse(sessionStorage.getItem("user"));
-
- // Fetch categories from API
- useEffect(() => {
- const fetchCategories = async () => {
- try {
- const response = await fetch("http://localhost:3030/api/category");
- if (!response.ok) throw new Error("Failed to fetch categories");
-
- const responseJson = await response.json();
- const data = responseJson.data;
-
- // Create an array of category names for the dropdown
- // Transform the object into an array of category names
- const categoryNames = [];
- const mapping = {};
-
- // Process the data properly to avoid rendering objects
- Object.entries(data).forEach(([id, name]) => {
- // Make sure each category name is a string
- const categoryName = String(name);
- categoryNames.push(categoryName);
- mapping[categoryName] = parseInt(id);
- });
-
- setCategories(categoryNames);
- setCategoryMapping(mapping);
- } catch (error) {
- console.error("Error fetching categories:", error);
- }
- };
-
- fetchCategories();
- }, []);
-
- const handleSave = async () => {
- // Check if the user has selected at least one category
- if (!(editingProduct.categories || []).length) {
- alert("Please select at least one category");
- return;
- }
-
- try {
- // First, upload images if there are any
- const imagePaths = [];
-
- // If we have files to upload, we'd handle the image upload here
- if (editingProduct.images && editingProduct.images.length > 0) {
- // Simulating image paths for demo purposes
- editingProduct.images.forEach((file) => {
- const simulatedPath = `/public/uploads/${file.name}`;
- imagePaths.push(simulatedPath);
- });
- }
-
- // Get the category ID from the first selected category
- const categoryName = (editingProduct.categories || [])[0];
- const categoryID = categoryMapping[categoryName] || 1; // Default to 3 if not found
-
- // Prepare payload according to API expectations
- const payload = {
- name: editingProduct.name || "",
- price: parseFloat(editingProduct.price) || 0,
- qty: 1,
- userID: storedUser.ID,
- description: editingProduct.description || "",
- category: categoryID,
- images: imagePaths,
- };
-
- console.log("Sending payload:", payload);
-
- const response = await fetch(
- "http://localhost:3030/api/product/addProduct",
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(payload),
- },
- );
-
- if (!response.ok) {
- const errorData = await response.text();
- throw new Error(`Failed to add product: ${errorData}`);
- }
-
- const data = await response.json();
- console.log("Product added:", data);
- if (onSave) onSave(data);
- } catch (error) {
- console.error("Error saving product:", error);
- alert(`Error saving product: ${error.message}`);
- }
- };
-
- const markAsSold = async () => {
- // This would call an API to move the product to the transaction table
- try {
- // API call would go here
- console.log("Moving product to transaction table:", editingProduct.id);
-
- // Toggle the sold status in the UI
- setEditingProduct((prev) => ({
- ...prev,
- isSold: !prev.isSold,
- }));
-
- // You would add your API call here to update the backend
- } catch (error) {
- console.error("Error marking product as sold:", error);
- }
- };
-
- const addCategory = () => {
- if (
- selectedCategory &&
- !(editingProduct.categories || []).includes(selectedCategory)
- ) {
- setEditingProduct((prev) => ({
- ...prev,
- categories: [...(prev.categories || []), selectedCategory],
- }));
- setSelectedCategory("");
- }
- };
-
- const removeCategory = (categoryToRemove) => {
- setEditingProduct((prev) => ({
- ...prev,
- categories: (prev.categories || []).filter(
- (cat) => cat !== categoryToRemove,
- ),
- }));
- };
-
- return (
-
- {/* Back Button */}
-
-
-
- {editingProduct?.id ? "Edit Your Product" : "List a New Product"}
-
-
-
- {/* Product Name */}
-
-
-
- setEditingProduct({ ...editingProduct, name: e.target.value })
- }
- className="w-full px-3 py-2 border border-gray-300 focus:border-emerald-500 focus:outline-none"
- />
-
-
- {/* Price */}
-
-
-
- setEditingProduct({
- ...editingProduct,
- price: e.target.value,
- })
- }
- className="w-full px-3 py-2 border border-gray-300 focus:border-emerald-500 focus:outline-none"
- />
-
-
- {/* Sold Status */}
-
-
- {editingProduct.isSold && (
-
- Sold
-
- )}
-
-
-
- {/* Categories */}
-
-
-
-
-
-
-
- {/* Selected Categories */}
- {(editingProduct.categories || []).length > 0 ? (
-
- {(editingProduct.categories || []).map((category, index) => (
-
- {category}
-
-
- ))}
-
- ) : (
-
- Please select at least one category
-
- )}
-
-
- {/* Description */}
-
-
-
-
-
- {/* Image Upload */}
-
-
-
-
{
- const files = Array.from(e.target.files).slice(0, 5);
- setEditingProduct((prev) => ({
- ...prev,
- images: [...(prev.images || []), ...files].slice(0, 5),
- }));
- }}
- className="hidden"
- id="image-upload"
- />
-
-
- {/* Image previews */}
- {(editingProduct.images || []).length > 0 && (
-
-
-
- {editingProduct.images.length}{" "}
- {editingProduct.images.length === 1 ? "image" : "images"}{" "}
- selected
-
-
-
-
- {editingProduct.images.map((img, idx) => (
-
-
})
-
-
- ))}
-
-
- )}
-
-
-
- {/* Actions */}
-
-
-
- {editingProduct.id && (
-
- )}
-
-
-
-
- );
-};
-
-export default ProductForm;
diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx
index 9782f87..9acdd69 100644
--- a/frontend/src/pages/Home.jsx
+++ b/frontend/src/pages/Home.jsx
@@ -66,7 +66,6 @@ const Home = () => {
location.reload();
}
}
- reloadPage();
useEffect(() => {
const fetchrecomProducts = async () => {
diff --git a/frontend/src/pages/Selling.jsx b/frontend/src/pages/Selling.jsx
index 4fdcde1..d5926fe 100644
--- a/frontend/src/pages/Selling.jsx
+++ b/frontend/src/pages/Selling.jsx
@@ -1,13 +1,14 @@
import { useState, useEffect } from "react";
import { useLocation, Link } from "react-router-dom";
-
-import ProductForm from "../components/ProductForm";
-import { X } from "lucide-react";
+import { X, ChevronLeft, Plus, Trash2 } from "lucide-react";
const Selling = () => {
const [products, setProducts] = useState([]);
const [showForm, setShowForm] = useState(false);
const storedUser = JSON.parse(sessionStorage.getItem("user"));
+ const [categories, setCategories] = useState([]);
+ const [categoryMapping, setCategoryMapping] = useState({});
+ const [selectedCategory, setSelectedCategory] = useState("");
const [editingProduct, setEditingProduct] = useState({
name: "",
@@ -17,6 +18,47 @@ const Selling = () => {
images: [],
});
+ function reloadPage() {
+ var doctTimestamp = new Date(performance.timing.domLoading).getTime();
+ var now = Date.now();
+ var tenSec = 10 * 1000;
+ if (now > doctTimestamp + tenSec) {
+ location.reload();
+ }
+ }
+
+ // Fetch categories from API
+ useEffect(() => {
+ const fetchCategories = async () => {
+ try {
+ const response = await fetch("http://localhost:3030/api/category");
+ if (!response.ok) throw new Error("Failed to fetch categories");
+
+ const responseJson = await response.json();
+ const data = responseJson.data;
+
+ // Create an array of category names for the dropdown
+ const categoryNames = [];
+ const mapping = {};
+
+ // Process the data properly to avoid rendering objects
+ Object.entries(data).forEach(([id, name]) => {
+ // Make sure each category name is a string
+ const categoryName = String(name);
+ categoryNames.push(categoryName);
+ mapping[categoryName] = parseInt(id);
+ });
+
+ setCategories(categoryNames);
+ setCategoryMapping(mapping);
+ } catch (error) {
+ console.error("Error fetching categories:", error);
+ }
+ };
+
+ fetchCategories();
+ }, []);
+
// Simulate fetching products from API/database on component mount
useEffect(() => {
const fetchProducts = async () => {
@@ -30,7 +72,7 @@ const Selling = () => {
"Content-Type": "application/json",
},
body: JSON.stringify({
- userID: storedUser.ID, // Assuming you have userId defined elsewhere in your component
+ userID: storedUser.ID,
}),
},
);
@@ -51,48 +93,137 @@ const Selling = () => {
}, []); // Add userId to dependency array if it might change
// Handle creating or updating a product
- const handleSaveProduct = () => {
- if (editingProduct.id) {
- // Update existing product
- setProducts(
- products.map((p) => (p.id === editingProduct.id ? editingProduct : p)),
- );
- } else {
- // Create new product
- const newProduct = {
- ...editingProduct,
- id: Date.now().toString(), // Generate a temporary ID
- };
- setProducts([...products, newProduct]);
+ const handleSaveProduct = async () => {
+ if (!(editingProduct.categories || []).length) {
+ alert("Please select at least one category");
+ return;
}
- // Reset form and hide it
- setShowForm(false);
- setEditingProduct({
- name: "",
- price: "",
- description: "",
- categories: [],
- images: [],
- });
+ try {
+ const imagePaths = [];
+
+ if (editingProduct.images && editingProduct.images.length > 0) {
+ editingProduct.images.forEach((file) => {
+ const simulatedPath = `/public/uploads/${file.name}`;
+ imagePaths.push(simulatedPath);
+ });
+ }
+
+ const categoryName = (editingProduct.categories || [])[0];
+ const categoryID = categoryMapping[categoryName] || 1;
+
+ const payload = {
+ name: editingProduct.name || "",
+ price: parseFloat(editingProduct.price) || 0,
+ qty: 1,
+ userID: storedUser.ID,
+ description: editingProduct.description || "",
+ category: categoryID,
+ images: imagePaths,
+ };
+
+ console.log("Sending payload:", payload);
+
+ const endpoint = editingProduct.ProductID
+ ? `http://localhost:3030/api/product/update/${editingProduct.ProductID}`
+ : "http://localhost:3030/api/product/addProduct";
+
+ const method = editingProduct.ProductID ? "PUT" : "POST";
+
+ const response = await fetch(endpoint, {
+ method,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(payload),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.text();
+ throw new Error(
+ `${
+ editingProduct.ProductID ? "Failed to update" : "Failed to add"
+ } product: ${errorData}`,
+ );
+ }
+
+ const data = await response.json();
+ console.log("Product saved:", data);
+
+ // Reset form and hide it
+ setShowForm(false);
+ setEditingProduct({
+ name: "",
+ price: "",
+ description: "",
+ categories: [],
+ images: [],
+ });
+
+ // Reload products
+ reloadPage();
+ } catch (error) {
+ console.error("Error saving product:", error);
+ alert(`Error saving product: ${error.message}`);
+ }
};
// Handle product deletion
- const handleDeleteProduct = (productId) => {
- if (window.confirm("Are you sure you want to delete this product?")) {
- setProducts(products.filter((p) => p.id !== productId));
+ const handleDeleteProduct = async (productId) => {
+ try {
+ // Replace with your actual API endpoint
+ const response = await fetch(
+ "http://localhost:3030/api/product/delProduct",
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ userID: storedUser.ID,
+ productID: productId,
+ }),
+ },
+ );
+ console.log("deleteproodidt");
+ reloadPage();
+
+ if (!response.ok) {
+ throw new Error("Network response was not ok");
+ }
+ } catch (error) {
+ console.error("Error fetching products:", error);
+ // You might want to set an error state here
}
};
// Handle editing a product
const handleEditProduct = (product) => {
+ // Convert category ID to category name if needed
+ const categoryName = getCategoryNameById(product.CategoryID);
+
setEditingProduct({
...product,
+ categories: categoryName ? [categoryName] : [],
images: product.images || [], // Ensure images array exists
});
+
setShowForm(true);
};
+ // Helper function to get category name from ID
+ const getCategoryNameById = (categoryId) => {
+ if (!categoryId || !categoryMapping) return null;
+
+ // Find the category name by ID
+ for (const [name, id] of Object.entries(categoryMapping)) {
+ if (id === categoryId) {
+ return name;
+ }
+ }
+ return null;
+ };
+
// Handle adding a new product
const handleAddProduct = () => {
setEditingProduct({
@@ -105,6 +236,49 @@ const Selling = () => {
setShowForm(true);
};
+ const addCategory = () => {
+ if (
+ selectedCategory &&
+ !(editingProduct.categories || []).includes(selectedCategory)
+ ) {
+ setEditingProduct((prev) => ({
+ ...prev,
+ categories: [...(prev.categories || []), selectedCategory],
+ }));
+ setSelectedCategory("");
+ }
+ };
+
+ const removeCategory = (categoryToRemove) => {
+ setEditingProduct((prev) => ({
+ ...prev,
+ categories: (prev.categories || []).filter(
+ (cat) => cat !== categoryToRemove,
+ ),
+ }));
+ };
+
+ const markAsSold = async () => {
+ // This would call an API to move the product to the transaction table
+ try {
+ // API call would go here
+ console.log(
+ "Moving product to transaction table:",
+ editingProduct.ProductID,
+ );
+
+ // Toggle the sold status in the UI
+ setEditingProduct((prev) => ({
+ ...prev,
+ isSold: !prev.isSold,
+ }));
+
+ // You would add your API call here to update the backend
+ } catch (error) {
+ console.error("Error marking product as sold:", error);
+ }
+ };
+
return (
@@ -120,12 +294,279 @@ const Selling = () => {
{showForm ? (
-
setShowForm(false)}
- />
+
+ {/* Back Button */}
+
+
+
+ {editingProduct?.ProductID
+ ? "Edit Your Product"
+ : "List a New Product"}
+
+
+
+ {/* Product Name */}
+
+
+
+ setEditingProduct({
+ ...editingProduct,
+ Name: e.target.value,
+ name: e.target.value,
+ })
+ }
+ className="w-full px-3 py-2 border border-gray-300 focus:border-emerald-500 focus:outline-none"
+ />
+
+
+ {/* Price */}
+
+
+
+ setEditingProduct({
+ ...editingProduct,
+ Price: e.target.value,
+ price: e.target.value,
+ })
+ }
+ className="w-full px-3 py-2 border border-gray-300 focus:border-emerald-500 focus:outline-none"
+ />
+
+
+ {/* Sold Status */}
+
+
+ {editingProduct.isSold && (
+
+ Sold
+
+ )}
+
+
+
+ {/* Categories */}
+
+
+
+
+
+
+
+ {/* Selected Categories */}
+ {(editingProduct.categories || []).length > 0 ? (
+
+ {(editingProduct.categories || []).map((category, index) => (
+
+ {category}
+
+
+ ))}
+
+ ) : (
+
+ Please select at least one category
+
+ )}
+
+
+ {/* Description */}
+
+
+
+
+
+ {/* Image Upload */}
+
+
+
+
{
+ const files = Array.from(e.target.files).slice(0, 5);
+ setEditingProduct((prev) => ({
+ ...prev,
+ images: [...(prev.images || []), ...files].slice(0, 5),
+ }));
+ }}
+ className="hidden"
+ id="image-upload"
+ />
+
+
+ {/* Image previews */}
+ {(editingProduct.images || []).length > 0 && (
+
+
+
+ {editingProduct.images.length}{" "}
+ {editingProduct.images.length === 1 ? "image" : "images"}{" "}
+ selected
+
+
+
+
+ {editingProduct.images.map((img, idx) => (
+
+
})
+
+
+ ))}
+
+
+ )}
+
+ {/* Show current image if editing */}
+ {editingProduct.image_url && (
+
+
Current image:
+
+

+
+
+ )}
+
+
+
+ {/* Actions */}
+
+
+
+ {editingProduct.ProductID && (
+
+ )}
+
+
+
+
) : (
<>
{products.length === 0 ? (
@@ -147,10 +588,7 @@ const Selling = () => {
key={product.ProductID}
to={`/product/${product.ProductID}`}
>
-
+
{product.image_url && product.image_url.length > 0 ? (
![]()
{
${product.Price}
- {product.categories && product.categories.length > 0 && (
+ {product.CategoryID && (
- {product.CategoryID.map((category) => (
-
- {category}
-
- ))}
+
+ {getCategoryNameById(product.CategoryID) ||
+ product.CategoryID}
+
)}
@@ -193,13 +627,21 @@ const Selling = () => {