Lewati ke konten
Skematik
Semua template
Web App / SaaS

AbsenUMKM — Absensi Selfie + GPS

AbsenKu adalah aplikasi web/PWA absensi karyawan yang dirancang untuk UMKM. Karyawan dapat melakukan absensi masuk dan keluar dengan swafoto dan verifikasi lokasi GPS dari perangkat seluler mereka. Admin/owner dapat mengelola karyawan dan lokasi kantor, serta mendapatkan rekap kehadiran bulanan otomatis yang dapat diunduh dalam format Excel untuk mempermudah proses penggajian.

Pakai template

AbsenUMKM — Absensi Selfie + GPS

AbsenKu adalah aplikasi web/PWA absensi karyawan yang dirancang untuk UMKM. Karyawan dapat melakukan absensi masuk dan keluar dengan swafoto dan verifikasi lokasi GPS dari perangkat seluler mereka. Admin/owner dapat mengelola karyawan dan lokasi kantor, serta mendapatkan rekap kehadiran bulanan otomatis yang dapat diunduh dalam format Excel untuk mempermudah proses penggajian.

Problem

UMKM sering menghadapi kesulitan dalam mengelola absensi karyawan secara efisien dan akurat. Solusi tradisional seperti mesin fingerprint mahal dan rumit, sementara pencatatan manual rentan terhadap kesalahan dan kecurangan. Dibutuhkan solusi absensi yang terjangkau, mudah digunakan, dan dapat diverifikasi untuk memastikan akurasi data kehadiran.

Goals

  • Mencatat absensi masuk/keluar karyawan dengan swafoto dan verifikasi lokasi GPS dalam radius 50 meter dari titik kantor yang dikonfigurasi.
  • Menyediakan rekap kehadiran bulanan otomatis yang dapat diunduh dalam format Excel dalam waktu kurang dari 5 detik untuk 50 karyawan.
  • Memungkinkan admin mengelola hingga 50 karyawan dan 5 lokasi kantor dalam satu akun owner.
  • Memastikan proses absensi (swafoto + GPS) selesai dalam waktu maksimal 10 detik per transaksi.
  • Memberikan laporan kehadiran yang akurat (status HADIR, TERLAMBAT, PULANG_CEPAT, TIDAK_ABSEN, LOKASI_TIDAK_VALID) berdasarkan jam kerja yang dikonfigurasi.

Non-Goals

  • Integrasi dengan sistem penggajian pihak ketiga.
  • Fitur manajemen cuti atau lembur.
  • Sistem penjadwalan shift karyawan yang kompleks.
  • Pengembangan aplikasi mobile native (fokus pada PWA).
  • Verifikasi biometrik wajah otomatis dari swafoto (verifikasi manual oleh admin).
  • Fitur absen offline.
  • Pendaftaran akun owner secara publik (akun owner dibuat manual oleh admin sistem atau seed awal).

Target Users

  • Owner/Admin UMKM (mis. pemilik toko, manajer kafe)Membutuhkan cara mudah untuk memantau kehadiran karyawan, mengelola data karyawan dan lokasi kantor, serta mendapatkan rekap absensi untuk penggajian tanpa biaya besar.
  • Karyawan UMKMMembutuhkan cara cepat dan sederhana untuk melakukan absensi masuk dan keluar menggunakan perangkat seluler pribadi mereka.

User Stories

  • must-have
    Sebagai Owner UMKM, saya ingin Mengkonfigurasi lokasi kantor saya dengan koordinat GPS dan radius validasi, agar Karyawan hanya bisa absen jika berada di dalam area kantor yang ditentukan..
  • must-have
    Sebagai Owner UMKM, saya ingin Menambahkan dan mengelola data karyawan (nama, PIN absensi, jam kerja default), agar Karyawan dapat terdaftar di sistem dan memiliki jadwal kerja yang jelas..
  • must-have
    Sebagai Karyawan, saya ingin Melakukan absensi masuk dengan swafoto dan verifikasi lokasi GPS, agar Kehadiran saya tercatat secara akurat dan terverifikasi..
  • must-have
    Sebagai Karyawan, saya ingin Melakukan absensi keluar dengan swafoto dan verifikasi lokasi GPS, agar Jam pulang saya tercatat dan total jam kerja saya dapat dihitung..
  • must-have
    Sebagai Owner UMKM, saya ingin Melihat rekap kehadiran bulanan semua karyawan dan mengunduhnya sebagai Excel, agar Saya bisa memproses penggajian dengan mudah dan akurat..
  • should-have
    Sebagai Owner UMKM, saya ingin Melihat riwayat absensi detail per karyawan, agar Saya dapat memeriksa absensi individu jika ada pertanyaan atau perselisihan..

User Flow

  1. Owner baru mengakses aplikasi dan login (akun owner di-seed atau dibuat admin sistem).
  2. Owner masuk ke halaman 'Lokasi Kantor' dan menambahkan lokasi kantor baru (nama, koordinat GPS, radius).
  3. Owner masuk ke halaman 'Karyawan' dan menambahkan karyawan baru (nama, email opsional, PIN absensi, jam kerja default).
  4. Karyawan mengunduh PWA ke perangkat seluler mereka.
  5. Karyawan membuka aplikasi dan login menggunakan PIN yang diberikan owner.
  6. Karyawan masuk ke halaman 'Absensi', memilih 'Absen Masuk', mengambil swafoto, dan mengizinkan akses lokasi.
  7. Sistem memverifikasi lokasi GPS dan mencatat absensi masuk jika valid.
  8. Pada akhir jam kerja, karyawan kembali ke halaman 'Absensi', memilih 'Absen Keluar', mengambil swafoto, dan mengizinkan akses lokasi.
  9. Sistem memverifikasi lokasi GPS dan mencatat absensi keluar jika valid.
  10. Owner mengakses halaman 'Rekap Kehadiran Bulanan', memilih bulan/tahun, dan melihat ringkasan absensi.
  11. Owner mengklik tombol 'Export ke Excel' untuk mengunduh laporan bulanan untuk penggajian.
