Ecommerce Template
Next.js·React 등으로 만든 최신 전자상거래 템플릿
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A complete ordering system for cafes and restaurants: customers browse a menu, say where they
are, and order. Staff watch orders land on a live board and move them through to completion.
Everything runs locally. No external APIs, no API keys, no third-party accounts. SQLite for
storage, local password hashing, locally generated and locally scanned QR codes.
• Customer app + staff dashboard:
• API (started automatically alongside it):
The database is created and seeded on first boot at data/khapee.db.README 전문 보기 ↗What it is: a food pre-ordering app for cafes and restaurants. Customers order from their phone before they arrive, from their table by scanning a QR code, or for pickup, and the kitchen sees each order on a live board and moves it through to ready.
Who it's for: students and anyone short on time who would rather order ahead than queue, and the local cafes and restaurants that serve them.
Status: live, with restaurants in Indore onboarded.
Live site: https://khapee.com
License: All rights reserved (see LICENSE).
Khapee was co-founded and built by two collaborators:
Customers browse a menu, say where they are, and order. Staff watch orders land on a live board and move them through to completion. The app is a single Node process with a SQLite database, local password hashing, and QR codes generated and scanned in the browser. Payments go straight from the customer's UPI app to the restaurant; Khapee never holds money. Running it locally needs no API keys or third-party accounts. How the live site is hosted is in DEPLOY.md.
The marketing website lives separately in website/ and does not talk to the app.
npm install
npm run dev
The database is created and seeded on first boot at data/khapee.db.
Password for all of them: password123
| Role | Restaurant | |
|---|---|---|
| Customer | [email protected] | — |
| Customer | [email protected] | — |
| Staff | [email protected] | Mornington Coffee House |
| Staff | [email protected] | Basil & Bay |
| Staff | [email protected] | The Tandoor Room |
| Staff | [email protected] | Sakura Bowl (closed) |
| Staff | [email protected] | Malwa Chai & Poha |
The seeded restaurants are invented demo businesses used to exercise the app. Customers can also order as a guest — an account is never required.
Three ways, all local:
/for-restaurants creates the owner account, the restaurant and its
tables (with QR codes) in one step, then drops you into the editor.npm run import -- data/sample-import.json
The JSON file describes restaurants, menus, staff logins and image paths; see data/sample-import.json for the shape. Image paths point at files on your own disk and are copied into the app's upload folder — nothing is fetched over the network, so use photos you took or otherwise have the right to use. Menus, descriptions and photography on other ordering platforms belong to those platforms and the restaurants; don't copy them in.
npm test
npm run seed
npm test boots a throwaway server against its own database and drives the whole API
(217 assertions covering all three ordering scenarios, group ordering, payments, location,
restaurant editing and their failure paths).
npm run seed wipes and reseeds the demo data. npm run build && npm start serves the built
app from the API server on port 4273.
Case 1 — already seated, verified by access code. Staff open Access codes → Generate new
code and read out something like K7X92P (or show its QR). The customer picks
I'm at the restaurant at checkout, types or scans the code, chooses their table, and orders.
Codes expire (default 10 minutes, live countdown on both sides), are single-use by default, are
bound to one restaurant, and can be cancelled by staff.
Case 2 — seated, identified by table QR. Every table has its own printable QR
(Tables in the dashboard). Scanning it opens that restaurant's menu with the table already
set, so checkout skips the code step entirely. The customer picks whether they'll pay at the
counter or through the app; either way the order lands as UNPAID and staff flip it to PAID
when they've been paid. No payment is processed by the app.
Case 3 — pickup, ordered before arriving. The customer picks Order for pickup, enters a
name, and gets an order number like #A482 plus a QR. Staff see it as a pickup order, prepare
it, and mark it Ready for pickup. On arrival, Verify order in the dashboard either scans the
customer's QR or takes the typed order number, shows the order, and hands over — Picked up.
One table, one shared session, everyone orders for themselves.
The first person picks Eating as a group? on the restaurant page, proves they're there (table
QR or staff code), chooses the table, and gets a group code like GTQV4 plus a QR to show round
the table. Everyone else scans it or types the code, sees which restaurant, which table, which
group before joining, then adds their own food. Items are attributed to whoever ordered them, and
the kitchen gets one ticket per table with each person's items grouped underneath their name.
Paying is flexible: pay for my items, pay the whole group bill, or enter any amount to split it however the table likes. The running total, amount paid and amount remaining update as people settle. The host closes the session once nothing is outstanding; after that no one can add to it, and it stays in everyone's order history.
The restaurant enters its own UPI ID under Restaurant → UPI. Khapee then builds a standard
upi://pay request — rendered as a QR locally — so money moves directly from the customer's UPI
app to the restaurant's account. There is no Razorpay, no PhonePe SDK, no API key, and no
account in the middle.
The honest consequence: without a payment provider or bank webhook, the app cannot verify a payment by itself. So the customer taps I've paid (optionally with their UTR), and the restaurant confirms it against their own UPI notification under Payments. Nothing is marked PAID until a human at the restaurant says it arrived. Restaurants that leave the UPI field blank simply take payment at the counter as before.
The browse page has a Near me button. It uses the browser's own geolocation API, sends the coordinates to this app's API only, and sorts by straight-line distance against coordinates the restaurants pinned themselves (Restaurant → Map pin). No maps service, no geocoding service, and no location data is stored. Restaurants without a pin still appear, just unsorted.
Dine in NEW → ACCEPTED → PREPARING → READY → COMPLETED
Takeaway / Pickup NEW → ACCEPTED → PREPARING → READY_FOR_PICKUP → PICKED_UP
Every restaurant chooses which of dine-in, takeaway, pickup and group tables it offers (Restaurant → How you take orders); anything switched off disappears from the customer's checkout and is refused by the API.
Staff can step an order forward, step it back one place to undo a mis-tap, or cancel it. The board updates over server-sent events, with an 8-second poll as a fallback; the customer's tracking page updates the same way.
server/ Express API
db.ts SQLite schema + additive migrations (better-sqlite3)
seed.ts demo restaurants, menus, tables, accounts
import.ts bulk import from a local JSON file
uploads.ts photo storage on disk (data/uploads)
auth.ts bcrypt hashing + session tokens
ids.ts access codes, order numbers, table tokens
events.ts server-sent-event fan-out
orders-service.ts order creation, pricing, access-code checks
routes/ auth · public · orders · staff
shared/orders.ts status flows shared by client and server
src/ React + Vite app (customer and staff)
tests/run.ts end-to-end API tests
Notes on the design:
localStorage with a per-order token, so a customer who never
signed in can still reopen their order; the token is required to read it.qrcode and decoded from the camera with jsqr.
Manual entry is offered everywhere a scanner appears, and the scanner degrades to a clear
message when the camera is unavailable.data/uploads, served by this app — or generated locally (a deterministic
gradient tile plus the dish glyph). No image service is involved either way.