aboutsummaryrefslogtreecommitdiffstats

Implementation Guide

This guide provides practical steps to implement the e-commerce platform based on the project plan.

Getting Started

1. Development Environment Setup

# Create project directory structure
mkdir -p ecom/{frontend,backend,admin,shared}

# Initialize frontend (Next.js)
cd ecom/frontend
npx create-next-app . --typescript --tailwind --eslint

# Initialize backend (Express.js + MongoDB)
cd ../backend
npm init -y
npm install express mongoose dotenv cors helmet jsonwebtoken bcrypt stripe

# Create basic directory structure for backend
mkdir -p config controllers middleware models routes services utils
touch server.js .env .env.example .gitignore README.md

2. Frontend Implementation Steps

Step 1: Set up basic components

  • Create layout components (header, footer, navigation)
  • Set up routing structure
  • Implement basic styling with Tailwind CSS

Step 2: Key pages implementation

  • Homepage with featured products and promotions
  • Product listing page with filtering
  • Product detail page
  • Cart page
  • Checkout flow
  • User account pages
// Example: Product Card Component (frontend/src/components/ProductCard.jsx)
import Image from 'next/image';
import Link from 'next/link';

const ProductCard = ({ product }) => {
  return (
    <div className="group relative">
      <div className="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-80">
        <Image
          src={product.imageSrc}
          alt={product.imageAlt}
          className="h-full w-full object-cover object-center lg:h-full lg:w-full"
          width={500}
          height={500}
        />
      </div>
      <div className="mt-4 flex justify-between">
        <div>
          <h3 className="text-sm text-gray-700">
            <Link href={`/products/${product.id}`}>
              <span aria-hidden="true" className="absolute inset-0" />
              {product.name}
            </Link>
          </h3>
          <p className="mt-1 text-sm text-gray-500">{product.color}</p>
        </div>
        <p className="text-sm font-medium text-gray-900">{product.price}</p>
      </div>
    </div>
  );
};

export default ProductCard;

Step 3: State management

  • Implement context providers for:
  • Shopping cart
  • User authentication
  • Product filtering
// Example: CartContext (frontend/src/contexts/CartContext.jsx)
import { createContext, useContext, useReducer, useEffect } from 'react';

const CartContext = createContext();

const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      // Check if item exists
      const existingItemIndex = state.items.findIndex(
        (item) => item.id === action.payload.id && 
                  item.selectedSize === action.payload.selectedSize
      );

      if (existingItemIndex >= 0) {
        // Item exists, update quantity
        const updatedItems = [...state.items];
        updatedItems[existingItemIndex].quantity += action.payload.quantity;

        return {
          ...state,
          items: updatedItems,
        };
      }

      // Item doesn't exist, add new
      return {
        ...state,
        items: [...state.items, action.payload],
      };

    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(
          (item) => !(item.id === action.payload.id && 
                     item.selectedSize === action.payload.selectedSize)
        ),
      };

    case 'UPDATE_QUANTITY':
      return {
        ...state,
        items: state.items.map((item) => {
          if (item.id === action.payload.id && 
              item.selectedSize === action.payload.selectedSize) {
            return {
              ...item,
              quantity: action.payload.quantity,
            };
          }
          return item;
        }),
      };

    case 'CLEAR_CART':
      return {
        ...state,
        items: [],
      };

    default:
      return state;
  }
};

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, {
    items: [],
  });

  // Load cart from localStorage on mount
  useEffect(() => {
    try {
      const savedCart = localStorage.getItem('cart');
      if (savedCart) {
        const parsedCart = JSON.parse(savedCart);
        dispatch({ type: 'REPLACE_CART', payload: parsedCart });
      }
    } catch (error) {
      console.error('Error loading cart from localStorage:', error);
    }
  }, []);

  // Save cart to localStorage on update
  useEffect(() => {
    try {
      localStorage.setItem('cart', JSON.stringify(state.items));
    } catch (error) {
      console.error('Error saving cart to localStorage:', error);
    }
  }, [state.items]);

  const addItem = (product, quantity = 1, selectedSize) => {
    dispatch({
      type: 'ADD_ITEM',
      payload: { ...product, quantity, selectedSize },
    });
  };

  const removeItem = (productId, selectedSize) => {
    dispatch({
      type: 'REMOVE_ITEM',
      payload: { id: productId, selectedSize },
    });
  };

  const updateQuantity = (productId, selectedSize, quantity) => {
    dispatch({
      type: 'UPDATE_QUANTITY',
      payload: { id: productId, selectedSize, quantity },
    });
  };

  const clearCart = () => {
    dispatch({ type: 'CLEAR_CART' });
  };

  const cartTotal = state.items.reduce(
    (total, item) => total + item.price * item.quantity,
    0
  );

  const itemCount = state.items.reduce(
    (count, item) => count + item.quantity,
    0
  );

  return (
    <CartContext.Provider
      value={{
        items: state.items,
        addItem,
        removeItem,
        updateQuantity,
        clearCart,
        cartTotal,
        itemCount,
      }}
    >
      {children}
    </CartContext.Provider>
  );
};

export const useCart = () => useContext(CartContext);

