A full-stack canteen ordering platform built around a hand-written FIFO Queue data structure — one independent queue per menu item, exactly like a real canteen kitchen.
Live Demo · Features · How the Queue Works · Getting Started · API Reference
Most "canteen management system" student projects are a CRUD app with a database and a nice UI — useful, but there's no actual data structure doing real work under the hood.
This one is different. The entire ordering pipeline is driven by a linked-list-based FIFO Queue implemented from scratch — no library, no shortcuts. Every dish on the menu gets its own queue instance, so Tea, Coffee, and Poha are all prepared independently and in parallel, the way a real canteen kitchen actually runs multiple stations at once.
On top of that core algorithm sits a complete, working system: real authentication, real inventory that auto-disables dishes when ingredients run out, live order tracking for customers, and a full admin console — all wired end-to-end, not mocked.
- 🍛 Browse menu across different categories
- 🛒 Live cart with quantity steppers and running totals
- 💳 Checkout with name entry and payment method (Cash / UPI / Card)
- 🎫 Auto-generated pickup tokens (
A101,A102, ...) - 📍 Real-time order tracking — live status, queue position, and estimated wait time
- 🔐 Real authentication (bcrypt-hashed passwords + JWT sessions, not a fake login screen)
- 📊 Dashboard — today's revenue, order counts, active queues, low-stock alerts
- 📦 Live Orders — every order, live status, search/filter, one-click collect
- ⏱️ Queue Management — the centerpiece: watch each dish's FIFO queue drain in real time as staff work through orders
- 🍽️ Menu Management — full CRUD, pricing, enable/disable toggles
- 📉 Inventory — ingredient stock tracking; dishes automatically go unavailable when a required ingredient hits zero
- 📈 Analytics — revenue trends, most-ordered items, peak hours, queue performance
- ⚙️ Settings — profile picture upload, password change, canteen branding
src/queue/FifoQueue.js — the DSA: a singly-linked-list FIFO queue
enqueue() / dequeue() / peek() are all O(1)
src/queue/QueueManager.js — owns ONE FifoQueue instance PER MENU ITEM
Map<menuItemId, FifoQueue>
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Tea Queue │ │ Coffee Queue│ │ Poha Queue │
│ A101 → A105 │ │ A104 │ │ A102 → A106 │
│ → A120 │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
Instead of one shared line for the whole canteen, each dish is its own station with its own queue — placing an order enqueues each cart line into its own item's queue, and admin actions (Start Preparing, Mark Ready, Collected) dequeue and progress that station independently. Queues rebuild themselves from the database on every server restart, so a crash or redeploy never loses an in-progress order.
The queue implementation is unit-tested in isolation — 12 tests covering FIFO ordering, empty-queue safety, pointer integrity after draining, mid-queue removal, and per-item isolation. Run them with npm test.
graph LR
subgraph Customer
A[Browse Menu] --> B[Cart]
B --> C[Checkout]
C --> D[Track Order]
end
subgraph Backend[Express API]
E[/api/menu/]
F[/api/orders/]
G[/api/queue/]
H[/api/inventory/]
I[/api/analytics/]
end
subgraph Core[DSA Core]
Q[QueueManager]
FQ1[FifoQueue: Tea]
FQ2[FifoQueue: Coffee]
FQ3[FifoQueue: Poha]
Q --> FQ1
Q --> FQ2
Q --> FQ3
end
subgraph Admin
J[Dashboard]
K[Queue Management]
L[Menu/Inventory]
M[Analytics]
end
C --> F
D --> F
F --> Q
K --> G
G --> Q
J --> I
L --> E
L --> H
public/ Frontend — plain HTML + Tailwind + vanilla JS
userpanel.html Customer: browse menu, cart
checkout.html Customer: name, payment, place order
orderstatus.html Customer: live token tracking
adminlogin.html Admin: authentication
admindashboard.html Admin: stats overview
liveorders.html Admin: live order table
queuemanagement.html Admin: the queue demo — Start/Ready/Collect
menumanagement.html Admin: menu CRUD
inventorymanagement.html Admin: ingredient stock
analytics.html Admin: revenue & trends
adminsettings.html Admin: profile & branding
src/
queue/FifoQueue.js The linked-list queue (core DSA)
queue/QueueManager.js One queue per menu item
routes/ Express route handlers (auth, menu, orders, queue, inventory, analytics, settings)
middleware/auth.js JWT session verification
db.js Lightweight JSON-file datastore (atomic writes)
seed.js Menu, ingredients, and admin user seed data
test/fifoqueue.test.js Unit tests for the queue implementation
server.js Express app entry point
| Layer | Technology |
|---|---|
| Runtime | Node.js 18+ |
| Server | Express 5 |
| Auth | bcrypt + JWT (httpOnly cookies) |
| Data store | JSON file with atomic writes (no external DB dependency) |
| Frontend | Vanilla JavaScript, Tailwind CSS (CDN) |
| Testing | Node's built-in assert — zero test-framework dependencies |
| Deployment | Docker, Render (render.yaml), or any Node host (Procfile) |
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>
npm install
npm run seed
npm startOpen http://localhost:3000 — the customer kiosk loads by default.
| Method | Route | Auth | Purpose |
|---|---|---|---|
POST |
/api/auth/login |
— | Admin login |
GET |
/api/menu |
— | Public menu |
POST |
/api/orders |
— | Place an order |
GET |
/api/orders/lookup/:token |
— | Track an order by token |
GET |
/api/queue |
admin | Snapshot of every item's queue |
POST |
/api/queue/:menuItemId/start |
admin | Dequeue + start preparing |
POST |
/api/queue/:menuItemId/ready |
admin | Mark the current item ready |
POST |
/api/queue/order-item/:id/collect |
admin | Mark collected |
GET/POST/PUT/DELETE |
/api/inventory[/:id] |
admin | Ingredient stock CRUD |
GET |
/api/analytics |
admin | Revenue, trends, peak hours |
PUT |
/api/auth/me |
admin | Update profile / avatar |
GET/PUT |
/api/settings |
mixed | Canteen branding |
(Full route list in src/routes/)
npm test12 unit tests on the FifoQueue class — FIFO ordering, empty-queue handling, linked-list pointer integrity after draining, mid-queue removal, and confirmation that independent per-item queues don't interfere with each other.
Deployment-ready out of the box:
render.yaml— one-click deploy via Render's Blueprint featureDockerfile— for Railway, Fly.io, or any container hostProcfile— for any Heroku-style Node host
Worth knowing about free hosting tiers: most free web-service plans (Render included) spin your app down after a period of inactivity and cold-start on the next request, and don't support a persistent disk — meaning locally-stored data resets on restart. Neither is a bug in this app; it's a tradeoff of $0 hosting. A free uptime pinger (e.g. cron-job.org hitting /api/health every ~10 minutes) keeps cold starts away; a paid tier removes both issues entirely.
Common setup issues (click to expand)
- Windows PowerShell:
The token '&&' is not a valid statement separator— PowerShell doesn't support&&. Run commands on separate lines, or join with;. - Blank page at
localhost:3000— something else may already be using port 3000. Run on a different port:$env:PORT=4000thennpm start, or setPORT=4000in a.envfile (see.env.example). - Page loads blank in an editor's built-in browser preview — always open the app in a real browser tab, not an in-editor preview pane (those run in a sandboxed iframe that blocks the cookies/fetch calls this app needs).
The thing being graded/evaluated here is the Queue implementation, not the persistence layer. A JSON file needs zero native modules and zero external services, so it runs identically on a laptop, in Docker, or on a free-tier host with no setup. Writes are atomic (write-then-rename), so a crash mid-write can't corrupt the file. Every route goes through a single readDB()/writeDB() pair in src/db.js, so swapping in Postgres or SQLite later is a contained, single-file change.
- WebSocket/Socket.io push instead of polling for true real-time updates
- Swap the JSON file store for Postgres/SQLite for larger-scale persistence
- Order cancellation using the queue's
removeWhere()operation - Role-based admin accounts (e.g. kitchen staff limited to Queue Management)
Deodatta Pagar Computer Engineering Student 📍 NASHIK