+
@@ -180,58 +116,28 @@ const Browse = () => {
- {/* Header */}
-
- Browse Products
-
-
- Find what you need from fellow students
-
+
Browse Products
+
Find what you need from fellow students
- {/* Search + Filter */}
- setSearchQuery(e.target.value)}
- className="pl-10 py-6 text-lg border-2 border-blue-200 focus:border-blue-500 rounded-lg"
- />
+ setSearchQuery(e.target.value)} className="pl-10 py-6 text-lg border-2 border-blue-200 focus:border-blue-500 rounded-lg" />
- {/* Filter Button */}
-
+
-
-
- Filter Products
-
-
+ Filter Products
- {/* Category */}
-
-
- {/* Price Range */}
- {/* Sort by */}
-
- setSortOrder(e.target.value)}
- >
+
+ setSortOrder(e.target.value)}>
- {/* Buttons */}
-
-
-
-
-
-
+
+
+
+
- {/* Products Grid */}
- {filteredProducts.length > 0 ? (
- filteredProducts.map((product) => (
-
navigate(`/product/${product.id}`)}
- className="hover:shadow-xl transition-shadow duration-300 border-2 border-blue-100 cursor-pointer"
- >
-
-
-
- {product.name}
-
-
- Category: {product.category}
-
-
-
-
- {product.price}
-
-
- Seller: {product.seller}
-
-
-
-
-
-
- ))
- ) : (
+ {filteredProducts.length > 0 ? filteredProducts.map((product) => (
+
navigate(`/product/${product.id}`)} className="hover:shadow-xl transition-shadow duration-300 border-2 border-blue-100 cursor-pointer">
+
+
+ {product.name}
+ Category: {product.category}
+
+
+ {product.price}
+ Seller: {product.seller}
+
+
+
+
+
+ )) : (
-
- No products found matching your search.
-
+
No products found matching your search.
)}
- {/* Back to Home Button */}
-
+
diff --git a/frontend/src/pages/Cart.jsx b/frontend/src/pages/Cart.jsx
deleted file mode 100644
index cacdf93..0000000
--- a/frontend/src/pages/Cart.jsx
+++ /dev/null
@@ -1,271 +0,0 @@
-import React, { useContext } from "react";
-import { CartContext } from "../components/CartContext";
-import { Link } from "react-router-dom";
-import { motion, AnimatePresence } from "framer-motion";
-
-const Cart = () => {
- const { state, dispatch } = useContext(CartContext);
-
- // Group items by ID and calculate quantities
- const cartItems = state.cart.reduce((acc, item) => {
- const existingItem = acc.find(i => i.id === item.id);
- if (existingItem) {
- existingItem.quantity += 1;
- existingItem.totalPrice = existingItem.quantity * existingItem.price;
- } else {
- acc.push({ ...item, quantity: 1, totalPrice: item.price });
- }
- return acc;
- }, []);
-
- const subtotal = cartItems.reduce((sum, item) => sum + item.totalPrice, 0);
- const shipping = subtotal > 0 ? 99 : 0;
- const total = subtotal + shipping;
-
- const updateQuantity = (id, newQuantity) => {
- if (newQuantity < 1) return;
-
- // Find all items with this ID
- const items = state.cart.filter(item => item.id === id);
-
- if (newQuantity > items.length) {
- // Add more items
- const itemToAdd = items[0];
- dispatch({ type: "ADD_ITEM", payload: itemToAdd });
- } else if (newQuantity < items.length) {
- // Remove items
- const itemsToRemove = items.length - newQuantity;
- for (let i = 0; i < itemsToRemove; i++) {
- const lastIndex = state.cart.map((item, index) => ({ item, index }))
- .filter(({ item }) => item.id === id)
- .pop().index;
- dispatch({ type: "REMOVE_ITEM", payload: lastIndex });
- }
- }
- };
-
- const removeItemCompletely = (id) => {
- const itemsToRemove = state.cart
- .map((item, index) => ({ item, index }))
- .filter(({ item }) => item.id === id)
- .map(({ index }) => index)
- .reverse(); // Remove from end to avoid index issues
-
- itemsToRemove.forEach(index => {
- dispatch({ type: "REMOVE_ITEM", payload: index });
- });
- };
-
- const clearCart = () => {
- dispatch({ type: "CLEAR_CART" });
- };
-
- return (
-
- {/* Header */}
-
-
- Your Shopping Cart
-
-
- {cartItems.length === 0 ? "Your cart is waiting to be filled! ๐๏ธ" : `You have ${cartItems.length} unique item${cartItems.length > 1 ? 's' : ''} in your cart`}
-
-
-
-
- {/* Cart Items */}
-
- {cartItems.length === 0 ? (
-
- ๐
-
- Your cart is empty
-
-
- Discover amazing products and add them to your cart!
-
-
- Start Shopping
-
-
- ) : (
- <>
- {/* Cart Header */}
-
-
- Cart Items ({state.cart.length})
-
-
-
-
- {/* Cart Items List */}
-
-
- {cartItems.map((item, index) => (
-
-
- {/* Product Image */}
-
-

-
-
- {/* Product Info */}
-
-
- {item.name}
-
-
- โน{item.price.toLocaleString()} each
-
-
- {/* Quantity Controls */}
-
-
-
- {item.quantity}
-
-
-
-
- {/* Total Price for this item */}
-
- โน{(item.totalPrice).toLocaleString()}
-
-
-
-
- {/* Remove Button */}
-
-
-
- ))}
-
-
- >
- )}
-
-
- {/* Order Summary - Only show when cart has items */}
- {cartItems.length > 0 && (
-
-
-
- Order Summary
-
-
- {/* Price Breakdown */}
-
-
- Subtotal ({state.cart.length} items)
- โน{subtotal.toLocaleString()}
-
-
- Shipping
- {shipping > 0 ? `โน${shipping}` : "Free"}
-
- {shipping > 0 && subtotal < 999 && (
-
- ๐ Add โน{(999 - subtotal).toLocaleString()} more for free shipping!
-
- )}
-
-
- Total
-
- โน{total.toLocaleString()}
-
-
-
-
-
- {/* Action Buttons */}
-
-
- Proceed to Payment
-
-
- Continue Shopping
-
-
-
- {/* Security Badge */}
-
-
- ๐
- Secure Checkout
-
-
- Your payment information is encrypted and secure
-
-
-
-
- )}
-
-
- {/* Background Decoration */}
-
-
- );
-};
-
-export default Cart;
\ No newline at end of file
diff --git a/frontend/src/pages/Cart.tsx b/frontend/src/pages/Cart.tsx
new file mode 100644
index 0000000..ba0d5c9
--- /dev/null
+++ b/frontend/src/pages/Cart.tsx
@@ -0,0 +1,175 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { motion, AnimatePresence } from "framer-motion";
+import { useCart, CartItem } from "../components/CartContext";
+
+const Cart: React.FC = () => {
+ const { state, updateQuantity, removeItemById, removeItemByIndex, clearCart } = useCart();
+
+ // Group items by ID and calculate quantities
+ const cartItems = state.items.reduce
((acc, item) => {
+ const existing = acc.find(i => i.productId === item.productId);
+ if (existing) {
+ existing.quantity += item.quantity;
+ return acc;
+ } else {
+ acc.push({ ...item });
+ return acc;
+ }
+ }, []);
+
+ const subtotal = cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
+ const shipping = subtotal > 0 ? 99 : 0;
+ const total = subtotal + shipping;
+
+ const handleUpdateQuantity = async (item: CartItem, newQuantity: number) => {
+ if (newQuantity < 1) return;
+ // Prefer backend id when available
+ try {
+ if (item._id) {
+ await updateQuantity({ itemId: item._id, quantity: newQuantity });
+ } else {
+ await updateQuantity({ productId: item.productId, quantity: newQuantity });
+ }
+ } catch (err) {
+ // ignore
+ }
+ };
+
+ const handleRemove = async (item: CartItem) => {
+ if (item._id) {
+ await removeItemById(item._id);
+ } else {
+ // remove first matching by index (fallback)
+ const idx = state.items.findIndex(i => i.productId === item.productId);
+ if (idx !== -1) await removeItemByIndex(idx);
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ Your Shopping Cart
+
+
+ {cartItems.length === 0 ? "Your cart is waiting to be filled! ๐๏ธ" : `You have ${cartItems.length} unique item${cartItems.length > 1 ? 's' : ''} in your cart`}
+
+
+
+
+ {/* Cart Items */}
+
+ {cartItems.length === 0 ? (
+
+ ๐
+
+ Your cart is empty
+
+
+ Discover amazing products and add them to your cart!
+
+
+ Start Shopping
+
+
+ ) : (
+ <>
+ {/* Cart Header */}
+
+
Cart Items ({state.items.length})
+
+
+ {/* Cart Items List */}
+
+
+ {cartItems.map((item) => (
+
+
+ {/* Product Image */}
+
+

+
+ {/* Product Info */}
+
+
{item.name}
+
โน{item.price.toLocaleString()} each
+
+
+
+ {item.quantity}
+
+
+ {/* Total Price for this item */}
+
โน{(item.price * item.quantity).toLocaleString()}
+
+
+ {/* Remove Button */}
+
+
+
+ ))}
+
+
+ >
+ )}
+
+
+ {cartItems.length > 0 && (
+
+
+
Order Summary
+
+
+
+ Subtotal ({state.items.length} items)
+ โน{subtotal.toLocaleString()}
+
+
+ Shipping
+ {shipping > 0 ? `โน${shipping}` : "Free"}
+
+ {shipping > 0 && subtotal < 999 && (
+
๐ Add โน{(999 - subtotal).toLocaleString()} more for free shipping!
+ )}
+
+
+ Total
+ โน{total.toLocaleString()}
+
+
+
+
+
+ Proceed to Payment
+
+
+
Continue Shopping
+
+
+
+ ๐Secure Checkout
+
+
Your payment information is encrypted and secure
+
+
+
+ )}
+
+
+ );
+};
+
+export default Cart;
\ No newline at end of file
diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx
deleted file mode 100644
index b62ca17..0000000
--- a/frontend/src/pages/Home.jsx
+++ /dev/null
@@ -1,187 +0,0 @@
-import React, { useContext } from "react";
-import { CartContext } from "../components/CartContext";
-import { Link } from "react-router-dom";
-import { motion, AnimatePresence } from "framer-motion";
-
-const products = [
- {
- id: 1,
- name: "Wireless Headphones",
- price: 2999,
- image: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "Premium sound quality with noise cancellation"
- },
- {
- id: 2,
- name: "Smartwatch",
- price: 1999,
- image: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "Track your fitness and stay connected"
- },
- {
- id: 3,
- name: "Bluetooth Speaker",
- price: 1499,
- image: "https://images.unsplash.com/photo-1608043152269-423dbba4e7e1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "Portable speaker with crystal clear audio"
- },
- {
- id: 4,
- name: "Gaming Mouse",
- price: 1299,
- image: "https://images.unsplash.com/photo-1527864550417-7fd91fc51a46?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "High precision gaming mouse with RGB lighting"
- },
- {
- id: 5,
- name: "Laptop Stand",
- price: 899,
- image: "https://images.unsplash.com/photo-1586953208448-b95a79798f07?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "Ergonomic aluminum laptop stand"
- },
- {
- id: 6,
- name: "Wireless Keyboard",
- price: 1799,
- image: "https://images.unsplash.com/photo-1587829741301-dc798b83add3?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
- description: "Mechanical wireless keyboard"
- }
-];
-
-const Home = () => {
- const { dispatch, setNotification, notification } = useContext(CartContext);
-
- const addToCart = (product) => {
- dispatch({ type: "ADD_ITEM", payload: product });
- setNotification(product);
- setTimeout(() => setNotification(null), 2000);
- };
-
- return (
-
- {/* Header */}
-
-
- Discover Amazing Products
-
-
- Explore our curated collection of premium electronics and accessories
-
-
-
- {/* Products Grid */}
-
-
- {products.map((product, index) => (
-
- {/* Product Image - Fixed with proper height */}
-
-

-
-
-
- {/* Product Info */}
-
-
-
- {product.name}
-
-
- โน{product.price.toLocaleString()}
-
-
-
-
- {product.description}
-
-
-
addToCart(product)}
- className="w-full bg-gradient-to-r from-blue-600 to-purple-600 text-white py-3 px-6 rounded-xl font-semibold hover:from-blue-700 hover:to-purple-700 transition-all duration-300 transform hover:scale-105 active:scale-95 shadow-lg hover:shadow-xl mt-auto"
- whileHover={{ scale: 1.02 }}
- whileTap={{ scale: 0.98 }}
- >
- Add to Cart ๐
-
-
-
- ))}
-
-
-
- {/* Floating Cart Button */}
-
-
- View Cart
- ๐
-
-
-
- {/* Notification */}
-
- {notification && (
-
-
-

-
-
-
- {notification.name}
-
-
- Added to cart successfully! ๐
-
-
-
- )}
-
-
- {/* Background Decoration */}
-
-
- );
-};
-
-export default Home;
\ No newline at end of file
diff --git a/frontend/src/pages/Payment.jsx b/frontend/src/pages/Payment.jsx
deleted file mode 100644
index a2ae629..0000000
--- a/frontend/src/pages/Payment.jsx
+++ /dev/null
@@ -1,496 +0,0 @@
-import React, { useState, useContext } from "react";
-import { CartContext } from "../components/CartContext";
-import { Link, useNavigate } from "react-router-dom";
-import { motion, AnimatePresence } from "framer-motion";
-
-const Payment = () => {
- const { state, dispatch } = useContext(CartContext);
- const navigate = useNavigate();
- const [paymentMethod, setPaymentMethod] = useState("card");
- const [formData, setFormData] = useState({
- cardNumber: "",
- cardName: "",
- expiry: "",
- cvv: "",
- upiId: "",
- wallet: "",
- bank: ""
- });
- const [isProcessing, setIsProcessing] = useState(false);
-
- const total = state.cart.reduce((sum, item) => sum + item.price, 0);
- const tax = total * 0.18;
- const shipping = 99;
- const finalTotal = total + tax + shipping;
-
- const handleInputChange = (e) => {
- const { name, value } = e.target;
-
- // Format card number with spaces
- if (name === "cardNumber") {
- const formattedValue = value.replace(/\s/g, '').replace(/(\d{4})/g, '$1 ').trim();
- setFormData(prev => ({ ...prev, [name]: formattedValue }));
- return;
- }
-
- // Format expiry date
- if (name === "expiry") {
- const formattedValue = value.replace(/\D/g, '').replace(/(\d{2})(\d)/, '$1/$2');
- setFormData(prev => ({ ...prev, [name]: formattedValue }));
- return;
- }
-
- setFormData(prev => ({ ...prev, [name]: value }));
- };
-
- const handlePayment = async (e) => {
- e.preventDefault();
- setIsProcessing(true);
-
- // Simulate payment processing
- setTimeout(() => {
- setIsProcessing(false);
- dispatch({ type: "CLEAR_CART" });
- navigate("/success");
- }, 3000);
- };
-
- const paymentMethods = [
- { id: "card", name: "Credit/Debit Card", icon: "๐ณ", color: "from-blue-500 to-blue-700" },
- { id: "upi", name: "UPI Payment", icon: "๐ฑ", color: "from-green-500 to-green-700" },
- { id: "wallet", name: "Wallet", icon: "๐", color: "from-purple-500 to-purple-700" },
- { id: "netbanking", name: "Net Banking", icon: "๐ฆ", color: "from-orange-500 to-orange-700" },
- ];
-
- const wallets = [
- { id: "paytm", name: "Paytm", icon: "๐ฒ" },
- { id: "phonepe", name: "PhonePe", icon: "๐ณ" },
- { id: "amazonpay", name: "Amazon Pay", icon: "๐" },
- { id: "other", name: "Other Wallets", icon: "๐ฐ" },
- ];
-
- const banks = [
- { id: "sbi", name: "State Bank of India" },
- { id: "hdfc", name: "HDFC Bank" },
- { id: "icici", name: "ICICI Bank" },
- { id: "axis", name: "Axis Bank" },
- { id: "kotak", name: "Kotak Mahindra Bank" },
- { id: "other", name: "Other Banks" },
- ];
-
- return (
-
-
- {/* Header */}
-
-
- Secure Checkout
-
-
- Complete your purchase with secure payment
-
-
-
-
- {/* Payment Methods & Form */}
-
- {/* Payment Method Selection */}
-
-
-
- Select Payment Method
-
-
- {paymentMethods.map((method) => (
-
setPaymentMethod(method.id)}
- className={`p-4 rounded-xl border-2 transition-all duration-200 ${
- paymentMethod === method.id
- ? `border-blue-500 bg-gradient-to-r ${method.color} text-white shadow-lg`
- : "border-gray-200 hover:border-gray-300 bg-white text-gray-700"
- }`}
- whileHover={{ scale: 1.02 }}
- whileTap={{ scale: 0.98 }}
- >
-
- {method.icon}
- {method.name}
-
-
- ))}
-
-
-
-
- {/* Payment Form */}
-
-
-
- {paymentMethod === "card" && "Card Details"}
- {paymentMethod === "upi" && "UPI Payment"}
- {paymentMethod === "wallet" && "Digital Wallet"}
- {paymentMethod === "netbanking" && "Net Banking"}
-
-
-
-
-
-
-
- {/* Order Summary */}
-
-
-
- Order Summary
-
-
- {/* Items List */}
-
- {state.cart.map((item, index) => (
-
-

-
-
- {item.name}
-
-
โน{item.price.toLocaleString()}
-
-
- ))}
-
-
- {/* Price Breakdown */}
-
-
- Subtotal ({state.cart.length} items)
- โน{total.toLocaleString()}
-
-
- Shipping
- โน{shipping}
-
-
- Tax (18%)
- โน{tax.toLocaleString()}
-
-
-
- Total Amount
-
- โน{finalTotal.toLocaleString()}
-
-
-
-
-
- {/* Back to Cart */}
-
- โ Back to Cart
-
-
-
-
-
- {/* Security Badges */}
-
- {[
- { icon: "๐", text: "256-bit SSL Secure" },
- { icon: "๐ก๏ธ", text: "PCI DSS Compliant" },
- { icon: "๐ฐ", text: "100% Money Back" },
- { icon: "โก", text: "Instant Refund" }
- ].map((badge, index) => (
-
- {badge.icon}
- {badge.text}
-
- ))}
-
-
-
- {/* Background Decoration */}
-
-
- );
-};
-
-export default Payment;
\ No newline at end of file
diff --git a/frontend/src/pages/Payment.tsx b/frontend/src/pages/Payment.tsx
new file mode 100644
index 0000000..f56b4ce
--- /dev/null
+++ b/frontend/src/pages/Payment.tsx
@@ -0,0 +1,222 @@
+import React, { useState } from "react";
+import { useNavigate, Link } from "react-router-dom";
+import { motion, AnimatePresence } from "framer-motion";
+import { useCart } from "../components/CartContext";
+
+const Payment: React.FC = () => {
+ const { state, clearCart } = useCart();
+ const navigate = useNavigate();
+ const [paymentMethod, setPaymentMethod] = useState<"card" | "upi" | "wallet" | "netbanking">("card");
+ const [formData, setFormData] = useState({
+ cardNumber: "",
+ cardName: "",
+ expiry: "",
+ cvv: "",
+ upiId: "",
+ wallet: "",
+ bank: "",
+ });
+ const [isProcessing, setIsProcessing] = useState(false);
+
+ const total = state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
+ const tax = Math.round(total * 0.18);
+ const shipping = total > 0 ? 99 : 0;
+ const finalTotal = total + tax + shipping;
+
+ const handleInputChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target;
+ if (name === "cardNumber") {
+ const formattedValue = value.replace(/\s/g, "").replace(/(\d{4})/g, "$1 ").trim();
+ setFormData(prev => ({ ...prev, [name]: formattedValue }));
+ return;
+ }
+ if (name === "expiry") {
+ const formattedValue = value.replace(/\D/g, "").replace(/(\d{2})(\d)/, "$1/$2");
+ setFormData(prev => ({ ...prev, [name]: formattedValue }));
+ return;
+ }
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handlePayment = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsProcessing(true);
+ // Simulate payment processing
+ setTimeout(async () => {
+ setIsProcessing(false);
+ await clearCart();
+ navigate("/success", { replace: true });
+ }, 2200);
+ };
+
+ return (
+
+
+
+ Secure Checkout
+ Complete your purchase with secure payment
+
+
+
+
+
+
+
Select Payment Method
+
+ {[
+ { id: "card", name: "Credit/Debit Card", icon: "๐ณ", color: "from-blue-500 to-blue-700" },
+ { id: "upi", name: "UPI Payment", icon: "๐ฑ", color: "from-green-500 to-green-700" },
+ { id: "wallet", name: "Wallet", icon: "๐", color: "from-purple-500 to-purple-700" },
+ { id: "netbanking", name: "Net Banking", icon: "๐ฆ", color: "from-orange-500 to-orange-700" },
+ ].map(method => (
+
+ ))}
+
+
+
+
+
+
+
{paymentMethod === "card" ? "Card Details" : paymentMethod === "upi" ? "UPI Payment" : paymentMethod === "wallet" ? "Digital Wallet" : "Net Banking"}
+
+
+
+
+
+
+
+
+ Order Summary
+
+
+ {state.items.map((item, idx) => (
+
+

+
+
{item.name}
+
โน{(item.price * item.quantity).toLocaleString()}
+
+
+ ))}
+
+
+
+
Subtotal ({state.items.length} items)โน{total.toLocaleString()}
+
Shippingโน{shipping}
+
Tax (18%)โน{tax.toLocaleString()}
+
Total Amountโน{finalTotal.toLocaleString()}
+
+
+ โ Back to Cart
+
+
+
+
+
+ );
+};
+
+export default Payment;
\ No newline at end of file
diff --git a/frontend/src/pages/ProductDetail.tsx b/frontend/src/pages/ProductDetail.tsx
index d004321..a5a4208 100644
--- a/frontend/src/pages/ProductDetail.tsx
+++ b/frontend/src/pages/ProductDetail.tsx
@@ -2,114 +2,112 @@ import { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { Button } from "../components/ui/button";
import { ArrowLeft } from "lucide-react";
-
-// Dummy product data
-const allProducts = [
- {
- id: 1,
- title: "Calculus Textbook",
- price: 45,
- description: "A comprehensive calculus guide ideal for engineering and science students. Covers differential, integral, and multivariable calculus with step-by-step examples and university-level exercises. Perfect for semester preparation and concept building.",
- images: ["/book1.jpg"],
- seller: { name: "Shubham", Used:"1.2 year", email: "xyz@gmail.com" },
- auction: { isAuction: true, highestBid: 50, minIncrement: 5, timeLeft: "10h 45m" }
- },
- {
- id: 2,
- title: "Laptop Stand",
- price: 20,
- description: "Ergonomic aluminum laptop stand that improves posture and cooling. Lightweight, foldable, and ideal for long study sessions in hostels or libraries. Adjustable height ensures comfort while typing or attending online classes.",
- images: ["/laptop1.jpg"],
- seller: { name: "Steve Rogers", Used:" 12 months", email: "xyz@gmail.com" },
- auction: { isAuction: false }
- },
-
- {
- id: 3,
- title: "Study Notes - Physics",
- price: 10,
- description: "Handwritten physics notes neatly organized chapter-wise, covering Mechanics, Thermodynamics, Electromagnetism, and Modern Physics. Simplified concepts and formulas for quick revision and last-minute preparation.",
- images: ["/notes1.jpg"],
- seller: { name: "Bruce Banner", Used: "1 year", email: "xyz@gmail.com" },
- auction: { isAuction: false }
- },
-
- {
- id: 4,
- title: "Scientific Calculator",
- price: 30,
- description: "Casio fx-991EX ClassWiz calculator with 552 functions. Ideal for engineering students โ handles matrices, statistics, and complex equations with ease. Excellent condition with original case and battery included.",
- images: ["/calc1.jpg"],
- seller: { name: "Peter", Used: "6 months", email: "xyz@gmail.com" },
- auction: { isAuction: true, highestBid: 35, minIncrement: 5, timeLeft: "6h 15m" }
- },
-
- {
- id: 5,
- title: "C++ Programming Book",
- price: 499,
- description: "โProgramming in C++โ by E. Balagurusamy. A complete guide to mastering Object-Oriented Programming. Covers basics to advanced topics like inheritance and polymorphism with solved examples and practice problems.",
- images: ["/book2.jpg"],
- seller: { name: "Thor", Used: "2 years", email: "xyz@gmail.com" },
- auction: { isAuction: true, highestBid: 550, minIncrement: 50, timeLeft: "12h 30m" }
- },
-
- {
- id: 6,
- title: "Desk Lamp",
- price: 15,
- description: "Adjustable LED desk lamp with 3 brightness levels. Energy-efficient, flexible design suitable for night study sessions. USB rechargeable and portable โ perfect for hostel or dorm desk setups.",
- images: ["/lamp1.jpg"],
- seller: { name: "Tony Stark", Used:"2 year", email: "xyz@gmail.com" },
- auction: { isAuction: true, highestBid: 18, minIncrement: 2, timeLeft: "5h 20m" }
- },
-
- {
- id: 7,
- title: "Organic Chemistry Notes",
- price: 25,
- description: "Comprehensive handwritten organic chemistry notes with reaction mechanisms, named reactions, and visual memory aids. Perfect for college exams and quick conceptual revision.",
- images: ["/notes2.jpg"],
- seller: { name: "Wanda", Used:"1 year", email: "xyz@gmail.com" },
- auction: { isAuction: false }
- },
-
- {
- id: 8,
- title: "Bluetooth Headphones",
- price: 150,
- description: "High-quality wireless headphones with deep bass, noise isolation, and 20-hour battery life. Ideal for music, online classes, or movies. Lightweight design with soft ear cushions for comfort.",
- images: ["/headphones.jpg"],
- seller: { name: "Natasha", Used: "8 months", email: "xyz@gmail.com" },
- auction: { isAuction: true, highestBid: 160, minIncrement: 10, timeLeft: "3h 45m" }
- }
+import { useCart } from "../components/CartContext";
+
+/**
+ * Updated ProductDetail.tsx
+ * - Converted to TSX
+ * - "Buy Now" adds to cart via CartContext and navigates to /payment
+ * - Auction "Place Bid" attempts to call backend auction endpoint (POST /api/auctions/:id/bid)
+ * and falls back to local alert if backend not reachable.
+ */
+
+type AuctionInfo = {
+ isAuction: boolean;
+ highestBid?: number;
+ minIncrement?: number;
+ timeLeft?: string;
+};
+
+type Product = {
+ id: number;
+ title: string;
+ price: number;
+ description: string;
+ images: string[];
+ seller: { name: string; Used?: string; email?: string };
+ auction: AuctionInfo;
+};
+
+const allProducts: Product[] = [
+ { id: 1, title: "Calculus Textbook", price: 45, description: "A comprehensive calculus guide ideal for engineering and science students. Covers differential, integral, and multivariable calculus with step-by-step examples and university-level exercises. Perfect for semester preparation and concept building.", images: ["/book1.jpg"], seller: { name: "Shubham", Used: "1.2 year", email: "xyz@gmail.com" }, auction: { isAuction: true, highestBid: 50, minIncrement: 5, timeLeft: "10h 45m" } },
+ { id: 2, title: "Laptop Stand", price: 20, description: "Ergonomic aluminum laptop stand that improves posture and cooling. Lightweight, foldable, and ideal for long study sessions in hostels or libraries. Adjustable height ensures comfort while typing or attending online classes.", images: ["/laptop1.jpg"], seller: { name: "Steve Rogers", Used: "12 months", email: "xyz@gmail.com" }, auction: { isAuction: false } },
+ { id: 3, title: "Study Notes - Physics", price: 10, description: "Handwritten physics notes neatly organized chapter-wise, covering Mechanics, Thermodynamics, Electromagnetism, and Modern Physics. Simplified concepts and formulas for quick revision and last-minute preparation.", images: ["/notes1.jpg"], seller: { name: "Bruce Banner", Used: "1 year", email: "xyz@gmail.com" }, auction: { isAuction: false } },
+ { id: 4, title: "Scientific Calculator", price: 30, description: "Casio fx-991EX ClassWiz calculator with 552 functions. Ideal for engineering students โ handles matrices, statistics, and complex equations with ease. Excellent condition with original case and battery included.", images: ["/calc1.jpg"], seller: { name: "Peter", Used: "6 months", email: "xyz@gmail.com" }, auction: { isAuction: true, highestBid: 35, minIncrement: 5, timeLeft: "6h 15m" } },
+ { id: 5, title: "C++ Programming Book", price: 499, description: "โProgramming in C++โ by E. Balagurusamy. A complete guide to mastering Object-Oriented Programming. Covers basics to advanced topics like inheritance and polymorphism with solved examples and practice problems.", images: ["/book2.jpg"], seller: { name: "Thor", Used: "2 years", email: "xyz@gmail.com" }, auction: { isAuction: true, highestBid: 550, minIncrement: 50, timeLeft: "12h 30m" } },
+ { id: 6, title: "Desk Lamp", price: 15, description: "Adjustable LED desk lamp with 3 brightness levels. Energy-efficient, flexible design suitable for night study sessions. USB rechargeable and portable โ perfect for hostel or dorm desk setups.", images: ["/lamp1.jpg"], seller: { name: "Tony Stark", Used: "2 year", email: "xyz@gmail.com" }, auction: { isAuction: true, highestBid: 18, minIncrement: 2, timeLeft: "5h 20m" } },
+ { id: 7, title: "Organic Chemistry Notes", price: 25, description: "Comprehensive handwritten organic chemistry notes with reaction mechanisms, named reactions, and visual memory aids. Perfect for college exams and quick conceptual revision.", images: ["/notes2.jpg"], seller: { name: "Wanda", Used: "1 year", email: "xyz@gmail.com" }, auction: { isAuction: false } },
+ { id: 8, title: "Bluetooth Headphones", price: 150, description: "High-quality wireless headphones with deep bass, noise isolation, and 20-hour battery life. Ideal for music, online classes, or movies. Lightweight design with soft ear cushions for comfort.", images: ["/headphones.jpg"], seller: { name: "Natasha", Used: "8 months", email: "xyz@gmail.com" }, auction: { isAuction: true, highestBid: 160, minIncrement: 10, timeLeft: "3h 45m" } }
];
export default function ProductDetailsPage() {
- const { id } = useParams();
+ const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
+ const { addItem } = useCart();
- // Find product by ID
- const product = allProducts.find((p) => p.id === parseInt(id));
+ const product = allProducts.find((p) => p.id === Number(id));
if (!product) return Product not found.
;
const [mainImage, setMainImage] = useState(product.images[0]);
+ const [bid, setBid] = useState(product.auction.isAuction ? (product.auction.highestBid || 0) + (product.auction.minIncrement || 1) : 0);
+ const [isBidding, setIsBidding] = useState(false);
- // Local bid state
- const [bid, setBid] = useState(
- product.auction.isAuction ? product.auction.highestBid + product.auction.minIncrement : 0
- );
+ const placeBid = async () => {
+ if (!product.auction.isAuction) {
+ alert("This item is not in auction.");
+ return;
+ }
+ const min = (product.auction.highestBid || 0) + (product.auction.minIncrement || 1);
+ if (bid < min) {
+ alert(`Bid must be at least โน${min}`);
+ return;
+ }
- const placeBid = () => {
- if (bid >= product.auction.highestBid + product.auction.minIncrement) {
- alert(`Bid of โน${bid} placed successfully!`);
- product.auction.highestBid = bid;
- setBid(bid + product.auction.minIncrement);
- } else {
- alert(`Bid must be at least โน${product.auction.highestBid + product.auction.minIncrement}`);
+ setIsBidding(true);
+ try {
+ // Try to hit backend auction endpoint
+ const token = localStorage.getItem("accessToken");
+ const headers: any = { "Content-Type": "application/json" };
+ if (token) headers["Authorization"] = `Bearer ${token}`;
+
+ const res = await fetch(`/api/auctions/${product.id}/bid`, {
+ method: "POST",
+ headers,
+ body: JSON.stringify({ amount: bid }),
+ });
+
+ if (res.ok) {
+ alert(`Bid of โน${bid} placed successfully!`);
+ // optimistically update local product highestBid
+ product.auction.highestBid = bid;
+ setBid((bid || 0) + (product.auction.minIncrement || 1));
+ } else {
+ // backend responded with error
+ const errText = await res.text();
+ alert(`Failed to place bid: ${errText || res.statusText}`);
+ }
+ } catch (err) {
+ // Fallback offline behavior
+ alert(`Could not reach server. Local simulated bid of โน${bid} placed.`);
+ product.auction.highestBid = bid;
+ setBid((bid || 0) + (product.auction.minIncrement || 1));
+ } finally {
+ setIsBidding(false);
}
};
+ const handleBuyNow = async () => {
+ // add to cart via context then navigate to payment
+ await addItem({
+ productId: String(product.id),
+ name: product.title,
+ price: product.price,
+ quantity: 1,
+ image: product.images?.[0],
+ });
+ navigate("/payment");
+ };
+
return (
@@ -119,65 +117,40 @@ export default function ProductDetailsPage() {
- {/* Back Button */}
-