Files
setrip/TRUST_ROADMAP.md
T
2026-05-09 00:55:40 +07:00

9.6 KiB

Setrip — Trust & Trip Detail Roadmap

Status implementasi yang menaikkan kepercayaan calon peserta — trip detail experience yang meyakinkan + sistem reputasi organizer yang transparan.

Prinsip: trust = fungsi dari (a) kelengkapan informasi trip dan (b) reputasi organizer yang transparan. Setiap fitur dievaluasi: apakah memberi calon peserta alasan obyektif untuk percaya?


Audit state sekarang (baseline)

Trip detail (~80% sudah ada):

  • Itinerary, include/exclude, meeting point — schema lengkap di Trip, ditampilkan via TripProgramBlock.
  • Participant preview (kartu confirmed peserta dengan avatar, kota, interests).
  • Slot tersisa (X / Y) dengan progress bar berwarna.
  • ⚠️ Urgency message ada tapi tidak mencolok saat slot menipis.
  • ⚠️ Itinerary text bebas — organizer butuh hint format supaya konsisten isi lengkap.

Review/trust (~40% sudah ada):

  • Model TripReview (rating + comment per trip+user, unique).
  • Trust panel di trip detail (verified badge, trips created, avg rating, review count).
  • Profil organizer publik /u/[id] belum tampilkan rating/review aggregate.
  • Belum ada total participants served, completion rate, rating breakdown.
  • Belum ada list ulasan terkumpul per organizer.
  • TripStatus.COMPLETED enum-nya ada tapi tidak pernah di-set.

File baseline: app/trips/[id]/page.tsx, server/services/trust.service.ts, server/services/review.service.ts, app/u/[id]/page.tsx.


PR-1 — Trip Detail Polish (UI only)

Cosmetic. Tidak ada migration, tidak ada perubahan service/repo.

# Item Status File
1.1 Urgency badge mencolok saat spotsLeft <= 3 (" Tinggal X spot!") di header progress bar app/trips/[id]/page.tsx
1.2 Participant preview ringkas di blok progress ("👥 Sudah join: Andi, Rina, Budi +4") — first impression tanpa scroll app/trips/[id]/page.tsx
1.3 Hint deskriptif + placeholder lebih konkret di field itinerary form create-trip features/trip/components/create-trip-form.tsx

Tindakan manual: tidak ada.


PR-2 — Organizer Trust Aggregates (service + UI, tanpa migration)

Selesai. tsc --noEmit lulus. Tanpa schema baru, tanpa migration.

Keputusan asumsi yang dipakai:

  • tripsCompletedTrip.status = COMPLETED (status itu tidak pernah di-set). Pakai endDate < now() (fallback date < now()) AND status != CLOSED.
  • tripsCancelled = Trip.status = CLOSED (organizer batalkan trip eksplisit).
  • completionRate butuh sample ≥ 3 (COMPLETION_RATE_MIN_SAMPLE di lib/trust.ts) supaya tidak menyesatkan organizer baru.
  • Rating breakdown di-render sebagai bar chart kecil (visual cue lebih kuat dari angka mentah).
  • OrganizerStatsPanel di profil publik tidak di-render untuk user yang murni peserta — query Prisma juga di-skip kalau organizedTrips.length === 0 && !isVerifiedOrganizer.
  • Trip detail: stat box "Trip dibuat" diganti jadi "Trip selesai" (lebih meaningful) + tambah "Peserta dilayani". Total 3 stat box, masih kompak.
# Item Status File
2.1 Extend OrganizerTrust type: tripsCompleted, tripsCancelled, totalParticipantsServed, completionRate, ratingBreakdown server/services/trust.service.ts
2.2 tripsCompleted di-derive dari endDate < now() (fallback date < now()) AND status != CLOSED server/services/trust.service.ts
2.3 totalParticipantsServed = count TripParticipant CONFIRMED di trip yang sudah lewat & tidak dibatalkan server/services/trust.service.ts
2.4 completionRate = tripsCompleted / (tripsCompleted + tripsCancelled). Null bila sample < 3 server/services/trust.service.ts, lib/trust.ts
2.5 ratingBreakdown via prisma.tripReview.groupBy({ by: ['rating'] }) server/services/trust.service.ts
2.6 Komponen OrganizerStatsPanel (badges + 4 stat box + bar chart breakdown) features/profile/components/organizer-stats-panel.tsx
2.7 Update OrganizerTrustPanel di trip detail — Trip selesai (+ subtitle "berjalan"), Peserta dilayani, Rating features/trip/components/organizer-trust-panel.tsx
2.8 Render OrganizerStatsPanel di /u/[id] (skip query untuk non-organizer) app/u/[id]/page.tsx

Tindakan manual: tidak ada.


PR-3 — Organizer Reviews Aggregator (service + UI, tanpa migration)

Selesai. tsc --noEmit lulus. Tanpa schema baru, tanpa migration.