Memuat diagram…

Features

Autentikasi Pengguna (Owner & Karyawan)

must-have

Sistem login untuk owner dan karyawan. Owner login dengan email/password. Karyawan login dengan PIN yang diberikan oleh owner, kemudian dapat mengatur password untuk login selanjutnya atau tetap menggunakan PIN.

  • Pengguna (owner/karyawan) dapat login menggunakan kredensial yang benar.
  • Sistem menolak login dengan kredensial yang salah dan menampilkan pesan error yang informatif.
  • Karyawan dapat login menggunakan PIN yang telah didaftarkan oleh owner.
  • Owner dapat mengatur ulang password karyawan.
  • Sesi pengguna dipertahankan setelah login hingga logout atau kadaluarsa.

Manajemen Karyawan

must-have

Owner dapat menambah, melihat, mengubah, dan menghapus data karyawan (nama, email, PIN absensi, jam kerja default).

  • Owner dapat melihat daftar semua karyawan yang terdaftar dalam akunnya (maks 50 karyawan).
  • Owner dapat menambahkan karyawan baru dengan nama, email (opsional), PIN absensi unik, dan jam kerja default (mis. 09:00-17:00).
  • Sistem menolak penambahan karyawan jika PIN absensi sudah digunakan oleh karyawan lain dalam akun owner yang sama.
  • Owner dapat mengubah detail karyawan yang sudah ada.
  • Owner dapat menghapus karyawan, dan karyawan tersebut tidak lagi muncul di daftar aktif atau dapat absen. Data absensi historis karyawan yang dihapus tetap tersimpan dan dapat diakses untuk laporan.

Konfigurasi Lokasi Kantor

must-have

Owner dapat mendefinisikan lokasi kantor dengan nama, koordinat GPS (latitude, longitude), dan radius validasi (default 50 meter).

  • Owner dapat melihat daftar lokasi kantor yang terdaftar dalam akunnya (maks 5 lokasi).
  • Owner dapat menambahkan lokasi kantor baru dengan nama unik, latitude, longitude, dan radius (default 50 meter, min 10m, max 200m).
  • Sistem menolak penambahan lokasi jika nama lokasi sudah digunakan oleh lokasi lain dalam akun owner yang sama.
  • Owner dapat mengubah detail lokasi kantor yang sudah ada.
  • Owner dapat menghapus lokasi kantor. Sistem akan menolak penghapusan jika ada karyawan yang masih terkait dengan lokasi tersebut dan menampilkan pesan error yang menyebutkan karyawan yang terkait.

Absensi Karyawan (Selfie & GPS)

must-have

Karyawan dapat melakukan absensi masuk dan keluar. Setiap absensi memerlukan swafoto dan verifikasi lokasi GPS. Absensi hanya valid jika karyawan berada dalam radius lokasi kantor yang ditentukan.

  • Karyawan dapat memilih untuk absen masuk atau keluar dari antarmuka aplikasi.
  • Saat absen, aplikasi meminta akses kamera untuk swafoto dan akses lokasi GPS.
  • Sistem berhasil mencatat absensi masuk jika swafoto diambil dan lokasi GPS karyawan berada dalam radius yang ditentukan dari lokasi kantor yang dipilih.
  • Sistem berhasil mencatat absensi keluar jika swafoto diambil dan lokasi GPS karyawan berada dalam radius yang ditentukan dari lokasi kantor yang dipilih.
  • Jika karyawan absen masuk di luar radius, absensi ditolak dengan pesan 'Anda berada di luar area kantor'.
  • Jika karyawan absen keluar di luar radius, absensi ditolak dengan pesan 'Anda berada di luar area kantor'.
  • Karyawan tidak dapat melakukan check-in dua kali dalam satu hari kerja tanpa check-out sebelumnya.
  • Karyawan tidak dapat melakukan check-out tanpa check-in di hari yang sama.

Riwayat Absensi Karyawan

should-have

Karyawan dapat melihat riwayat absensi pribadi mereka, dan owner dapat melihat riwayat absensi detail untuk setiap karyawan.

  • Karyawan dapat melihat daftar riwayat absensi mereka sendiri, termasuk tanggal, jam masuk/keluar, dan status (Hadir, Terlambat, Pulang Cepat, Tidak Absen, Lokasi Tidak Valid).
  • Owner dapat memilih karyawan dari daftar dan melihat riwayat absensi detail karyawan tersebut.
  • Riwayat absensi ditampilkan secara paginasi, dengan opsi filter berdasarkan bulan/tahun.
  • Foto selfie absensi dapat dilihat pada detail riwayat.

Rekap Kehadiran Bulanan & Export Excel

must-have

