Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
FinCards — Designed to be remembered. Undangan digital yang terasa seperti film pendek. Rancang dulu sampai puas, bayar saat mau diterbitkan, sebar link-nya lewat WhatsApp.
Translation pending
LifestyleTypeScript · JavaScriptGitHub @hafidz-AOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full 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.