diff --git a/007 Student Contributions/RamanSingh/index.html b/007 Student Contributions/RamanSingh/index.html
new file mode 100644
index 0000000..6aeee2f
Binary files /dev/null and b/007 Student Contributions/RamanSingh/index.html differ
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 (
+
+
+
+ {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
+
+
+
+ );
+}
diff --git a/5 MERN/1-ecommerce-app/frontend/src/components/ProductCard.jsx b/5 MERN/1-ecommerce-app/frontend/src/components/ProductCard.jsx
new file mode 100644
index 0000000..1ca9233
--- /dev/null
+++ b/5 MERN/1-ecommerce-app/frontend/src/components/ProductCard.jsx
@@ -0,0 +1,18 @@
+export default function ProductCard({ product, onAddToCart, loading }) {
+ return (
+
+
+
+
{product.category}
+
{product.name}
+
{product.description}
+
+ ${product.price.toFixed(2)}
+
+
+
+
+ );
+}
diff --git a/5 MERN/1-ecommerce-app/frontend/src/main.jsx b/5 MERN/1-ecommerce-app/frontend/src/main.jsx
new file mode 100644
index 0000000..d106f4f
--- /dev/null
+++ b/5 MERN/1-ecommerce-app/frontend/src/main.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+import './styles.css';
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+
+);
diff --git a/5 MERN/1-ecommerce-app/frontend/src/styles.css b/5 MERN/1-ecommerce-app/frontend/src/styles.css
new file mode 100644
index 0000000..6519131
--- /dev/null
+++ b/5 MERN/1-ecommerce-app/frontend/src/styles.css
@@ -0,0 +1,189 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ color: #1f2937;
+ background: #f3f4f6;
+}
+
+* { box-sizing: border-box; }
+
+body {
+ margin: 0;
+}
+
+.layout {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 1rem;
+}
+
+.header h1 {
+ margin-bottom: 0.25rem;
+}
+
+.header p,
+.muted {
+ color: #6b7280;
+}
+
+.banner {
+ margin: 1rem 0;
+ background: #dbeafe;
+ color: #1e3a8a;
+ border: 1px solid #bfdbfe;
+ padding: 0.75rem;
+ border-radius: 0.5rem;
+}
+
+.content {
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ gap: 1rem;
+ align-items: flex-start;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 1rem;
+}
+
+.card {
+ background: #fff;
+ border-radius: 0.75rem;
+ overflow: hidden;
+ border: 1px solid #e5e7eb;
+}
+
+.card__image {
+ width: 100%;
+ height: 140px;
+ object-fit: cover;
+}
+
+.card__content { padding: 0.75rem; }
+.card__category { color: #2563eb; font-size: 0.85rem; margin: 0; }
+.card__description { color: #6b7280; font-size: 0.9rem; min-height: 54px; }
+.card__footer { display: flex; justify-content: space-between; align-items: center; }
+
+button {
+ border: none;
+ background: #2563eb;
+ color: white;
+ border-radius: 0.4rem;
+ padding: 0.4rem 0.7rem;
+ cursor: pointer;
+}
+
+button:disabled {
+ background: #93c5fd;
+ cursor: not-allowed;
+}
+
+.cart-panel {
+ background: #fff;
+ border: 1px solid #e5e7eb;
+ border-radius: 0.75rem;
+ padding: 1rem;
+ position: sticky;
+ top: 1rem;
+}
+
+.cart-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ gap: 0.75rem;
+}
+
+.cart-item {
+ border-bottom: 1px solid #e5e7eb;
+ padding-bottom: 0.75rem;
+}
+
+.cart-item__controls {
+ display: flex;
+ gap: 0.35rem;
+ align-items: center;
+ margin-top: 0.35rem;
+}
+
+.danger { background: #dc2626; }
+
+.summary {
+ margin-top: 1rem;
+ border-top: 1px solid #e5e7eb;
+ padding-top: 1rem;
+}
+
+.summary p {
+ margin: 0.35rem 0;
+ display: flex;
+ justify-content: space-between;
+}
+
+.summary__total {
+ font-size: 1.1rem;
+ font-weight: 700;
+}
+
+.checkout-btn {
+ width: 100%;
+ margin-top: 1rem;
+ padding: 0.6rem;
+}
+
+.modal-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.35);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.modal {
+ background: white;
+ border-radius: 0.75rem;
+ width: min(520px, 96vw);
+ padding: 1rem;
+}
+
+.checkout-form {
+ display: grid;
+ gap: 0.75rem;
+}
+
+.checkout-form label {
+ display: grid;
+ gap: 0.2rem;
+}
+
+input,
+textarea {
+ border: 1px solid #d1d5db;
+ border-radius: 0.4rem;
+ padding: 0.45rem;
+ font: inherit;
+}
+
+.modal__actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 0.5rem;
+}
+
+.ghost {
+ background: #e5e7eb;
+ color: #374151;
+}
+
+@media (max-width: 900px) {
+ .content {
+ grid-template-columns: 1fr;
+ }
+
+ .cart-panel {
+ position: static;
+ }
+}
diff --git a/5 MERN/1-ecommerce-app/frontend/vite.config.js b/5 MERN/1-ecommerce-app/frontend/vite.config.js
new file mode 100644
index 0000000..efe6335
--- /dev/null
+++ b/5 MERN/1-ecommerce-app/frontend/vite.config.js
@@ -0,0 +1,9 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 5173
+ }
+});