Owner dapat melihat rekapitulasi kehadiran bulanan untuk semua karyawan dan mengunduhnya dalam format Excel.

  • Owner dapat melihat ringkasan kehadiran bulanan (mis. jumlah hari hadir, terlambat, tidak absen) untuk semua karyawan dalam satu tabel.
  • Owner dapat memilih bulan dan tahun untuk rekapitulasi.
  • Owner dapat mengunduh rekapitulasi bulanan dalam format file Excel (.xlsx) yang berisi data absensi lengkap per karyawan per hari (tanggal, jam masuk, jam keluar, status).
  • File Excel yang diunduh berisi data untuk maksimal 50 karyawan dan 31 hari dalam waktu kurang dari 5 detik.
  • Perhitungan status (Hadir, Terlambat, Pulang Cepat, Tidak Absen, Lokasi Tidak Valid) dilakukan secara otomatis berdasarkan jam kerja default karyawan dan data absensi.

Tech Stack

Frontend: Next.js (React) dengan App Router
Backend: Next.js API Routes
Database: PostgreSQL
Hosting: Vercel

Menggunakan Next.js untuk full-stack development, memanfaatkan fitur PWA untuk pengalaman mobile-friendly. PostgreSQL sebagai database relasional yang handal. Drizzle ORM untuk interaksi database yang type-safe.

Architecture

Aplikasi ini menggunakan arsitektur monolitik dengan Next.js App Router, di mana frontend (React) dan backend (API Routes) berada dalam satu codebase. Data disimpan di PostgreSQL, dan semua interaksi database dilakukan melalui Drizzle ORM. Autentikasi berbasis JWT untuk mengamankan API, dan semua data bisnis difilter berdasarkan `owner_id` untuk mendukung multi-tenancy.

Frontend (Next.js/React)

Menyediakan antarmuka pengguna interaktif untuk owner dan karyawan, mengelola state UI, dan berkomunikasi dengan API backend. Bertanggung jawab untuk akses kamera dan GPS di sisi klien.

Backend (Next.js API Routes)

Menangani permintaan HTTP dari frontend, melakukan validasi data, logika bisnis (mis. verifikasi lokasi, perhitungan status absensi), interaksi dengan database, dan mengelola autentikasi/otorisasi. Semua query database difilter berdasarkan `owner_id` dari sesi pengguna yang terautentikasi.

Database (PostgreSQL)

Menyimpan semua data aplikasi secara persisten, termasuk informasi pengguna, karyawan, lokasi kantor, dan catatan absensi.

Drizzle ORM

Menyediakan lapisan abstraksi untuk berinteraksi dengan PostgreSQL dari backend secara type-safe.

Alur data

  1. Pengguna (Owner/Karyawan) mengakses aplikasi web/PWA.
  2. Karyawan melakukan Absen Masuk/Keluar: UI meminta akses kamera & lokasi GPS, mengambil swafoto, mendapatkan koordinat.
  3. Frontend mengirim data absensi (employee_id, location_id, lat, lon, foto) ke API Backend (POST /api/attendance/check-in atau /check-out).
  4. Backend memvalidasi input, memverifikasi token JWT, memfilter data berdasarkan `owner_id` dari sesi pengguna, menghitung jarak GPS menggunakan `src/lib/geo.ts`.
  5. Jika valid, Backend menyimpan foto ke `public/uploads/attendance/`, mencatat URL, dan memperbarui/membuat `attendance_records` di PostgreSQL melalui Drizzle ORM.
  6. Backend mengembalikan respons sukses/gagal ke Frontend.
  7. Frontend memperbarui UI sesuai respons (mis. menampilkan status absensi terbaru).
Memuat diagram…

Database Schema

Ini struktur data aplikasimu. Kamu tidak perlu paham semuanya — AI yang akan membuatnya. Kolom created_at/updated_at ditambahkan otomatis tiap tabel.

usersTabel untuk menyimpan informasi login semua pengguna (owner dan karyawan).
iduuidPrimary Key
emailtextEmail pengguna, harus unik
password_hashtextHash password pengguna
roleenum:UserRolePeran pengguna (OWNER atau EMPLOYEE)
owner_user_iduuidID user owner untuk karyawan, null jika user adalah owner
employeessoft deleteTabel untuk menyimpan data karyawan yang dikelola oleh owner.
iduuidPrimary Key
owner_iduuidForeign Key ke user owner. Semua data karyawan terisolasi per owner.
user_iduuidForeign Key ke user login karyawan, nullable jika karyawan belum punya akun login terpisah dari PIN
nametextNama lengkap karyawan
employee_pinintegerPIN unik untuk absensi karyawan (unik hanya untuk baris aktif — partial unique index (UNIQUE WHERE deleted_at IS NULL) dengan owner_id)
default_work_start_timetimeJam kerja default masuk (HH:MM) dalam WIB
default_work_end_timetimeJam kerja default keluar (HH:MM) dalam WIB
locationssoft deleteTabel untuk menyimpan data lokasi kantor yang dikonfigurasi oleh owner.
iduuidPrimary Key
owner_iduuidForeign Key ke user owner. Semua data lokasi terisolasi per owner.
nametextNama lokasi kantor (unik hanya untuk baris aktif — partial unique index (UNIQUE WHERE deleted_at IS NULL) dengan owner_id)
latitudenumericKoordinat latitude lokasi kantor
longitudenumericKoordinat longitude lokasi kantor
radius_metersintegerRadius validasi GPS dalam meter (default 50)
attendance_recordsTabel untuk menyimpan catatan absensi masuk dan keluar karyawan.
iduuidPrimary Key
employee_iduuidForeign Key ke karyawan yang melakukan absensi
owner_iduuidForeign Key ke user owner. Semua data absensi terisolasi per owner.
location_iduuidForeign Key ke lokasi kantor tempat absensi dilakukan
check_in_timetimestampWaktu absensi masuk (WIB)
check_in_latitudenumericLatitude saat check-in
check_in_longitudenumericLongitude saat check-in
check_in_photo_urltextURL foto selfie saat check-in
check_out_timetimestampWaktu absensi keluar (WIB)
check_out_latitudenumericLatitude saat check-out
check_out_longitudenumericLongitude saat check-out
check_out_photo_urltextURL foto selfie saat check-out
statusenum:AttendanceStatusStatus absensi (mis. PRESENT, LATE_CHECK_IN, MISSING_CHECK_OUT)
datedateTanggal absensi (unik komposit dengan employee_id dan owner_id)

