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
Install Dependencies
cd source/web
npm install
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
Start Development Server
The app will be available at http://localhost:5173
Build for Production
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
Development
Build
Preview
Lint
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.