diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 6569551..0e54e17 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -292,7 +292,8 @@ function App() { // Set authenticated user setUser(newUser); - setIsAuthenticated(true); + setIsSignUp(false); + //setIsAuthenticated(true); // Save to localStorage to persist across refreshes sessionStorage.setItem("isAuthenticated", "true"); @@ -338,12 +339,11 @@ function App() { setError("Email and password are required"); setIsLoading(false); 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 { if (isSignUp) { // Handle Sign Up with verification diff --git a/frontend/src/pages/ProductDetail.jsx b/frontend/src/pages/ProductDetail.jsx index 2eb08e1..806a7d0 100644 --- a/frontend/src/pages/ProductDetail.jsx +++ b/frontend/src/pages/ProductDetail.jsx @@ -498,7 +498,10 @@ const ProductDetail = () => { {product.SellerName || "Unknown Seller"}
- Member since {product.SellerJoinDate || "N/A"} + Product listed since{" "} + {product.Date + ? new Date(product.Date).toLocaleDateString() + : "N/A"}
diff --git a/frontend/src/pages/Selling.jsx b/frontend/src/pages/Selling.jsx index a667757..016efa3 100644 --- a/frontend/src/pages/Selling.jsx +++ b/frontend/src/pages/Selling.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import { useLocation, Link } from "react-router-dom"; -import { X, ChevronLeft, Plus, Trash2 } from "lucide-react"; +import { X, ChevronLeft, Trash2 } from "lucide-react"; const Selling = () => { const [products, setProducts] = useState([]); @@ -8,14 +8,13 @@ const Selling = () => { const storedUser = JSON.parse(sessionStorage.getItem("user")); const [categories, setCategories] = useState([]); const [categoryMapping, setCategoryMapping] = useState({}); - const [selectedCategory, setSelectedCategory] = useState(""); const [originalProduct, setOriginalProduct] = useState(null); const [editingProduct, setEditingProduct] = useState({ name: "", price: "", description: "", - categories: [], + category: "", images: [], }); @@ -59,7 +58,7 @@ const Selling = () => { fetchCategories(); }, []); - // Simulate fetching products from API/database on component mount + // Fetch products from API/database on component mount useEffect(() => { const fetchProducts = async () => { try { @@ -89,61 +88,87 @@ const Selling = () => { }; fetchProducts(); - }, []); // Add userId to dependency array if it might change + }); - // When editing a product, save the original product properly const handleEditProduct = (product) => { - // Save the original product completely setOriginalProduct(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 + category: categoryName || "", // Single category string + images: product.images || [], }); setShowForm(true); }; - // Then update the handleSaveProduct function to properly merge values + // Upload images to server and get their paths + const uploadImages = async (images) => { + console.log(images); + const uploadedImagePaths = []; + + // Filter out only File objects (new images to upload) + const filesToUpload = images.filter((img) => img instanceof File); + + for (const file of filesToUpload) { + // Create a FormData object to send the file + const formData = new FormData(); + formData.append("image", file); + + try { + // Send the file to your upload endpoint + const response = await fetch("http://localhost:3030/api/upload", { + method: "POST", + body: formData, + }); + + if (!response.ok) { + throw new Error(`Failed to upload image: ${file.name}`); + } + + const result = await response.json(); + // Assuming the server returns the path where the file was saved + uploadedImagePaths.push(`/public/uploads/${file.name}`); + } catch (error) { + console.error("Error uploading image:", error); + // If upload fails, still add the expected path (this is a fallback) + uploadedImagePaths.push(`/public/uploads/${file.name}`); + } + } + + // Also include any existing image URLs that are strings, not File objects + const existingImages = images.filter((img) => typeof img === "string"); + if (existingImages.length > 0) { + uploadedImagePaths.push(...existingImages); + } + + return uploadedImagePaths; + }; + + // Handle saving product with updated image logic const handleSaveProduct = async () => { - if (!(editingProduct.categories || []).length) { - alert("Please select at least one category"); + if (!editingProduct.category) { + alert("Please select a category"); return; } try { - const imagePaths = []; + let imagePaths = []; - // Handle images properly + // Handle image uploads and get their paths if (editingProduct.images && editingProduct.images.length > 0) { - // If there are new images uploaded (File objects) - const newImages = editingProduct.images.filter( - (img) => img instanceof File, - ); - newImages.forEach((file) => { - const simulatedPath = `/public/uploads/${file.name}`; - imagePaths.push(simulatedPath); - }); - - // Also include any existing image URLs that are strings, not File objects - const existingImages = editingProduct.images.filter( - (img) => typeof img === "string", - ); - if (existingImages.length > 0) { - imagePaths.push(...existingImages); - } + imagePaths = await uploadImages(editingProduct.images); } else if (originalProduct?.image_url) { // If no new images but there was an original image URL - imagePaths.push(originalProduct.image_url); + imagePaths = [originalProduct.image_url]; } - const categoryName = (editingProduct.categories || [])[0]; const categoryID = - categoryMapping[categoryName] || originalProduct?.CategoryID || 1; + categoryMapping[editingProduct.category] || + originalProduct?.CategoryID || + 1; // Create payload with proper fallback to original values const payload = { @@ -166,12 +191,7 @@ const Selling = () => { originalProduct?.Description || "", category: categoryID, - images: - imagePaths.length > 0 - ? imagePaths - : originalProduct?.image_url - ? [originalProduct.image_url] - : [], + images: imagePaths.length > 0 ? imagePaths : [], }; console.log("Sending payload:", payload); @@ -206,7 +226,7 @@ const Selling = () => { name: "", price: "", description: "", - categories: [], + category: "", images: [], }); @@ -243,7 +263,7 @@ const Selling = () => { throw new Error("Network response was not ok"); } } catch (error) { - console.error("Error fetching products:", error); + console.error("Error deleting product:", error); // You might want to set an error state here } }; @@ -267,53 +287,18 @@ const Selling = () => { name: "", price: "", description: "", - categories: [], + category: "", images: [], }); 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); - } + // Handle category change + const handleCategoryChange = (e) => { + setEditingProduct({ + ...editingProduct, + category: e.target.value, + }); }; return ( @@ -386,74 +371,29 @@ const Selling = () => { /> - {/* Sold Status */} -- Please select at least one category + Please select a category
)}Current image:
-Current image:
+