Session-based order tracking for Side Hustle Coffee. A lightweight PWA for logging orders during service, tracking material usage, and reviewing session history.
npm install
npm run devOpen http://localhost:5173. For iOS PWA experience, add to home screen.
This app helps a coffee shop owner track orders during pop-up sessions. It's not a POS — no payments, no receipts. The goal is operational insight: what's being ordered, how long sessions run, and eventually, what it costs to operate.
- Start a session — captures a snapshot of the current menu
- Log orders as they come in — each timestamped with customizations
- End the session — locks the snapshot, records duration
- Review history — see what was ordered, when, and how much
-
Menu snapshots: When a session starts, we freeze a copy of the menu. Mid-session edits update both the live menu AND the active session's snapshot. Closed sessions keep their original snapshot for accurate historical data.
-
Denormalized orders: Orders store
itemNameand customization values directly, not foreign keys. This means historical orders stay accurate even if menu items are renamed or deleted later. -
Local-first: All data lives in IndexedDB via Dexie. No backend, no auth. Data persists in the browser.
| Layer | Choice | Notes |
|---|---|---|
| Framework | React 18 | Standard hooks, no class components |
| Routing | React Router 6 | Nested routes under AppShell |
| State | Zustand | Three stores: menu, session, order |
| Storage | Dexie (IndexedDB) | Typed tables, reactive queries available |
| Styling | Tailwind + ShadCN | Custom "Warm Counter" theme |
| Build | Vite | PWA plugin configured |
src/
├── components/
│ ├── ui/ # ShadCN primitives (Button, Card, Dialog, etc.)
│ ├── layout/ # AppShell, Header
│ ├── session/ # SessionCard, SessionStats, OrderList, OrderEntry
│ └── menu/ # MenuItemCard, MenuItemForm
├── pages/
│ ├── Home.tsx # Landing, start session, recent sessions
│ ├── ActiveSession.tsx # Live order logging
│ ├── SessionHistory.tsx # List of closed sessions
│ ├── SessionDetail.tsx # View a specific closed session
│ └── Menu.tsx # Manage items and customizations
├── stores/
│ ├── menuStore.ts # Menu items + customizations
│ ├── sessionStore.ts # Sessions + active session
│ └── orderStore.ts # Orders for current/viewed session
├── db/
│ ├── index.ts # Dexie setup + seeding
│ └── types.ts # TypeScript interfaces
└── lib/
└── utils.ts # cn(), formatters, ID generation
MenuItem {
id, name, category, baseCost?, available, createdAt, updatedAt
}
CustomizationOption {
id, category (temperature|milk|syrup|size), name, costModifier?, available
}
Session {
id, status (active|closed), startedAt, endedAt?, customerCount?, notes,
menuSnapshot: { items: MenuItem[], customizations: CustomizationOption[] }
}
Order {
id, sessionId, timestamp, itemName, itemCategory,
customizations: { temperature?, milk?, syrup?, size? },
notes
}| Role | Color | Hex |
|---|---|---|
| Background | Cream | #FAF7F2 |
| Surface | White | #FFFFFF |
| Primary | Espresso | #5C4033 |
| Accent | Terracotta | #C67D4D |
| Muted | Oat | #D4C9BE |
| Text | Dark Roast | #2C2420 |
Fonts: Inter (body), Fraunces (display headings)
- Start/end sessions
- Log orders with customizations
- View session history and details
- Menu item CRUD (add, edit, hide, delete)
- Menu snapshots per session
- Mid-session menu edits update active snapshot
- PWA manifest configured
- Responsive, mobile-first layout
Planned features roughly in priority order:
- Customization CRUD — add/edit/remove milk types, syrups, sizes from the Menu page
- Order editing — tap an order to modify it (currently can only delete)
- Confirmation toasts — feedback when orders are added, sessions ended, etc.
- Cost tracking — set costs on items/customizations, calculate session totals
- Session summary export — CSV or shareable summary
- Usage reports — charts showing popular items, busy times, trends
- Multi-location support — if Side Hustle expands
- Cloud sync — optional backup/restore
- Offline resilience — service worker caching for full offline use
Components are manually added (no CLI in this setup). Copy from ui.shadcn.com, paste into src/components/ui/, and update imports to use @/lib/utils for cn().
Stores use Zustand with async actions that write to Dexie first, then update local state. Example:
addItem: async (item) => {
const newItem = { ...item, id: generateId(), ... };
await db.menuItems.add(newItem); // persist
set((state) => ({ items: [...state.items, newItem] })); // update UI
}npm run dev -- --hostThen open http://<your-ip>:5173 on your phone. For PWA install testing, you'll need HTTPS (use a tunnel like ngrok or deploy to Vercel/Netlify).
npm run dev # Start dev server
npm run build # Production build
npm run preview # Preview production build locallyThings to clarify as development continues:
- Do you want to track who placed each order, or is it purely anonymous volume tracking?
- For cost tracking, should we support different costs per customization (e.g., oat milk costs more)?
- Any interest in a "quick session" mode that skips the full UI for rapid logging?
- Should closed sessions be editable, or strictly read-only?
Built with care for Side Hustle Coffee ☕