Otomatisasi buat Formulir rapi kirm langsung ke pesan WhatsApp
Platform otomatisasi pemesanan dan komunikasi bagi UMKM di Indonesia β mentransformasi interaksi WhatsApp yang tidak terstruktur menjadi data yang sistematis, profesional, dan siap ditindaklanjuti.
Proposisi Nilai:
- Efisiensi Operasional β Memangkas waktu respons dan pencatatan pesanan secara manual.
- Akurasi Data β Eliminasi risiko kesalahan perhitungan melalui Mesin Struk Otomatis.
- Profesionalisme β Format pesan WhatsApp yang rapi dan terstruktur di hadapan pelanggan.
- Form Builder β antarmuka drag-and-drop untuk menyusun dan mengurutkan field (Teks, Angka, Pilihan, Tanggal, Area Teks), lengkap dengan konfigurasi label, placeholder, dan status wajib isi.
- WhatsApp Template Editor β editor templat dengan placeholder dinamis (
{{Nama}},{{Total}}), dukungan sintaks Markdown WhatsApp (tebal, miring), dan preview real-time dalam bentuk chat bubble. - Mesin Struk Otomatis (Auto-Receipt Engine) β kalkulasi otomatis untuk field bertipe angka/perkalian, rendering data respons ke format teks Markdown yang rapi.
- Formulir Publik & Distribusi β halaman formulir publik tanpa autentikasi, langsung terintegrasi ke WhatsApp melalui tautan
wa.medan fungsi Copy to Clipboard. - Dashboard Dasar β daftar formulir yang telah dibuat dan tabel respons masuk untuk pemantauan.
- Rate Limiting β maksimal 20 pengiriman per IP per jam untuk mencegah spam.
- Autentikasi β register & login dengan email + password via Supabase Auth.
- Mobile-First β formulir publik dioptimalkan untuk layar seluler, karena mayoritas responden UMKM mengakses via ponsel.
- Watermark β footer "Dibuat dengan Formatic" pada formulir publik sebagai strategi akuisisi organik.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, React 19) |
| Language | TypeScript |
| Styling | Tailwind CSS 4 + shadcn/ui |
| Auth & Database | Supabase (PostgreSQL, Auth, RLS) |
| Reorder | Tombol β²/βΌ (tanpa library) |
| Validation | Zod 4 |
| Icons | Lucide React |
| Hosting | Vercel (frontend) + Supabase (backend) |
- Node.js 20.9+
- A Supabase project
git clone https://github.com/your-username/formatic.git
cd formatic
npm installcp .env.example .env.localEdit .env.local dengan credentials dari Supabase Dashboard:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-publishable-keyJalankan SQL berikut di Supabase SQL Editor untuk membuat tabel dan RLS policies:
Click to expand full SQL schema
-- ENUM untuk tipe field
CREATE TYPE field_type AS ENUM ('text', 'number', 'select', 'date', 'textarea');
-- Tabel forms
CREATE TABLE forms (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
description TEXT DEFAULT '',
wa_phone TEXT DEFAULT '',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_forms_user_id ON forms(user_id);
CREATE INDEX idx_forms_slug ON forms(slug);
-- Tabel form_fields
CREATE TABLE form_fields (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
form_id UUID NOT NULL REFERENCES forms(id) ON DELETE CASCADE,
label TEXT NOT NULL,
field_type field_type NOT NULL DEFAULT 'text',
options JSONB DEFAULT NULL,
is_required BOOLEAN DEFAULT FALSE,
placeholder TEXT DEFAULT '',
sort_order INT NOT NULL DEFAULT 0
);
CREATE INDEX idx_form_fields_form_id ON form_fields(form_id);
-- Tabel wa_templates
CREATE TABLE wa_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
form_id UUID NOT NULL REFERENCES forms(id) ON DELETE CASCADE UNIQUE,
body TEXT NOT NULL DEFAULT ''
);
-- Tabel responses
CREATE TABLE responses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
form_id UUID NOT NULL REFERENCES forms(id) ON DELETE CASCADE,
data JSONB NOT NULL DEFAULT '{}',
rendered_message TEXT NOT NULL DEFAULT '',
ip_address INET,
submitted_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_responses_form_id ON responses(form_id);
CREATE INDEX idx_responses_submitted_at ON responses(submitted_at DESC);
-- Auto-update updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER forms_updated_at
BEFORE UPDATE ON forms
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Enable RLS
ALTER TABLE forms ENABLE ROW LEVEL SECURITY;
ALTER TABLE form_fields ENABLE ROW LEVEL SECURITY;
ALTER TABLE wa_templates ENABLE ROW LEVEL SECURITY;
ALTER TABLE responses ENABLE ROW LEVEL SECURITY;
-- RLS: Owner CRUD
CREATE POLICY "Users can manage own forms"
ON forms FOR ALL
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());
CREATE POLICY "Users can manage own form fields"
ON form_fields FOR ALL
USING (form_id IN (SELECT id FROM forms WHERE user_id = auth.uid()))
WITH CHECK (form_id IN (SELECT id FROM forms WHERE user_id = auth.uid()));
CREATE POLICY "Users can manage own templates"
ON wa_templates FOR ALL
USING (form_id IN (SELECT id FROM forms WHERE user_id = auth.uid()))
WITH CHECK (form_id IN (SELECT id FROM forms WHERE user_id = auth.uid()));
-- RLS: Public access (form publik)
CREATE POLICY "Public can read active forms"
ON forms FOR SELECT
USING (is_active = TRUE);
CREATE POLICY "Public can read fields of active forms"
ON form_fields FOR SELECT
USING (form_id IN (SELECT id FROM forms WHERE is_active = TRUE));
CREATE POLICY "Public can read templates of active forms"
ON wa_templates FOR SELECT
USING (form_id IN (SELECT id FROM forms WHERE is_active = TRUE));
-- RLS: Responses
CREATE POLICY "Anyone can submit responses"
ON responses FOR INSERT
WITH CHECK (TRUE);
CREATE POLICY "Form owners can read responses"
ON responses FOR SELECT
USING (form_id IN (SELECT id FROM forms WHERE user_id = auth.uid()));Di Supabase Dashboard, aktifkan Email provider di Authentication > Providers.
npm run devBuka http://localhost:3000 π
βββ app/
β βββ (auth)/ # Login & register (route group)
β βββ dashboard/ # Authenticated: form builder, responses
β β βββ forms/
β β βββ new/ # Buat form baru
β β βββ [id]/ # Edit form + WA template
β β βββ responses/ # Lihat response
β βββ f/[slug]/ # Public form (responden)
β β βββ success/ # Submit success + WA actions
β βββ actions.ts # Server actions (CRUD)
β βββ page.tsx # Landing page
βββ components/
β βββ ui/ # shadcn/ui primitives
β βββ form-builder/ # Drag-drop form editor
β βββ wa-template/ # WA template editor + preview
β βββ form-public/ # Public form renderer + WA buttons
β βββ dashboard/ # Dashboard cards & tables
βββ lib/
β βββ supabase/ # Client, server, public helpers
β βββ wa-template.ts # Render {{placeholder}} template
β βββ receipt-engine.ts # Auto-receipt calculation & rendering
β βββ rate-limit.ts # Per-IP rate limiter
β βββ utils.ts # cn(), generateSlug()
βββ types/ # Shared TypeScript types
βββ proxy.ts # Supabase session refresh (middleware)
flowchart LR
A[Buat Formulir\n+ Template WA] --> B[Bagikan Link\n/f/[slug]]
B --> C[Responden Isi\nForm Publik]
C --> D[Mesin Struk\nOtomatis]
D --> E[Pesan WhatsApp\n+ Kirim via WA\n+ Salin Pesan]
- Integrasi WhatsApp β tidak menggunakan WhatsApp Business API resmi. Hanya mengandalkan deep link (
wa.me) dan clipboard API. - Free Tier Supabase β basis data dibatasi 500 MB, bandwidth 5 GB/bulan. Diimplementasikan kebijakan retensi: respons otomatis dihapus setelah > 90 hari.
- Performa β waktu muat halaman formulir publik harus di bawah 2 detik (LCP < 2s).
- Keamanan β Row Level Security (RLS) diterapkan secara ketat di Supabase untuk isolasi data antar-pengguna.
- Fitur yang dikecualikan dari MVP β monetisasi, notifikasi real-time, logika kondisional, integrasi API pihak ketiga.
- Validasi Product-Market Fit dalam waktu 30 hari pasca-peluncuran.
- Minimal 20 pengguna awal (beta testers) dari segmen UMKM lokal.
- Biaya operasional di bawah Rp 50.000/bulan dengan memanfaatkan free tier infrastruktur.
Kontribusi sangat welcome! Cara berkontribusi:
- Fork repo ini
- Buat branch baru (
git checkout -b feature/amazing-feature) - Commit perubahan (
git commit -m 'Add amazing feature') - Push ke branch (
git push origin feature/amazing-feature) - Buka Pull Request
Dibuat oleh milikbersama.com