Relasi

  • users.owner_user_id → users.id (many-to-one)
  • employees.owner_id → users.id (many-to-one)
  • employees.user_id → users.id (one-to-one, nullable)
  • locations.owner_id → users.id (many-to-one)
  • attendance_records.employee_id → employees.id (many-to-one)
  • attendance_records.owner_id → users.id (many-to-one)
  • attendance_records.location_id → locations.id (many-to-one)
Memuat diagram…

Struktur Folder

Struktur ini dikunci agar AI agent membangun dengan susunan file yang sama.

  • src/
  • src/app/
  • src/app/(auth)/ — Route group untuk halaman autentikasi (login)
  • src/app/(auth)/login/ — Halaman login
  • src/app/(auth)/api/ — API routes untuk autentikasi
  • src/app/(auth)/api/login/route.ts — Endpoint login
  • src/app/(app)/ — Route group untuk halaman aplikasi utama (terproteksi)
  • src/app/(app)/dashboard/ — Halaman dashboard owner
  • src/app/(app)/employees/ — Halaman daftar karyawan
  • src/app/(app)/employees/create/ — Form tambah karyawan
  • src/app/(app)/employees/[id]/ — Halaman detail karyawan
  • src/app/(app)/employees/[id]/edit/ — Form edit karyawan
  • src/app/(app)/locations/ — Halaman daftar lokasi kantor
  • src/app/(app)/locations/create/ — Form tambah lokasi kantor
  • src/app/(app)/locations/[id]/edit/ — Form edit lokasi kantor
  • src/app/(app)/attendance/ — Halaman absensi karyawan
  • src/app/(app)/attendance/check-in/route.ts — API check-in
  • src/app/(app)/attendance/check-out/route.ts — API check-out
  • src/app/(app)/attendance/employee/[employeeId]/route.ts — API riwayat absensi per karyawan
  • src/app/(app)/my-attendance/ — Halaman riwayat absensi pribadi karyawan
  • src/app/(app)/reports/monthly/ — Halaman rekap kehadiran bulanan
  • src/app/(app)/reports/monthly/route.ts — API rekap bulanan
  • src/app/(app)/reports/monthly/export/route.ts — API export Excel
  • src/components/ — Komponen UI yang dapat digunakan kembali
  • src/lib/ — Utility functions (mis. geo.ts, auth.ts)
  • src/server/ — Logika bisnis dan layanan backend (mis. services, controllers)
  • src/db/ — Konfigurasi database dan skema Drizzle
  • src/db/schema.ts — Definisi skema database
  • public/ — Aset statis (ikon PWA, foto upload)
  • public/uploads/attendance/ — Folder untuk menyimpan foto selfie absensi
  • middleware.ts — Middleware autentikasi

API Endpoints

POST
/api/auth/loginMelakukan autentikasi pengguna (owner atau karyawan). Menerima email/PIN dan password. Mengembalikan JWT token jika berhasil.
GET
/api/employeesMendapatkan daftar semua karyawan yang terdaftar di akun owner. Paginated.paginatedauth: OWNER
POST
/api/employeesMenambahkan karyawan baru ke akun owner. Body: { name, email?, employee_pin, default_work_start_time, default_work_end_time }.auth: OWNER
GET
/api/employees/:idMendapatkan detail karyawan berdasarkan ID.auth: OWNER
PUT
/api/employees/:idMemperbarui detail karyawan berdasarkan ID. Body: { name?, email?, employee_pin?, default_work_start_time?, default_work_end_time? }.auth: OWNER
DELETE
/api/employees/:idMenghapus (soft delete) karyawan berdasarkan ID. Data absensi historis tetap tersimpan.auth: OWNER
GET
/api/locationsMendapatkan daftar semua lokasi kantor yang terdaftar di akun owner.auth: OWNER
POST
/api/locationsMenambahkan lokasi kantor baru. Body: { name, latitude, longitude, radius_meters? }.auth: OWNER
GET
/api/locations/:idMendapatkan detail lokasi kantor berdasarkan ID.auth: OWNER
PUT
/api/locations/:idMemperbarui detail lokasi kantor berdasarkan ID. Body: { name?, latitude?, longitude?, radius_meters? }.auth: OWNER
DELETE
/api/locations/:idMenghapus (soft delete) lokasi kantor berdasarkan ID. Akan ditolak jika ada karyawan yang masih terkait dengan lokasi tersebut.auth: OWNER
POST
/api/attendance/check-inKaryawan melakukan absensi masuk. Membutuhkan swafoto dan koordinat GPS. Akan ditolak jika di luar radius kantor atau sudah check-in hari ini. Body: { employee_id, location_id, latitude, longitude, photo: File }.auth: EMPLOYEE
POST
/api/attendance/check-outKaryawan melakukan absensi keluar. Membutuhkan swafoto dan koordinat GPS. Akan ditolak jika di luar radius kantor atau belum check-in hari ini. Body: { employee_id, location_id, latitude, longitude, photo: File }.auth: EMPLOYEE
GET
/api/attendance/employee/:employeeIdMendapatkan riwayat absensi detail untuk karyawan tertentu. Dapat difilter berdasarkan bulan dan tahun. Paginated.paginatedauth: OWNER/EMPLOYEE
GET
/api/reports/monthlyMendapatkan rekapitulasi kehadiran bulanan untuk semua karyawan di akun owner. Dapat difilter berdasarkan bulan dan tahun. Paginated.paginatedauth: OWNER
POST
/api/reports/monthly/exportMengunduh rekapitulasi kehadiran bulanan dalam format Excel. Dapat difilter berdasarkan bulan dan tahun. Akan dinonaktifkan untuk mencegah double-submit.auth: OWNER

