Skip to content

GodrezJr2/Lumina-Card

Repository files navigation

Lumina Card

Digital Invitation + Guest Management β€” UAS E-Business Project

A Next.js 14 SaaS that combines digital wedding/event invitations with QR-code guest check-in, souvenir tracking, and a hybrid offline-first sync mode. Built as a final project for the E-Business course at Universitas Multimedia Nusantara.

Next.js TypeScript Prisma TiDB Tailwind Vercel

Live Demo Β· Demo Flow Β· Sample Invitation


What it does

  • Digital invitation page per couple/event at /i/[slug] (14 templates with different aesthetics & animations).
  • Guest list management with WhatsApp share helper (wa.me/?text= link, no API).
  • QR check-in scanner with three input modes (camera, photo upload, manual paste).
  • Souvenir tracking mode β€” same scanner, toggle between check-in and souvenir distribution. Server enforces "must check-in before souvenir" and prevents double-claim.
  • Hybrid offline-first sync β€” operator can scan to local MySQL during the event, then push to TiDB Cloud after WiFi is back.
  • Midtrans Snap payment integration (sandbox toggle via env) for template purchases and per-event service plans.

What it's not

  • Not a finished product. This is an academic project built within ~3 weeks.
  • Not load-tested. Numbers below are estimates from free-tier limits, not benchmarks.
  • Not a replacement for an EO's full operational stack β€” there's no email automation, no real-time push, no recurring billing yet.
  • Souvenir tracking appears uncommon in Indonesian digital-invitation tools we surveyed; we did not exhaustively verify the entire market.

Table of Contents


Tech Stack

Layer Choice
Framework Next.js 14.2 (App Router) + React 18 + TypeScript 5
Database TiDB Cloud (production), MySQL 8 / XAMPP (local dev)
ORM Prisma 5.14 (relationMode: prisma)
Auth bcryptjs (cost 10) + httpOnly cookie session
Storage Cloudinary (free tier, auto webp/avif)
Payments Midtrans Snap (sandbox + production)
QR Scanner html5-qrcode (camera) + jsqr (photo)
Animation Framer Motion 12 + anime.js 4
Hosting Vercel free tier

Why these choices

Decision Reason
TiDB Cloud over PlanetScale Free 5GB tier, MySQL-compatible, no shard lock-in.
Cloudinary over R2 Free 25GB and on-the-fly transform β€” saves CDN config work for an MVP.
bcryptjs + cookies over NextAuth Single-tenant SaaS doesn't need OAuth provider plumbing yet.
UUID v4 over AES-256 for QR Token validated by DB lookup, not by client decode β€” no shared key to leak.
Manual sync over WebSocket The bottleneck we observed is venue connectivity, not message latency.

Architecture

                     Vercel (Next.js 14 App Router)
                              β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚                      β”‚                      β”‚
   Public routes         API routes              Admin routes
   /, /catalog,          /api/checkin            /admin/dashboard
   /i/[slug],            /api/sync/push          /admin/scanner
   /inv/[token]          /api/payment/midtrans   /admin/users
                                  β”‚
                                  β–Ό
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚  TiDB Cloud (MySQL-compatible)β”‚
                  β”‚  User Β· Event Β· Guest         β”‚
                  β”‚  Attendance Β· ChangeLog       β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β–²                  β–²
                       β”‚                  β”‚
              Cloudinary CDN       XAMPP MySQL (local)
              (image upload)       (offline scanner mode)

Hybrid offline-first sync

Venue (no internet)              Post-event (WiFi)
─────────────────                ─────────────────
Laptop + XAMPP                   POST /api/sync/push
MySQL local            ─────►    1. Read local guests
Scanner posts to                 2. Open TiDB connection
localhost                        3. UPSERT by token (UUID = idempotent)
                                 4. Forward souvenir flags

Token-based UPSERT means re-running the sync is safe: each guest has a globally-unique UUID, and the merge resolves to the same row.


Quick Start

Prerequisites

  • Node.js β‰₯ 18
  • TiDB Cloud account (free)
  • Cloudinary account (free)
  • Midtrans Sandbox account (free)
  • Optional: XAMPP / MySQL 8 on localhost:3306 for offline-first mode

Install

git clone https://github.com/GodrezJr2/Lumina-Card.git
cd Lumina-Card
npm install
cp .env.example .env
# fill in DATABASE_URL, MIDTRANS_*, CLOUDINARY_*
npx prisma generate
npx prisma db push
node scripts/seed-demo.mjs        # 5 users + 2 events + 13 guests
npm run dev                       # http://localhost:3001

Required env vars

DATABASE_URL="mysql://user:pass@host:4000/db?sslaccept=strict"
PROD_DATABASE_URL="..."           # only needed for offline-first sync
MIDTRANS_SERVER_KEY="SB-Mid-server-..."
NEXT_PUBLIC_MIDTRANS_CLIENT_KEY="SB-Mid-client-..."
MIDTRANS_IS_PRODUCTION="false"
CLOUDINARY_CLOUD_NAME="..."
CLOUDINARY_API_KEY="..."
CLOUDINARY_API_SECRET="..."

Demo Accounts

For local development, use the seed scripts (excluded from this repo for credential hygiene). After running them, accounts cover all five RBAC tiers: SUPER_ADMIN, FULL_SERVICE_CLIENT, DIY_CLIENT, USHER_STAFF, and BASIC_USER.

