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.