A multi-step product bundle builder built as a frontend take-home project. Shoppers configure a security system through a 4-step accordion (cameras → plan → sensors → extra protection), while a live review panel keeps selected items, quantities, variants, and pricing in sync.
Built to closely match the provided Figma design with responsive layouts for desktop, tablet, and mobile.
- 4-step accordion builder — Step 1 (Cameras) is open by default, with live "N selected" counters.
- Product cards — discount badges, color/variant chips, quantity steppers, descriptions, and compare-at pricing.
- Independent variant quantities — each variant maintains its own quantity. Switching variants never loses previously selected quantities.
- Live review panel — selected items are grouped by category and stay synchronized with the builder.
- Dynamic pricing — subtotal, savings, discounts, and total update as quantities change.
- Data-driven UI — products and steps are rendered from structured data rather than product-specific markup.
- Persistence — "Save my system for later" stores the configuration in
localStorageand restores it on return. - Responsive design — desktop, tablet, and mobile layouts based on the provided design.
- REST API — bundle and product data are served through a lightweight Express backend.
- React + TypeScript
- Vite
- Tailwind CSS
- TanStack Query
- Axios
- react-hot-toast
- Lucide React
- Node.js + Express
- TypeScript
- REST API
- CORS
bundle-builder/
├── src/
│ ├── components/ # Reusable UI components
│ ├── context/ # Global bundle state and reducer
│ ├── hooks/ # Custom React hooks
│ ├── types/ # Shared TypeScript types
│ ├── utils/ # Helper and utility functions
│ ├── services/ # API communication
│ └── assets/ # Static images and icons
│
├── server/
│ └── src/
│ ├── controllers/ # Request handling and API logic
│ ├── data/ # Bundle steps and product data
│ ├── routes/ # API endpoint definitions
│ └── index.ts # Express server entry point
│
├── .env.example # Environment variable template
├── .gitignore
└── package.json # Frontend dependencies and scripts
components/— Contains UI components such as the builder, product cards, step accordion, quantity stepper, and review panel.context/— Manages the shared bundle state using React Context anduseReducer.hooks/— Contains reusable custom hooks for calculations, API queries, and product variant logic.types/— Contains shared TypeScript types and interfaces.utils/— Contains reusable helper functions such as localStorage handling and image URL utilities.services/— Handles communication with the backend API.assets/— Contains static images, icons, and other frontend assets.
controllers/— Handles API request logic and prepares responses.data/— Contains the structured bundle steps and product data used by the API.routes/— Defines the available API endpoints and connects them to the controllers.index.ts— Initializes the Express server, middleware, routes, and CORS configuration.
The frontend and backend are separate Node projects within the same repository, each with its own package.json.
- Node.js 18+
- npm
git clone https://github.com/cheetah-10/Bundle-builder.git
cd Bundle-builderCreate .env from .env.example:
cp .env.example .envDefault:
VITE_API_BASE_URL=http://localhost:5000/apiNo secrets or API keys are required.
cd server
npm install
npm run devRuns on http://localhost:5000.
Health check:
http://localhost:5000/health
In a second terminal, from the project root:
npm install
npm run devVite will provide the local development URL.
Both frontend and backend need to be running.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/steps |
Returns the 4 builder steps |
| GET | /api/products |
Returns all products |
| GET | /api/bundle-config |
Returns steps and products |
| GET | /health |
Health check |
The application is fully data-driven. Products contain their configuration, pricing, images, categories, and optional variants.
{
id: string;
stepId: string;
title: string;
description?: string;
learnMoreUrl?: string;
price: number;
compareAtPrice?: number;
discount?: string;
image: string;
category: string;
defaultQuantity?: number;
variants?: Variant[];
}Variant quantities are tracked independently using a product/variant key, allowing multiple variants of the same product to appear separately in the review panel.
- Context + useReducer — used for centralized bundle state and predictable quantity/variant updates without introducing Redux.
- TanStack Query — handles API fetching and caching while keeping data-fetching logic outside UI components.
- Data-driven rendering — product-specific markup is avoided so products can be changed through data.
- Explicit persistence —
localStorageis updated when the user clicks "Save my system for later", matching the requested configure → save → return flow. - Lightweight API — Express serves the bundle data without introducing an unnecessary database for this prototype.
- Add automated unit and interaction tests.
- Add a root-level command to run frontend and backend together.
- Expand accessibility and keyboard interaction testing.
- Add more comprehensive API error/loading states.