3. Backend Implementation Steps

Step 1: Database models

  • Create schemas for products, users, orders
  • Set up database connection
// Example: Product Model (backend/models/Product.js)
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
    trim: true
  },
  slug: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  description: {
    type: String,
    required: true
  },
  price: {
    type: Number,
    required: true,
    min: 0
  },
  compareAtPrice: {
    type: Number,
    min: 0
  },
  images: [{
    url: String,
    alt: String
  }],
  category: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Category',
    required: true
  },
  variants: [{
    color: String,
    size: String,
    inventory: Number
  }],
  tags: [String],
  featured: {
    type: Boolean,
    default: false
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  }
}, { timestamps: true });

// Add index for search
productSchema.index({ 
  name: 'text', 
  description: 'text',
  tags: 'text'
});

module.exports = mongoose.model('Product', productSchema);

Step 2: API endpoints

  • Create RESTful routes for products, users, orders
  • Implement authentication middleware
// Example: Product Routes (backend/routes/products.js)
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');
const { authenticate, isAdmin } = require('../middleware/authMiddleware');

// Public routes
router.get('/', productController.getAllProducts);
router.get('/featured', productController.getFeaturedProducts);
router.get('/category/:categoryId', productController.getProductsByCategory);
router.get('/search', productController.searchProducts);
router.get('/:id', productController.getProductById);
router.get('/slug/:slug', productController.getProductBySlug);

// Protected routes (admin only)
router.post('/', authenticate, isAdmin, productController.createProduct);
router.put('/:id', authenticate, isAdmin, productController.updateProduct);
router.delete('/:id', authenticate, isAdmin, productController.deleteProduct);
router.post('/bulk', authenticate, isAdmin, productController.bulkImportProducts);

module.exports = router;

Step 3: Payment integration

  • Set up Stripe API for payment processing
  • Implement checkout logic
// Example: Payment Controller (backend/controllers/paymentController.js)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const Order = require('../models/Order');
const Product = require('../models/Product');

exports.createCheckoutSession = async (req, res) => {
  try {
    const { items, shippingAddress } = req.body;

    // Validate cart items and get from database to ensure price integrity
    const productIds = items.map(item => item.id);
    const products = await Product.find({ _id: { $in: productIds } });

    // Map products to line items for Stripe
    const lineItems = items.map(item => {
      const product = products.find(p => p._id.toString() === item.id);

      if (!product) {
        throw new Error(`Product not found: ${item.id}`);
      }

      return {
        price_data: {
          currency: 'inr',
          product_data: {
            name: product.name,
            images: product.images.map(img => img.url),
          },
          unit_amount: Math.round(product.price * 100), // Stripe requires amount in cents
        },
        quantity: item.quantity,
      };
    });

    // Create a new order in pending state
    const order = new Order({
      user: req.user ? req.user._id : null,
      items: items.map(item => ({
        product: item.id,
        quantity: item.quantity,
        price: products.find(p => p._id.toString() === item.id).price,
        selectedVariant: {
          size: item.selectedSize,
          color: item.selectedColor
        }
      })),
      shippingAddress,
      total: items.reduce((sum, item) => {
        const product = products.find(p => p._id.toString() === item.id);
        return sum + (product.price * item.quantity);
      }, 0),
      status: 'pending'
    });

    await order.save();

    // Create Stripe checkout session
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: lineItems,
      mode: 'payment',
      success_url: `${process.env.FRONTEND_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.FRONTEND_URL}/cart`,
      metadata: {
        orderId: order._id.toString()
      }
    });

    res.json({ id: session.id, url: session.url });
  } catch (error) {
    console.error('Payment session error:', error);
    res.status(500).json({ error: error.message });
  }
};

exports.handleWebhook = async (req, res) => {
  const signature = req.headers['stripe-signature'];

  try {
    const event = stripe.webhooks.constructEvent(
      req.body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );

    if (event.type === 'checkout.session.completed') {
      const session = event.data.object;
      const orderId = session.metadata.orderId;

      // Update order status to paid
      await Order.findByIdAndUpdate(orderId, {
        status: 'paid',
        paymentId: session.payment_intent
      });

      // Here you would also update inventory, send confirmation emails, etc.
    }

    res.json({ received: true });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(400).send(`Webhook Error: ${error.message}`);
  }
};

4. Admin Dashboard Implementation

  • Implement pages for product, order, and user management
  • Create forms for product creation and editing
  • Develop dashboard for sales analytics

5. Deployment Strategy

Development Environment

  • Local development with Docker

Staging Environment

  • AWS/Vercel deployment
  • Continuous integration with GitHub Actions

Production Environment

  • Load-balanced setup
  • Database backups
  • CDN for static assets

Key Features Implementation Details

1. Country/Currency Selector

// Example component
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';

const countryCurrencyMap = {
  US: { currency: 'USD', symbol: '$' },
  IN: { currency: 'INR', symbol: '₹' },
  GB: { currency: 'GBP', symbol: '£' },
  // Add more countries as needed
};

