c4efe4453b
- ✅ - ✅ - ✅
5.3 KiB
5.3 KiB
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()danuserRepo.findById()ada di server/repositories/user.repo.ts. - ✅
Usermodel lengkap dengan relasi ketrips,participations,bookings,tripReviews,organizerVerification,payouts. - ❌ Tidak ada page
/admin/users. - ❌ Tidak ada field
suspendeddiUser. - ❌ 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 |
| 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 |
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 (
signIncallback return false kalauuser.suspended). Untuk JWT session sudah aktif, ceksuspendeddisessioncallback 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 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 |
| 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 |
| 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:
- Brief admin: kriteria suspend (3 kategori: scam, harassment, repeated TOS violation). Hindari subjective suspend.
- Tulis halaman info "Akun ditangguhkan" yang ditampilkan saat suspended user coba login (jelaskan kenapa & cara appeal via email).
- 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).