Peran (RBAC)

OWNER
Pemilik UMKM atau admin utama. Memiliki akses penuh ke manajemen karyawan, lokasi, dan laporan.· kelola karyawan, kelola lokasi kantor, melihat semua riwayat absensi, melihat dan mengunduh rekap kehadiran bulanan
EMPLOYEE
Karyawan UMKM. Hanya dapat melakukan absensi dan melihat riwayat absensi pribadi.· melakukan absensi masuk/keluar, melihat riwayat absensi pribadi

Enum

UserRoleOWNER · EMPLOYEE
AttendanceStatusPRESENT · ABSENT · LATE_CHECK_IN · EARLY_CHECK_OUT · MISSING_CHECK_OUT · INVALID_LOCATION

Environment Variables

DATABASE_URLbackendwajibURL koneksi database PostgreSQL
JWT_SECRETbackendwajibSecret key untuk menandatangani dan memverifikasi JWT
NEXT_PUBLIC_API_URLfrontendURL dasar API (untuk deployment, di lokal bisa relatif)

Library Inti

react · frontendnext · sharedreact-dom · frontendreact-query · frontendreact-hook-form · frontendzod · shareddrizzle-orm · backendpg · backendbcrypt · backendjsonwebtoken · backendmulter · backendexceljs · backendreact-leaflet · frontendleaflet · frontendzustand · frontendtailwindcss · dev

Tasks (siap kirim ke AI agent)

T1
Inisialisasi Proyek Next.js & Drizzle ORM· M

Setup proyek Next.js dengan TypeScript, Tailwind CSS, dan konfigurasi Drizzle ORM untuk PostgreSQL.

Lihat isi perintah
Buat proyek Next.js baru dengan TypeScript. Konfigurasi Tailwind CSS. Setup Drizzle ORM dengan driver `pg` untuk PostgreSQL. Buat file `src/db/schema.ts` dan `drizzle.config.ts` dasar. Pastikan koneksi database dapat diatur via environment variable `DATABASE_URL`.
T2
Definisi Skema Database Awal (User, Role Enum)· S· Butuh dulu: T1

Definisikan tabel `users` dan enum `UserRole` di `src/db/schema.ts`.

Lihat isi perintah
Di `src/db/schema.ts`, definisikan enum `UserRole` dengan nilai 'OWNER' dan 'EMPLOYEE'. Buat tabel `users` dengan kolom `id` (uuid, PK), `email` (text, unique, not nullable), `password_hash` (text, not nullable), `role` (enum:UserRole, not nullable), `owner_user_id` (uuid, nullable, FK ke `users.id` untuk karyawan yang terhubung ke owner mereka). Tambahkan `created_at` dan `updated_at` otomatis.
T3
Implementasi Autentikasi Dasar (Login)· M· Butuh dulu: T2

Buat API endpoint untuk login pengguna (owner/karyawan) menggunakan email/password atau PIN.

Lihat isi perintah
Buat route handler `src/app/(auth)/api/login/route.ts` (POST). Implementasikan validasi input menggunakan Zod. Gunakan `bcrypt` untuk membandingkan `password_hash` atau PIN. Jika login berhasil, buat JWT token dan kembalikan. Jika gagal, kembalikan error. Pastikan `JWT_SECRET` diambil dari env var.
T4
Halaman Login UI· M· Butuh dulu: T3

Buat halaman login di frontend yang terhubung ke API autentikasi.

Lihat isi perintah
Buat komponen UI untuk halaman login di `src/app/(auth)/login/page.tsx`. Halaman ini harus memiliki form untuk email/PIN dan password. Gunakan `react-hook-form` dan `zod` untuk validasi form di sisi klien. Kirim data ke endpoint `/api/login` dan tangani respons (sukses/gagal).
T5
Middleware Autentikasi· S· Butuh dulu: T3

Buat middleware untuk melindungi route API yang memerlukan autentikasi.

Lihat isi perintah
Buat middleware di `src/middleware.ts` yang memverifikasi JWT token dari header `Authorization`. Jika token valid, ekstrak `user_id` dan `role` lalu teruskan request. Jika tidak valid atau tidak ada token, kembalikan respons 401 Unauthorized. Terapkan middleware ini untuk semua route di `/api/(app)/`.
T6
Halaman Dashboard Owner· S· Butuh dulu: T4, T5

Buat halaman dashboard sederhana untuk owner setelah login.

Lihat isi perintah
Buat halaman dashboard di `src/app/(app)/dashboard/page.tsx`. Halaman ini harus menampilkan pesan selamat datang dan nama owner. Pastikan halaman ini dilindungi oleh middleware autentikasi dan hanya bisa diakses oleh role 'OWNER'.
T7
Definisi Skema Database (Employees, Locations, Attendance)· M· Butuh dulu: T2

Lengkapi skema database dengan tabel `employees`, `locations`, `attendance_records` dan enum terkait.