Live deployment credentials are not published. Browse the public invitation pages above for a read-only view.


Project Structure

wedding-app/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ (public routes: /, /catalog, /pricing, /i/[slug], /inv/[token]/qr)
β”‚   β”œβ”€β”€ admin/        β€” dashboard, events, guests, scanner, broadcast, users, panel
β”‚   └── api/          β€” auth, events, guests, checkin, sync, upload, payment/midtrans
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ templates/    β€” 14 invitation templates
β”‚   β”œβ”€β”€ ImageUpload.tsx
β”‚   β”œβ”€β”€ MusicPlayer.tsx
β”‚   └── RoleGate.tsx
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ prisma.ts          β€” production client (TiDB)
β”‚   β”œβ”€β”€ prisma-local.ts    β€” local client (XAMPP, for sync)
β”‚   β”œβ”€β”€ cloudinary.ts
β”‚   β”œβ”€β”€ roles.ts           β€” 5-tier RBAC permission map
β”‚   └── catalog-templates.ts
β”œβ”€β”€ prisma/schema.prisma   β€” User Β· Event Β· Guest Β· Attendance Β· ChangeLog
└── scripts/
    β”œβ”€β”€ seed-demo.mjs      β€” reset + seed (idempotent)
    β”œβ”€β”€ seed-passwords.mjs β€” reset all passwords to "123456"
    └── list-users.mjs     β€” CLI table dump

API Reference

Auth

Method Endpoint Description
POST /api/auth/login Sets user_id + user_role cookies
POST /api/auth/logout Clear session
GET /api/auth/me Current user

Events & guests

Method Endpoint Description
GET /api/events List events + aggregate stats
POST /api/events Create event with auto-generated slug
GET /api/guests?eventId={id} Guests with attendance
POST /api/guests/bulk Bulk import (pipe / tab / comma separators)

Check-in & sync

Method Endpoint Description
POST /api/checkin { token, mode: "checkin" | "souvenir" }
POST /api/sync/push Push local DB to TiDB Cloud (idempotent UPSERT)

Payment

Method Endpoint Description
POST /api/payment/midtrans/create Snap transaction (template OR plan)
POST /api/payment/midtrans/notification Webhook with SHA512 verify

Sample β€” souvenir mode check-in

POST /api/checkin
{ "token": "550e8400-e29b-...", "mode": "souvenir" }

Success β€” 200:

{ "name": "Citra", "status": "Checked_In", "mode": "souvenir",
  "pickedUpSouvenir": true, "message": "Souvenir berhasil diberikan!" }

Not yet checked in β€” 409:

{ "name": "Dewi", "status": "Opened",
  "message": "Tamu belum check-in. Wajib check-in dulu sebelum ambil souvenir." }

Database Schema

User    1 ───── N  Event
Event   1 ───── N  Guest
Guest   1 ───── 1  Attendance
User    1 ───── N  ChangeLog (audit)

Guest status flow

Draft ──send WA──► Sent ──open link──► Opened ──scan QR──► Checked_In
                                                               β”‚
                                                  scan in     β–Ό
                                                  souvenir    Souvenir picked
                                                  mode

Schema details: see prisma/schema.prisma.


Security Notes

Area Implementation
Transport HTTPS via Vercel + Let's Encrypt
DB TiDB Cloud ?sslaccept=strict
Password bcryptjs cost 10
Session httpOnly cookie, 7-day TTL
Authorization 5-tier RBAC, route-level <RoleGate>
QR token UUID v4 (122-bit), validated against DB
Webhook Midtrans SHA512 signature verify
Audit log ChangeLog table records actor + before/after JSON
Soft delete User.deletedAt, restorable by SUPER_ADMIN
Card data None stored β€” Midtrans handles tokenization

We don't claim formal compliance certifications. Card data flows entirely through Midtrans, which itself is PCI-DSS Level 1.


Pricing

PER-EVENT SERVICE PLANS
─ Basic        Rp  79.000   200 guests, 1 staff
─ Professional Rp 199.000   1.000 guests, unlimited staff, WA blast
─ Enterprise   custom       white-label, API, SLA

TEMPLATE CATALOG (one-time)
─ 14 templates  Rp 149.000 – Rp 399.000

Operational costs at MVP scale: ~Rp 0/month (everything on free tiers) plus ~Rp 192K/year for the domain. Midtrans fees (2.9–3.5% per transaction) are passed through.


Known Limits & Roadmap

Known limits

  • No real-time dashboard updates β€” admin pages re-fetch on navigation, not live push.
  • Bulk import is textarea paste only (CSV file upload is on the roadmap).
  • No 2FA on SUPER_ADMIN accounts.
  • WhatsApp distribution is a wa.me link generator β€” no automated blast.
  • Recurring subscription billing not implemented (everything is pay-per-event).

Roadmap

  • RSVP response tracking (Hadir/Tidak/Mungkin + plus-ones)
  • Live scanner stats counter
  • CSV file upload with header detection
  • Mobile drawer nav for admin
  • WhatsApp blast via baileys (self-hosted)
  • EO subscription billing
  • 2FA for SUPER_ADMIN

Documentation


License

MIT. Fork it, learn from it, adapt it.


Built as an E-Business UAS project at Universitas Multimedia Nusantara Β· 2026

Live Demo Β· Repo

About

Web Digital Invitation

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages