Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added 007 Student Contributions/RamanSingh/index.html
Binary file not shown.
36 changes: 36 additions & 0 deletions 5 MERN/1-ecommerce-app/README.md
Original file line number Diff line number Diff line change
@@ -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.
15 changes: 15 additions & 0 deletions 5 MERN/1-ecommerce-app/backend/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
56 changes: 56 additions & 0 deletions 5 MERN/1-ecommerce-app/backend/src/data.js
Original file line number Diff line number Diff line change
@@ -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
}
];
145 changes: 145 additions & 0 deletions 5 MERN/1-ecommerce-app/backend/src/server.js
Original file line number Diff line number Diff line change
@@ -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}`);
});
12 changes: 12 additions & 0 deletions 5 MERN/1-ecommerce-app/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MERN eCommerce</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
19 changes: 19 additions & 0 deletions 5 MERN/1-ecommerce-app/frontend/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
Loading