Lihat isi perintah
Di `src/db/schema.ts`, tambahkan tabel `employees` (id, owner_id FK users.id, user_id FK users.id nullable, name, employee_pin unique per owner, default_work_start_time, default_work_end_time, softDelete: true). Tambahkan tabel `locations` (id, owner_id FK users.id, name unique per owner, latitude, longitude, radius_meters, softDelete: true). Tambahkan tabel `attendance_records` (id, employee_id FK employees.id, owner_id FK users.id, location_id FK locations.id, check_in_time, check_in_latitude, check_in_longitude, check_in_photo_url, check_out_time, check_out_latitude, check_out_longitude, check_out_photo_url, status enum:AttendanceStatus, date unique composite with employee_id and owner_id). Definisikan enum `AttendanceStatus` (PRESENT, ABSENT, LATE_CHECK_IN, EARLY_CHECK_OUT, MISSING_CHECK_OUT, INVALID_LOCATION). Pastikan semua tabel domain memiliki `owner_id` sebagai FK ke `users.id` (role OWNER).
T8
API: Manajemen Karyawan (CRUD)· L· Butuh dulu: T7, T5

Buat API endpoint untuk menambah, melihat, mengubah, dan menghapus data karyawan.

Lihat isi perintah
Buat route handlers untuk `/api/(app)/employees` (GET, POST) dan `/api/(app)/employees/[id]` (GET, PUT, DELETE). Gunakan Zod untuk validasi input. Pastikan semua operasi difilter berdasarkan `owner_id` dari sesi pengguna. Saat membuat karyawan, buat juga entri di tabel `users` dengan `role='EMPLOYEE'` dan `owner_user_id` yang merujuk ke owner. Saat menghapus, lakukan soft delete pada `employees` dan hapus entri `users` terkait (jika ada).
T9
UI: Halaman Daftar Karyawan· M· Butuh dulu: T8

Buat halaman untuk menampilkan daftar karyawan dan tombol tambah karyawan.

Lihat isi perintah
Buat halaman `src/app/(app)/employees/page.tsx` yang menampilkan daftar karyawan dalam tabel. Gunakan `react-query` untuk fetching data dari `/api/employees`. Tambahkan tombol 'Tambah Karyawan' yang mengarahkan ke form tambah karyawan.
T10
UI: Form Tambah/Edit Karyawan· M· Butuh dulu: T9

Buat form untuk menambah dan mengedit data karyawan.

Lihat isi perintah
Buat komponen form di `src/app/(app)/employees/create/page.tsx` dan `src/app/(app)/employees/[id]/edit/page.tsx`. Form ini harus bisa menerima input nama, email (opsional), PIN absensi, jam kerja default (HH:MM). Gunakan `react-hook-form` dan `zod` untuk validasi. Kirim data ke endpoint `/api/employees` atau `/api/employees/[id]`.
T11
API: Manajemen Lokasi Kantor (CRUD)· L· Butuh dulu: T7, T5

Buat API endpoint untuk menambah, melihat, mengubah, dan menghapus data lokasi kantor.

Lihat isi perintah
Buat route handlers untuk `/api/(app)/locations` (GET, POST) dan `/api/(app)/locations/[id]` (GET, PUT, DELETE). Gunakan Zod untuk validasi input. Pastikan semua operasi difilter berdasarkan `owner_id` dari sesi pengguna. Saat menghapus lokasi, tolak jika ada `attendance_records` yang merujuk ke lokasi tersebut. Lakukan soft delete pada `locations`.
T12
UI: Halaman Daftar Lokasi Kantor· M· Butuh dulu: T11

Buat halaman untuk menampilkan daftar lokasi kantor dan tombol tambah lokasi.

Lihat isi perintah
Buat halaman `src/app/(app)/locations/page.tsx` yang menampilkan daftar lokasi kantor dalam tabel. Gunakan `react-query` untuk fetching data dari `/api/locations`. Tambahkan tombol 'Tambah Lokasi' yang mengarahkan ke form tambah lokasi.
T13
UI: Form Tambah/Edit Lokasi Kantor· M· Butuh dulu: T12

Buat form untuk menambah dan mengedit data lokasi kantor, termasuk input koordinat GPS.

Lihat isi perintah
Buat komponen form di `src/app/(app)/locations/create/page.tsx` dan `src/app/(app)/locations/[id]/edit/page.tsx`. Form ini harus bisa menerima input nama lokasi, latitude, longitude, dan radius (default 50 meter). Gunakan `react-hook-form` dan `zod` untuk validasi. Kirim data ke endpoint `/api/locations` atau `/api/locations/[id]`.
T14
UI: Integrasi Peta untuk Lokasi· M· Butuh dulu: T13

Tampilkan peta interaktif di form lokasi untuk membantu input koordinat.

Lihat isi perintah
Pada form tambah/edit lokasi di `src/app/(app)/locations/create/page.tsx` dan `src/app/(app)/locations/[id]/edit/page.tsx`, integrasikan library peta (misal: `react-leaflet`) untuk menampilkan peta. Izinkan pengguna memilih titik di peta untuk mengisi otomatis latitude dan longitude. Tampilkan juga lingkaran radius di sekitar titik yang dipilih.
T15
API: Pengaturan Jam Kerja Default· S· Butuh dulu: T8

API untuk mengatur jam kerja default per karyawan (sudah termasuk di T8, ini untuk konfirmasi).

Lihat isi perintah
Pastikan API PUT `/api/(app)/employees/[id]` di T8 sudah mendukung update `default_work_start_time` dan `default_work_end_time`.
T16
UI: Tampilan Detail Karyawan· S· Butuh dulu: T10