Keputusan asumsi yang dipakai:

  • Default limit 20 ulasan terbaru — cukup untuk MVP, tidak perlu pagination dulu.
  • Komponen RSC (server component, no "use client") — pure render, tidak ada interaktivitas.
  • Tipe OrganizerReviewItem di-extract dari Awaited<ReturnType<typeof reviewRepo.findByOrganizer>>[number] supaya schema repo = sumber kebenaran tanpa duplikasi tipe.
  • Fetch trust + reviews via single Promise.all di /u/[id] (paralel, hemat 1 round-trip).
  • Komponen di-skip kalau reviews.length === 0 — biar tidak makin "kosong" di profil organizer baru. Stats panel sudah punya pesan "Belum ada ulasan".
  • Rating ditampilkan sebagai bintang penuh (★★★★☆) bukan angka — visual cue lebih kuat untuk testimoni.
  • Header section: "X terbaru dari Y ulasan" kalau di-limit, atau cuma "Y ulasan" kalau seluruh list ditampilkan.
# Item Status File
3.1 reviewService.getReviewsByOrganizer(organizerId, limit?) + tipe OrganizerReviewItem server/services/review.service.ts
3.2 Repo helper findByOrganizer (default limit 20, urut newest, include user + trip) server/repositories/review.repo.ts
3.3 Komponen OrganizerReviewsList (avatar + name + bintang + trip link + tanggal + comment) features/review/components/organizer-reviews-list.tsx
3.4 Render di /u/[id] di bawah OrganizerStatsPanel, fetch via Promise.all paralel app/u/[id]/page.tsx

Tindakan manual: tidak ada.


PR-4 — Trip Completion Mechanism (opsional, butuh diskusi)

Saat ini Trip.status = COMPLETED tidak pernah di-set oleh kode mana pun. PR ini hanya perlu kalau ingin pakai status sebagai sumber kebenaran formal (bukan computed-from-endDate).

Opsi:

  • A. Manual — organizer klik "Tandai trip selesai" pasca-pulang. Pro: kontrol di organizer. Con: gampang lupa, status tidak accurate kalau organizer pasif.
  • B. Cron job — daily job set status = COMPLETED untuk trip dengan endDate < today() AND status IN ('OPEN','FULL'). Pro: otomatis akurat. Con: butuh infra cron (belum ada di project).
  • C. Skip — biarkan computed-from-endDate di service layer. Pro: paling sederhana, sejalan dengan PR-2 yang juga compute on-the-fly. Con: field status jadi sebagian "live" (OPEN/FULL/CLOSED murni, COMPLETED computed).

Rekomendasi: C dulu sampai ada kebutuhan riil untuk transisi formal (mis. trigger payout organizer pasca-trip atau notif post-trip continuity di Phase C SOCIAL_ROADMAP).

# Item Status
4.1 Pilih opsi A/B/C
4.2 Implementasi sesuai pilihan (atau dokumentasikan keputusan kalau C)

Anti-list (yang harus DITOLAK kalau muncul)

  • Model OrganizerReview terpisahTripReview sudah cukup, 1 trip = 1 organizer. Bikin model baru = duplikasi data + sumber kebenaran ambigu.
  • Denormalisasi cache (mis. User.cachedAvgRating) sebelum aggregate query terbukti lambat. Premature optimization → drift jadi tech debt cepat.
  • Auto-hapus review buruk atau organizer "respon" review (untuk MVP). Bisa nanti — fokus dulu menampilkan data jujur.
  • "Trust score" gabungan satu angka — kasih breakdown agar calon peserta evaluasi sendiri. Single number gampang dimanipulasi & menyesatkan.
  • Review user/peserta (no-show, kooperatif?) — itu C6 di SOCIAL_ROADMAP.md. Scope berbeda, jangan campur.
  • Rating dengan setengah bintang / kustom 1-10 — tetap 1-5 integer (sudah di schema). Granularitas lebih halus tidak meningkatkan trust, hanya menambah noise.

Saran phasing

PR berurutan. Setiap PR mandiri (siap di-deploy):

  1. PR-1 — Trip detail polish. Cepat, low-risk, no migration. Mulai dari sini.
  2. PR-2 — Trust aggregates di service + UI. Read-only, no migration.
  3. PR-3 — Reviews list per organizer di service + UI. Read-only, no migration.
  4. PR-4 — Diskusi opsi completion mechanism (atau skip kalau opsi C dipilih).

Pertanyaan terbuka sebelum PR-2:

  1. Apakah tripsCompleted count termasuk trip dengan status = CLOSED yang endDate < now()? Saran: tidak — CLOSED = dibatalkan, dipisah ke tripsCancelled.
  2. Threshold minimum supaya completionRate ditampilkan? Saran: min 3 trip selesai supaya angka tidak menyesatkan (1 trip dibatalkan dari 1 trip = 0% looks bad untuk organizer baru).
  3. Tampilkan rating breakdown sebagai bar chart atau hanya angka? Saran: bar chart kecil — visual cue lebih kuat untuk credibility.