Lewati ke isi

10. Mobile App (Portal Karyawan)

Bab ini menjelaskan aplikasi mobile WaktuKerja — Progressive Web App (PWA) khusus untuk karyawan. Bukan native iOS/Android — di-install dari browser via "Add to Home Screen" lalu berfungsi seperti aplikasi native (fullscreen, ikon di home screen, splash screen).

Audiens bab ini: karyawan & HR yang membantu onboarding. HR tidak login di mobile app — HR pakai portal web admin di komputer.

10.1 Instalasi

Android (Chrome)

  1. Buka browser Chrome di HP
  2. Akses URL aplikasi mobile (mis. https://mobile.waktukerja.com — minta ke HR Anda)
  3. Chrome otomatis menampilkan prompt "Install app" di bawah address bar (atau via menu ⋮ → "Install app" / "Add to Home screen")
  4. Konfirmasi — ikon WaktuKerja (Kawung Jam) muncul di home screen
  5. Buka ikon → aplikasi berjalan fullscreen tanpa address bar

iOS (Safari)

  1. Buka Safari (browser lain tidak support PWA install di iOS)
  2. Akses URL aplikasi mobile
  3. Tap tombol Share (kotak dengan panah) di bawah
  4. Scroll → tap "Add to Home Screen"
  5. Konfirmasi nama default "WaktuKerja" → tap Add
  6. Ikon muncul di home screen; buka untuk launch fullscreen

HTTPS wajib: fitur akses kamera (Check In/Out face capture) hanya jalan di HTTPS (kecuali localhost). Pastikan URL pakai https://.

10.2 Login

Layar login full-screen dengan gradient terracotta + logo Kawung Jam + wordmark "waktuKerja".

Field: - Username — sama dengan login web admin. Format umum: emp.{kode-tenant}.{nomor-induk} (mis. emp.log01.0001) - Password — sama dengan login web

Tap Masuk. Setelah sukses, langsung ke Beranda.

Kalau role akun = HR → akan ditolak dengan pesan "Akun ini bukan karyawan. Gunakan portal web admin." App mobile strictly KARYAWAN-only.

Logout: ada di tab Akun (sudut kanan atas, tombol "Keluar"). Logout = hapus token dari device → kembali ke layar login.

10.3 Navigasi (Bottom Tab)

App pakai pola bottom navigation dengan 5 tab utama yang selalu visible di bawah:

Tab Icon Isi
Beranda rumah Dashboard greeting + shift hari ini + Check In/Out + statistik periode aktif + pengajuan terbaru
Notifikasi bell Feed notifikasi (pengajuan diproses, slip gaji baru, tukar shift, dll) + badge unread
Pengajuan dokumen List sub-menu: Cuti, Ijin, Tukar Shift, Lembur, Perjalanan Dinas
Penggajian uang List sub-menu: Riwayat Gaji, Gaji Non Reguler
Akun user Profil + Data Alamat + Data Keluarga + Riwayat Kerja + Riwayat Pendidikan + Jadwal Kerja + Realisasi Kehadiran + tombol Keluar

Tab aktif di-highlight terracotta + bold. Saat di sub-page (mis. di /me/cuti dari tab Pengajuan), tab parent ("Pengajuan") tetap highlight.

Top bar menampilkan: - Title halaman (sub-label parent tab kalau sedang di sub-page, mis. "PENGGAJIAN / Riwayat Gaji") - Back arrow ← (kalau di sub-page, navigate kembali) - Tombol "Keluar" (hanya di tab Akun)

10.4 Beranda (Dashboard)

Saat buka app, default ke Beranda.

Greeting — "Selamat pagi/siang/sore" + nama depan + emoji 👋. Update otomatis sesuai waktu HP.

Check In / Check Out (face capture)

Dua tombol prominent berwarna moss (Check In) dan terracotta (Check Out) di paling atas. Pakai untuk absen masuk/pulang.

Tap → kamera depan selfie aktif dengan overlay lingkaran face frame: 1. Posisikan wajah di dalam lingkaran 2. Tap shutter (lingkaran putih besar di bawah) 3. Preview foto muncul — pilih Ulangi atau Konfirmasi 4. Konfirmasi → kirim ke server (face recognition match dengan profile karyawan)

Status iter ini: UI face capture sudah jadi, upload ke server belum ter-wire (API face recognition di-develop terpisah). Untuk sekarang foto di-capture tapi belum dikirim — fungsi absen real harus pakai cara lain (mis. tap-in mesin fingerprint kantor).

Permission kamera: browser akan minta izin akses kamera saat pertama tap. Tap "Izinkan". Kalau menolak → ada error screen dengan instruksi buka pengaturan browser.

Shift hari ini

Card di bawah Check In/Out: - Kode + nama shift (mis. T2-OFC — Pagi Office) - Jam masuk – jam pulang (mis. 08:00 – 17:00) - Window tap masuk yang valid (mis. 07:30 – 09:00 — di luar ini tap akan invalid) - Kalau hari libur: tampil "Libur 🌴" + nama hari libur

Statistik periode aktif

4 tile grid 2×2: - Kehadiranhadir / hari_kerja di periode aktif (mis. 15/22 hari) - Lembur disetujui — total durasi lembur DISETUJUI (jam + menit) - Cuti tahun ini — total hari cuti DISETUJUI tahun berjalan - Pengajuan pending — count semua pengajuan (cuti+ijin+lembur+tukar shift) status PENDING

Pengajuan terbaru

Card list 3 pengajuan terbaru (kind + ID + status badge + detail singkat). Tap → navigate ke list pengajuan sesuai jenis.

Akses cepat

2 link cepat ke halaman yang sering di-akses: Realisasi Kehadiran + Riwayat Gaji.

10.5 Notifikasi

Tab kedua di bottom nav. Badge merah muncul kalau ada notifikasi belum dibaca (count up to "9+").

Jenis notifikasi

Sistem otomatis trigger notifikasi pada event berikut:

Event Kapan Icon warna
PENGAJUAN_STATUS HR approve / reject / cancel cuti / ijin / lembur / tukar shift Anda hijau (moss)
TUKAR_SHIFT_REQUEST Rekan kerja request tukar shift dengan Anda — menunggu respond terracotta
SLIP_GAJI_BARU HR Tutup Bulan periode — slip gaji baru tersedia di Riwayat Gaji indigo
GAJI_NON_REGULER HR Finalize SK gaji non reguler (THR/Bonus/dll) — Anda terdaftar di SK ochre
JADWAL_OVERRIDE HR mengubah shift Anda di tanggal tertentu (mis. swap manual) indigo gelap

Header bar

  • Toggle Semua / Belum dibaca (N) — filter list
  • Tombol Tandai semua dibaca (muncul hanya kalau ada unread)

Card per notifikasi

  • Icon bundar berwarna sesuai jenis (di kiri)
  • Title (bold kalau unread, regular kalau sudah dibaca)
  • Body 2 baris max (truncated dengan ellipsis)
  • Relative time ("3 jam lalu", "2 hari lalu", atau tanggal kalau >7 hari)
  • Dot indikator terracotta + border-left 3px terracotta kalau unread

Tap card → mark as read otomatis + navigate ke halaman terkait (mis. notif cuti DISETUJUI → buka /me/cuti).

Polling

App fetch unread count tiap 60 detik saat tab aktif + saat user kembali ke app dari background. Notifikasi muncul tanpa perlu manual refresh.

Realtime push (Web Push API) belum diimplementasikan — feature follow-up. Saat ini polling cukup untuk frequency notif HRIS tipikal.

10.6 Tab Pengajuan

5 sub-menu sebagai card list. 4 sub-menu sudah full functional di mobile (Cuti, Ijin, Lembur, Tukar Shift) — karyawan bisa submit + lihat detail + batalkan tanpa buka portal web. Perjalanan Dinas masih placeholder (read di portal web).

Sub-menu Status
Cuti ✅ List + ajukan + detail + batalkan (bila PENDING)
Ijin ✅ List + ajukan + detail + batalkan (bila PENDING)
Tukar Shift ✅ List peran-aware + ajukan (pilih rekan) + respond accept/reject + batalkan
Lembur ✅ List + ajukan + detail + batalkan (bila DIPERINTAHKAN)
Perjalanan Dinas ⏳ Placeholder — buka portal web untuk lihat SPD yang melibatkan Anda

Pola umum 4 halaman pengajuan

Keempat halaman pakai pattern konsisten:

  • Filter pills di atas (horizontal scroll): Semua / Menunggu / Disetujui / Ditolak / Dibatalkan
  • List card stacked vertical, sorted terbaru di atas. Card berisi info ringkas + status badge berwarna
  • FAB + (tombol bundar terracotta di pojok kanan bawah, terangkat di atas bottom nav) → buka bottom-sheet form ajukan
  • Tap card → bottom-sheet detail (slide up dari bawah, drag handle, header sticky, body scrollable)
  • Aksi kontekstual di footer sheet: tombol "Batalkan Pengajuan" muncul hanya kalau pengajuan masih PENDING/DIPERINTAHKAN

Toast confirmation muncul di bawah setelah aksi berhasil. Notifikasi terkait (PENGAJUAN_STATUS) di-broadcast otomatis saat HR approve/reject.

Cuti

Card list pengajuan cuti dengan border-left terracotta.

Form Ajukan: - Jenis cuti — dropdown: Cuti Tahunan / Cuti Besar / Cuti Melahirkan / Cuti Menikah / Cuti Kematian / Cuti Lainnya - Tanggal mulai + tanggal selesai (selesai >= mulai, validasi inline) - Alasan — textarea (opsional)

Detail sheet: status banner + semua field + (kalau bukan PENDING) info diputus oleh + catatan approval. Kalau PENDING, ada section "Alasan Pembatalan" + tombol "Batalkan Pengajuan" crimson.

Ijin

Card list dengan border-left ochre. Form mirip cuti tapi field berbeda: - Tanggal (1 hari saja) - Jenis — Satu Hari / Setengah Hari - Keperluan — input text wajib (mis. "kontrol dokter", "antar anak sekolah") - Catatan tambahan — textarea opsional

Lembur

Card list dengan border-left indigo. Status istilah berbeda dari cuti/ijin: - DIPERINTAHKAN → label "Menunggu" (ochre badge) — menunggu approval HR - DISETUJUI / DITOLAK / DIBATALKAN — sama pattern

Card: tanggal + jam range monospace + durasi auto-compute (mis. "3j 30m") + hint "lintas malam" kalau jam_selesai < jam_mulai + clamp tugas 2 baris.

Form Ajukan: - Tanggal - Jam Mulai + Jam Selesai (24h, validasi mulai != selesai) - Banner info "lintas malam" otomatis muncul kalau jam selesai < jam mulai - Tugas — textarea wajib (max 2000 char) - Catatan — textarea opsional

Detail sheet termasuk durasi total + slot index ("#1 dari 2" — sistem support max 2 slot lembur per hari).

Tukar Shift

Card list dengan border-left moss. Peran-aware: karyawan bisa muncul sebagai Pengaju (ajukan tukar) atau Penerima (di-ajak rekan). Badge peran di kiri card: - Pengaju badge indigo - Penerima badge ochre

Swap diagram di tiap card menampilkan pertukaran shift visual: [Shift Anda] ↔ [Shift Rekan] dengan kode shift + jam dalam monospace.

Form Ajukan: - Rekan (Penerima) — tap tombol untuk buka picker sheet, search by NIK/nama (debounced 300ms, hasil max 20). Self otomatis ter-exclude - Tanggal — server snapshot shift Anda + rekan dari jadwal harian - Alasan — textarea opsional

Detail sheet menampilkan swap diagram lengkap dengan label kontekstual ("Anda (Pengaju)" atau "Anda (Penerima)"), tombol berbeda per peran: - Pengaju + PENDING → tombol "Batalkan Pengajuan" crimson - Penerima + PENDING → dua tombol berdampingan: "Tolak" (crimson) + "Terima" (moss). Banner info di atas form menjelaskan "Terima akan otomatis tukar shift Anda & rekan di tanggal X." - Selain itu → cuma tombol tutup

10.7 Tab Penggajian

2 sub-menu sebagai card list:

Sub-menu Status
Riwayat Gaji ✅ List periode arsip + bottom-sheet detail breakdown komponen
Gaji Non Reguler ⏳ Placeholder — buka portal web untuk SK THR / Bonus / Gaji XIII / Insentif

Riwayat Gaji

Halaman list periode gaji yang sudah tutup-bulan (/me/penggajian/riwayat). Periode aktif yang masih di-proses HR tidak muncul — hanya yang sudah ter-arsip (nominal final).

Card per periode dengan border-left terracotta: - Kode periode monospace + rentang tanggal - THP highlight — kotak ber-background terracotta soft, label "Take-Home Pay" + nominal IDR formatted besar - 2 mini-stat side-by-side: Pendapatan (moss) + Potongan (crimson) - Footer: "Diarsipkan " - Tap card → buka bottom-sheet detail

Detail sheet: - 4 stat cards grid 2×2: Pendapatan (moss) / Potongan (crimson) / Benefit (indigo) / THP (terracotta, emphasis dengan background tinted) - Breakdown komponen di-group per kategori (PENDAPATAN / POTONGAN / BENEFIT). Tiap section punya count + list row: - Nama komponen (bold) + kode (mono) + model (FORMULA / FASILITAS / UPLOAD / PINJAMAN) di subtitle - Nominal IDR di kanan — warna crimson kalau POTONGAN, otherwise default text

Sumber data: kalkulasi_komponen_karyawan filter karyawan_id + periode dengan diarsipkan_pada IS NOT NULL.

10.8 Tab Akun

Hero card identitas dengan gradient terracotta + avatar inisial nama + username monospace.

Sub-menu list:

Profil

Halaman read-only data karyawan login: - Avatar bundar dengan inisial nama - Posisi: jabatan + level (golongan) + atasan + kelompok kerja + masa kerja (X tahun Y bulan dari tanggal_bergabung) + tipe penempatan - Biodata: email, nomor telepon, tempat & tanggal lahir, jenis kelamin, agama, golongan darah - NPWP (jika ada)

Footer info: "Update alamat, keluarga, dan riwayat kerja/pendidikan via menu terpisah di tab Akun. Untuk perubahan data sensitif (NPWP, gaji pokok, bank rekening), hubungi HR."

Data Alamat, Data Keluarga, Riwayat Kerja, Riwayat Pendidikan (CRUD)

Empat sub-menu personalia karyawan dengan CRUD penuh di mobile (tambah, edit, hapus). Pakai endpoint /me/biodata/{child} di backend — biodata_id auto-derive dari JWT karyawan_id, owner-check di service layer. Soft-delete (deleted_at), bukan hard delete.

Pola UI konsisten untuk keempat halaman: - List card stacked dengan border-left warna pembeda - FAB + terracotta di pojok kanan bawah → bottom-sheet form "Tambah …" - Tiap card ada footer 2 tombol berdampingan: Edit (outline) + Hapus (crimson outline) - Tap Edit → bottom-sheet form pre-filled dengan data current - Tap Hapus → dialog konfirmasi native browser (confirm()) → toast "berhasil dihapus" / error toast - Submit form → toast "ditambahkan / diperbarui" + list otomatis refresh

Data Alamat (/me/profile/alamat)

Card per alamat dengan kategori tag berwarna: - Alamat KTP (indigo) - Alamat Tinggal (moss) - Kontak Darurat (crimson) - Lainnya (mute)

Isi card: nama kontak + telepon (monospace) + alamat/catatan (multi-line, pre-wrap).

Form Tambah/Edit: - Tipe Alamat — dropdown 4 opsi di atas - Nama Kontak — input text (max 150, opsional) - Nomor Telepon — input tel (max 25, opsional) - Alamat / Catatan — textarea 4 baris (alamat lengkap atau detail tambahan)

Data Keluarga (/me/profile/keluarga)

Card per anggota keluarga dengan border-left terracotta. Header: nama lengkap (bold) + label relasi (Suami/Istri/Anak/Ayah/Ibu/dll). Badge "Tertanggung" (moss) di sudut kanan kalau tertanggung=true. Body: tanggal lahir + usia auto-compute ("12 Mei 1990 · 35 tahun").

Form Tambah/Edit: - Nama Lengkap — input text wajib (max 150) - Relasi — dropdown 13 enum: Suami / Istri / Anak / Ayah / Ibu / Saudara Kandung / Paman / Bibi / Keponakan / Kakek / Nenek / Sepupu / Lainnya - Tanggal Lahir — date picker (opsional) - Checkbox Tertanggung — dengan label penjelas: "Centang kalau anggota ini termasuk tanggungan untuk perhitungan PPh21"

Riwayat Kerja (/me/profile/riwayat-kerja)

Card per pengalaman kerja, sorted desc by tahun_masuk. Border-left moss. - Nama perusahaan (bold) - Jabatan terakhir (terracotta) - Periode mono "2020 – 2023" (atau "2018 – Sekarang" kalau tahun_keluar=null) + kota - (Optional) Gaji terakhir formatted IDR di section terpisah

Form Tambah/Edit: - Nama Perusahaan — input wajib (max 200) - Jabatan Terakhir (max 150) - Kota (max 100) - Tahun Masuk + Tahun Keluar — dua number input side-by-side (range 1900–2200), inputMode numeric - Gaji Terakhir (IDR) — number input dengan hint "Hanya angka — tanpa titik / koma"

Riwayat Pendidikan (/me/profile/riwayat-pendidikan)

Card per jenjang pendidikan, sorted desc by tahun_lulus. Border-left warna by kategori: - Formal (indigo) - Kedinasan (terracotta) - Non-Formal (moss)

Header: nama lembaga (bold) + kategori tag di kanan. Body: "Lulus " (mono) + kota. Optional: nomor ijazah (mono) di section terpisah.

Form Tambah/Edit: - Kategori — dropdown 3 opsi - Nama Lembaga / Sekolah — input wajib (max 200, mis. "SMA Negeri 1 Jakarta", "Universitas Indonesia") - Kota (max 100) - Tahun Lulus — number input (1900–2200) - Nomor Ijazah (max 100)

Jadwal Kerja

Halaman kalender shift per hari untuk 1 periode: - Dropdown periode di atas (kalau >1 periode tersedia) - Info bar: kode periode + range tanggal + nama kelompok kerja - 2 stat chip: Hari Kerja / Hari Libur (dari total hari periode) - List per minggu (mulai Senin): - Kolom tanggal (hari + nomor) - Kolom shift (kode + nama + jam datang–pulang) - Badge status berwarna: Kerja (moss), Libur (gray), Libur Kalender (ochre, dengan nama hari libur) - Tanggal Minggu di-highlight merah - Libur kalender pakai background tinted ochre

Realisasi Kehadiran

Halaman kalender bulanan visual dengan color-code per status: - Dropdown periode + info bar - 4 stat cards: % Kehadiran / Tidak Hadir (XTY) / Izin + Cuti (+SPD) / Libur - Legenda 7 status berwarna - Grid kalender 7 kolom (Sen-Min) — tiap tile: - Tanggal di atas-kiri - Badge status 2-3 char di atas-kanan - Kode shift di bawah (kalau hari kerja) - Warna background: OS/HD moss (hadir), XTY crimson (tidak hadir tanpa keterangan), IZ/AZ terracotta (izin), CT indigo (cuti), LB ochre (libur kalender), SPD ungu (perjalanan dinas) - Tap tile → modal detail dengan: - Status banner - Shift terjadwal - Clock-in time (tap masuk) - Clock-out time (tap pulang) - Catatan + warning kalau ada invalid_capture_count > 0

10.9 Restriksi

  • HR tidak bisa login di mobile app (HTTP 403). Pakai portal web admin.
  • Logo + branding sama persis dengan web — Kawung Jam SVG + wordmark "WaktuKerja". File logo-waktukerja.svg shared antara frontend/public/ dan mobile/public/.
  • Theme sinkron dengan portal web (Vibrant Tenun: krem + sogan + prada emas + indigo batik). Font Plus Jakarta Sans + Lora dari Google Fonts.
  • Offline: PWA service worker cache static asset (HTML/CSS/JS/SVG) — UI loading instan setelah first visit. API request selalu hit network (no offline cache) — data karyawan harus fresh, lebih baik error daripada tampil data lama.

10.10 Troubleshooting

"Login pertama gagal, baru bisa setelah reload" → Sudah di-fix. Kalau masih terjadi, hapus cache browser dan coba lagi. Atau hapus app dari home screen dan re-install.

"Tombol Check In tap kamera tidak muncul" → Browser belum dapat izin akses kamera. Buka pengaturan browser → Site Settings → izinkan kamera untuk URL aplikasi. Refresh app.

"Notifikasi telat masuk" → App polling tiap 60 detik. Saat HP background (app tidak active), polling pause. Buka app → polling resume + immediate fetch. Untuk realtime, fitur Web Push di-develop terpisah.

"Logo tidak muncul di home screen Android" → Beberapa launcher Android tidak render SVG icon dengan baik. Workaround: pakai launcher default (Nova / Pixel Launcher / Samsung One UI) untuk hasil terbaik. Generate PNG icon untuk production deployment.

"Mau install di iOS tapi tidak ada tombol Add to Home Screen" → Pastikan pakai Safari (bukan Chrome iOS — Chrome iOS pakai engine WebKit yang sama tapi PWA support beda). Tap tombol Share di bawah, scroll ke bawah, "Add to Home Screen" ada di antara opsi share.

"Saat install dapet 'No icon' atau ikon kosong" → Kemungkinan cache PWA browser stale. Coba: buka URL di tab baru, clear cache, re-install. Atau install dari mode incognito.


Lihat juga