From aa8fe659d35409519a52ac9aff2f9567ae96767e Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 28 Oct 2025 18:33:26 +0530 Subject: [PATCH 1/3] Added Raman Singh contribution file --- 007 Student Contributions/RamanSingh/index.html | Bin 0 -> 46 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 007 Student Contributions/RamanSingh/index.html diff --git a/007 Student Contributions/RamanSingh/index.html b/007 Student Contributions/RamanSingh/index.html new file mode 100644 index 0000000000000000000000000000000000000000..50067cc009f80a367d746a4f7a6cb7049b090c3c GIT binary patch literal 46 scmezW&xRp`!H~g@!Gj@{AqNQa859`8fn*Voox)(lpbwSjW#D1}0R1`$h5!Hn literal 0 HcmV?d00001 From 9d3f8007930ff1b936d23ba0e75e16e0c867f515 Mon Sep 17 00:00:00 2001 From: Raman Date: Wed, 29 Oct 2025 01:15:37 +0530 Subject: [PATCH 2/3] Updated index.html with new content --- 007 Student Contributions/RamanSingh/index.html | Bin 46 -> 614 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/007 Student Contributions/RamanSingh/index.html b/007 Student Contributions/RamanSingh/index.html index 50067cc009f80a367d746a4f7a6cb7049b090c3c..6aeee2f13c5a34fc2159b78119c56bd953d651b0 100644 GIT binary patch literal 614 zcmZvaOHaZ;6ot>)#Q#uP3+T>8W#c0*L=Eb~Woa8*Qz?N8@z1N@87PlPnoRH9dmeZ0 z%@7MD}c*0~5Je=%A) ziV1OwR-&2aA!C(Sd~em&#YXE8Zvx9IW-L?^Ej3k*QbWeNHQhAiy&AOyl@S;0GV*4n z{JT4qQ oXJ?0+b;t`C>4E5;%7PvuCOxC}L7al>1s5x}XWKtVo87*mKW3I)>i_@% literal 46 scmezW&xRp`!H~g@!Gj@{AqNQa859`8fn*Voox)(lpbwSjW#D1}0R1`$h5!Hn From 7d94de66fd4586834f396395806d986740894b19 Mon Sep 17 00:00:00 2001 From: ramansingh8747 Date: Mon, 16 Mar 2026 10:12:52 +0530 Subject: [PATCH 3/3] Add complete MERN eCommerce demo app with cart and checkout --- 5 MERN/1-ecommerce-app/README.md | 36 ++++ 5 MERN/1-ecommerce-app/backend/package.json | 15 ++ 5 MERN/1-ecommerce-app/backend/src/data.js | 56 ++++++ 5 MERN/1-ecommerce-app/backend/src/server.js | 145 ++++++++++++++ 5 MERN/1-ecommerce-app/frontend/index.html | 12 ++ 5 MERN/1-ecommerce-app/frontend/package.json | 19 ++ 5 MERN/1-ecommerce-app/frontend/src/App.jsx | 159 +++++++++++++++ .../frontend/src/components/CartPanel.jsx | 48 +++++ .../frontend/src/components/CheckoutModal.jsx | 55 +++++ .../frontend/src/components/ProductCard.jsx | 18 ++ 5 MERN/1-ecommerce-app/frontend/src/main.jsx | 10 + .../1-ecommerce-app/frontend/src/styles.css | 189 ++++++++++++++++++ .../1-ecommerce-app/frontend/vite.config.js | 9 + 13 files changed, 771 insertions(+) create mode 100644 5 MERN/1-ecommerce-app/README.md create mode 100644 5 MERN/1-ecommerce-app/backend/package.json create mode 100644 5 MERN/1-ecommerce-app/backend/src/data.js create mode 100644 5 MERN/1-ecommerce-app/backend/src/server.js create mode 100644 5 MERN/1-ecommerce-app/frontend/index.html create mode 100644 5 MERN/1-ecommerce-app/frontend/package.json create mode 100644 5 MERN/1-ecommerce-app/frontend/src/App.jsx create mode 100644 5 MERN/1-ecommerce-app/frontend/src/components/CartPanel.jsx create mode 100644 5 MERN/1-ecommerce-app/frontend/src/components/CheckoutModal.jsx create mode 100644 5 MERN/1-ecommerce-app/frontend/src/components/ProductCard.jsx create mode 100644 5 MERN/1-ecommerce-app/frontend/src/main.jsx create mode 100644 5 MERN/1-ecommerce-app/frontend/src/styles.css create mode 100644 5 MERN/1-ecommerce-app/frontend/vite.config.js diff --git a/5 MERN/1-ecommerce-app/README.md b/5 MERN/1-ecommerce-app/README.md new file mode 100644 index 0000000..7276f81 --- /dev/null +++ b/5 MERN/1-ecommerce-app/README.md @@ -0,0 +1,36 @@ +# MERN eCommerce App + +This is a complete MERN-style eCommerce demo with: + +- Product listing +- Cart management (add/update/remove) +- Checkout flow with customer details + +## Project structure + +- `backend` - Express API +- `frontend` - React + Vite UI + +## Run locally + +### 1) Backend + +```bash +cd backend +npm install +npm run start +``` + +Server starts on `http://localhost:5000`. + +### 2) Frontend + +```bash +cd frontend +npm install +npm run dev +``` + +App runs on `http://localhost:5173` and uses backend API at `http://localhost:5000/api`. + +To customize API URL, set `VITE_API_URL` in a `.env` file. diff --git a/5 MERN/1-ecommerce-app/backend/package.json b/5 MERN/1-ecommerce-app/backend/package.json new file mode 100644 index 0000000..62b6afd --- /dev/null +++ b/5 MERN/1-ecommerce-app/backend/package.json @@ -0,0 +1,15 @@ +{ + "name": "mern-ecommerce-backend", + "version": "1.0.0", + "description": "Backend API for MERN eCommerce demo", + "main": "src/server.js", + "type": "module", + "scripts": { + "start": "node src/server.js", + "dev": "node --watch src/server.js" + }, + "dependencies": { + "cors": "^2.8.5", + "express": "^4.19.2" + } +} diff --git a/5 MERN/1-ecommerce-app/backend/src/data.js b/5 MERN/1-ecommerce-app/backend/src/data.js new file mode 100644 index 0000000..31ab5ff --- /dev/null +++ b/5 MERN/1-ecommerce-app/backend/src/data.js @@ -0,0 +1,56 @@ +export const products = [ + { + id: 'p1', + name: 'Wireless Headphones', + description: 'Noise-cancelling over-ear headphones with 40hr battery life.', + image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400', + category: 'Audio', + price: 99.99, + stock: 12 + }, + { + id: 'p2', + name: 'Smart Watch', + description: 'Fitness tracking, heart-rate monitor, and message notifications.', + image: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400', + category: 'Wearables', + price: 149.99, + stock: 10 + }, + { + id: 'p3', + name: 'Mechanical Keyboard', + description: 'RGB backlit keyboard with tactile switches and USB-C.', + image: 'https://images.unsplash.com/photo-1511467687858-23d96c32e4ae?w=400', + category: 'Accessories', + price: 79.99, + stock: 15 + }, + { + id: 'p4', + name: '4K Monitor', + description: '27-inch UHD display with HDR support and slim bezels.', + image: 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=400', + category: 'Displays', + price: 299.99, + stock: 8 + }, + { + id: 'p5', + name: 'Portable SSD 1TB', + description: 'High-speed external storage with USB 3.2 Gen2 support.', + image: 'https://images.unsplash.com/photo-1597872200969-2b65d56bd16b?w=400', + category: 'Storage', + price: 119.99, + stock: 20 + }, + { + id: 'p6', + name: 'Gaming Mouse', + description: 'Ultra-light ergonomic mouse with customizable DPI settings.', + image: 'https://images.unsplash.com/photo-1629429407756-1f5c7a6f37ce?w=400', + category: 'Accessories', + price: 49.99, + stock: 25 + } +]; diff --git a/5 MERN/1-ecommerce-app/backend/src/server.js b/5 MERN/1-ecommerce-app/backend/src/server.js new file mode 100644 index 0000000..0ca8417 --- /dev/null +++ b/5 MERN/1-ecommerce-app/backend/src/server.js @@ -0,0 +1,145 @@ +import express from 'express'; +import cors from 'cors'; +import { products } from './data.js'; + +const app = express(); +const PORT = process.env.PORT || 5000; + +app.use(cors()); +app.use(express.json()); + +const cart = new Map(); + +const getCartDetails = () => { + const items = [...cart.values()].map((entry) => ({ + id: entry.product.id, + name: entry.product.name, + price: entry.product.price, + quantity: entry.quantity, + subtotal: Number((entry.product.price * entry.quantity).toFixed(2)) + })); + + const subtotal = Number(items.reduce((sum, item) => sum + item.subtotal, 0).toFixed(2)); + const tax = Number((subtotal * 0.1).toFixed(2)); + const total = Number((subtotal + tax).toFixed(2)); + + return { + items, + summary: { + itemCount: items.reduce((sum, item) => sum + item.quantity, 0), + subtotal, + tax, + total + } + }; +}; + +app.get('/api/health', (_, res) => { + res.json({ status: 'ok' }); +}); + +app.get('/api/products', (_, res) => { + res.json(products); +}); + +app.get('/api/cart', (_, res) => { + res.json(getCartDetails()); +}); + +app.post('/api/cart', (req, res) => { + const { productId, quantity = 1 } = req.body; + const parsedQuantity = Number(quantity); + + if (!productId || Number.isNaN(parsedQuantity) || parsedQuantity <= 0) { + return res.status(400).json({ message: 'productId and positive quantity are required' }); + } + + const product = products.find((item) => item.id === productId); + if (!product) { + return res.status(404).json({ message: 'Product not found' }); + } + + const current = cart.get(productId)?.quantity || 0; + const updatedQty = current + parsedQuantity; + + if (updatedQty > product.stock) { + return res.status(400).json({ message: 'Quantity exceeds stock' }); + } + + cart.set(productId, { product, quantity: updatedQty }); + return res.status(201).json(getCartDetails()); +}); + +app.patch('/api/cart/:productId', (req, res) => { + const { productId } = req.params; + const { quantity } = req.body; + const parsedQuantity = Number(quantity); + + if (Number.isNaN(parsedQuantity) || parsedQuantity < 0) { + return res.status(400).json({ message: 'quantity must be zero or a positive number' }); + } + + const existing = cart.get(productId); + if (!existing) { + return res.status(404).json({ message: 'Cart item not found' }); + } + + if (parsedQuantity === 0) { + cart.delete(productId); + return res.json(getCartDetails()); + } + + if (parsedQuantity > existing.product.stock) { + return res.status(400).json({ message: 'Quantity exceeds stock' }); + } + + cart.set(productId, { ...existing, quantity: parsedQuantity }); + return res.json(getCartDetails()); +}); + +app.delete('/api/cart/:productId', (req, res) => { + const { productId } = req.params; + + if (!cart.has(productId)) { + return res.status(404).json({ message: 'Cart item not found' }); + } + + cart.delete(productId); + return res.json(getCartDetails()); +}); + +app.post('/api/checkout', (req, res) => { + const { customer } = req.body; + const { items, summary } = getCartDetails(); + + if (!items.length) { + return res.status(400).json({ message: 'Cart is empty' }); + } + + if (!customer?.name || !customer?.email || !customer?.address) { + return res.status(400).json({ message: 'Customer name, email, and address are required' }); + } + + items.forEach((item) => { + const product = products.find((productItem) => productItem.id === item.id); + if (product) { + product.stock -= item.quantity; + } + }); + + cart.clear(); + + const orderId = `ORD-${Date.now()}`; + + return res.status(201).json({ + message: 'Order placed successfully', + orderId, + chargedAmount: summary.total, + customer, + createdAt: new Date().toISOString() + }); +}); + +app.listen(PORT, () => { + console.log(`Server listening on http://localhost:${PORT}`); +}); diff --git a/5 MERN/1-ecommerce-app/frontend/index.html b/5 MERN/1-ecommerce-app/frontend/index.html new file mode 100644 index 0000000..294485b --- /dev/null +++ b/5 MERN/1-ecommerce-app/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + MERN eCommerce + + +
+ + + diff --git a/5 MERN/1-ecommerce-app/frontend/package.json b/5 MERN/1-ecommerce-app/frontend/package.json new file mode 100644 index 0000000..51f0eb2 --- /dev/null +++ b/5 MERN/1-ecommerce-app/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "mern-ecommerce-frontend", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "vite": "^5.4.2" + } +} diff --git a/5 MERN/1-ecommerce-app/frontend/src/App.jsx b/5 MERN/1-ecommerce-app/frontend/src/App.jsx new file mode 100644 index 0000000..4826b52 --- /dev/null +++ b/5 MERN/1-ecommerce-app/frontend/src/App.jsx @@ -0,0 +1,159 @@ +import { useEffect, useState } from 'react'; +import ProductCard from './components/ProductCard'; +import CartPanel from './components/CartPanel'; +import CheckoutModal from './components/CheckoutModal'; + +const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:5000/api'; + +const emptyCart = { + items: [], + summary: { + itemCount: 0, + subtotal: 0, + tax: 0, + total: 0 + } +}; + +export default function App() { + const [products, setProducts] = useState([]); + const [cart, setCart] = useState(emptyCart); + const [loading, setLoading] = useState(false); + const [message, setMessage] = useState(''); + const [checkoutOpen, setCheckoutOpen] = useState(false); + const [checkoutLoading, setCheckoutLoading] = useState(false); + + const fetchInitialData = async () => { + try { + const [productsRes, cartRes] = await Promise.all([fetch(`${API_BASE}/products`), fetch(`${API_BASE}/cart`)]); + const [productsData, cartData] = await Promise.all([productsRes.json(), cartRes.json()]); + setProducts(productsData); + setCart(cartData); + } catch { + setMessage('Unable to connect to the backend API. Start backend server on port 5000.'); + } + }; + + useEffect(() => { + fetchInitialData(); + }, []); + + const addToCart = async (productId) => { + setLoading(true); + try { + const response = await fetch(`${API_BASE}/cart`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ productId, quantity: 1 }) + }); + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Failed to add item'); + } + setCart(data); + setMessage('Item added to cart'); + } catch (error) { + setMessage(error.message); + } finally { + setLoading(false); + } + }; + + const updateQuantity = async (productId, quantity) => { + if (quantity <= 0) { + return removeItem(productId); + } + + try { + const response = await fetch(`${API_BASE}/cart/${productId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ quantity }) + }); + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Failed to update quantity'); + } + setCart(data); + } catch (error) { + setMessage(error.message); + } + }; + + const removeItem = async (productId) => { + try { + const response = await fetch(`${API_BASE}/cart/${productId}`, { method: 'DELETE' }); + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Failed to remove item'); + } + setCart(data); + } catch (error) { + setMessage(error.message); + } + }; + + const placeOrder = async (customer) => { + setCheckoutLoading(true); + try { + const response = await fetch(`${API_BASE}/checkout`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ customer }) + }); + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || 'Checkout failed'); + } + + setMessage(`✅ Order ${data.orderId} placed successfully for $${data.chargedAmount.toFixed(2)}.`); + setCart(emptyCart); + setCheckoutOpen(false); + fetchInitialData(); + return true; + } catch (error) { + setMessage(error.message); + return false; + } finally { + setCheckoutLoading(false); + } + }; + + return ( +
+
+

MERN eCommerce Store

+

Browse products, manage your cart, and complete checkout.

+
+ + {message &&
{message}
} + +
+
+

Products

+
+ {products.map((product) => ( + + ))} +
+
+ + setCheckoutOpen(true)} + checkoutLoading={checkoutLoading} + /> +
+ + setCheckoutOpen(false)} + onSubmit={placeOrder} + submitting={checkoutLoading} + /> +
+ ); +} diff --git a/5 MERN/1-ecommerce-app/frontend/src/components/CartPanel.jsx b/5 MERN/1-ecommerce-app/frontend/src/components/CartPanel.jsx new file mode 100644 index 0000000..d83e78d --- /dev/null +++ b/5 MERN/1-ecommerce-app/frontend/src/components/CartPanel.jsx @@ -0,0 +1,48 @@ +export default function CartPanel({ cart, onQuantityChange, onRemove, onCheckoutClick, checkoutLoading }) { + return ( + + ); +} diff --git a/5 MERN/1-ecommerce-app/frontend/src/components/CheckoutModal.jsx b/5 MERN/1-ecommerce-app/frontend/src/components/CheckoutModal.jsx new file mode 100644 index 0000000..4f57c3f --- /dev/null +++ b/5 MERN/1-ecommerce-app/frontend/src/components/CheckoutModal.jsx @@ -0,0 +1,55 @@ +import { useState } from 'react'; + +const defaultForm = { + name: '', + email: '', + address: '' +}; + +export default function CheckoutModal({ open, onClose, onSubmit, submitting }) { + const [form, setForm] = useState(defaultForm); + + if (!open) return null; + + const handleChange = (event) => { + setForm((previous) => ({ ...previous, [event.target.name]: event.target.value })); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + const success = await onSubmit(form); + if (success) { + setForm(defaultForm); + } + }; + + return ( +
+
event.stopPropagation()}> +

Checkout Details

+
+ + +