Files
setrip/ADMIN_USER_MGMT_ROADMAP.md
T
arifal c4efe4453b -
- 
- 
- 
2026-05-18 18:31:16 +07:00

82 lines
5.3 KiB
Markdown

# Setrip — Admin User Management Roadmap
Admin perlu bisa cari user dan **suspend** akun yang melakukan abuse (scam, harassment, fake review).
> **Skenario nyata:** organizer scam berkali-kali bikin trip palsu pakai alias berbeda. Peserta lapor harassment dari user lain di grup WA trip. Saat ini admin cuma bisa refund korban — pelaku tetap bisa lanjut bikin trip baru / join trip lain.
---
## Baseline
-`userRepo.findByEmail()` dan `userRepo.findById()` ada di [server/repositories/user.repo.ts](server/repositories/user.repo.ts).
-`User` model lengkap dengan relasi ke `trips`, `participations`, `bookings`, `tripReviews`, `organizerVerification`, `payouts`.
- ❌ Tidak ada page `/admin/users`.
- ❌ Tidak ada field `suspended` di `User`.
- ❌ Tidak ada guard di auth/server actions yang reject suspended user.
- ❌ Tidak ada stats user (total signups, organizer aktif, peserta aktif).
---
## Phase 1 — User List & Detail View ⏳
Baseline visibility. Sama pola dengan trip ops — list + search + detail.
**Keputusan asumsi:**
- Search by email exact match dulu (paling sering dipakai admin saat ada laporan); kalau perlu, tambah name LIKE search nanti.
- Detail page tampilkan: profile, verification status, booking history (sebagai peserta), trip history (sebagai organizer), payout history (sebagai organizer), review yang dibuat & diterima.
- Sensitive info (password hash, OAuth tokens) **tidak** ditampilkan.
| # | Item | Status | File |
|---|---|---|---|
| 1.1 | `userRepo.searchForAdmin({ q?, role?, suspended? })` — q match email atau name (case insensitive) | ⏳ | [server/repositories/user.repo.ts](server/repositories/user.repo.ts) |
| 1.2 | Page `/admin/users` — list + search bar + filter (organizer/participant/suspended) | ⏳ | `app/admin/users/page.tsx` |
| 1.3 | Page `/admin/users/[id]` — profil + tabs (Bookings, Trips Dibuat, Reviews, Verification) | ⏳ | `app/admin/users/[id]/page.tsx` |
| 1.4 | Stats card di top: total bookings, total spent, total revenue (kalau organizer), verification status | ⏳ | `app/admin/users/[id]/page.tsx` |
| 1.5 | Link "Users" di admin navbar | ⏳ | [app/admin/layout.tsx](app/admin/layout.tsx) |
**Tindakan manual:** tidak ada.
---
## Phase 2 — User Suspension ⏳
Toggle suspend yang mencegah suspended user login + melakukan aksi mutatif.
**Keputusan asumsi:**
- Tambah 4 kolom di `User`: `suspended Boolean @default(false)`, `suspendedAt DateTime?`, `suspendedReason String?`, `suspendedBy String?` (FK User admin).
- **Block sign-in** di NextAuth callbacks (`signIn` callback return false kalau `user.suspended`). Untuk JWT session sudah aktif, cek `suspended` di `session` callback lalu invalidate.
- **Block mutating actions** via helper `requireActiveUser(session)` yang dipanggil di awal setiap server action mutating (joinTrip, createTrip, addReview, dst).
- Suspended user **tetap bisa** lihat data sendiri (refund history, dll) — tidak hard-delete supaya audit trail terjaga.
- Suspended organizer otomatis sembunyikan trip OPEN/FULL miliknya dari public list — tambah filter di `tripRepo.findOpen` (`organizer: { suspended: false }`).
- Unsuspend = toggle false + clear field — tetap simpan history via [ADMIN_AUDIT_ROADMAP.md](ADMIN_AUDIT_ROADMAP.md) Phase 4.
| # | Item | Status | File |
|---|---|---|---|
| 2.1 | Migration: tambah `suspended`, `suspendedAt`, `suspendedReason`, `suspendedBy` di `User` | ⏳ | `prisma/migrations/` |
| 2.2 | `userService.suspendUser(userId, adminId, reason)` + `unsuspendUser(userId, adminId)` | ⏳ | `server/services/user.service.ts` |
| 2.3 | Block sign-in di NextAuth `signIn` callback | ⏳ | [lib/auth.ts](lib/auth.ts) |
| 2.4 | Helper `requireActiveUser(session)` throw kalau suspended | ⏳ | `lib/auth-guards.ts` |
| 2.5 | Wire `requireActiveUser` di semua mutating server action (joinTripAction, createTripAction, createReviewAction, dst) | ⏳ | `features/*/actions.ts` |
| 2.6 | Filter trip public list: organizer tidak suspended | ⏳ | [server/repositories/trip.repo.ts](server/repositories/trip.repo.ts) |
| 2.7 | UI: tombol "Suspend" / "Unsuspend" di `/admin/users/[id]` + modal reason wajib | ⏳ | `app/admin/users/[id]/page.tsx` |
| 2.8 | Badge "SUSPENDED" di user list + detail header (visual jelas) | ⏳ | `app/admin/users/[id]/page.tsx` |
| 2.9 | Server action `suspendUserAction` + `unsuspendUserAction` (guard isAdmin) | ⏳ | `features/admin/actions.ts` (baru) atau `features/user/actions.ts` |
**Tindakan manual:**
1. Brief admin: kriteria suspend (3 kategori: scam, harassment, repeated TOS violation). Hindari subjective suspend.
2. Tulis halaman info "Akun ditangguhkan" yang ditampilkan saat suspended user coba login (jelaskan kenapa & cara appeal via email).
3. Pertimbangkan suspended user di Midtrans webhook — saat ada payment masuk untuk suspended user's booking, tetap di-PAID (uang tetap diterima, refund proses normal).
---
## Phase 3 — User Analytics (low priority, skip MVP) ⏳
Dashboard stats untuk growth tracking. Skip sampai ada kebutuhan konkret.
| # | Item | Status | File |
|---|---|---|---|
| 3.1 | Stats endpoint: total user, signup per minggu (4 minggu terakhir), organizer aktif (yang punya OPEN/FULL trip), peserta aktif (booking PAID) | ⏳ | `app/admin/users/stats/page.tsx` |
| 3.2 | Chart sederhana (HTML/SVG inline, no chart library) | ⏳ | `app/admin/users/stats/page.tsx` |
**Tindakan manual:** tidak ada (skip phase ini).