
Project Overview
Ucansee
Your Digital Identity, Beautifully Crafted, Create and share your beautiful digital cards easily.
Tech Stack
Ringkasan Project
Ucansee adalah aplikasi SaaS untuk membuat digital card atau halaman profil publik. User bisa membuat kartu digital, mengatur profil, menambahkan link, memilih template, upload gambar, publish kartu, lalu membagikan URL publik seperti:
/cards/[slug]
Selain fitur utama untuk user, aplikasi juga memiliki dashboard analytics, sistem subscription/plan, billing manual via QRIS dan WhatsApp, serta panel admin untuk mengatur user, template, plan subscription, dan site settings.
Tech Stack
Frontend
- Next.js 13 App Router
- React 18
- TypeScript
- Tailwind CSS
- shadcn-style UI components berbasis Radix UI
- lucide-react untuk icon
- react-hook-form untuk form state
- Zod untuk validasi form
- Sonner untuk toast notification
- Recharts untuk visualisasi analytics
- dnd-kit untuk drag and drop link sorting
Backend
- Next.js Route Handlers di folder
app/api - Supabase Auth untuk autentikasi user
- Supabase Postgres sebagai database utama
- Supabase Row Level Security melalui migration/policies
- Supabase RPC/Postgres function untuk logic subscription dan limit
- Supabase Service Role khusus untuk endpoint admin tertentu
Storage dan Media
- Cloudinary sebagai provider utama upload image
- Signed upload menggunakan endpoint internal
/api/cloudinary/sign - Supabase Storage masih tersedia sebagai fallback di komponen upload
Infrastruktur Aplikasi
- Middleware Next.js untuk proteksi route dashboard dan admin
- Environment variable untuk Supabase, Cloudinary, dan analytics salt
- App Router dengan kombinasi Server Component dan Client Component
Workflow Utama Aplikasi
1. User Membuka Landing Page
Entry point aplikasi ada di app/page.tsx.
Saat landing page dibuka, server mengambil data dari tabel site_settings. Data ini dipakai untuk menampilkan nama site, title, description, pricing, dan mode khusus seperti is_free_mode.
Landing page menampilkan:
- Header dan navigasi
- Hero section
- Interactive simulator
- Feature section
- Pricing section yang datanya dinamis
- CTA ke register
- Footer dengan terms dan privacy
Jika is_free_mode aktif, aplikasi menampilkan banner bahwa semua fitur premium dibuka.
2. Auth dan Session
Autentikasi menggunakan Supabase Auth. User bisa register, login, forgot password, dan update password.
Middleware di middleware.ts bertugas untuk:
- Refresh session Supabase agar Server Component tetap mendapat session terbaru
- Melindungi semua route
/dashboard - Redirect user yang belum login ke
/login - Melindungi route
/dashboard/admin - Mengizinkan hanya user dengan role
adminatausuper_admin - Redirect user yang sudah login dari
/loginatau/registerke/dashboard
Public card route sengaja dikecualikan dari middleware agar kartu yang sudah dipublish tetap bisa diakses publik.
3. Dashboard User
Setelah login, user masuk ke /dashboard.
Di halaman dashboard, aplikasi:
- Mengambil user dari Supabase Auth
- Mengambil profile berdasarkan
user_id - Mengambil daftar card milik user
- Menghitung jumlah card
- Mengambil plan aktif melalui RPC
get_user_plan - Menampilkan analytics ringkas melalui
DashboardAnalyticsClient
Dashboard juga menyediakan tombol:
- Create New Card
- Upgrade to Pro jika user masih di plan free
4. Membuat Card Baru
Flow create card dimulai dari /dashboard/create.
Sebelum editor dibuka, aplikasi melakukan pengecekan:
- Mengambil subscription detail dari
/api/subscription - Mengambil jumlah card user dari
/api/cards?count=true - Membandingkan jumlah card dengan limit plan
- Jika limit tercapai, user diarahkan untuk upgrade
- Jika belum, editor
CardEditorFullditampilkan
Jika user sebelumnya mencoba simulator di landing page, data sementara bisa diambil dari localStorage dengan key ucansee_pending_card.
5. Editor Card
Komponen utama editor ada di components/CardEditorFull.tsx.
Editor menggunakan stepper tiga tahap:
-
Profile
- Nama
- Title/profession
- Description
- Avatar image
- Cover image
-
Links
- Menambah link
- Mengatur urutan link
- Mengaktifkan/nonaktifkan link
- Mendukung link biasa dan header/group
-
Design & Settings
- Memilih template
- Mengatur custom slug
- Publish/unpublish card
Editor menggunakan:
react-hook-formuntuk state formzoduntuk validasi input- Debounced slug checking ke
/api/cards/check-slug - Live mobile preview menggunakan
TemplateRenderer - Upgrade modal jika user mencoba fitur yang terkunci plan
Saat save:
- Aplikasi memastikan user login
- Mengambil atau membuat profile user
- Membuat slug otomatis jika user tidak mengisi slug
- Memvalidasi reserved slug seperti
dashboard,api,admin, dan lain-lain - Mengecek uniqueness slug
- Insert atau update data ke tabel
cards - Menghapus link lama untuk card tersebut
- Insert ulang daftar link terbaru sesuai urutan form
- Redirect ke
/dashboard/cards
6. Upload Image
Upload gambar dilakukan lewat komponen ImageUpload.tsx.
Default provider adalah Cloudinary. Alurnya:
- User memilih file gambar
- Komponen memvalidasi tipe file harus image
- Komponen membatasi ukuran maksimal 5MB
- Client meminta signature ke
/api/cloudinary/sign - Server membuat signature menggunakan
CLOUDINARY_API_SECRET - Client upload langsung ke Cloudinary dengan signature tersebut
- URL hasil upload disimpan ke form sebagai
avatar_urlataucover_url
Keuntungan pendekatan ini:
- Secret Cloudinary tidak pernah terekspos ke browser
- File tidak perlu melewati server aplikasi
- Upload lebih efisien karena langsung ke Cloudinary
7. Template System
Rendering kartu dilakukan oleh:
components/templates/TemplateRenderer.tsx
components/templates/DynamicTemplate.tsx
Semua template bersifat dinamis. Template disimpan di tabel templates dengan field seperti:
codenamedescriptionconfigstatusis_activeis_proplan_requiredexclusive_user_id
Editor mengambil template aktif dari Supabase. Template kemudian difilter:
- Template public bisa dipakai semua user sesuai plan
- Template exclusive hanya muncul untuk user tertentu
- Template pro terkunci untuk user free, kecuali free mode aktif atau user punya akses khusus
Admin memiliki Template Builder di:
/dashboard/admin/templates/builder
Di builder, admin bisa:
- Membuat template baru
- Mengedit JSON config
- Melihat live preview
- Menandai template sebagai draft/published/archived
- Menandai template sebagai Pro
- Memberi template eksklusif ke user tertentu
8. Public Card Page
Halaman publik kartu ada di:
app/cards/[slug]/page.tsx
Alurnya:
- Route menerima
slug - Server mengambil data card dari Supabase REST API
- Jika card tidak ditemukan, return
notFound - Jika
is_publicfalse, returnnotFound - Server mengambil link aktif berdasarkan
card_id - Data card dan links dikirim ke
TemplateRenderer AnalyticsTrackermencatat event view
Public page dibuat no-store agar perubahan publish, link, atau template bisa langsung terlihat.
9. Analytics
Analytics memiliki dua bagian utama:
- Tracking event
- Aggregation untuk dashboard
Endpoint tracking:
POST /api/analytics
Data yang dikirim minimal:
card_idevent_typemetadata
Untuk menjaga privacy, aplikasi tidak menyimpan raw visitor identity sebagai identifier utama. Visitor dihitung dengan hash dari:
- IP
- User-Agent
- Tanggal hari ini
- Salt dari environment variable
Hasil hash menjadi visitor_id harian. Dengan cara ini aplikasi bisa menghitung unique visitor harian tanpa menyimpan data mentah sebagai identifier permanen.
Endpoint stats:
GET /api/analytics/stats
Endpoint ini mengolah data dari card_analytics menjadi:
- Total views
- Unique visitors
- Link clicks
- Engagement rate
- Trend harian
- Device distribution
- OS distribution
- Country distribution
- Top clicked links
Data ini ditampilkan di dashboard analytics user.
10. Subscription dan Limit
Subscription logic berada di lib/subscription.ts dan beberapa endpoint API.
Fitur yang diatur oleh plan:
- Maksimal jumlah card
- Maksimal link per card
- Rentang analytics
- Akses template Pro
- Trial period
- Grace period
- Custom domain
- Remove branding
- Contact form
Helper penting:
getUserPlangetPlanLimitsgetSubscriptionDetailscanCreateCardcanAddLinkcanAccessTemplategetAnalyticsDaysLimit
Endpoint /api/subscription mengembalikan detail plan user, termasuk effective plan. Jika is_free_mode aktif dari site_settings, user bisa mendapatkan effective plan business meskipun plan aslinya free.
Billing page ada di /dashboard/billing. Sistem pembayaran bersifat manual:
- Admin mengatur QRIS dan template pesan WhatsApp di site settings
- User scan QRIS
- User klik tombol konfirmasi via WhatsApp
- Admin melakukan aktivasi secara manual
11. Admin Workflow
Admin area berada di /dashboard/admin.
Akses admin dilindungi di dua level:
- Middleware mengecek role sebelum user masuk route admin
- API admin juga mengecek role sebelum menjalankan logic sensitif
Fitur admin meliputi:
- Melihat user
- Mengatur role dan subscription user
- Melihat card
- Mengelola template
- Mengelola subscription plan
- Mengatur site settings
- Mengatur payment config, QRIS, free mode, SEO, dan konten legal
Untuk endpoint tertentu seperti mengambil semua auth users, aplikasi memakai Supabase Service Role. Ini hanya dilakukan di server route, bukan di browser.
Data Model Utama
profiles
Menyimpan data user aplikasi yang terhubung ke Supabase Auth.
Field penting:
iduser_idfull_nameavatar_urlrolesubscription_tier
cards
Menyimpan kartu digital milik profile.
Field penting:
idprofile_idtitlenameslugdescriptionavatar_urlcover_urlis_publictemplate_codetheme_configcreated_atupdated_at
links
Menyimpan daftar link di dalam card.
Field penting:
idcard_idtitleurliconpositionis_activeparent_idtype
templates
Menyimpan template dinamis yang bisa dipilih user.
Field penting:
codenameconfigstatusis_activeis_proplan_requiredexclusive_user_iddeleted_at
subscription_plans
Menyimpan konfigurasi plan.
Field penting:
slugnameprice_monthlymax_cardsmax_linksmax_analytics_daystrial_daysgrace_period_daysfeatures
card_analytics
Menyimpan event analytics.
Field penting:
card_idevent_typevisitor_idmetadatacreated_at
site_settings
Menyimpan konfigurasi global aplikasi.
Contoh konfigurasi:
- Nama site
- Deskripsi site
- Free mode
- Payment config
- QRIS URL
- WhatsApp confirmation template
- Terms dan privacy content
- SEO settings
Security dan Authorization
Beberapa mekanisme security yang digunakan:
- Supabase Auth untuk login/session
- Middleware untuk proteksi route dashboard
- Role-based access untuk admin
- API admin tetap melakukan authorization check di server
- Service role hanya digunakan di server-side API
- Public card hanya tampil jika
is_public = true - Reserved slug mencegah bentrok dengan route sistem
- Slug uniqueness dicek sebelum save
- Cloudinary API secret hanya dipakai di server route
- Analytics visitor id menggunakan hash harian, bukan raw identity permanen
- Supabase policies dan migrations mengatur akses data
Alur Data End-to-End
Contoh alur ketika user membuat dan membagikan card:
- User login via Supabase Auth
- Middleware mengizinkan akses ke
/dashboard - User klik Create New Card
- Aplikasi mengecek plan dan card limit
- User mengisi data profile, links, template, dan slug
- User upload avatar/cover ke Cloudinary
- Editor melakukan live preview dengan
TemplateRenderer - Slug dicek melalui API
- User save card
- Data card disimpan ke tabel
cards - Data links disimpan ke tabel
links - Jika publish aktif, card bisa dibuka di
/cards/[slug] - Public page mengambil card dan links
- Template dirender sesuai config
- AnalyticsTracker mengirim event view/click
- Dashboard analytics menampilkan data agregasi
Tentang Arsitektur
Saya membangun aplikasi ini dengan Next.js App Router karena cocok untuk aplikasi SaaS yang butuh gabungan server-rendered page, protected dashboard, dan API route dalam satu codebase. Untuk data dan auth saya memakai Supabase agar development lebih cepat, tetapi tetap punya Postgres, RLS, dan server-side access pattern yang jelas.
Tentang Editor
Bagian paling penting adalah CardEditorFull. Editor dibuat multi-step supaya user tidak kewalahan. Di dalamnya ada form validation, upload image, template selection, slug availability check, subscription limit, dan live preview. Jadi user bisa melihat hasil kartu secara real-time sebelum publish.
Tentang Template
Template tidak di-hardcode sebagai komponen statis saja, tetapi dibuat dynamic melalui config dari database. Ini membuat admin bisa membuat atau mengubah template tanpa perlu deploy ulang aplikasi. Ada juga akses template berdasarkan plan atau user tertentu.
Tentang Subscription
Limit plan tidak hanya dicek di UI. Aplikasi juga punya helper dan RPC untuk menentukan effective plan, trial, grace period, serta limit card/link/template. Ini memisahkan business logic subscription dari komponen UI.
Tentang Security
Saya menerapkan proteksi berlapis: middleware untuk route protection, server-side check di API admin, Supabase Auth untuk session, dan RLS/policy di database. Untuk resource publik seperti card page, aplikasi tetap mengecek is_public sebelum menampilkan data.
Tentang Analytics
Analytics dibuat privacy-conscious. Untuk unique visitor, aplikasi membuat hash dari IP, user-agent, tanggal, dan salt. Dengan begitu aplikasi bisa menghitung unique daily visitor tanpa menyimpan identifier mentah secara permanen.
Tentang Upload
Upload gambar menggunakan signed upload ke Cloudinary. Browser meminta signature ke server, lalu upload langsung ke Cloudinary. Ini menjaga secret tetap aman dan mengurangi beban server aplikasi.
Tantangan Teknis
Beberapa tantangan yang menarik di project ini:
- Menjaga public card tetap bisa diakses tanpa login, sementara dashboard dan admin tetap protected
- Membuat editor yang kompleks tetapi tetap mudah digunakan
- Menyatukan live preview dengan data form yang berubah real-time
- Mengatur template access berdasarkan plan, free mode, dan exclusive user
- Menangani slug agar unik, aman, dan tidak bentrok dengan route sistem
- Membuat analytics yang cukup berguna tanpa terlalu invasif terhadap privacy
- Mengatur admin feature yang membutuhkan service role tanpa mengekspos credential ke client
Potensi Improvement
Beberapa peningkatan yang bisa dikembangkan berikutnya:
- Menambahkan automated test untuk API subscription, slug check, dan analytics
- Memindahkan save card ke server action atau API route agar limit enforcement lebih kuat di server
- Menambahkan payment gateway otomatis agar aktivasi plan tidak manual
- Menambahkan custom domain per card untuk plan Business
- Menambahkan audit log untuk aktivitas admin
- Menambahkan rate limiting untuk endpoint analytics dan slug check
- Menambahkan cache strategy yang lebih detail untuk public card dan template
- Menambahkan export QR code untuk kartu publik
"Project Ini Tentang Apa?"
Ucansee adalah SaaS digital card builder. User bisa login, membuat kartu digital, mengatur profil dan link, memilih template, upload gambar, publish kartu ke URL publik, lalu melihat analytics performa kartu. Di sisi admin, aplikasi mendukung pengelolaan user, template, subscription plan, payment setting, dan global site settings.
"Tech Stack-nya Apa?"
Project ini menggunakan Next.js 13 App Router, React, TypeScript, Tailwind CSS, Radix/shadcn-style components, Supabase untuk auth dan Postgres database, Cloudinary untuk image upload, Zod dan React Hook Form untuk form validation, Recharts untuk analytics chart, serta dnd-kit untuk drag and drop link ordering.
"Bagian Tersulitnya Apa?"
Bagian tersulit adalah menyatukan editor real-time, dynamic template, subscription limit, dan public rendering. Editor harus nyaman dipakai, tetapi tetap harus memvalidasi slug, membatasi fitur berdasarkan plan, upload gambar dengan aman, dan langsung menampilkan preview sesuai template yang dipilih. Di sisi lain, public card harus cepat, aman, dan hanya tampil jika memang dipublish.