A modern, high-performance Coffee Shop Frontend application built with Next.js 15 (App Router) and TypeScript. This project serves as a customer-facing portal for browsing coffee menus, customizing orders with granular preferences, and managing a digital checkout flow.
This application is designed to provide a seamless ordering experience. It integrates with a .NET API backend and employs a sophisticated Service Layer architecture that allows for instant switching between live production data and local mock environments.
- Dynamic Menu & Search: Browse products by category with debounced search and server-side pagination.
- Granular Customization: Detailed drink configuration including:
- Sugar Levels: (e.g., No Sugar, Normal, Extra).
- Ice Levels: (e.g., No Ice, Normal, Extra).
- Coffee Strength: Specialized levels with dynamic pricing.
- Special Instructions: Custom text notes for baristas (max 200 chars).
- Hybrid Cart System:
- Persistent local cart (LocalStorage).
- Intelligent backend synchronization (detects changes via hashing to minimize API calls).
- Support for Dine-in and Takeaway order types with a dedicated selector.
- Real-time Checkout & Payment:
- Order creation and multi-step updates.
- KHQR Payment Simulation: Real-time status polling with interactive feedback and success/failure states.
- Visual Polish: Built with Tailwind CSS 4 and Shadcn UI components for a premium look and feel.
- Framework: Next.js 15 (App Router & Turbopack)
- Language: TypeScript (Strict typing for API DTOs)
- State Management: TanStack Query v5 (Server state) & React Context (UI state)
- Styling: Tailwind CSS 4 & Shadcn UI
- API Client: Axios with request/response interceptors
- Icons & Feedback: Lucide React & Sonner
The application follows a modular architecture to ensure separation of concerns and maintainability:
- UI Layer (
/src/components): Pure and logic-heavy React components. - Hook Layer (
/src/hooks): Encapsulates React Query logic and polling mechanisms. - Service Layer (
/src/service): The "Brain" of the data flow. It decides whether to fetch from the API Layer or the Mock Layer based on environment variables. - API Layer (
/src/api): Low-level Axios implementations targeting the .NET backend. - Mock Layer (
/src/mock): Realistic data simulations for development without a running backend.
src/
├── api/ # Axios implementations for each domain
├── app/ # Next.js App Router (Pages, Layouts, Routes)
├── components/ # UI components (ui/, pages/, and shared)
├── contexts/ # Global React Contexts (e.g., CartContext)
├── hooks/ # Custom hooks and React Query implementations
├── lib/ # Utilities like axios instance and tailwind-merge
├── mock/ # Local mock data and service simulations
├── providers/ # Global context providers (ReactQueryProvider)
├── service/ # Switching layer between mock and real API
├── types/ # TypeScript interfaces (api/ matching backend DTOs)
└── utils/ # Shared helper functions (Cart hashing, building payloads)
- Node.js: 20.x or later
- Package Manager: npm or pnpm
npm installCreate a .env.local file in the root directory:
NEXT_PUBLIC_API_URL=https://your-api-endpoint.com/api
NEXT_PUBLIC_USE_MOCK=true # Set to 'false' to connect to live backend
NEXT_PUBLIC_IS_DEVELOPMENT=trueStarts the server with Turbopack for fast HMR.
npm run devRuns linting, formatting, and builds the production-ready bundle.
npm run build
npm run startnpm run lint # Runs ESLint checks
npm run format # Formats code using Prettier- Versioning: All requests automatically include the header/param
api-version=2026-01-01via Axios interceptors. - DTO Safety: Types in
src/types/apiare mapped exactly to the .NET backend DTOs to ensure runtime consistency. - Cart Syncing: During the checkout transition, the application compares a local
cartHashwith the existing backend order to decide between anupdateOrder (PUT)or simply viewing the existing checkout.
- Mock Switch: Toggle
NEXT_PUBLIC_USE_MOCKin your.envto work completely offline with the data insrc/mock/. - Hooks Safety: All API hooks are designed with the "Rules of Hooks" in mind. Avoid calling
useLookupsoruseCartinside conditional returns. - State Hydration: The cart and payment QR data are hydrated from
localStorageto ensure users don't lose their progress on page refreshes.
- Internationalization: Add i18n support for Khmer and English languages.