Open-Source Cold Email & Warmup Platform
Warmbly 是開源的冷郵件平台:溫暖您的收件箱、發送活動、並對每個回覆進行分流。可在我們的雲端運行或自行託管整個系統。
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Nail store loyalty
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Loyalty check-in for Best Nails: an iPad kiosk where customers type their phone number on a numpad to check in, and a staff admin for customers, check-ins and rewards.
The program: every check-in earns 1 point · 10 points = 10% off · a birthday 10% off, once a year within 7 days of the birthday. Customers can redeem either at the kiosk after checking in, or staff can redeem them in the admin.
Gift vouchers: a customer buys prepaid credit (e.g. $100) and spends it over one or more visits. Staff create one under Gift vouchers (the customer pays at the till as usual): the app generates a code like BN48207138: the business prefix and 8 digits, the last one a check digit that catches most typos (Generate makes a new one), or staff type the number printed on a voucher card. BN is the business prefix, which an admin can change in Settings if they run several businesses. At each visit staff enter today's service amount. If it costs more than what's left, the app uses the rest and shows how much to take at the till. A use entered by mistake is put back with Adjust balance (admin). Vouchers are valid for 3 years (the legal minimum in Australia), and the dashboard shows the unspent credit.
Design: Claude Design canvas (kiosk screens, admin pages, design tokens).
Next.js 16 (App Router, Cache Components, Server Actions) · React 19 · Tailwind CSS v4 + shadcn/ui · Supabase (Postgres, Auth, RLS) · Zod · Vitest + pgTAP
Requires Node 20+, Docker Desktop (running) for local Supabase.
npm install
npx supabase start # Postgres + Auth in Docker; applies migrations and seed
npx supabase status -o env # copy API_URL / PUBLISHABLE_KEY / SECRET_KEY into .env.local (see .env.example)
npm run dev
[email protected] / bestnails-dev (admin, local only). Add staff accounts in Settings → Accounts| Piece | Where |
|---|---|
| Schema, RLS, check-in / redeem / adjust functions | supabase/migrations/ |
| Points rules | _award_check_in: 1 point per check-in, one per shop day (in settings.timezone) |
| Redeeming | kiosk_redeem / redeem_reward spend points on a reward; kiosk_redeem_birthday / redeem_birthday use the birthday discount (_birthday_status decides eligibility). Kiosk redemption needs a check-in in the last 12 hours |
| Phone numbers | lib/phone.ts: Australian numbers are stored as +61…; non-standard numbers are accepted with a warning, never blocked |
| Kiosk Server Actions (signed-in account, minimal data out) | app/kiosk/actions.ts |
| Kiosk UI (numpad, screens, idle reset) | components/kiosk/ |
| Admin Server Actions (staff session re-checked, RLS client) | app/admin/actions.ts |
| Admin reads | lib/admin-data.ts |
Points are an append-only ledger (point_transactions); customers.points_balance is a cached total that a trigger only lets the ledger functions change. Concurrent check-ins for the same customer serialise on a row lock, so a double tap can't count twice.
The kiosk never talks to Supabase from the browser. Its Server Actions use the service key only after checking that an account is signed in on the device, and they return just a first name, masked phone, points and visit count.
npm test # unit tests (phone rules, promotion scheduling)
npm run test:db # pgTAP: check-in rules, one check-in a day, permissions, redeem/adjust, gift vouchers, business rules, ledger consistency
npm run typecheck && npm run lint && npm run build
supabase/migrations/, then npx supabase migration up && npm run db:typesnpm run db:reset (this also signs everyone out)Create a Supabase project and run npx supabase link then npx supabase db push. Don't run seed.sql in production. Create the first admin once: add a user in the Supabase dashboard (Authentication → Users, with "Auto confirm"), then in the SQL editor run insert into public.staff (user_id, display_name, role) select id, 'Your name', 'admin' from auth.users where email = '[email protected]';. Everyone after that is added in Settings → Accounts. Deploy the app to Vercel with the three env vars. On the iPad, sign in, open /kiosk, "Add to Home Screen" (no address bar), and turn on Guided Access while a customer has it.
Known limits: "Forgot password?" emails need your own email (SMTP) set up in Supabase; until then an admin resets passwords in Settings → Accounts.