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.
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 UMKM — Membutuhkan cara cepat dan sederhana untuk melakukan absensi masuk dan keluar menggunakan perangkat seluler pribadi mereka.
User Stories
- must-haveSebagai 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-haveSebagai 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-haveSebagai Karyawan, saya ingin Melakukan absensi masuk dengan swafoto dan verifikasi lokasi GPS, agar Kehadiran saya tercatat secara akurat dan terverifikasi..
- must-haveSebagai 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-haveSebagai Owner UMKM, saya ingin Melihat rekap kehadiran bulanan semua karyawan dan mengunduhnya sebagai Excel, agar Saya bisa memproses penggajian dengan mudah dan akurat..
- should-haveSebagai Owner UMKM, saya ingin Melihat riwayat absensi detail per karyawan, agar Saya dapat memeriksa absensi individu jika ada pertanyaan atau perselisihan..
User Flow
- Owner baru mengakses aplikasi dan login (akun owner di-seed atau dibuat admin sistem).
- Owner masuk ke halaman 'Lokasi Kantor' dan menambahkan lokasi kantor baru (nama, koordinat GPS, radius).
- Owner masuk ke halaman 'Karyawan' dan menambahkan karyawan baru (nama, email opsional, PIN absensi, jam kerja default).
- Karyawan mengunduh PWA ke perangkat seluler mereka.
- Karyawan membuka aplikasi dan login menggunakan PIN yang diberikan owner.
- Karyawan masuk ke halaman 'Absensi', memilih 'Absen Masuk', mengambil swafoto, dan mengizinkan akses lokasi.
- Sistem memverifikasi lokasi GPS dan mencatat absensi masuk jika valid.
- Pada akhir jam kerja, karyawan kembali ke halaman 'Absensi', memilih 'Absen Keluar', mengambil swafoto, dan mengizinkan akses lokasi.
- Sistem memverifikasi lokasi GPS dan mencatat absensi keluar jika valid.
- Owner mengakses halaman 'Rekap Kehadiran Bulanan', memilih bulan/tahun, dan melihat ringkasan absensi.
- Owner mengklik tombol 'Export ke Excel' untuk mengunduh laporan bulanan untuk penggajian.
Features
Autentikasi Pengguna (Owner & Karyawan)
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
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
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)
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
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
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
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
- Pengguna (Owner/Karyawan) mengakses aplikasi web/PWA.
- Karyawan melakukan Absen Masuk/Keluar: UI meminta akses kamera & lokasi GPS, mengambil swafoto, mendapatkan koordinat.
- Frontend mengirim data absensi (employee_id, location_id, lat, lon, foto) ke API Backend (POST /api/attendance/check-in atau /check-out).
- Backend memvalidasi input, memverifikasi token JWT, memfilter data berdasarkan `owner_id` dari sesi pengguna, menghitung jarak GPS menggunakan `src/lib/geo.ts`.
- Jika valid, Backend menyimpan foto ke `public/uploads/attendance/`, mencatat URL, dan memperbarui/membuat `attendance_records` di PostgreSQL melalui Drizzle ORM.
- Backend mengembalikan respons sukses/gagal ke Frontend.
- Frontend memperbarui UI sesuai respons (mis. menampilkan status absensi terbaru).
Database Schema
Ini struktur data aplikasimu. Kamu tidak perlu paham semuanya — AI yang akan membuatnya. Kolom created_at/updated_at ditambahkan otomatis tiap tabel.
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)
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
Peran (RBAC)
Enum
Environment Variables
Library Inti
Tasks (siap kirim ke AI agent)
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`.
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.
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.
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).
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)/`.
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'.
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).
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).
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.
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]`.
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`.
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.
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]`.
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.
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`.
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.
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)).
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)).
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.