Halaman untuk melihat detail karyawan dan mengeditnya.

Lihat isi perintah
Buat halaman `src/app/(app)/employees/[id]/page.tsx` untuk menampilkan detail karyawan. Sertakan tombol 'Edit' yang mengarahkan ke form edit karyawan.
T17
API: Absensi Masuk (Check-in)· L· Butuh dulu: T7, T5

Buat API endpoint untuk karyawan melakukan absensi masuk dengan swafoto dan koordinat GPS.

Lihat isi perintah
Buat route handler `src/app/(app)/attendance/check-in/route.ts` (POST). Endpoint ini menerima `employee_id`, `location_id`, `latitude`, `longitude`, dan `photo` (file upload). Validasi input menggunakan Zod. Simpan foto ke `public/uploads/attendance/` dan catat URL-nya. Hitung jarak GPS karyawan ke lokasi kantor yang dipilih. Jika di luar radius, tolak dengan status `INVALID_LOCATION`. Jika sudah check-in hari ini, tolak. Catat `check_in_time`, `check_in_latitude`, `check_in_longitude`, `check_in_photo_url` dan `status='PRESENT'` (atau `LATE_CHECK_IN` jika melewati `default_work_start_time` karyawan, timezone Asia/Jakarta (WIB)).
T18
API: Absensi Keluar (Check-out)· L· Butuh dulu: T17

Buat API endpoint untuk karyawan melakukan absensi keluar dengan swafoto dan koordinat GPS.

Lihat isi perintah
Buat route handler `src/app/(app)/attendance/check-out/route.ts` (POST). Endpoint ini menerima `employee_id`, `location_id`, `latitude`, `longitude`, dan `photo` (file upload). Validasi input menggunakan Zod. Simpan foto ke `public/uploads/attendance/` dan catat URL-nya. Hitung jarak GPS karyawan ke lokasi kantor yang dipilih. Jika di luar radius, tolak dengan status `INVALID_LOCATION`. Jika belum check-in hari ini, tolak. Update `attendance_records` yang ada dengan `check_out_time`, `check_out_latitude`, `check_out_longitude`, `check_out_photo_url` dan perbarui `status` (misal: `EARLY_CHECK_OUT` jika sebelum `default_work_end_time` karyawan, timezone Asia/Jakarta (WIB)).
T19
UI: Halaman Absensi Karyawan· L· Butuh dulu: T18, T14

Buat halaman untuk karyawan melakukan check-in/check-out.

Lihat isi perintah
Buat halaman `src/app/(app)/attendance/page.tsx` yang hanya bisa diakses oleh role 'EMPLOYEE'. Halaman ini harus menampilkan tombol 'Absen Masuk' dan 'Absen Keluar'. Saat tombol ditekan, minta akses kamera dan lokasi GPS. Tampilkan preview foto dan koordinat GPS sebelum submit. Kirim data ke `/api/attendance/check-in` atau `/api/attendance/check-out`. Tampilkan pesan sukses/gagal. Pastikan ada indikator status absensi hari ini (sudah check-in/check-out).
T20
Helper: Perhitungan Jarak GPS· S

Buat fungsi helper untuk menghitung jarak antara dua koordinat GPS.

Lihat isi perintah
Buat file `src/lib/geo.ts` yang berisi fungsi `getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2)` menggunakan formula Haversine. Fungsi ini akan digunakan oleh API absensi untuk memverifikasi lokasi.
T21
API: Riwayat Absensi Karyawan (Individual)· M· Butuh dulu: T7, T5

Buat API endpoint untuk melihat riwayat absensi seorang karyawan.

Lihat isi perintah
Buat route handler `src/app/(app)/attendance/employee/[employeeId]/route.ts` (GET). Endpoint ini menerima `employeeId` dari path dan parameter query `month`, `year` untuk filter. Kembalikan daftar `attendance_records` untuk karyawan tersebut, diurutkan berdasarkan tanggal. Pastikan data difilter berdasarkan `owner_id` dari sesi pengguna. Implementasikan paginasi.
T22
UI: Halaman Riwayat Absensi Karyawan (Owner)· M· Butuh dulu: T21, T16

Buat halaman di sisi owner untuk melihat riwayat absensi detail per karyawan.

Lihat isi perintah
Pada halaman detail karyawan `src/app/(app)/employees/[id]/page.tsx`, tambahkan tab atau bagian untuk 'Riwayat Absensi'. Gunakan `react-query` untuk fetching data dari `/api/attendance/employee/[id]`. Tampilkan riwayat dalam tabel dengan kolom tanggal, jam masuk, jam keluar, status, dan link untuk melihat foto selfie. Tambahkan filter bulan/tahun dan kontrol paginasi.
T23
UI: Halaman Riwayat Absensi Karyawan (Karyawan)· M· Butuh dulu: T21, T19

Buat halaman di sisi karyawan untuk melihat riwayat absensi pribadi.

Lihat isi perintah
Buat halaman `src/app/(app)/my-attendance/page.tsx` yang hanya bisa diakses oleh role 'EMPLOYEE'. Halaman ini menampilkan riwayat absensi karyawan yang sedang login. Gunakan `react-query` untuk fetching data dari `/api/attendance/employee/[current_employee_id]`. Tampilkan riwayat dalam tabel dengan kolom tanggal, jam masuk, jam keluar, status, dan link untuk melihat foto selfie. Tambahkan filter bulan/tahun dan kontrol paginasi.
T24
API: Update Status Absensi Otomatis (Missing Check-out)· S· Butuh dulu: T18

