A modern React storefront and admin dashboard for a sneaker/shoe e-commerce platform, built with Vite, React 18, Tailwind CSS, and shadcn/ui.
This repository is the frontend-only client of the Crest Walk platform. It renders the public storefront (catalog, product detail, cart, wishlist, checkout, order tracking) and a role-guarded admin dashboard (products, categories, brands, inventory, orders, users, promotions, reviews, banners, revenue & bestseller analytics), talking to a separate backend REST API over JWT-authenticated HTTP.
- Overview — preview & key features
- Tech & Architecture — stack, folder layout, providers, routes, runtime flows
- Getting Started — install, configure, run, test, build
- API Reference — the backend contract this app consumes
- Project Info — author, contact, license
![]() Home |
![]() Product Catalog |
|---|---|
![]() Product Detail |
![]() Cart & Checkout |
![]() Admin Dashboard |
![]() Admin Table |
- Storefront
- Home, product catalog with search/filtering, product detail with variants (size), reviews, and related items.
- Guest-friendly shopping: cart and wishlist work before login, then merge into the account experience after authentication.
- Checkout, order placement, and order-tracking/history lookup.
- Authentication & Session
- JWT access token (kept in memory +
localStorage) with an httpOnly refresh-token cookie issued by the backend. - Silent token refresh: an Axios response interceptor transparently retries a request once after a
401, usingPOST /auth/refresh-token. - Session restored automatically on page reload from
localStorage.
- JWT access token (kept in memory +
- Cart & Wishlist
- Dual-mode cart (
CartContext): unauthenticated users get alocalStorage-backedguest_cart; authenticated users are synced live against the backend cart API on every mutation. - Wishlist is server-synced and requires authentication, with optimistic toggle helpers (
isInWishlist,toggleWishlist).
- Dual-mode cart (
- Role-Based Admin Dashboard
AdminRouteroute guard redirects non-admins away from/admin/*and shows a toast notice.- Full CRUD screens for Products, Categories, Brands, Inventory, Orders, Users, Promotions (vouchers), Reviews (moderation), and Banners.
- Analytics screens for Revenue and Bestsellers.
- Shared
AdminLayoutshell andAdminPaginationBarcomponent reused across every admin list screen.
- UI System
- Built on shadcn/ui (Radix UI primitives + Tailwind), generated/configured via
components.json, giving accessible dialogs, dropdowns, forms, tables, sidebars, carousels, toasts, tooltips, and charts out of the box. - Toast notifications via both
sonnerand the shadcnToaster.
- Built on shadcn/ui (Radix UI primitives + Tailwind), generated/configured via
- Resilient API Layer
- One dedicated Axios-based client module per backend resource under
src/api/, so each screen imports a small, typed surface instead of calling Axios directly. - Shared helpers normalize paginated list responses (
normalizeListPagination.js) and Axios error payloads (formatApiErrorMessage.js) so every screen handles loading/error/pagination the same way.
- One dedicated Axios-based client module per backend resource under
- Framework & Build: React 18, Vite 5 (
@vitejs/plugin-react-swc), path alias@→src/ - Language: JavaScript (JSX); Vitest config in TypeScript
- Styling & UI: Tailwind CSS,
tailwindcss-animate, shadcn/ui, Radix UI primitives,lucide-reacticons,class-variance-authority,clsx/tailwind-merge - Routing: React Router DOM 6 (
BrowserRouter, nested<Route>, guarded admin subtree) - Server State:
@tanstack/react-query(QueryClientProvider) - Client/Global State: React Context (
AuthContext,CartContext,WishlistContext) - HTTP: Axios with request/response interceptors for auth headers and silent token refresh
- Forms & Validation:
react-hook-form,@hookform/resolvers,zod - Other UX libraries:
framer-motion,embla-carousel-react,recharts(admin analytics charts),date-fns,sonner,next-themes,cmdk,vaul,react-day-picker,input-otp,react-resizable-panels - Testing: Vitest,
@testing-library/react,@testing-library/jest-dom,jsdom - Tooling: ESLint 9 (flat config), PostCSS, Autoprefixer
crest-walk-fe/
├── public/ # Static assets (favicon, robots.txt, images)
├── docs/screenshots/ # (create this) preview images referenced by this README
├── src/
│ ├── api/ # One Axios client per backend resource (public + admin*), plus axiosClient.js
│ │ # — the configured instance with auth headers & silent token refresh
│ ├── components/ # Shared UI: Header, Footer, Layout, AdminLayout, AdminRoute guard, ProductCard
│ │ └── ui/ # shadcn/ui primitives (button, dialog, table, sidebar, toast, ...)
│ ├── config/ # Runtime config (API_URL from VITE_API_URL)
│ ├── contexts/ # AuthContext, CartContext (guest + synced), WishlistContext
│ ├── data/ # Static/demo fixtures for local UI development
│ ├── hooks/ # use-mobile, use-toast
│ ├── lib/ # utils (cn), pagination/error/auth-response normalizers
│ ├── pages/ # One page per route (Home, Products, Cart, Checkout, ...)
│ │ └── admin/ # Admin-only pages, mounted under the <AdminRoute> guard
│ ├── test/ # Vitest setup + sample test
│ ├── App.jsx # Route table + global provider tree (see diagram below)
│ └── main.jsx # React DOM entry point
├── vite.config.js / vitest.config.ts / eslint.config.js / tailwind.config.js
├── index.html # Vite HTML entry
└── package.json
graph TD
QC["QueryClientProvider<br/>(@tanstack/react-query)"] --> TP["TooltipProvider"]
TP --> BR["BrowserRouter"]
BR --> AP["AuthProvider"]
AP --> WP["WishlistProvider"]
WP --> CP["CartProvider"]
CP --> RT["Routes"]
RT --> PUB["Public routes<br/>/ /products /product/:id /cart /wishlist<br/>/auth /checkout /profile /order-tracking /about /*"]
RT --> ADM["AdminRoute guard"]
ADM --> ADMR["/admin/* routes<br/>(dashboard, products, categories, brands,<br/>orders, users, inventory, promotions,<br/>revenue, bestsellers, reviews, banners)"]
| Path | Page | Notes |
|---|---|---|
/ |
Index |
Home / landing |
/products |
Products |
Catalog, search & filters |
/product/:id |
ProductDetail |
Detail, variants, reviews |
/cart |
Cart |
Guest or server-synced cart |
/wishlist |
Wishlist |
Requires login to persist |
/auth |
Auth |
Login / Register |
/checkout |
Checkout |
Order placement |
/profile |
Profile |
Account settings |
/order-tracking |
OrderTracking |
Order history / lookup |
/about |
About |
About page |
* |
NotFound |
404 fallback |
/admin |
AdminDashboard |
🔒 admin only |
/admin/products |
AdminProducts |
🔒 admin only |
/admin/categories |
AdminCategories |
🔒 admin only |
/admin/brands |
AdminBrands |
🔒 admin only |
/admin/orders |
AdminOrders |
🔒 admin only |
/admin/users |
AdminUsers |
🔒 admin only |
/admin/inventory |
AdminInventory |
🔒 admin only |
/admin/promotions |
AdminPromotions |
🔒 admin only |
/admin/revenue |
AdminRevenue |
🔒 admin only |
/admin/bestsellers |
AdminBestsellers |
🔒 admin only |
/admin/reviews |
AdminReviews |
🔒 admin only |
/admin/banners |
AdminBanners |
🔒 admin only |
🔒 Everything under
/adminis nested inside<Route element={<AdminRoute />}>, which renders an<Outlet />only whenisAuthenticated && user.role === "admin"; otherwise it redirects to/.
🧩 Click to expand key runtime flows (Auth, Silent Refresh, Cart)
sequenceDiagram
autonumber
participant U as User
participant Page as Auth Page
participant Ctx as AuthContext
participant Api as auth.api.js
participant Ax as axiosClient
participant BE as Backend API
U->>Page: Submit email + password
Page->>Ctx: handleLogin(email, password)
Ctx->>Api: login(email, password)
Api->>Ax: POST /auth/login
Ax->>BE: POST /api/v1/auth/login
BE-->>Ax: 200 { user, accessToken } + Set-Cookie refreshToken (httpOnly)
Ax-->>Api: response.data
Api-->>Ctx: data
Ctx->>Ctx: applyToken(accessToken, user) → localStorage + axiosClient default header
Ctx-->>Page: { success: true, user }
Page-->>U: Redirect (home / profile)
sequenceDiagram
autonumber
participant Page as Any authenticated page
participant Ax as axiosClient
participant BE as Backend API
Page->>Ax: Request with Authorization: Bearer <expired>
Ax->>BE: Forward request
BE-->>Ax: 401 Unauthorized
Ax->>BE: POST /auth/refresh-token (refreshToken cookie, withCredentials)
BE-->>Ax: 200 { accessToken }
Ax->>Ax: setAccessToken(newToken)
Ax->>BE: Retry original request with new token
BE-->>Ax: 200 OK
Ax-->>Page: response.data
sequenceDiagram
autonumber
participant U as User
participant Ctx as CartContext
participant LS as localStorage (guest_cart)
participant Api as cart.api.js
participant BE as Backend API
U->>Ctx: addToCart(product, size, quantity)
alt Not authenticated (guest)
Ctx->>Ctx: merge/append item in local state
Ctx->>LS: persist items as guest_cart
else Authenticated
Ctx->>Api: add({ product_id, size, quantity })
Api->>BE: POST /cart/add (Bearer token)
BE-->>Api: 200 { success, data }
Ctx->>Api: get() — re-fetch source of truth
Api->>BE: GET /cart
BE-->>Ctx: { items: [...] }
end
-
Clone the repository
git clone https://github.com/MT-KS-04/crest-walk-fe.git cd crest-walk-fe -
Install dependencies
npm install # or: bun install -
Configure environment variables
Create a
.envfile in the project root:# Base URL of the backend REST API (must include /api/v1) VITE_API_URL=http://localhost:3000/api/v1
If
VITE_API_URLis not set,src/config/index.config.jsfalls back tohttp://localhost:8080/api/v1. Make sure this matches wherever your backend (crest-walk-api) is actually running. -
Run the dev server
npm run dev
The app is served at
http://localhost:3000(configured invite.config.js). -
Lint
npm run lint
-
Run tests
npm run test # single run (Vitest) npm run test:watch # watch mode
-
Production build & preview
npm run build # production build → dist/ npm run build:dev # build in development mode (unminified, for debugging) npm run preview # serve the built dist/ locally
This repository has no server code — every screen calls a separate backend (VITE_API_URL, versioned under /api/v1) through the clients in src/api/. Summary of the contract this frontend integrates against:
| Method | Endpoint | Access | Notes |
|---|---|---|---|
GET |
/auth/me |
Bearer token | Current user profile |
POST |
/auth/register |
Public | { email, password, role? } |
POST |
/auth/login |
Public | Sets refreshToken httpOnly cookie |
POST |
/auth/refresh-token |
Cookie | Requires refreshToken cookie |
POST |
/auth/forgot-password / /auth/reset-password |
Public | Password reset flow |
| Method | Endpoint | Access |
|---|---|---|
GET |
/products, /products/search, /products/filter, /products/:id |
Public |
GET POST PUT DELETE |
/cart, /cart/add, /cart/update, /cart/remove |
Auth required |
POST GET DELETE |
/wishlist/add, /wishlist, /wishlist/:productId |
Auth required |
POST GET GET |
/orders/checkout, /orders, /orders/:id |
Auth required |
POST GET |
/reviews/add, /reviews/:productId |
Auth required to post |
GET |
/payment/vnpay_return |
Public (payment gateway redirect) |
| Resource | Endpoints |
|---|---|
| Products | GET/POST /admin/products, GET/PUT/DELETE /admin/products/:id (multipart images) |
| Categories | GET/POST /admin/categories, GET/PUT/DELETE /admin/categories/:id |
| Brands | GET/POST /admin/brands, GET/PUT/DELETE /admin/brands/:id |
| Orders | GET /admin/orders, GET /admin/orders/:id, PUT /admin/orders/:id/status |
| Users | GET /admin/users, GET /admin/users/:id, PUT /admin/users/:id/status, PUT /admin/users/:id/reset-password |
| Inventory | GET /admin/inventory, PATCH /admin/inventory/:productId/size/:size |
| Vouchers | GET/POST /admin/vouchers, GET/PUT/DELETE /admin/vouchers/:id |
| Stats | GET /admin/stats/revenue, GET /admin/stats/bestsellers |
| Reviews | GET /admin/reviews, GET /admin/reviews/:id, PUT /admin/reviews/:id/status, DELETE /admin/reviews/:id |
| Banners | GET/POST /admin/banners (multipart image_url), GET/PUT/DELETE /admin/banners/:id |
All authenticated requests send
Authorization: Bearer <accessToken>via theaxiosClientinterceptor; the refresh cookie is sent automatically thanks towithCredentials: true.
This project is conceptualized and implemented by K'To Mis & His Team. Feel free to reach out via the following channels 👇
- K'To Mis
- 📧 Email: [email protected]
- 🐙 GitHub: @MT-KS-04
- His Team
- 🐙 GitHub: @duytran1652004
- 🐙 GitHub: @diikann-r
- 🐙 GitHub: @VanNghia2112
- 🐙 GitHub: @duyhieu304
This project is distributed under the Apache License 2.0. See the LICENSE file for full terms, rights, and limitations.
© 2026 K'To Mis & His Team. All rights reserved.
Crest Walk — a React storefront and admin dashboard for a shoe e-commerce platform.






