AbsenUMKM — Absensi Selfie + GPS
Aplikasi SaaS absensi karyawan berbasis web (PWA) yang menggantikan mesin fingerprint mahal untuk UMKM dengan 5-50 karyawan. Karyawan absen masuk/keluar cukup dari HP dengan swafoto (selfie) dan verifikasi lokasi GPS di dalam radius kantor. Admin/owner mendapat rekap kehadiran bulanan otomatis (hadir, terlambat, pulang cepat, absen) dan bisa mengunduhnya sebagai file Excel siap-olah untuk penggajian. Multi-tenant: satu instance melayani banyak bisnis dengan data terisolasi.
AbsenUMKM — Absensi Selfie + GPS untuk UMKM
Aplikasi SaaS absensi karyawan berbasis web (PWA) yang menggantikan mesin fingerprint mahal untuk UMKM dengan 5-50 karyawan. Karyawan absen masuk/keluar cukup dari HP dengan swafoto (selfie) dan verifikasi lokasi GPS di dalam radius kantor. Admin/owner mendapat rekap kehadiran bulanan otomatis (hadir, terlambat, pulang cepat, absen) dan bisa mengunduhnya sebagai file Excel siap-olah untuk penggajian. Multi-tenant: satu instance melayani banyak bisnis dengan data terisolasi.
Problem
UMKM di Indonesia (warung, toko, bengkel, kafe, klinik kecil) kesulitan mencatat kehadiran karyawan secara akurat. Mesin fingerprint mahal (Rp1-3 juta), butuh instalasi, dan tidak cocok untuk karyawan yang berpindah/lapangan. Pencatatan manual di buku atau grup WhatsApp rawan titip absen, sulit direkap, dan menyita waktu owner saat menghitung gaji. Owner butuh cara murah, cepat, dan tepercaya untuk memastikan karyawan benar-benar hadir di lokasi kerja pada jam yang benar, serta merekapnya tanpa kerja manual.
Goals
- Karyawan bisa absen masuk & keluar dalam < 15 detik hanya dengan HP (selfie + GPS), tanpa perangkat tambahan.
- Mencegah titip absen melalui bukti swafoto dan validasi radius lokasi kantor (geofence).
- Owner/admin mendapat rekap kehadiran bulanan otomatis per karyawan (hadir/terlambat/absen) tanpa hitung manual.
- Rekap bulanan bisa diunduh sebagai file Excel (.xlsx) siap dipakai untuk penggajian.
- Onboarding bisnis baru (daftar, set lokasi, tambah karyawan) selesai < 10 menit tanpa bantuan teknis.
- Isolasi data antar-bisnis terjamin (multi-tenant) sehingga aman dipakai banyak UMKM sekaligus.
Non-Goals
- Perhitungan gaji/payroll otomatis (potongan, lembur, THR) — cukup export Excel untuk diolah manual dulu.
- Manajemen cuti/izin dengan alur persetujuan berjenjang.
- Penjadwalan shift kompleks dan rotasi otomatis.
- Face recognition / liveness detection berbasis AI untuk mencocokkan wajah — MVP cukup menyimpan bukti swafoto.
- Aplikasi native iOS/Android di App Store/Play Store — MVP berupa web PWA.
- Integrasi mesin fingerprint/RFID atau perangkat keras eksternal.
- Notifikasi push/WhatsApp otomatis untuk keterlambatan.
- Modul multi-cabang lanjutan dengan hierarki manajer per cabang (MVP mendukung beberapa titik lokasi dalam satu bisnis, tanpa hierarki peran per cabang).
Target Users
- Owner UMKM (pemilik toko/bengkel/kafe, 25-50 th, awam teknologi) — Ingin tahu siapa hadir, terlambat, atau bolos tanpa perlu ke lokasi; butuh rekap bulanan cepat untuk hitung gaji; solusi murah tanpa beli alat.
- Admin/HR merangkap (staf senior atau istri owner yang mengurus SDM) — Mengelola daftar karyawan, mengatur lokasi & jam kerja, memeriksa rekap harian, dan mengekspor data ke Excel untuk penggajian.
- Karyawan (kasir, teknisi, barista, 18-40 th, HP Android kelas menengah) — Cara absen yang cepat dan mudah tiap datang/pulang, bisa dari HP sendiri, dan bisa melihat riwayat kehadirannya sendiri.
User Stories
- must-haveSebagai karyawan, saya ingin absen masuk dengan mengambil swafoto dan lokasi GPS saya terdeteksi otomatis, agar kehadiran saya tercatat resmi tanpa perlu mesin fingerprint atau tanda tangan manual.
- should-haveSebagai karyawan, saya ingin melihat riwayat absensi saya sendiri (tanggal, jam masuk, jam pulang, status), agar saya bisa memastikan kehadiran saya tercatat benar dan tahu jika pernah terlambat.
- must-haveSebagai owner UMKM, saya ingin menetapkan titik lokasi kantor beserta radius toleransi dan jam kerja, agar karyawan hanya bisa absen saat berada di lokasi kerja dan sistem tahu siapa yang terlambat.
- must-haveSebagai admin, saya ingin melihat rekap kehadiran bulanan seluruh karyawan dalam satu tabel, agar saya bisa menilai kedisiplinan dan menyiapkan dasar penghitungan gaji.
- must-haveSebagai admin, saya ingin mengunduh rekap kehadiran bulanan sebagai file Excel, agar saya bisa mengolahnya lebih lanjut untuk penggajian di spreadsheet.
- must-haveSebagai admin, saya ingin menambah, mengundang, dan menonaktifkan karyawan, agar daftar karyawan selalu sesuai jumlah pegawai aktif dan mantan karyawan tak bisa absen lagi.
Features
Autentikasi & Onboarding Bisnis (Multi-tenant)
Owner mendaftarkan bisnis (nama usaha, zona waktu), otomatis menjadi tenant baru dengan dirinya sebagai owner. Login berbasis email+password (Supabase Auth). Setiap data ditautkan ke business_id dan diisolasi via Row Level Security.
- Owner bisa mendaftar dengan email, password, dan nama bisnis; setelah verifikasi, record 'businesses' dan profil owner (role=owner) terbentuk.
- Pengguna yang login hanya bisa membaca/menulis data milik business_id-nya sendiri; percobaan akses tenant lain ditolak oleh RLS (uji lintas-tenant gagal aksesnya).
- Sesi login bertahan (persisted) dan pengguna diarahkan ke dashboard sesuai peran (owner/admin → dashboard admin, employee → halaman absen).
- Password minimal 8 karakter divalidasi di form; email duplikat menampilkan pesan error yang jelas dalam Bahasa Indonesia.
Manajemen Karyawan
Admin/owner menambah karyawan (nama, no. HP, jabatan), mengundang lewat email atau membuat kode/link undangan, mengedit data, dan menonaktifkan karyawan tanpa menghapus riwayat.
- Admin bisa menambah karyawan; karyawan menerima cara masuk (email undangan Supabase atau link/kode aktivasi) dan bisa set password lalu login.
- Daftar karyawan menampilkan nama, jabatan, status (aktif/nonaktif), dan bisa difilter aktif/nonaktif.
- Menonaktifkan karyawan membuat ia tidak bisa lagi melakukan absen, namun riwayat absensinya tetap tersimpan dan muncul di rekap.
- Batas jumlah karyawan aktif tidak melebihi 50 tervalidasi; melebihi menampilkan pesan yang jelas.
Konfigurasi Lokasi Kantor & Jam Kerja
Owner menetapkan satu atau lebih titik lokasi kerja (latitude, longitude, radius meter) dan aturan jam kerja (jam masuk, jam pulang, toleransi keterlambatan menit, hari kerja). Bisa memilih lokasi via peta atau input koordinat.
- Owner bisa menyimpan lokasi dengan latitude, longitude, dan radius (mis. 50-500 m); minimal 1 lokasi wajib ada sebelum karyawan bisa absen.
- Owner bisa menetapkan jam masuk, jam pulang, dan toleransi terlambat (menit); nilai tersimpan dan dipakai untuk penentuan status.
- Titik lokasi bisa dipilih dari posisi GPS perangkat saat ini (tombol 'gunakan lokasi saya') atau input koordinat manual.
- Radius yang tersimpan dipakai sebagai ambang validasi absen (uji: absen di luar radius ditolak, di dalam radius diterima).
Absen Masuk & Keluar (Selfie + GPS) — Fitur Inti
Karyawan membuka halaman absen, sistem meminta izin kamera & lokasi, karyawan mengambil swafoto, sistem menangkap koordinat GPS, menghitung jarak ke lokasi kantor terdekat (Haversine), dan menyimpan absen jika dalam radius. Status (Hadir/Terlambat) dihitung otomatis berdasar jam kerja.
- Halaman absen meminta izin kamera dan lokasi; jika ditolak, tampil instruksi cara mengaktifkan ulang.
- Karyawan bisa mengambil swafoto dari kamera depan; foto terunggah ke storage privat dan URL/refnya tersimpan di record absensi.
- Sistem menghitung jarak posisi ke lokasi kantor terdekat; absen HANYA berhasil jika jarak ≤ radius yang dikonfigurasi, jika di luar radius muncul pesan 'Anda berada X meter di luar lokasi kerja' dan absen dibatalkan.
- Absen masuk kedua kali di hari yang sama dicegah (idempoten per karyawan per hari untuk check-in); check-out hanya bisa setelah check-in.
- Status otomatis: 'Terlambat' jika check_in melewati jam masuk + toleransi, selain itu 'Hadir'; waktu disimpan dalam zona waktu bisnis.
- Setiap absen menyimpan: waktu, koordinat, jarak (meter), lokasi terpilih, dan URL swafoto.
Riwayat Absensi Karyawan
Karyawan bisa melihat daftar absensinya sendiri (tanggal, jam masuk, jam keluar, status, dan thumbnail swafoto) beserta ringkasan bulan berjalan.
- Karyawan melihat riwayat absensinya sendiri terurut dari terbaru, memuat tanggal, jam masuk, jam keluar, dan status.
- Karyawan tidak bisa melihat riwayat karyawan lain (dibatasi RLS).
- Tampil ringkasan bulan berjalan: jumlah hari hadir, jumlah terlambat, dan jumlah tidak absen.
Dashboard Rekap Kehadiran Bulanan
Admin melihat rekap seluruh karyawan untuk bulan terpilih: matriks per hari atau tabel ringkas (Hadir/Terlambat/Absen/Pulang Cepat) dengan total per karyawan. Bisa memilih bulan & tahun, dan melihat detail absen (termasuk swafoto & jarak) per baris.
- Admin memilih bulan & tahun; tabel menampilkan semua karyawan aktif+nonaktif yang punya data di periode itu.
- Per karyawan ditampilkan total: jumlah Hadir, Terlambat, dan Tidak Hadir (hari kerja tanpa absen), dihitung berdasar hari kerja bisnis.
- Admin bisa membuka detail satu absen dan melihat swafoto, koordinat, jarak, jam masuk & keluar.
- Perhitungan status pada rekap konsisten dengan status yang tersimpan saat absen (tidak ada perbedaan hasil).
Export Rekap ke Excel (.xlsx)
Admin mengunduh rekap bulan terpilih sebagai file Excel dengan kolom rapi (nama, jabatan, per-tanggal status/jam, total hadir/terlambat/absen), siap dipakai untuk penggajian.
- Tombol 'Export Excel' menghasilkan file .xlsx valid yang bisa dibuka di Microsoft Excel & Google Sheets tanpa error.
- File berisi header periode (bulan-tahun & nama bisnis), satu baris per karyawan, kolom rekap harian, dan kolom total (Hadir/Terlambat/Absen).
- Jam dan tanggal diformat sesuai zona waktu bisnis dan format lokal Indonesia (mis. HH:mm, dd/MM/yyyy).
- Export hanya memuat data business_id milik pengguna (tidak bocor lintas-tenant) dan menyertakan tanggal generate.
Tech Stack
Library kunci: @supabase/supabase-js & @supabase/ssr (auth/session), exceljs (generate .xlsx), zod (validasi), date-fns / date-fns-tz (zona waktu Asia/Jakarta). Perhitungan jarak pakai formula Haversine sendiri (tanpa API peta berbayar). Peta pemilih lokasi opsional pakai Leaflet + OpenStreetMap gratis; jika ingin cepat, cukup tombol 'gunakan lokasi saya' + input koordinat. Semua string UI Bahasa Indonesia.
Tasks (siap kirim ke AI agent)
Menyiapkan kerangka aplikasi, koneksi Supabase, styling, dan deploy skeleton ke Vercel.
Lihat isi perintah
Buat proyek baru bernama 'absen-umkm' dengan Next.js 14 (App Router) + TypeScript + Tailwind CSS. Instal dependency: @supabase/supabase-js, @supabase/ssr, zod, date-fns, date-fns-tz, exceljs. Buat file lib/supabaseClient.ts (browser client) dan lib/supabaseServer.ts (server client via @supabase/ssr membaca cookies). Tambahkan .env.example berisi NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY. Konfigurasi PWA: buat app/manifest.ts (name 'AbsenUMKM', display standalone, theme color) dan set metadata viewport mobile-first di app/layout.tsx. Buat halaman placeholder app/page.tsx (landing sederhana), app/login/page.tsx, app/(admin)/dashboard/page.tsx, app/(app)/absen/page.tsx. Semua teks Bahasa Indonesia. Tambah README.md dengan langkah setup Supabase & deploy Vercel. Definition of done: `npm run build` sukses tanpa error TypeScript, dev server jalan, dan struktur folder rapi (app/, lib/, components/).
Membuat tabel businesses, profiles, locations, work_settings, attendance beserta RLS dan bucket storage untuk selfie.
Lihat isi perintah
Buat file migrasi SQL di supabase/migrations/0001_init.sql untuk skema multi-tenant Postgres berikut. Tabel: (1) businesses(id uuid pk default gen_random_uuid(), name text, timezone text default 'Asia/Jakarta', owner_id uuid references auth.users, created_at timestamptz default now()); (2) profiles(id uuid pk references auth.users, business_id uuid references businesses, role text check in ('owner','admin','employee'), full_name text, phone text, job_title text, status text default 'active' check in ('active','inactive'), created_at timestamptz default now()); (3) locations(id uuid pk, business_id uuid references businesses, name text, latitude double precision, longitude double precision, radius_meters int default 100, created_at); (4) work_settings(business_id uuid pk references businesses, work_start_time time, work_end_time time, late_tolerance_minutes int default 10, work_days int[] default '{1,2,3,4,5,6}'); (5) attendance(id uuid pk, business_id uuid, employee_id uuid references profiles, work_date date, check_in_at timestamptz, check_in_selfie_path text, check_in_lat double precision, check_in_lng double precision, check_in_location_id uuid, check_in_distance_m int, check_out_at timestamptz, check_out_selfie_path text, check_out_lat double precision, check_out_lng double precision, status text check in ('present','late','absent'), created_at). Tambah unique index (business_id, employee_id, work_date) untuk mencegah dobel check-in per hari. Aktifkan RLS pada semua tabel dan tulis policy: pengguna hanya bisa SELECT/INSERT/UPDATE baris dengan business_id yang cocok dengan profiles.business_id miliknya; hanya role owner/admin yang boleh menulis locations & work_settings & mengelola profiles; employee hanya boleh menulis attendance miliknya sendiri (employee_id = auth.uid()). Buat bucket storage privat 'selfies' dengan policy akses per business_id (path diawali business_id/). Sertakan helper SQL function current_business_id() SECURITY DEFINER yang mengembalikan business_id dari profiles pengguna login. Definition of done: file migrasi bisa dijalankan bersih di project Supabase, RLS aktif, dan ada komentar SQL menjelaskan tiap policy.Alur daftar bisnis baru, login, session, proteksi route, dan pengalihan berdasar peran.
Lihat isi perintah
Implementasikan autentikasi memakai Supabase Auth (email+password) dengan @supabase/ssr. Buat: (1) app/register/page.tsx — form daftar (nama bisnis, nama owner, email, password) dengan validasi zod (password ≥ 8). Saat submit, panggil server action app/register/actions.ts yang: sign up user, buat row businesses (owner_id = user.id), lalu buat profiles(role='owner', business_id, full_name). (2) app/login/page.tsx + action untuk sign in. (3) middleware.ts untuk refresh session & proteksi route: user belum login diarahkan ke /login; setelah login, role owner/admin → /dashboard, employee → /absen. (4) components/SignOutButton.tsx. (5) lib/auth.ts helper getCurrentProfile() yang mengembalikan {id, business_id, role}. Tangani error umum (email sudah dipakai, kredensial salah) dengan pesan Bahasa Indonesia. Definition of done: bisa daftar bisnis baru → otomatis login → masuk dashboard; logout bekerja; mengakses /dashboard tanpa login redirect ke /login; uji manual bahwa dua bisnis berbeda tidak saling melihat data (RLS).Halaman admin untuk menambah, mengundang, mengedit, dan menonaktifkan karyawan.
Lihat isi perintah
Buat modul manajemen karyawan di app/(admin)/karyawan/page.tsx (daftar) dan komponen form. Fitur: (1) Tabel karyawan (nama, jabatan, no HP, status) dengan filter aktif/nonaktif, memakai data profiles milik business_id via RLS. (2) Tombol 'Tambah Karyawan' membuka form (nama, no HP, jabatan, email); server action app/(admin)/karyawan/actions.ts memakai Supabase admin client (SERVICE_ROLE_KEY, hanya di server) untuk membuat user via inviteUserByEmail ATAU membuat user + kirim link set-password, lalu insert profiles(role='employee', business_id, status='active'). (3) Edit data karyawan. (4) Toggle nonaktif/aktif yang mengubah profiles.status; karyawan nonaktif tidak boleh absen (dicek juga di T6). (5) Validasi jumlah karyawan aktif ≤ 50, jika melebihi tampilkan pesan. Semua aksi hanya boleh oleh role owner/admin (cek di server). Definition of done: admin bisa menambah karyawan yang lalu bisa login & set password; menonaktifkan karyawan menghilangkannya dari daftar aktif tapi riwayat tetap ada; batas 50 tervalidasi; UI Bahasa Indonesia, responsif mobile.
Halaman pengaturan untuk titik lokasi + radius dan aturan jam kerja/toleransi.
Lihat isi perintah
Buat app/(admin)/pengaturan/page.tsx berisi dua bagian. (A) Lokasi Kerja: form tambah/edit lokasi (nama, latitude, longitude, radius_meters) tersimpan ke tabel locations; sediakan tombol 'Gunakan Lokasi Saya' yang membaca Geolocation API untuk mengisi lat/lng; tampilkan daftar lokasi tersimpan dengan opsi hapus; minimal 1 lokasi wajib. Opsional: peta pemilih pakai Leaflet + tile OpenStreetMap (react-leaflet) untuk geser pin — jika dilewati, cukup input koordinat + tombol lokasi saya. (B) Jam Kerja: form work_settings (jam masuk, jam pulang, toleransi keterlambatan menit, pilihan hari kerja Senin-Minggu) upsert ke tabel work_settings by business_id. Validasi dengan zod (radius 20-1000 m, jam pulang > jam masuk). Hanya owner/admin yang boleh mengubah (cek server + RLS). Definition of done: owner bisa menyimpan ≥1 lokasi dan aturan jam kerja; nilai tersimpan benar dan terbaca ulang; tombol 'Gunakan Lokasi Saya' mengisi koordinat dari GPS perangkat; UI Bahasa Indonesia.
Alur absen karyawan lengkap: kamera, GPS, Haversine, upload selfie, penentuan status, anti-dobel.
Lihat isi perintah
Implementasikan halaman absen di app/(app)/absen/page.tsx untuk role employee. Alur: (1) Saat load, minta izin kamera (getUserMedia video facingMode 'user') dan lokasi (Geolocation getCurrentPosition high accuracy); jika ditolak tampilkan instruksi mengaktifkan ulang. (2) Tampilkan tombol 'Absen Masuk' / 'Absen Keluar' sesuai status hari ini (query attendance work_date = hari ini zona bisnis). (3) Saat absen: ambil frame kamera ke canvas → JPEG blob (kompres ~0.6, maksimal ~300KB), tangkap koordinat GPS. (4) Kirim ke Route Handler app/api/absen/route.ts (server, runtime nodejs): validasi via zod, ambil profile (harus status active, jika inactive tolak), ambil locations bisnis, hitung jarak ke tiap lokasi pakai fungsi haversine di lib/geo.ts, pilih lokasi terdekat; jika jarak > radius tolak dengan pesan 'Anda berada {jarak} meter di luar lokasi kerja'. (5) Jika valid, upload selfie ke storage bucket 'selfies' path {business_id}/{employee_id}/{tanggal}-{in|out}.jpg, lalu untuk check-in: cek belum ada check-in hari ini (unique constraint), hitung status ('late' jika check_in_at > jam masuk + toleransi, else 'present') pakai date-fns-tz zona bisnis, insert attendance; untuk check-out: pastikan sudah check-in, update kolom check_out. (6) Tampilkan konfirmasi sukses dengan jam & status. Buat lib/geo.ts berisi haversineMeters(lat1,lng1,lat2,lng2). Definition of done: karyawan aktif bisa check-in di dalam radius (tercatat + selfie tersimpan) dan ditolak di luar radius; dobel check-in dicegah; status Terlambat terhitung benar; check-out hanya setelah check-in; karyawan nonaktif ditolak; berjalan di Chrome Android mobile.Rekap kehadiran per bulan untuk admin dan halaman riwayat pribadi karyawan.
Lihat isi perintah
Bangun dua tampilan. (A) Dashboard admin app/(admin)/dashboard/page.tsx: pemilih bulan & tahun; ambil attendance business_id pada rentang bulan; render tabel per karyawan dengan kolom total Hadir (present), Terlambat (late), dan Tidak Hadir (jumlah hari kerja pada periode dikurangi hari yang ada absennya, berdasar work_settings.work_days). Sediakan mode detail: klik baris/tanggal membuka modal berisi jam masuk, jam keluar, status, jarak, dan swafoto (ambil signed URL dari storage privat). Hitung 'absen' hanya untuk hari kerja yang sudah lewat. (B) Riwayat karyawan app/(app)/riwayat/page.tsx: daftar absensi milik sendiri (RLS) terurut terbaru dengan tanggal, jam masuk, jam keluar, status, thumbnail selfie; plus ringkasan bulan berjalan (hadir/terlambat/tidak hadir). Buat util bersama lib/attendance.ts untuk menghitung ringkasan agar logika konsisten dengan status tersimpan. Definition of done: admin melihat rekap seluruh karyawan untuk bulan terpilih dengan total benar; detail menampilkan swafoto via signed URL; karyawan hanya melihat riwayatnya sendiri; angka rekap konsisten dengan status yang tersimpan; responsif.
Endpoint & tombol untuk mengunduh rekap bulan terpilih sebagai file Excel rapi.
Lihat isi perintah
Buat Route Handler app/api/export/route.ts (runtime nodejs) yang menerima query bulan & tahun, memverifikasi user role owner/admin dan business_id, lalu memakai library exceljs untuk membangun workbook: sheet 'Rekap Kehadiran' dengan judul (nama bisnis + 'Periode: {Bulan} {Tahun}'), baris header kolom (No, Nama, Jabatan, kolom per tanggal 1..akhir bulan berisi status/jam ringkas, Total Hadir, Total Terlambat, Total Tidak Hadir), lalu satu baris per karyawan. Format tanggal dd/MM/yyyy dan jam HH:mm sesuai zona waktu bisnis (date-fns-tz). Set response header Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet dan Content-Disposition attachment filename 'rekap-{bulan}-{tahun}.xlsx'; tulis workbook ke buffer via workbook.xlsx.writeBuffer(). Pastikan hanya data business_id pengguna yang diambil. Tambahkan tombol 'Export Excel' di dashboard (T7) yang memanggil endpoint dan memicu unduhan. Definition of done: file .xlsx terunduh, terbuka tanpa error di Excel & Google Sheets, berisi header periode + total per karyawan yang cocok dengan angka di dashboard, dan tidak membocorkan data tenant lain.Penyempurnaan UX mobile, penanganan error/izin, pengujian alur penuh, dan deploy produksi.
Lihat isi perintah
Lakukan pemantapan akhir. (1) UX: pastikan seluruh halaman mobile-first, tambahkan loading & empty state, toast sukses/gagal, dan penanganan ramah untuk izin kamera/lokasi ditolak (instruksi + tombol coba lagi). (2) PWA: verifikasi manifest & installable, tambahkan ikon (192/512), dan offline fallback sederhana untuk halaman statis. (3) Keamanan: pastikan SERVICE_ROLE_KEY hanya dipakai di server, semua endpoint memeriksa peran & business_id, dan uji lintas-tenant (akun bisnis A tidak bisa melihat/mengekspor data bisnis B). (4) QA skenario: daftar bisnis → set lokasi & jam → tambah 2 karyawan → karyawan check-in di dalam & di luar radius → check-out → cek terlambat → lihat rekap → export Excel; catat hasil di CHECKLIST.md. (5) Deploy: hubungkan repo ke Vercel, set environment variables, jalankan migrasi Supabase produksi, dan verifikasi build produksi. Definition of done: `npm run build` bersih, seluruh skenario QA lulus dan terdokumentasi di CHECKLIST.md, aplikasi live di URL Vercel dengan Supabase produksi, dan tidak ada kebocoran data lintas-tenant.
Risks
- Akurasi GPS pada HP kelas menengah/di dalam ruangan bisa meleset puluhan meter sehingga karyawan valid tertolak — mitigasi: radius default cukup longgar (mis. 100 m), tampilkan jarak terukur, dan izinkan owner menyesuaikan radius.
- Titip absen tetap mungkin bila karyawan saling foto — MVP hanya menyimpan bukti swafoto tanpa liveness/face-match; mitigasi jangka pendek: selfie wajib dari kamera langsung (bukan galeri) + stempel waktu & lokasi; face recognition ditunda (non-goal).
- Izin kamera/lokasi ditolak atau browser tidak mendukung getUserMedia (mis. iOS Safari lama) menghambat absen — mitigasi: deteksi dukungan, instruksi aktivasi, dan uji lintas-browser.
- Kebocoran data antar-tenant jika RLS/pengecekan peran keliru — mitigasi: RLS ketat di semua tabel + uji lintas-tenant eksplisit di T9.
- Biaya storage swafoto membengkak seiring pemakaian — mitigasi: kompresi JPEG saat capture dan kebijakan retensi (mis. hapus/ arsip foto > beberapa bulan) di iterasi berikutnya.
- Zona waktu & perhitungan status keliru (mis. lewat tengah malam) — mitigasi: simpan timestamptz + hitung status dengan date-fns-tz zona bisnis, sertakan unit test untuk kasus batas.
- Adopsi rendah karena karyawan enggan pakai HP pribadi — risiko produk/komersial; mitigasi di luar MVP (edukasi, mode kiosk 1 perangkat).
Open Questions
- Apakah owner ingin izin/cuti dan pulang-cepat dicatat sebagai status tersendiri di MVP, atau cukup Hadir/Terlambat/Absen?
- Bagaimana model harga SaaS (per bisnis, per karyawan, atau flat) dan bagaimana batas paket ditegakkan di aplikasi?
- Perlukah mode kiosk (satu perangkat/tablet di kasir untuk semua karyawan) selain absen dari HP masing-masing?
- Apakah butuh dukungan beberapa cabang dengan admin terpisah per cabang, atau cukup beberapa titik lokasi dalam satu bisnis?
- Untuk undangan karyawan, lebih baik lewat email atau link/kode WhatsApp mengingat banyak karyawan UMKM tak aktif email?
- Apakah rekap perlu format tambahan (PDF) selain Excel untuk arsip/cetak?
- Berapa lama swafoto absen perlu disimpan (retensi) demi privasi dan biaya storage?