尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Multi-tenant SaaS undangan pernikahan digital. Beberapa template cinematic, banyak pasangan — tiap pasangan punya URL slug, akun pemilik, dashboard editor, dan database RSVP / Gift / Tamu sendiri. Model bisnis: self-serve, bayar sekali (couple daftar → pilih template → edit preview gratis → bayar via Midtrans untuk terbit).
fincards.land/ ← marketing
fincards.land/signup · /login ← akun pemilik (Supabase Auth)
fincards.land/onboarding ← pilih template + slug + data → buat draft
fincards.land/[template]/[slug] ← undangan publik (mis. /lovebirds/adi-rani)
fincards.land/[template]/[slug]/dashboard ← editor + data (khusus pemilik)
fincards.land/[template]/[slug]/checkin ← check-in QR/manual (buku tamu, Premium)
fincards.land/admin ← konsol operator (allowlist email)
| Layer | Tech |
|---|---|
| Framework | Next.js 14 (App Router) + React 18 + TypeScript |
| UI | CSS Modules + CSS variables (tanpa Tailwind/UI library) |
| Animasi | motion 12 + GSAP 3.15 + lenis; Solary + three.js |
| Backend | Supabase (Postgres + Storage + RLS) |
| Auth | Supabase Auth (email+password, verifikasi email, reset password, MFA TOTP opsional) |
| Payments | Midtrans (Snap redirect + notification webhook) |
| Resend | |
| Hosting | Vercel |
Arsitektur multi-template dijelaskan lengkap di tutorial-multi.md. Konteks arsitektur untuk sesi Claude: CLAUDE.md.
npm install
supabase/schema.sql (base), lalu semua file di
supabase/migrations/ berurutan sesuai tanggal. (Base saja tidak cukup — kolom seperti
owner_user_id, is_paid, gateway_* (payment), kolom *_enc, tabel guests/attendances/template_plans/
refund_requests semuanya datang dari migration.)invitation-media ada + public (dibuat oleh schema.sql).template_plans (atau lewat /admin → Templates → Plans).Sign up di resend.com → API Keys → Create. Opsional untuk dev: kalau
RESEND_API_KEY kosong, sendAdminEmail mengembalikan false tanpa melempar error — email tidak
terkirim, sisanya tetap jalan.
.env.localBuat file .env.local di root project. Tidak ada file contoh yang di-commit — sengaja, supaya
tidak ada berkas berbau env di repo sama sekali (.gitignore memblokir semua .env*). Daftar
lengkapnya ada di bawah:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
SUPABASE_SERVICE_ROLE_KEY=eyJ... # server-only
# App
NEXT_PUBLIC_SITE_URL=http://localhost:3000 # production: https://www.fincards.land
# Enkripsi data-at-rest (WAJIB — tanpa ini RSVP/gift/tamu tak bisa didekripsi)
APP_ENCRYPTION_KEY=... # 32-byte hex, untuk config PII + RSVP/gift
GUESTS_ENCRYPTION_KEY=... # 32-byte hex, untuk tabel guests + token RSVP
# Pembayaran (Midtrans)
MIDTRANS_SERVER_KEY=... # Dashboard → Settings → Access Keys
MIDTRANS_IS_PRODUCTION=false # 'true' = API production, selain itu sandbox
# Konsol admin — email yang boleh buka /admin (pisahkan koma)
[email protected]
# Email (Resend)
RESEND_API_KEY=re_...
[email protected]
Generate kunci hex cepat: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
npm run dev
Ini jalur utama — Anda tidak perlu melakukan apa-apa untuk onboarding biasa:
Untuk kasus khusus (giveaway, testing, bantu customer non-teknis), pakai salah satu:
node scripts/create-invitation.mjs budi-sari pass123aman `
--bride="Sari Wulandari" --groom="Budi Hartono" `
--date=2026-08-20T16:00 --venue="Hotel Grand Hyatt, Jakarta" `
[email protected] --plan=premium `
--template=lovebirds --full
Script ini membuat akun Supabase Auth (email + password) lalu insert invitation dengan
owner_user_id ke akun itu. Flag --full = seed 14 section penuh (Lovebirds); tanpa --full = 6
section starter./admin → Invitations → New (buat comp invitation dari konsol).Edit semua teks & foto (upload ke Supabase Storage), reorder section (drag-drop), atur palette/ornament/ musik/meta, kelola tamu (import CSV, kirim link WhatsApp), lihat RSVP/Gift + export CSV, buku tamu + QR check-in (Premium), reset password sendiri via /forgot-password, kelola akun & data di /profile.
PUT /api/invitation/<slug>/config:
401/403 = sesi login habis (login ulang); 409 = ada tab lain menyimpan (banner reload muncul); 500 =
cek log server / Vercel.invitation-media masih ada +
public, quota Storage belum penuh.invitations.email./refund; Anda putuskan di /admin → Payments
→ Refund requests (operator yang memutuskan, bukan otomatis). Takedown cepat: /admin set
suspended_at (sembunyikan halaman dari semua orang).-- Semua undangan
select slug, template_id, plan, is_paid, is_published, expires_at, created_at
from invitations order by created_at desc;
-- RSVP / gift / tamu per undangan (kolom PII terenkripsi di DB — angka agregat aman dilihat)
select i.slug,
count(distinct r.id) as rsvp, count(distinct g.id) as gifts, count(distinct gu.id) as guests
from invitations i
left join rsvps r on r.invitation_id = i.id
left join gift_confirmations g on g.invitation_id = i.id
left join guests gu on gu.invitation_id = i.id
group by i.slug order by rsvp desc;
Sebagian besar tugas monitoring/revenue kini ada di /admin (Invitations, Payments & revenue, Activity). Cek quota service di dashboard masing-masing (Supabase Usage, Resend, Vercel, Midtrans).
Harga tidak di-hardcode — tersimpan di tabel template_plans per template, diedit lewat /admin →
Templates → Plans. Plan: basic / premium; Premium membuka buku tamu (ledger kehadiran + QR
check-in). duration_days NULL = seumur hidup, N = aktif N hari lalu perlu diperpanjang.
wedding-saas-next/
├── src/
│ ├── app/
│ │ ├── page.tsx · layout.tsx ← marketing
│ │ ├── signup · login · verify-signup · forgot-password · reset-password
│ │ ├── onboarding · profile · terms · privacy · refund
│ │ ├── [template]/[slug]/ ← undangan publik + phone-frame
│ │ │ ├── page.tsx · InvitationView.tsx
│ │ │ ├── checkin/ ← QR / manual check-in
│ │ │ └── dashboard/ ← editor + tab RSVP/Gifts/Guests/BukuTamu/Tutorial + PaymentGate
│ │ ├── admin/ ← konsol operator
│ │ └── api/ ← rsvp, gift, guestbook, checkin, upload, auth/*, payment/midtrans/webhook, invitation/[slug]/{config,publish,meta,music,theme}
│ ├── all-templates/<id>/ ← lovebirds, solary (sections/config/Shell)
│ ├── config/ ← templateIndex.js, templateCatalog.js
│ ├── editor/ ← block editor (schemas, fields, EditorRoot)
│ ├── lib/ ← supabase, auth, crypto, guests, payments, admin, i18n, email, security …
│ └── components/ · hooks/ · styles/ · utils/
├── scripts/ ← create-invitation, seed-full-config, capture, diag-*, backfill/encrypt
└── supabase/ ← schema.sql (base) + migrations/ (30+ .sql)
.env.local, termasuk kunci enkripsi & Midtrans).NEXT_PUBLIC_SITE_URL ke URL production (penting untuk OG tag, link reset, dan redirect finish
Midtrans Snap).https://www.fincards.land/api/payment/midtrans/webhook.npm run typecheck # tsc --noEmit
npm run test # vitest (unit + integrasi)
npm run test:e2e # Playwright
npm run test:all # typecheck + unit + e2e
npm run check:tokens # guardrail design token
npm run verify:security # enkripsi PII at-rest + RLS anon = 0 baris
Kontrak cakupan: TEST-MATRIX.md · laporan: TEST-REPORT.md · bug produk nyata: BUG-LEDGER.md.