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

5.3 KiB

Setrip — Admin Trip Operations Roadmap

Admin perlu visibilitas penuh atas trip dan bisa intervensi (cancel + auto-refund) saat organizer unreachable atau ada masalah safety.

Skenario nyata: peserta lapor trip berjalan tidak sesuai itinerary. Organizer tidak responsif. Hari berikutnya peserta minta refund. Saat ini admin harus refund satu-satu manual via /admin/refunds tanpa konteks trip atau cara cancel trip-nya.


Baseline

  • tripService.closeTrip(tripId, organizerId) di server/services/trip.service.ts sudah handle cancel + auto-refund semua booking PAID atomically. Hanya menerima organizerId — perlu varian admin.
  • tripRepo.findAll() dan tripRepo.findById() ada — siap dipakai untuk admin list/detail.
  • Tidak ada page /admin/trips.
  • Tidak ada UI search/filter trip untuk admin.
  • Tidak ada UI view detail trip dari sisi admin (kondisi participant, booking, payment).

Phase 1 — Trip List + Detail View (admin read-only)

Foundation. Tanpa cara cari & lihat trip, admin tidak tahu apa yang mau di-intervene.

Keputusan asumsi:

  • Reuse tripRepo.findAll() tapi tambah filter param: status, organizerId, q (search title/destination).
  • Detail page reuse tripService.getTripById() yang sudah include participants, images, reviews, itineraryItems.
  • Tampilkan semua participant (PENDING/CONFIRMED/CANCELLED) — admin perlu konteks lengkap.
  • Drill-down ke booking detail (lihat ADMIN_PAYMENT_OPS_ROADMAP.md) untuk lihat payment timeline.
# Item Status File
1.1 tripRepo.searchForAdmin({ q?, status?, organizerId?, dateFrom?, dateTo? }) server/repositories/trip.repo.ts
1.2 Page /admin/trips — list + tab status (OPEN/FULL/CLOSED/COMPLETED) + search bar app/admin/trips/page.tsx
1.3 Filter: tanggal berangkat range, organizer (dropdown), kategori app/admin/trips/page.tsx
1.4 Page /admin/trips/[id] — full detail (trip core + itinerary items + participants + bookings ringkasan) app/admin/trips/[id]/page.tsx
1.5 Badge metrics di detail: peserta PAID/AWAITING/PENDING, total revenue (sum amount PAID), refund total app/admin/trips/[id]/page.tsx
1.6 Tambah link "Trips" di admin navbar app/admin/layout.tsx

Tindakan manual: tidak ada.


Phase 2 — Admin Force-Cancel Trip dengan Auto-Refund

Tombol "Cancel trip" di admin detail page yang setara dengan organizer cancel, tapi dilakukan oleh admin untuk emergency intervention.

Keputusan asumsi:

  • Tidak buat method baru di service. Refactor tripService.closeTrip agar terima actor: { type: "ORGANIZER", id } | { type: "ADMIN", id, reason }. Atomic dalam satu serializable transaction (sama seperti existing).
  • Refund yang dibuat pakai RefundReason.ORGANIZER_CANCELLED (tetap, karena dari perspektif peserta sama saja). Tambah adminNote di refund record kalau actor ADMIN supaya audit trail jelas.
  • Tambah kolom Trip.cancelledByAdminId (nullable) + Trip.cancelledReason di schema — bukan kolom umum, hanya saat admin yang cancel.
  • Modal konfirmasi wajib tampilkan: jumlah booking PAID yang akan auto-refund, total nominal. Kalau organizer yang biasa cancel sudah ada confirm modal di cancel-trip-button.tsx — reuse pola.
  • Idempotent: kalau trip sudah CLOSED, tolak dengan pesan jelas.
# Item Status File
2.1 Migration: tambah cancelledByAdminId (FK User) + cancelledReason di Trip prisma/migrations/
2.2 Refactor tripService.closeTrip terima actor discriminated union server/services/trip.service.ts
2.3 Server action adminCancelTripAction(tripId, reason) — guard isAdmin, panggil closeTrip dengan actor ADMIN features/trip/actions.ts
2.4 UI: tombol "Cancel trip (admin)" di /admin/trips/[id] dengan modal konfirmasi + textarea reason wajib app/admin/trips/[id]/page.tsx (atau component terpisah)
2.5 Tampilkan badge "Dibatalkan admin" + reason di trip detail saat cancelledByAdminId not null app/(public)/trips/[id]/page.tsx — opsional, transparansi

Tindakan manual:

  1. Setelah deploy, brief admin tentang kapan boleh pakai (kriteria: organizer unreachable >7 hari, dispute peserta tidak terselesaikan, safety issue).
  2. Tulis SOP internal: kategori reason yang valid + template komunikasi ke peserta.

Phase 3 — Trip Edit Override (opsional, low priority)

Admin bisa edit field non-critical (description, meetingPoint, itinerary) atas request organizer saat organizer tidak bisa login. Skip untuk MVP.

# Item Status File
3.1 tripService.adminUpdateTrip(tripId, partial, adminId, reason) — whitelist field server/services/trip.service.ts
3.2 UI form edit di /admin/trips/[id]/edit app/admin/trips/[id]/edit/page.tsx
3.3 Audit log entry untuk setiap edit (siapa, field apa, before/after) TBD (lihat ADMIN_AUDIT_ROADMAP.md)

Tindakan manual: tidak ada (skip phase ini sampai ada keluhan konkret).