Implementasi logika untuk menandai absensi yang tidak memiliki check-out sebagai 'MISSING_CHECK_OUT' pada akhir hari.

Lihat isi perintah
Buat sebuah cron job atau serverless function yang berjalan setiap hari pada pukul 23:59 WIB. Fungsi ini akan mencari semua `attendance_records` dengan `check_in_time` pada hari itu dan `check_out_time` yang masih `null`. Untuk setiap record yang ditemukan, update `status` menjadi `MISSING_CHECK_OUT`. Ini bisa diimplementasikan sebagai route handler yang dipanggil oleh cron job eksternal atau langsung sebagai fungsi internal yang dijadwalkan.
T25
Pengelolaan File Upload (Selfie)· S· Butuh dulu: T17, T18

Pastikan file selfie disimpan dengan aman dan dapat diakses.

Lihat isi perintah
Pastikan API check-in dan check-out menyimpan file foto selfie ke direktori `public/uploads/attendance/` dengan nama file unik (misal: UUID + timestamp). Pastikan URL foto yang disimpan di database dapat diakses oleh frontend.
T26
API: Rekap Kehadiran Bulanan· L· Butuh dulu: T7, T24

Buat API endpoint untuk mendapatkan rekapitulasi kehadiran bulanan semua karyawan.

Lihat isi perintah
Buat route handler `src/app/(app)/reports/monthly/route.ts` (GET). Endpoint ini menerima parameter query `month` dan `year`. Agregasikan data `attendance_records` untuk semua karyawan di bawah `owner_id` yang sedang login. Hitung jumlah hari hadir, terlambat, pulang cepat, tidak absen, dan lokasi tidak valid untuk setiap karyawan dalam bulan yang diminta. Gunakan `default_work_start_time` dan `default_work_end_time` karyawan untuk menentukan status. Kembalikan data rekapitulasi per karyawan. Pastikan timezone Asia/Jakarta (WIB) digunakan untuk semua perhitungan tanggal/waktu.
T27
UI: Halaman Rekap Kehadiran Bulanan· M· Butuh dulu: T26

Buat halaman untuk owner melihat rekapitulasi kehadiran bulanan.

Lihat isi perintah
Buat halaman `src/app/(app)/reports/monthly/page.tsx`. Halaman ini harus menampilkan tabel rekapitulasi kehadiran bulanan dari API `/api/reports/monthly`. Tambahkan dropdown untuk memilih bulan dan tahun. Tampilkan ringkasan status kehadiran per karyawan.
T28
API: Export Rekap Kehadiran ke Excel· L· Butuh dulu: T26

Buat API endpoint untuk mengunduh rekapitulasi bulanan dalam format Excel.

Lihat isi perintah
Buat route handler `src/app/(app)/reports/monthly/export/route.ts` (GET). Endpoint ini menerima parameter query `month` dan `year`. Ambil data rekapitulasi dari fungsi yang sama dengan T26. Gunakan library seperti `exceljs` atau `xlsx` untuk membuat file Excel (.xlsx) dari data tersebut. Set header respons agar browser mengunduh file. Pastikan format Excel mudah dibaca, dengan kolom untuk Tanggal, Nama Karyawan, Jam Masuk, Jam Keluar, Status, dan Durasi Kerja.
T29
UI: Tombol Export Excel· S· Butuh dulu: T28, T27

Tambahkan tombol 'Export ke Excel' di halaman rekap kehadiran bulanan.

Lihat isi perintah
Pada halaman `src/app/(app)/reports/monthly/page.tsx`, tambahkan tombol 'Export ke Excel'. Ketika tombol diklik, panggil endpoint `/api/reports/monthly/export` dengan parameter bulan dan tahun yang sedang dipilih. Pastikan tombol dinonaktifkan saat proses export sedang berjalan untuk mencegah double-submit.
T30
PWA Manifest & Service Worker· S· Butuh dulu: T19

Konfigurasi aplikasi sebagai PWA.

Lihat isi perintah
Konfigurasi `next.config.js` untuk mendukung PWA. Buat `manifest.json` di direktori `public` dengan ikon, nama, dan deskripsi aplikasi. Buat `service-worker.js` dasar untuk caching aset statis. Pastikan aplikasi dapat diinstal ke layar utama perangkat seluler.

Risks

  • Akurasi GPS dapat bervariasi tergantung perangkat dan lingkungan, berpotensi menyebabkan absensi ditolak meskipun karyawan berada di area kantor. Mitigasi: Radius validasi yang cukup fleksibel (mis. 50m) dan kemampuan admin untuk meninjau secara manual.
  • GPS spoofing: Karyawan mungkin mencoba memalsukan lokasi GPS. Mitigasi: Kombinasi dengan swafoto akan mempersulit spoofing, namun tidak sepenuhnya menghilangkan risiko. Verifikasi manual oleh admin tetap diperlukan.
  • Kualitas swafoto yang buruk: Foto buram atau gelap dapat menyulitkan verifikasi manual. Mitigasi: Instruksi jelas untuk mengambil foto yang baik dan kemampuan admin untuk meninjau.
  • Perubahan data master (mis. lokasi kantor, jam kerja) dapat mempengaruhi laporan historis jika tidak ada snapshot. Mitigasi: Data absensi (attendance_records) mencatat waktu check-in/out dan lokasi saat itu, sehingga laporan historis tidak terpengaruh perubahan master data di kemudian hari. Status absensi dihitung berdasarkan konfigurasi saat laporan dibuat.
  • Potensi double-submit absensi jika koneksi lambat. Mitigasi: Disable tombol submit setelah klik pertama dan implementasi anti-double-submit di backend.