export default function CurrencySelector() {
  const [selectedCountry, setSelectedCountry] = useState('IN');
  const router = useRouter();

  // Effect to set country based on user's location or saved preference
  useEffect(() => {
    const savedCountry = localStorage.getItem('selectedCountry');
    if (savedCountry) {
      setSelectedCountry(savedCountry);
    } else {
      // Could add geolocation API to auto-detect
    }
  }, []);

  // Update when user changes selection
  const handleCountryChange = (e) => {
    const country = e.target.value;
    setSelectedCountry(country);
    localStorage.setItem('selectedCountry', country);

    // Force refresh to update prices
    router.reload();
  };

  return (
    <div className="relative">
      <select
        value={selectedCountry}
        onChange={handleCountryChange}
        className="block appearance-none bg-white border border-gray-300 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
      >
        <option value="IN">India ()</option>
        <option value="US">United States ($)</option>
        <option value="GB">United Kingdom (£)</option>
        {/* Add more countries */}
      </select>
      <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
        <svg className="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
          <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
        </svg>
      </div>
    </div>
  );
}
// Example implementation
import { useState } from 'react';
import Image from 'next/image';

export default function ProductGallery({ images }) {
  const [mainImage, setMainImage] = useState(images[0]);

  return (
    <div className="grid gap-4">
      <div className="relative h-96 overflow-hidden rounded-lg">
        <Image
          src={mainImage.url}
          alt={mainImage.alt}
          layout="fill"
          objectFit="cover"
          className="w-full h-full object-center object-cover"
        />
      </div>
      <div className="grid grid-cols-5 gap-2">
        {images.map((image, i) => (
          <button
            key={i}
            onClick={() => setMainImage(image)}
            className={`relative h-20 overflow-hidden rounded-md ${
              mainImage.url === image.url ? 'ring-2 ring-indigo-500' : ''
            }`}
          >
            <Image
              src={image.url}
              alt={image.alt}
              layout="fill"
              objectFit="cover"
              className="w-full h-full object-center object-cover"
            />
          </button>
        ))}
      </div>
    </div>
  );
}

3. Promotional Banner Component

// Example implementation
import { useState, useEffect } from 'react';

export default function PromoBanner({ code, message, expiry }) {
  const [isVisible, setIsVisible] = useState(true);

  // Check if banner should be shown (e.g., not dismissed recently)
  useEffect(() => {
    const bannerDismissed = localStorage.getItem('promoBannerDismissed');
    if (bannerDismissed) {
      const dismissTime = parseInt(bannerDismissed);
      // Show again after 1 day
      if (Date.now() - dismissTime < 24 * 60 * 60 * 1000) {
        setIsVisible(false);
      }
    }
  }, []);

  const handleDismiss = () => {
    setIsVisible(false);
    localStorage.setItem('promoBannerDismissed', Date.now().toString());
  };

  if (!isVisible) return null;

  return (
    <div className="bg-indigo-600 py-3">
      <div className="mx-auto max-w-7xl px-3 sm:px-6 lg:px-8">
        <div className="flex flex-wrap items-center justify-between">
          <div className="flex w-0 flex-1 items-center">
            <span className="flex rounded-lg bg-indigo-800 p-2">
              <svg className="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
              </svg>
            </span>
            <p className="ml-3 font-medium text-white">
              <span className="md:hidden">{message}</span>
              <span className="hidden md:inline">
                {message} Use code <span className="font-bold">{code}</span> at checkout
              </span>
            </p>
          </div>
          <div className="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
            <a
              href="#"
              className="flex items-center justify-center rounded-md border border-transparent bg-white px-4 py-2 text-sm font-medium text-indigo-600 shadow-sm hover:bg-indigo-50"
            >
              Shop Now
            </a>
          </div>
          <div className="order-2 flex-shrink-0 sm:order-3 sm:ml-3">
            <button
              type="button"
              onClick={handleDismiss}
              className="-mr-1 flex rounded-md p-2 hover:bg-indigo-500 focus:outline-none focus:ring-2 focus:ring-white sm:-mr-2"
            >
              <span className="sr-only">Dismiss</span>
              <svg className="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

Testing and Quality Assurance

Unit Testing

# Frontend testing
cd frontend
npm test

# Backend testing
cd ../backend
npm test

End-to-End Testing

# Install Cypress
cd frontend
npm install cypress --save-dev

# Run E2E tests
npx cypress open

Performance Optimization Strategies

  1. Image Optimization - Use Next.js Image component - Implement lazy loading - Serve responsive images

  2. Code Splitting - Leverage Next.js automatic code splitting - Use dynamic imports for large components

  3. Server-Side Rendering - Use SSR for product listings and detail pages - Implement Incremental Static Regeneration for frequently updated pages

  4. API Response Caching - Implement Redis for caching - Add HTTP cache headers

Security Best Practices

  1. Authentication - Implement JWT with refresh tokens - Store tokens securely in HTTP-only cookies

  2. API Security - Implement rate limiting - Use CORS protection - Validate all inputs

  3. Payment Data - Use Stripe Elements for secure payment form - Never store sensitive payment information

Ongoing Maintenance Plan

  1. Regular dependency updates
  2. Security patches
  3. Performance monitoring
  4. Backup strategy
  5. Scaling plan as traffic increases