E■Commerce Frontend: Requirements &
Ready■to■Use Code
This document summarizes the current frontend status, required changes, and includes production■ready
code snippets you can paste into your React (Vite) project. It’s structured so you can hand it to a teammate
and implement quickly.
1) Project Overview
• Stack: React + Vite + React Router. Pages present: Home, Shop, Cart, Login, Help. Navigation bar
includes a search box. Cart currently uses localStorage when present.
2) Quick Audit Findings
- Routing/Links: Mismatch in paths and case sensitivity (e.g., 'HELP' vs '/help'); stray route '/Navbar'
- Shop: Hardcoded products; product images not present; 'Add to Cart' not wired.
- Cart: Reads from localStorage but nothing writes yet.
- Search: UI exists but not connected to filtering.
- Login: Links to '/signup' but no Signup page defined.
- Missing: 404 route, API base URL env, placeholders for images.
3) Target Routes (Final)
Path Component Notes
/ Home
/shop Shop Supports ?q= query for search
/cart Cart Shows local or server cart
/login Login
/help Help
* Fallback 404 Catch-all route
4) App.jsx (Clean Routes + 404)
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Navbar from './components/Navbar';
import Login from './components/Login';
import Home from './components/Home';
import Help from './components/Help';
import Cart from './components/Cart';
import Shop from './components/Shop';
const App = () => {
return (
<>
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/shop" element={<Shop />} />
<Route path="/cart" element={<Cart />} />
<Route path="/login" element={<Login />} />
<Route path="/help" element={<Help />} />
<Route path="*" element={<div style={{padding:24}}>404: Not Found</div>} />
</Routes>
</>
);
};
export default App;
5) Navbar.jsx (Fixed Links + Search Navigation)
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import './Navbar.css';
const Navbar = () => {
const [q, setQ] = useState('');
const navigate = useNavigate();
const runSearch = () => {
const query = q.trim();
navigate(query ? `/shop?q=${encodeURIComponent(query)}` : '/shop');
};
return (
<nav className="navbar">
<div className="logo">■ QualityKart</div>
<div className="search-bar">
<input
type="text"
placeholder="Search products..."
value={q}
onChange={(e) => setQ(e.target.value)}
aria-label="Search products"
/>
<button onClick={runSearch}>■</button>
</div>
<ul className="nav-links">
<li><Link to="/">Home</Link></li>
<li><Link to="/shop">Shop</Link></li>
<li><Link to="/cart">Cart</Link></li>
<li><Link to="/login">Login</Link></li>
<li><Link to="/help">Help</Link></li>
</ul>
</nav>
);
};
export default Navbar;
6) API Helper (frontend)
// src/lib/api.js
export const API = (path) => `${import.meta.env.VITE_API_URL}${path}`;
export async function getJSON(path, token) {
const res = await fetch(API(path), {
headers: token ? { Authorization: `Bearer ${token}` } : {}
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
export async function postJSON(path, body, token, method='POST') {
const res = await fetch(API(path), {
method,
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {})
},
body: JSON.stringify(body)
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
7) Shop.jsx (Search + Add■to■Cart via Modal + localStorage)
import React, { useEffect, useState } from "react";
import "./Shop.css";
import CartModal from "./CartModel";
import { useLocation } from 'react-router-dom';
import { getJSON } from '../lib/api';
const Shop = () => {
const [products, setProducts] = useState([]);
const [active, setActive] = useState(null);
const location = useLocation();
const params = new URLSearchParams(location.search);
const q = params.get('q') || '';
useEffect(() => {
// If backend is running, fetch real products:
const run = async () => {
try {
const data = await getJSON(`/api/products${q ? `?q=${encodeURIComponent(q)}` : ''}`);
setProducts(data.map(p => ({
id: p._id,
name: p.title,
price: p.price,
image: p.images?.[0] || '/placeholder1.png'
})));
} catch (e) {
// fallback if backend not available
const fallback = [
{ id: "p1", name: "Sample Product 1", price: 299, image: "/placeholder1.png" },
{ id: "p2", name: "Sample Product 2", price: 499, image: "/placeholder2.png" },
];
setProducts(fallback.filter(p => p.name.toLowerCase().includes(q.toLowerCase())));
}
};
run();
}, [q]);
const addToCart = (item) => {
const cart = JSON.parse(localStorage.getItem('cart') || '[]');
const idx = cart.findIndex(ci => ci.id === item.id);
if (idx >= 0) cart[idx].quantity += item.quantity;
else cart.push(item);
localStorage.setItem('cart', JSON.stringify(cart));
setActive(null);
alert("Added to cart!");
};
return (
<div className="shop-container">
<h2>Our Products {q && `(search: ${q})`}</h2>
<div className="product-grid">
{products.map(product => (
<div className="product-card" key={product.id}>
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>■ {product.price}</p>
<button onClick={() => setActive(product)}>Add to Cart</button>
</div>
))}
</div>
{active && (
<CartModal
product={active}
onClose={() => setActive(null)}
onAddToCart={addToCart}
/>
)}
</div>
);
};
export default Shop;
8) Cart.jsx (localStorage version)
import React, { useEffect, useState } from 'react';
const Cart = () => {
const [cartItems, setCartItems] = useState([]);
useEffect(() => {
let cart = JSON.parse(localStorage.getItem('cart')) || [];
setCartItems(cart);
}, []);
if (cartItems.length === 0) return <h2>Your cart is empty</h2>;
const grandTotal = cartItems.reduce((sum, it) => sum + it.price * it.quantity, 0);
return (
<div>
<h1>Your Cart</h1>
{cartItems.map(item => (
<div key={item.id} style={{ border: '1px solid #ccc', margin: 10, padding: 10 }}>
<h3>{item.name}</h3>
<p>Quantity: {item.quantity}</p>
<p>Price per item: ■{item.price}</p>
<p>Total: ■{item.price * item.quantity}</p>
</div>
))}
<h3>Grand Total: ■{grandTotal}</h3>
</div>
);
};
export default Cart;
9) Login.jsx (calls backend, stores token)
import React, { useState } from 'react';
import './Login.css';
import { postJSON } from '../lib/api';
import { useNavigate } from 'react-router-dom';
const Login = () => {
const [email,setEmail] = useState('');
const [password,setPassword] = useState('');
const [error,setError] = useState('');
const navigate = useNavigate();
const submit = async (e) => {
e.preventDefault();
try {
const { token, user } = await postJSON('/api/auth/login', { email, password });
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
navigate('/');
} catch (err) {
setError('Invalid email or password');
}
};
return (
<div className="login-container">
<h2>Login</h2>
{error && <div style={{color:'red'}}>{error}</div>}
<form className="login-form" onSubmit={submit}>
<input type="email" placeholder="Email" value={email} onChange={e=>setEmail(e.target.value)} req
<input type="password" placeholder="Password" value={password} onChange={e=>setPassword(e.target
<button type="submit">Login</button>
</form>
<p className="signup-text">Don’t have an account? <a href="/signup">Sign up</a></p>
</div>
);
};
export default Login;
10) Environment & Assets
• Create a frontend .env at project root:
VITE_API_URL=http://localhost:4000
• Put placeholder images in /public:
public/placeholder1.png
public/placeholder2.png
• Later, replace with real product image URLs coming from backend (Product.images[0]).
11) Next Steps (after this doc)
- Add Signup page and form validation.
- Create a Product Details page (/product/:id).
- Global cart context so cart count shows in Navbar.
- Switch from localStorage cart to backend cart when logged in (feature flag).
- Admin UI for product CRUD.
- Move token to httpOnly cookies in production for better security.
This PDF contains everything your teammate needs to align the frontend with the backend starter (already
generated for you).