尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Table service for restaurants, from either side. A diner scans the QR at their
table, browses the menu, customises and pays (card / Apple Pay / Google Pay via
Stripe) — or a waiter takes the order on their own phone, carries the plates out
and collects the bill in cash, by card, in equal parts or a hundred pesos at a
time. There is a counter till for walk-ins, a kitchen board that keeps working
when the wifi does not, and tickets on paper for the pass.
Built with Next.js (App Router) · Supabase · Stripe.檢視完整 README ↗Table service for restaurants, from either side. A diner scans the QR at their table, browses the menu, customises and pays (card / Apple Pay / Google Pay via Stripe) — or a waiter takes the order on their own phone, carries the plates out and collects the bill in cash, by card, in equal parts or a hundred pesos at a time. There is a counter till for walk-ins, a kitchen board that keeps working when the wifi does not, and tickets on paper for the pass. Regulars get a visit card with rewards, and customers the restaurant trusts can run an account and pay it later. A register-only plan (Caja) runs the till on its own, with a menu diners read on their phones and order at the counter.
Built with Next.js (App Router) · Supabase · Stripe.
📄 Product & technical spec: docs/SPEC.md — concept, flows,
data model, security, and the open decisions to make next.
supabase/{schema,seed,drop,purge}.sql ← DDL + seed + reset SQL (run via pnpm db:*)
scripts/ ← the gates: api, rls, roles, smoke, layout,
promises, money, attack, dialogs (pnpm <name>)
src/lib/supabase/{client,server,admin}.ts ← 3 Supabase clients (publishable + secret)
src/lib/csp.ts ← what the browser may load and talk to
src/middleware.ts ← refreshes auth sessions, writes the policy
src/lib/legal/ ← the terms and the aviso, and the PDFs built
from them (node scripts/legal-pdf.mjs)
src/app/r/[restaurantId]/t/[tableId]/ ← the diner's menu and bill (QR target)
src/app/order/[orderId]/ ← live order tracking
src/app/dashboard/orders/ ← the kitchen board, and what is ready to carry
src/app/dashboard/table-order/ ← the waiter's pad
src/app/dashboard/bills/ ← open bills, collecting, the calculator
src/app/dashboard/pos/ ← the counter till
src/app/dashboard/accounts/ ← customer accounts: open, charge, collect
src/app/dashboard/loyalty/ ← the visit card program
src/app/cuenta/[code]/ ← a customer's account statement (its QR)
src/app/rewards/ ← a diner's visit card, by its code
src/app/dashboard/[menu]/ ← menus, sections, products, extras
src/app/dashboard/{tables,promotions,analytics,settings,staff,plan,admin}/
src/app/api/checkout/ ← prices a cart and opens Stripe Checkout
src/app/api/webhooks/stripe{,/connect}/ ← the only place a payment is believed
src/app/api/table-{order,bill,payment}/ ← what the waiter takes and collects
src/app/api/print/cloudprnt/[token]/ ← what a kitchen printer asks for
src/app/globals.css ← design system
Requires Node ≥ 18.18 (Next.js 15). An
.nvmrcpins Node 22 — runnvm use.
This is a complete Next.js project — just install and go.
nvm use # or ensure your node is ≥ 18.18
corepack enable # makes the pinned pnpm available
pnpm install
The app uses two Supabase projects so development never touches production data. Config lives in per-environment files (all gitignored):
| File | Used by | Points at |
|---|---|---|
.env.development.local | pnpm dev, pnpm db:* | your dev Supabase project |
.env.production.local | pnpm build/start, pnpm db:*:prod | your prod Supabase project |
cp .env.development.local.example .env.development.local # fill in dev keys
cp .env.production.local.example .env.production.local # fill in prod keys (later)
Each needs the Supabase URL + keys, Stripe keys, and a DATABASE_URL (Supabase →
Connect → Session pooler URI, with your DB password). Next.js loads the
right file automatically based on NODE_ENV.
With DATABASE_URL set in .env.development.local:
pnpm db:reset # drop + create + seed the dev database, prints a demo URL
Database scripts (default = dev; append :prod to target production, which
prompts for confirmation on destructive ops):
| Command | What it does |
|---|---|
pnpm db:create | Create tables / RLS / realtime (structure only). |
pnpm db:seed | Insert the demo restaurant + menu, and (dev only) create the test logins below. Idempotent. |
pnpm db:reset | Drop + create + seed — a clean slate. Use this most. |
pnpm db:purge | Empty every table, keep the structure. |
pnpm db:drop | Drop all tables entirely. |
pnpm db:mock | Add a self-contained demo restaurant ("Demo Bistro") with a full menu, ~250 orders across 30 days, a named team, live orders and service requests — everything to present the app. Repeatable. |
pnpm db:dropmock | Remove only the demo data (Demo Bistro + its logins); leaves the base seed untouched. |
…:prod | Same, against the prod DB (e.g. pnpm db:create:prod, pnpm db:mock:prod). |
Prefer the dashboard? You can still paste
supabase/schema.sql(thensupabase/seed.sql) into the Supabase SQL Editor and Run.
pnpm dev
Restaurant: open http://localhost:3000/signup to create an account (this also
creates your restaurant), then you land on /dashboard. Sign in again any time
at http://localhost:3000/login.
Test logins (dev only): pnpm db:seed / pnpm db:reset create five ready
accounts, password test123, each with its own "Test Restaurant N" — and each
on a different plan, so every tier can be demonstrated by signing in rather
than by editing the database. These are never created on prod.
| login | plan | what it is for |
|---|---|---|
[email protected] | Carta (free) | The gates. Table QRs fall back to the counter menu, coupons and promotions are refused, the dish ceiling bites. It keeps the tables and dishes the seed built, which is what a real downgrade looks like. |
[email protected] | Servicio | The entry paid tier with tables: open bills, promotions, the visit card, customer accounts. No coupons, no menu schedules. |
[email protected] | Casa | Everything that is buyable — coupons, menu schedules, staff discounts, a year of analytics. |
[email protected] | Grupo | What the top tier unlocks, before the tier itself is finished. |
[email protected] | Servicio, trial expired yesterday | A lapsed trial without waiting thirty days for one: the app settles it to Carta on the first request, exactly as it would in life. |
Customer menu: http://localhost:3000/r/<restaurantId>/t/<tableId> — pnpm db:reset
prints a ready-to-open demo URL.
Presentation data: pnpm db:mock builds "Demo Bistro" — a fully populated
restaurant to demo every feature (orders board, analytics, service requests,
staff). Logins: [email protected] (owner), demo-manager@, demo-waiter@,
demo-cashier@, demo-kitchen@, password demo123. Run pnpm db:dropmock to remove it again.
Install the Stripe CLI, then forward webhooks to your local server:
stripe listen --forward-to localhost:3000/api/webhooks/stripe
Copy the whsec_... it prints into STRIPE_WEBHOOK_SECRET in
.env.development.local. Use test card 4242 4242 4242 4242, any future expiry,
any CVC.
Push to GitHub (you've done this).
vercel.com → Add New → Project → import your repo.
Under Environment Variables, add all the keys from your
.env.production.local (paste the secret values here directly — they live
only in Vercel, never in chat or git).
Deploy. You'll get a URL like https://tabletap.vercel.app.
Add BOTH production webhooks. There are two Stripe accounts in play, so there are two endpoints, each with its own signing secret:
| endpoint | register as | events |
|---|---|---|
/api/webhooks/stripe | events on your account | customer.subscription.* |
/api/webhooks/stripe/connect | events on connected accounts | checkout.session.completed, checkout.session.expired |
Their secrets go into Vercel as STRIPE_WEBHOOK_SECRET and
STRIPE_WEBHOOK_SECRET_CONNECT. The wrong one fails every event with a 400
that looks like a delivery problem. Until they exist a card payment never
marks its order paid — pnpm money:prod is the check that catches it.
See docs/before-launch.md for the rest of what only
the business can supply: live keys, a mail provider, and the legal identity.
src/lib/csp.ts): no inline script anywhere, and connect-src limited to us
and Supabase, so a script that somehow ran has nowhere to send a bill.pnpm rls attacks every tenant table as every role, and pnpm attack
asks what a signed-in person can do that they should not — judged on whether
a peso moved, never on the absence of an error.docs/SPEC.md — what the app is: the flows, the money, the
roles, and why each decision went the way it did.docs/regressions.md — every bug that has actually
shipped here, and what now catches each one automatically.docs/before-launch.md — the things outside the code
that only the business can supply, and what each one breaks while it is missing.CLAUDE.md — how code is written here, and the gate to run
before shipping anything.