Skip to main content

Overview

The Don Palito Jr web application is the customer-facing e-commerce platform where users can browse products, manage their cart, complete purchases, and track orders. Built with modern React technologies and styled with a custom design system.

Technology Stack

React 18.2

Core UI framework with modern hooks and context API

Vite 5.0

Lightning-fast build tool and development server

Tailwind CSS 3.4

Utility-first CSS framework

DaisyUI 4.12

Tailwind CSS component library

Key Dependencies

  • Authentication: Clerk React (@clerk/clerk-react)
  • State Management: React Context API + Custom Hooks
  • Data Fetching: TanStack Query (@tanstack/react-query)
  • HTTP Client: Axios
  • Forms: React Hook Form + Yup validation
  • Routing: React Router DOM v6
  • Payments: Stripe React
  • UI Components: React Icons, React Toastify

Setup and Development

1

Install Dependencies

cd source/web
npm install
2

Configure Environment

Create a .env file with the following variables:
VITE_API_URL=http://localhost:3000/api
VITE_CLERK_PUBLISHABLE_KEY=your_clerk_key
VITE_ADMIN_URL=http://localhost:5173
VITE_STRIPE_PUBLISHABLE_KEY=your_stripe_key
3

Start Development Server

npm run dev
The app will be available at http://localhost:5173
4

Build for Production

npm run build
Output will be in the dist/ directory

Project Structure

The web app uses path aliases for clean imports:
// vite.config.js
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/components'),
      '@pages': path.resolve(__dirname, './src/pages'),
      '@services': path.resolve(__dirname, './src/services'),
      '@utils': path.resolve(__dirname, './src/utils'),
      '@hooks': path.resolve(__dirname, './src/hooks'),
      '@context': path.resolve(__dirname, './src/context'),
      '@assets': path.resolve(__dirname, './src/assets'),
    },
  },
})

Directory Layout

src/
├── components/
│   ├── auth/          # ProtectedRoute, AdminRoute
│   ├── cart/          # CartItem, OrderSummary
│   ├── checkout/      # StripeCheckoutForm, CheckoutStepper
│   ├── common/        # Button, Card, Input, Modal, Loading
│   ├── layout/        # Header, Footer, Navbar, Layout
│   ├── products/      # ProductCard
│   └── profile/       # AddressCard, OrderTimeline, RatingModal
├── contexts/
│   ├── AuthContext.jsx
│   └── CartContext.jsx
├── pages/
│   ├── auth/          # Login, Register, ForgotPassword
│   ├── checkout/      # Checkout, CheckoutSuccess
│   ├── info/          # About, Contact, FAQ, Terms, Privacy
│   ├── profile/       # Profile, Orders, OrderDetail, Wishlist
│   ├── Cart.jsx
│   ├── Catalog.jsx
│   ├── Home.jsx
│   └── ProductDetail.jsx
├── services/
│   └── api.js         # Axios instance with interceptors
└── App.jsx            # Root component with routing

Key Features

Authentication with Clerk

The app integrates Clerk for authentication with Spanish localization:
import { ClerkProvider } from '@clerk/clerk-react';
import { esES } from '@clerk/localizations';

function App() {
  return (
    <ClerkProvider
      publishableKey={CLERK_KEY}
      localization={esES}
    >
      <QueryClientProvider client={queryClient}>
        <ClerkTokenSync />
        <AppRoutes />
      </QueryClientProvider>
    </ClerkProvider>
  )
}
The ClerkTokenSync component synchronizes Clerk’s token getter with Axios interceptors, automatically attaching JWT tokens to API requests.

API Integration

Axios is configured with request/response interceptors for automatic token injection and error handling:
// src/services/api.js
const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
  withCredentials: true,
});

// Request interceptor - attach Clerk token
api.interceptors.request.use(
  async (config) => {
    if (_getToken) {
      try {
        const token = await _getToken();
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
      } catch {
        // User not authenticated - continue without token
      }
    }
    return config;
  },
  (error) => Promise.reject(error)
);

Routing Structure

The app uses React Router v6 with protected routes:
<Routes>
  <Route path="/" element={<Layout />}>
    {/* Public routes */}
    <Route index element={<Home />} />
    <Route path="catalogo" element={<Catalog />} />
    <Route path="producto/:id" element={<ProductDetail />} />
    <Route path="carrito" element={<Cart />} />
    
    {/* Auth routes */}
    <Route path="login" element={<Login />} />
    <Route path="registro" element={<Register />} />
    
    {/* Protected routes */}
    <Route path="perfil" element={<ProtectedRoute><Profile /></ProtectedRoute>} />
    <Route path="checkout" element={<ProtectedRoute><Checkout /></ProtectedRoute>} />
  </Route>
</Routes>

Custom Theme

The app uses a custom DaisyUI theme matching Don Palito Jr’s brand:
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          primary: "#B06A4A",      // Warm terracotta brown
          secondary: "#5B3A29",    // Dark coffee
          accent: "#C34928",       // Red-orange
        },
        ui: {
          background: "#F3E6D4",   // Cream/beige
          surface: "#FFFFFF",      // White
          border: "#999999",       // Gray
        },
      },
    },
  },
  plugins: [require('daisyui')],
  daisyui: {
    themes: [
      {
        donpalito: {
          "primary": "#B06A4A",
          "secondary": "#5B3A29",
          "accent": "#C34928",
          "base-100": "#F3E6D4",
        },
      },
    ],
  },
}

Data Fetching with React Query

TanStack Query handles server state with caching and automatic refetching:
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      retry: 1,
    },
  },
});

Build Optimization

Vite is configured with code splitting for optimal bundle sizes:
// vite.config.js
build: {
  outDir: 'dist',
  sourcemap: false,
  rollupOptions: {
    output: {
      manualChunks: {
        'vendor-react': ['react', 'react-dom', 'react-router-dom'],
        'vendor-forms': ['react-hook-form', 'yup', '@hookform/resolvers'],
        'vendor-ui': ['react-icons', 'react-toastify'],
        'vendor-utils': ['axios', 'moment'],
        'vendor-query': ['@tanstack/react-query'],
        'vendor-stripe': ['@stripe/react-stripe-js', '@stripe/stripe-js'],
      },
    },
  },
  chunkSizeWarningLimit: 1000,
}

Available Scripts

npm run dev

Best Practices

  • Use path aliases (@components, @pages, etc.) for clean imports
  • Wrap protected pages with <ProtectedRoute> component
  • Use React Query for all server state management
  • Follow the DaisyUI theme color system for consistency
  • Keep components small and focused on single responsibility
Never commit .env files to version control. Use .env.example as a template.

Build docs developers (and LLMs) love