A POS and inventory admin dashboard paired with an AI-powered customer storefront.
At a Glance | AI Recommendations | Features | Tech Stack | Getting Started | Auth Model | Order Lifecycle | License
Table of Contents
RetailFlow is a full-stack retail platform combining a POS / inventory admin dashboard with an AI-powered customer storefront. A retailer manages products, stock, and both in-store and online orders from one dashboard; customers browse, create accounts, check out, and get OpenAI-powered product recommendations on a separate storefront app.
| Area | What it covers | Full details |
|---|---|---|
| Apps | Monorepo of three apps sharing one PostgreSQL database: API, Admin, and Store | Apps |
| AI recommendations | OpenAI-powered product picks on the homepage, product page, and cart, with a deterministic fallback so the storefront never breaks without an API key | AI Recommendations |
| Customer storefront | Amazon.in-style browsing, accounts, cart and checkout, order tracking, and AI-powered recommendations | Customer Storefront |
| Retailer admin | Staff login, product, category and inventory management, POS sales, Online Orders, customers, and reports | Retailer Admin |
| Backend | Modular Express API with two JWT auth realms, one Sale model for POS and online orders, and OpenAI recommendation endpoints |
Backend |
| Tech stack | React 19, TypeScript, Vite, Tailwind CSS v4, Node.js, Express 5, Prisma 7, PostgreSQL, OpenAI API | Tech Stack |
| Getting started | Database, backend environment, install, migrate and seed, then run all three apps | Getting Started |
| Auth model | Separate staff and customer JWT realms, each with its own secret | Auth Model |
| Order lifecycle | POS sales are created as DELIVERED; online orders move from PENDING to DELIVERED, or CANCELLED |
Order Lifecycle |
| Scripts | Dev server, build, migration, staff-user, and catalog-seed commands | Scripts |
| Roadmap | Redis, Docker, CI; returns, sales visits, offline-first PWA | Roadmap |
| License | MIT | License |
This is a monorepo with three apps sharing one PostgreSQL database:
| App | Path | Purpose |
|---|---|---|
| API | backend/ |
Express + Prisma REST API |
| Admin | frontend/ |
Retailer dashboard — POS, inventory, online order fulfillment, reports |
| Store | shop/ |
Customer storefront — browsing, accounts, cart, checkout, AI recommendations |
RetailFlow uses the OpenAI API to suggest products to customers on the storefront, based on what they have viewed, bought, or put in their cart.
AI-powered recommendations in three places, each with a deterministic fallback so the storefront never breaks without an API key:
| Placement | Section title | Behavior |
|---|---|---|
| Homepage | "Recommended for You" | Personalized from a signed-in customer's browsing/purchase history; falls back to best-sellers for guests or new accounts |
| Product page | "You May Also Like" | Related items from the same category re-ranked by the model |
| Cart | "Frequently Bought Together" | Complementary add-ons not already in the cart |
How it works:
| Step | What happens |
|---|---|
| 1. Shortlist | The backend builds a capped list of active, in-stock candidate products, filtered by category: the categories a signed-in customer has viewed or purchased (homepage), the current product's category (product page), or the categories of the items in the cart (cart). The product being viewed and any items already in the cart are excluded. |
| 2. Ask the model | The shortlist and the shopper's context (recent views and purchases, the product being viewed, or the cart contents) are sent to the OpenAI Chat Completions API in JSON mode. |
| 3. Validate | Only product IDs from the shortlist are accepted. Anything else the model returns is dropped, so it can never invent a product. |
| 4. Cache | Results are held in an in-memory cache for 10 minutes to avoid redundant calls. |
| 5. Fall back | If no API key is set, the request fails or takes longer than 8 seconds, or the model returns nothing usable, the storefront shows deterministic results instead: best-sellers on the homepage, and products from the same category (or the cart's categories) on the product page and cart. |
Endpoints:
| Endpoint | Auth | Purpose |
|---|---|---|
GET /api/recommendations/home |
Optional customer token; personalized when signed in | Homepage picks |
GET /api/recommendations/product/:id |
None | Related products for a product page |
POST /api/recommendations/cart |
None; body { "productIds": [1, 2] } |
Add-ons for a cart |
Turn it on: set OPENAI_API_KEY in backend/.env (optionally OPENAI_MODEL, which defaults to gpt-4o-mini), as described in Getting Started. The client is created lazily, so the server starts normally without a key and simply uses the fallbacks.
Where the code lives: backend/src/modules/recommendations/ (routes, controller, service) and the RecommendedProducts component in shop/src/components/.
- Amazon.in-style marketplace UI: two-tier navy header with a category strip, homepage category tiles and per-category product rows, a filterable/sortable listing page, and a buy-box style product page (star ratings, M.R.P. strike-through pricing, Add to Cart + Buy Now)
- 32 seeded products across 9 categories (Electronics, Mobiles & Accessories, Fashion, Home & Kitchen, Beauty, Books, Sports & Fitness, Toys & Baby, Grocery) — see
backend/scripts/seed-catalog.ts - Product browsing with search, category filters, and images
- Customer accounts: signup/login (JWT), persistent order history
- Cart and guest or logged-in checkout
- Order tracking with a fulfillment status stepper (Pending → Confirmed → Packed → Shipped → Delivered)
- AI-powered product recommendations on the homepage, product page, and cart — see AI Recommendations
- Staff login (JWT, role-based
Usermodel) - Product, category, and inventory management, including image uploads
- Point-of-sale sales flow with a running cart, discounts, tax, and multiple payment methods
- Online Orders: a dedicated view for orders placed through the storefront, separate from in-store POS sales, with guarded status transitions (e.g. an order can't jump from Pending straight to Shipped)
- Customer management, sales history with printable invoices, and sales/profit/payment-method reports
- Modular Express API (
routes→controller→serviceper feature), Prisma ORM over PostgreSQL via the@prisma/adapter-pgdriver adapter - Two independent JWT auth realms — staff and customer — with separate secrets, so a token minted for one can never be replayed against the other
- A single
Salemodel backs both POS sales and online orders (source: POS | ONLINE), avoiding a duplicateOrdermodel while keeping existing POS behavior untouched - AI recommendation endpoints use the official
openaiSDK with a lazily-constructed client (the server never crashes on startup ifOPENAI_API_KEYis unset), a capped/category-filtered candidate list sent to the model, strict validation that the model only picks from real candidate IDs, and an in-memory TTL cache to avoid redundant calls
| Layer | Technologies |
|---|---|
| Frontend (both apps) | React 19, TypeScript, Vite, Tailwind CSS v4, React Router |
| Backend | Node.js, Express 5, TypeScript, Prisma 7, PostgreSQL |
| Auth | JWT (jsonwebtoken), password hashing (bcryptjs) |
| AI | OpenAI API (Chat Completions, JSON mode) |
| Uploads | multer, served as static files |
Setup steps: database, environment, install and seed, run everything
Create a PostgreSQL database and point DATABASE_URL at it in backend/.env (copy backend/.env.example as a starting point).
In backend/.env, set:
DATABASE_URL=postgresql://user:password@localhost:5432/retailflow
JWT_STAFF_SECRET=<random 32+ byte secret, e.g. `openssl rand -hex 32`>
JWT_CUSTOMER_SECRET=<a different random secret>
OPENAI_API_KEY= # optional — recommendations fall back gracefully without it
OPENAI_MODEL=gpt-4o-mini # optional override
PORT=4000
cd backend
npm install
npx prisma migrate deploy
npx prisma generate
# Create your first admin login
npx tsx scripts/create-staff-user.ts "Your Name" you@example.com yourpassword
# Populate a multi-category product catalog (idempotent — safe to re-run)
npx tsx scripts/seed-catalog.ts| Command | Starts |
|---|---|
cd backend && npm run dev |
http://localhost:4000 |
cd frontend && npm run dev |
Admin dashboard, http://localhost:5173 |
cd shop && npm run dev |
Customer storefront, http://localhost:5174 |
Both frontends read their API base URL from VITE_API_BASE_URL in their own .env (defaults to http://localhost:4000).
Two separate JWT realms, each with its own secret and localStorage token, so a leaked customer token can never authenticate as staff (or vice versa):
| Realm | Access | Endpoints |
|---|---|---|
Staff (User model, roles ADMIN/MANAGER/STAFF) |
Required on virtually every admin-only route: inventory, customers, reports, POS sales, and order-status updates. | POST /api/auth/staff/login |
Customer (Customer model) |
Optional on public storefront routes (so guest checkout still works) but required for order history/detail. Signing up with an email that already exists as a passwordless walk-in customer (created via an in-store POS sale) claims that existing record instead of creating a duplicate. | POST /api/auth/customer/signupPOST /api/auth/customer/login |
Product browsing (GET /api/products, GET /api/categories) stays fully public.
Every sale — in-store or online — is a Sale row distinguished by source:
| Source | Created | Status handling |
|---|---|---|
POS (source: POS) |
Created instantly by staff | orderStatus is set straight to DELIVERED since there's no shipping step. |
Online (source: ONLINE) |
Created via POST /api/orders from the storefront |
Starts at orderStatus: PENDING and is advanced by staff through the admin Online Orders page: PENDING → CONFIRMED → PACKED → SHIPPED → DELIVERED, or CANCELLED from any state before SHIPPED. Transitions are validated server-side against an explicit allowed-transitions map. |
Available commands: dev server, build, migrations, seeding
| Command | Where | What |
|---|---|---|
npm run dev |
each app | Start the dev server |
npm run build |
frontend, shop |
Type-check + production build |
npx prisma migrate deploy |
backend |
Apply pending migrations |
npx tsx scripts/create-staff-user.ts <name> <email> <password> [role] |
backend |
Create or update an admin/staff login |
npx tsx scripts/seed-catalog.ts |
backend |
Seed/update the 9-category, 32-product demo catalog |
- Redis, Docker, CI
- Returns, sales visits, offline-first PWA