Pista
Pista is an AI-powered, white-label ordering platform for cafés and restaurant chains. A brand configures its identity and menu once, and gets a polished mobile-first ordering app with AI recommendations, food intelligence, orders, loyalty and an admin dashboard.
This site has two parts: Developer docs (how Pista is built and how to run, extend and deploy it) and User guides (how customers order and how café owners run the admin dashboard).
What's included
🛍️ Customer app
Menu, item detail with AI "Know your cup", AI assistant, cart, checkout, orders, rewards.
📊 Admin dashboard
Analytics, orders, menu CRUD, customers, discounts, branding and settings.
🎨 White-label engine
Brand name, colours, font and AI flags — re-theme the whole app live.
🔐 Real backend
Prisma + SQLite, NextAuth email/password auth, REST API routes.
Getting started
Prerequisites
- Node.js 18.18+ (Node 20 LTS recommended)
- npm 9+
Install & run
cd pista
npm install # also runs `prisma generate`
npm run setup # creates the SQLite DB (prisma db push) + seeds data
npm run dev # http://localhost:3000
Open http://localhost:3000. The marketing site is at /; click View live demo to open the ordering app at /menu. The app is mobile-first — narrow your window or use device emulation.
demo@pista.app / password. This account has the admin role, so it can open the dashboard at /admin. New sign-ups are customers.Useful scripts
| Script | What it does |
|---|---|
npm run dev | Start the dev server with hot reload |
npm run setup | prisma db push + seed menu, users, sample orders |
npm run seed | Re-seed the database |
npm run build | prisma generate + production build |
npm start | Run the production build |
Architecture
Pista is a single Next.js 14 (App Router) application. The customer app and admin dashboard are route groups in the same project; both talk to the database through API route handlers (and a few server reads).
Browser (customer app / admin dashboard)
│ fetch()
▼
Next.js App Router ── pages (client components)
│
├─ /api/menu, /api/menu/[id] → read menu (public)
├─ /api/ai → scripted recommender
├─ /api/orders, /api/orders/[id] → place / list / update orders
├─ /api/brand → white-label config
├─ /api/items, /api/items/[id] → menu CRUD (admin)
├─ /api/discounts/* → promo codes
├─ /api/admin/* → analytics, all-orders, customers
└─ /api/auth/[...nextauth] → NextAuth
│
▼
Prisma Client ──► SQLite (prisma/dev.db)
Tech stack
| Layer | Choice |
|---|---|
| Framework | Next.js 14 (App Router), React 18 |
| Styling | Tailwind CSS; brand colours via CSS variables |
| Database | SQLite via Prisma ORM |
| Auth | NextAuth (credentials, JWT sessions), bcrypt |
| AI | Rule-based recommender (swappable for an LLM) |
Project structure
pista/
├─ app/
│ ├─ layout.js # root layout + providers + fonts
│ ├─ globals.css # design tokens (white-label CSS vars)
│ ├─ page.js # marketing landing (/)
│ ├─ menu/page.js # ordering app home
│ ├─ item/[id]/page.js # item detail + AI info
│ ├─ ai/page.js # Pista AI assistant
│ ├─ cart, checkout, success, account, login, register
│ ├─ admin/ # dashboard (layout + sub-pages)
│ │ ├─ layout.js # sidebar + admin-role guard
│ │ ├─ page.js # Overview (analytics)
│ │ ├─ orders, menu, customers, discounts, branding, settings
│ └─ api/ # route handlers (REST)
├─ components/ # Providers, Header, BottomNav, ProductCard, AdminUI
├─ lib/ # db.js, auth.js, admin.js, ai.js, menu.js
├─ prisma/ # schema.prisma, seed.js, dev.db
└─ public/docs/ # this documentation site
Data model
Defined in prisma/schema.prisma. Array fields on Item (ingredients, allergens, tags, sizes) are stored as JSON strings and parsed by parseItem() in lib/db.js.
| Model | Key fields |
|---|---|
| Brand | name, brandHex, darkHex, font, subdomain, aiAssistant, aiCards, aiUpsell, aiLoyalty |
| Category | id, label, sort |
| Item | name, categoryId, price, img, desc, kcal, caffeine, protein, sugar, origin, ingredients, allergens, tags, sizes, aiTip, live |
| User | name, email, password (bcrypt), role (customer/admin), points |
| Order | userId, subtotal, tax, reward, discount, discountCode, total, fulfilment, payment, status |
| OrderItem | orderId, itemId, name, size, milk, unit, qty |
| Discount | code, percent, active |
API reference
All routes live under /api. Auth is via the NextAuth session cookie. "Admin" routes require a user with role: "admin" and return 403 otherwise.
Menu
{ categories, items }. all=1 (admin UI) includes hidden items.AI
{ query }. Returns { intro, picks:[{ item, why }] } scored over live items.Orders
{ lines, fulfilment, payment, discountCode? }. Recomputes totals server-side, applies promo, awards points.{ status } — preparing → ready → completed → cancelled.Menu management (admin)
live / price.409 — hide it instead).Brand, discounts & admin data
{ code } → { valid, percent } for checkout.PATCH|DELETE /api/discounts/[id] to toggle / remove.Auth
{ name, email, password } — creates a customer.Auth & roles
Authentication is handled by NextAuth's Credentials provider (lib/auth.js). Passwords are hashed with bcrypt; sessions are JWTs. The user's role is embedded in the JWT and exposed on session.user.role.
- customer — default for sign-ups. Can order, view their orders and earn points.
- admin — can open
/adminand call admin APIs. The seededdemo@pista.appis an admin.
Server routes guard with requireAdmin() (lib/admin.js), which returns 401 when signed out and 403 for non-admins. The admin UI is additionally guarded in app/admin/layout.js.
role = "admin" in the database (e.g. via npx prisma studio).White-label theming
Brand colours are CSS variables (RGB channels) defined in app/globals.css and consumed by Tailwind (bg-brand, text-brand-dark, bg-brand-tint). The BrandProvider in components/Providers.js loads the brand from /api/brand, applies the variables to :root, and the admin "Publish" button persists changes.
:root {
--brand: 122 176 74; /* pistachio */
--brand-dark: 54 81 31;
--brand-tint: ...; /* derived */
}
Because every brand-coloured element reads these variables, changing the primary colour in Admin → Branding re-themes the entire customer app instantly.
AI recommender
The assistant is a transparent, rule-based recommender in lib/ai.js, served from POST /api/ai. It maps the user's free-text intent to scoring rules over item metadata (tags, caffeine, calories, protein, rating) and returns the top picks with a human-readable reason for each.
The return shape is { intro, picks: [{ item, why }] }. To switch to a real LLM, replace recommend() with an API call (e.g. the Claude API) that returns the same shape — no UI changes needed.
Deployment
Hosting the app
Pista runs anywhere Next.js does. For Vercel:
- Push the repo to GitHub and import it into Vercel.
- SQLite isn't suitable for serverless — switch
DATABASE_URLto a hosted Postgres and change the Prismaprovidertopostgresql. - Set env vars:
DATABASE_URL,NEXTAUTH_URL,NEXTAUTH_SECRET(openssl rand -base64 32). - Run
prisma migrate deployand seed once.
Hosting these docs
This docs site is a self-contained static page (public/docs/index.html). When the app runs it is served at /docs. To host it on its own, deploy the public/docs folder to any static host:
# preview locally
npx serve public/docs
# or drop public/docs into Netlify / GitHub Pages / Vercel
NEXTAUTH_SECRET, move off SQLite, and review the demo credentials.User guide — for customers
How to order on a Pista-powered café app.
1. Browse the menu
Open the app and browse by category (Ice Blended, Hot Coffee, Tea, Food). The ✨ Picked for you row highlights signature and top-rated items. Tap any item to see details.
2. Know what you're ordering
Each item has a "Know your cup" card: bean origin, ingredients, allergens and nutrition (calories, protein, sugar, caffeine), plus an AI tip. Choose a size and milk, then Add to bag.
3. Ask Pista AI
Not sure what to get? Open Pista AI and tap a mood ("Cold & refreshing", "High protein", "Low caffeine") or type your own. It ranks the menu and explains why each pick fits — add any suggestion straight to your bag.
4. Checkout
Review your bag (with an AI pairing suggestion), then check out: choose pickup / dine-in / delivery, add a promo code, pick a payment method and place the order. You'll need an account — sign up takes a moment.
5. Track orders & earn rewards
Your Account shows order history, status and loyalty points (earned on every order).
User guide — for café owners
Running your store from the Pista admin dashboard at /admin (admin login required).
Overview
Your home screen: revenue, order count, average order value and customer count, a 14-day revenue chart, your top sellers, and the latest orders.
Orders
See every order, filter by status, and advance them: Preparing → Ready → Completed. Updates are saved instantly and reflected in the customer's account.
Menu
Add, edit and delete items. Set price, photo, description, nutrition, origin, ingredients, allergens, tags and an AI tip. Toggle an item live to show/hide it without deleting. (Items with past orders can be hidden but not deleted, to preserve history.)
Customers
Your registered customers with order counts, lifetime spend and points.
Discounts
Create promo codes (e.g. WELCOME10 for 10% off). Enable, disable or delete them; customers apply them at checkout.
Branding
Set your brand name, subdomain, colours and font. The live preview updates as you edit; click Publish to apply it to your whole customer app.
Settings
Store details and AI feature flags — turn the assistant, food-intelligence cards, smart upsells and AI loyalty on or off.
FAQ & troubleshooting
I can't open /admin
The dashboard requires the admin role. Sign in with demo@pista.app / password, or promote your user in the database.
The menu is empty
Run npm run setup (or npm run seed) to create and populate the database.
Login fails right after install
Make sure the database is seeded and NEXTAUTH_SECRET is set in .env. Restart npm run dev after changing env vars.
Build warns about Prisma / Next versions
Informational only. Versions are pinned for stability; upgrade deliberately.
How do I reset the data?
Delete prisma/dev.db and run npm run setup again.