Files
setrip/ADMIN_AUDIT_ROADMAP.md
T

6.2 KiB

Setrip — Admin Audit & Investigation Roadmap

Status keseluruhan: 🚧 Partial — Phase 1 delivered, Phase 2-4 pending.

Admin perlu mencari lintas entity (booking/payment/refund/user/trip) dan export untuk compliance + investigasi dispute.

Skenario nyata: auditor bertanya "tunjukkan semua refund yang di-approve admin X di bulan Juni 2026 dengan total lebih dari Rp 5 juta". Saat ini admin harus query DB manual atau ambil screenshot satu-satu. Tidak ada cara cari berdasarkan kombinasi reviewer + tanggal + nominal.


Baseline

  • Data audit sudah ada di schema: Refund.reviewedBy/reviewedAt/adminNote, Payout.processedBy/processedAt/adminNote, OrganizerVerification.reviewedBy/reviewedAt/rejectionReason.
  • Existing list pages (/admin/refunds, /admin/payouts, /admin/verifications) sudah grouping by status tab.
  • Tidak ada filter date range / reviewer / amount / reason.
  • Tidak ada kolom "reviewer email" di list — harus klik detail.
  • Tidak ada global search (cari berdasarkan email user, order id, trip id).
  • Tidak ada CSV export.
  • Tidak ada audit log untuk action admin di entity lain (User suspension, Trip force-cancel, Verification reopen).

Phase 1 — Filter & Search Enhancements DELIVERED

# Item Status File
1.1 Filter date range (dateFrom, dateTo) di /admin/refunds app/admin/refunds/page.tsx
1.2 Filter reviewer (admin email dropdown) di /admin/refunds app/admin/refunds/page.tsx
1.3 Filter reason di /admin/refunds app/admin/refunds/page.tsx
1.4 Filter date range + processor di /admin/payouts app/admin/payouts/page.tsx
1.5 Filter date range + reviewer di /admin/verifications app/admin/verifications/page.tsx
1.6 Komponen reusable AdminFilterBar (date range + reviewer dropdown + optional reason) features/admin/components/admin-filter-bar.tsx
1.7 Repo helper: filter params di refundRepo.listByStatus, payoutRepo.listByStatus, organizerRepo.listByStatus server/repositories/*.ts
1.8 Helper listAdminEmails() untuk dropdown reviewer lib/admin.ts

Tindakan manual: tidak ada.

Catatan: reviewer email column di list belum ditambah — info sudah ada di refund/payout/verification card detail (Diproses oleh ...). Bisa ditambah saat dibutuhkan untuk skim cepat._


Phase 2 — Global Search

Satu search box yang resolve ke entity detail page paling relevan.

Keputusan asumsi:

  • Input string user, prefix-based dispatch:
    • Email format (@) → user search → redirect ke /admin/users/[id]
    • Mulai midtrans- / manual- → payment lookup by externalOrderId/admin/bookings/[bookingId]
    • Mulai cm (cuid pattern) + length 25 → coba lookup berurutan: trip → booking → user
    • Else: full-text search di trip title/destination
  • Pakai server action atau route handler /api/admin/search — return list hasil + jenis entity.
  • UI: searchbar di admin layout (top-right) yang dropdown hasil.
# Item Status File
2.1 adminSearchService.resolve(query) — dispatch ke repo lookup yang tepat server/services/admin-search.service.ts
2.2 Route handler /api/admin/search?q=... (GET, guard isAdmin) app/api/admin/search/route.ts
2.3 Component AdminSearchBar di admin layout — debounced, dropdown hasil features/admin/components/admin-search-bar.tsx
2.4 Page /admin/search?q=... untuk full results kalau dropdown limit terlampaui app/admin/search/page.tsx

Tindakan manual: tidak ada.


Phase 3 — CSV Export

Export untuk laporan keuangan & compliance.

Keputusan asumsi:

  • Stream CSV via route handler — jangan load semua ke memory.
  • Pakai filter yang sama dengan list page — admin pakai URL filter lalu klik "Export".
  • Header CSV: human-readable bahasa Indonesia (mis. "Tanggal Approve", "Email Peserta", "Nominal Refund").
  • Tidak ada Excel/xlsx — CSV cukup, mudah dibuka di Sheets/Excel.
# Item Status File
3.1 Helper lib/csv.tsstreamCsv(headers, rows) return Response lib/csv.ts
3.2 Route /api/admin/export/refunds — pakai filter dari query string app/api/admin/export/refunds/route.ts
3.3 Route /api/admin/export/payouts app/api/admin/export/payouts/route.ts
3.4 Route /api/admin/export/verifications (tanpa NIK / KTP — hanya metadata) app/api/admin/export/verifications/route.ts
3.5 Tombol "Export CSV" di tiap admin list page semua app/admin/*/page.tsx

Tindakan manual:

  1. Test export di staging — pastikan tidak leak data sensitif (NIK harus tetap encrypted/excluded).
  2. Update kebijakan privasi: data export hanya untuk internal compliance.

Phase 4 — Generic Admin Audit Log

Tabel AdminActionLog untuk action di entity yang belum punya audit field (User suspend, Trip force-cancel, Verification reopen, dst).

Keputusan asumsi:

  • Single tabel polymorphic: AdminActionLog { adminId, action, entityType, entityId, payload Json?, createdAt }.
  • Append-only, never update/delete.
  • Service helper auditLog.record(...) dipanggil eksplisit di setiap action admin (tidak via Prisma middleware — terlalu magic).
  • View page /admin/audit-log dengan filter adminId, entityType, action, date range.
# Item Status File
4.1 Model AdminActionLog + migration prisma/schema.prisma
4.2 Helper auditLog.record({ adminId, action, entityType, entityId, payload? }) server/services/audit-log.service.ts
4.3 Wire auditLog.record di semua admin server action existing (refund approve/reject/mark, payout markPaid, verification approve/reject) features/*/actions.ts
4.4 Page /admin/audit-log dengan filter + pagination app/admin/audit-log/page.tsx

Tindakan manual: tidak ada.