111 lines
10 KiB
Markdown
111 lines
10 KiB
Markdown
# Setrip — Social Repositioning Roadmap
|
||
|
||
Status implementasi reposisi dari "open trip pendakian" → "platform untuk menemukan teman aktivitas & trip bareng".
|
||
|
||
> **Prinsip pembeda:** Setrip bukan trip-marketplace. Trip adalah kendaraan untuk koneksi sosial (stranger → teman → circle). Setiap fitur dievaluasi: apakah memperkuat **find a companion**, atau hanya **book a trip**? Kalau cuma yang kedua → tolak.
|
||
|
||
---
|
||
|
||
## Phase A — Quick wins (sinyal sosial dari data yang sudah ada) ✅
|
||
|
||
Selesai. `tsc --noEmit` lulus. Migration `20260508120000_add_profile_vibe` belum di-apply ke DB.
|
||
|
||
| # | Item | Status | File |
|
||
|---|---|---|---|
|
||
| A1 | Hapus ikon `🏔️` hardcoded di trip detail → ikon + label kategori dinamis | ✅ | [app/trips/[id]/page.tsx](app/trips/[id]/page.tsx) |
|
||
| A2 | Banner onboarding profil di layout (muncul kalau `UserProfile` kosong) | ✅ | [components/shared/profile-nudge-banner.tsx](components/shared/profile-nudge-banner.tsx), [app/layout.tsx](app/layout.tsx) |
|
||
| A3 | Confirmed-peserta dirombak: chip nama → kartu (avatar + kota + 3 tag minat) | ✅ | [server/repositories/trip.repo.ts](server/repositories/trip.repo.ts), [app/trips/[id]/page.tsx](app/trips/[id]/page.tsx) |
|
||
| A4 | Field `vibe` (CHILL/BALANCED/HARDCORE) di `UserProfile` + UI editor + badge di profil publik | ✅ | [prisma/schema.prisma](prisma/schema.prisma), [prisma/migrations/20260508120000_add_profile_vibe/migration.sql](prisma/migrations/20260508120000_add_profile_vibe/migration.sql), [lib/vibe.ts](lib/vibe.ts), [features/profile/schemas.ts](features/profile/schemas.ts), [features/profile/actions.ts](features/profile/actions.ts), [server/repositories/profile.repo.ts](server/repositories/profile.repo.ts), [server/repositories/user.repo.ts](server/repositories/user.repo.ts), [server/services/profile.service.ts](server/services/profile.service.ts), [features/profile/components/profile-editor.tsx](features/profile/components/profile-editor.tsx), [app/profile/page.tsx](app/profile/page.tsx), [app/u/[id]/page.tsx](app/u/[id]/page.tsx) |
|
||
|
||
**Tindakan manual:** jalankan `npx prisma migrate deploy` (atau `dev`) untuk apply migration `20260507185257_add_user_profile` + `20260508120000_add_profile_vibe`.
|
||
|
||
---
|
||
|
||
## Phase B — Discovery (people-first, bukan price-first) ✅
|
||
|
||
Selesai. `tsc --noEmit` lulus. Migration `20260508130000_add_trip_vibe` belum di-apply ke DB.
|
||
|
||
**Keputusan desain:** `Trip` reuse enum `Vibe` yang sama dengan `UserProfile` (alih-alih bikin `pace`/`level` baru) supaya matching peserta↔trip langsung selaras tanpa mapping.
|
||
|
||
| # | Item | Status | File |
|
||
|---|---|---|---|
|
||
| B1 | Halaman `/people` — daftar user dengan profil terisi | ✅ | [app/people/page.tsx](app/people/page.tsx), [server/repositories/user.repo.ts](server/repositories/user.repo.ts) (`findPeople`), [server/services/profile.service.ts](server/services/profile.service.ts) |
|
||
| B2 | Filter kota, interests, vibe di `/people` | ✅ | [features/profile/components/people-filter.tsx](features/profile/components/people-filter.tsx), [features/profile/components/user-card.tsx](features/profile/components/user-card.tsx) |
|
||
| B3 | Field `vibe` di `Trip` + tampil di trip detail & TripCard + filter di `/trips` | ✅ | [prisma/schema.prisma](prisma/schema.prisma), [prisma/migrations/20260508130000_add_trip_vibe/migration.sql](prisma/migrations/20260508130000_add_trip_vibe/migration.sql), [features/trip/schemas.ts](features/trip/schemas.ts), [features/trip/actions.ts](features/trip/actions.ts), [server/services/trip.service.ts](server/services/trip.service.ts), [features/trip/components/create-trip-form.tsx](features/trip/components/create-trip-form.tsx), [features/trip/components/trip-card.tsx](features/trip/components/trip-card.tsx), [app/trips/[id]/page.tsx](app/trips/[id]/page.tsx) |
|
||
| B4 | TripCard: 3 avatar peserta confirmed + counter `+N` | ✅ | [server/repositories/trip.repo.ts](server/repositories/trip.repo.ts) (include participants di `findOpen`), [features/trip/components/trip-card.tsx](features/trip/components/trip-card.tsx), [app/page.tsx](app/page.tsx), [app/trips/page.tsx](app/trips/page.tsx) |
|
||
| B5 | TripCard: badge "✨ X peserta sama minat" untuk user login | ✅ | [features/trip/components/trip-card.tsx](features/trip/components/trip-card.tsx) (compute overlap), homepage & `/trips` (fetch viewer interests) |
|
||
| B6 | Filter ukuran grup (Small ≤10 / Medium 11–20 / Large 21+) | ✅ | [server/repositories/trip.repo.ts](server/repositories/trip.repo.ts) (`GroupSize` filter), [features/trip/components/trip-filter.tsx](features/trip/components/trip-filter.tsx), [app/trips/page.tsx](app/trips/page.tsx) |
|
||
| B7 | Section "Budget Friendly" → "Lagi Ramai" (social proof) | ✅ | [app/page.tsx](app/page.tsx) — sort by `participantCount desc`, framing "kamu nggak bakal jalan sendirian" |
|
||
| B+ | Link `/people` di navbar (desktop + mobile) | ✅ | [components/shared/navbar.tsx](components/shared/navbar.tsx) |
|
||
|
||
**Tindakan manual:** jalankan `npx prisma migrate deploy` untuk apply migration `20260508130000_add_trip_vibe` (selain 2 migration Phase A yang masih pending).
|
||
|
||
---
|
||
|
||
## Patch — KYC liveness photo rename (di luar fase social repositioning)
|
||
|
||
Mengubah foto liveness dari "selfie memegang KTP" (pola KYC standar) menjadi "memegang kertas tulisan SETRIP".
|
||
|
||
| Item | Status | File |
|
||
|---|---|---|
|
||
| Field `selfieKey` → `livenessKey` di `OrganizerVerification` | ✅ | [prisma/schema.prisma](prisma/schema.prisma), [prisma/migrations/20260508140000_rename_selfie_to_liveness/migration.sql](prisma/migrations/20260508140000_rename_selfie_to_liveness/migration.sql) |
|
||
| Storage kind `selfie` → `liveness` (path `liveness/<id>.<ext>`) | ✅ | [lib/secure-storage.ts](lib/secure-storage.ts) |
|
||
| Validasi + action + service + verify-form + review-card | ✅ | [features/organizer/schemas.ts](features/organizer/schemas.ts), [features/organizer/actions.ts](features/organizer/actions.ts), [server/services/organizer.service.ts](server/services/organizer.service.ts), [features/organizer/components/verify-form.tsx](features/organizer/components/verify-form.tsx), [features/organizer/components/review-card.tsx](features/organizer/components/review-card.tsx) |
|
||
| API routes `/api/upload/kyc` & `/api/files/kyc/[id]/[kind]` | ✅ | [app/api/upload/kyc/route.ts](app/api/upload/kyc/route.ts), [app/api/files/kyc/[id]/[kind]/route.ts](app/api/files/kyc/%5Bid%5D/%5Bkind%5D/route.ts) |
|
||
| Halaman verify, admin, seed, README, ARCHITECTURE | ✅ | [app/verify/page.tsx](app/verify/page.tsx), [app/admin/verifications/page.tsx](app/admin/verifications/page.tsx), [app/create-trip/page.tsx](app/create-trip/page.tsx), [prisma/seed.ts](prisma/seed.ts), [README.md](README.md), [ARCHITECTURE.md](ARCHITECTURE.md) |
|
||
|
||
**Trade-off keamanan yang sudah dikomunikasikan:** pola "selfie + KTP" lebih kuat (membuktikan KTP fisik di tangan pemilik). Pola "selfie + kertas SETRIP" lebih lemah dari sisi binding KTP↔orang, tapi mengurangi paparan KTP user dan masih mencegah replay dari platform lain. Risiko fraud naik sedikit — tetap dipilih atas request user.
|
||
|
||
**Catatan migrasi data lama:** kolom DB di-rename, tapi nilai-nilai key lama masih punya prefix `selfie/` (mis. `selfie/abc.jpg`). Setelah migration di-apply, validasi schema menolak prefix lama → user dengan pengajuan PENDING perlu re-upload foto liveness baru. Folder fisik `uploads/private/selfie/` tidak dipakai lagi, bisa dihapus manual setelah konfirmasi tidak ada data aktif yang merujuk.
|
||
|
||
**Tindakan manual:** jalankan `npx prisma migrate deploy` untuk apply `20260508140000_rename_selfie_to_liveness` (sekarang total 4 migration pending kalau belum pernah deploy).
|
||
|
||
---
|
||
|
||
## Phase C — Interaksi & continuity (separate, lebih besar) ⏳
|
||
|
||
Belum mulai. Setiap item bisa jadi PR terpisah karena perlu schema baru + UI substansial.
|
||
|
||
| # | Item | Status | Catatan |
|
||
|---|---|---|---|
|
||
| C1 | Model `TripMessage` — Q&A publik per trip (sebelum berangkat) | ⏳ pending | Schema + actions + UI di trip detail. Calon peserta tanya organizer tanpa keluar app. |
|
||
| C2 | Group chat untuk peserta CONFIRMED (post-confirmation) | ⏳ pending | Bisa pakai tabel `TripMessage` yang sama dengan flag `audience` (PUBLIC/CONFIRMED_ONLY). |
|
||
| C3 | Model `Connection` (follow / circle) antar user | ⏳ pending | Foundation untuk "from strangers → circle". Halaman "Circle saya". |
|
||
| C4 | Notifikasi: organizer punya pending join, peserta dapat balasan Q&A, dst | ⏳ pending | Bisa email dulu, in-app belakangan. |
|
||
| C5 | Post-trip continuity: tombol "follow buddies dari trip ini" + album foto bareng | ⏳ pending | Momen konversi stranger → circle terbesar saat ini terbuang. |
|
||
| C6 | Review user (bukan cuma trip) — reputasi peserta (no-show? kooperatif?) | ⏳ pending | Lengkapi trust layer. Anti-scam. |
|
||
| C7 | Onboarding flow wajib post-register (bukan banner) — minta minimal 3 interests + city + vibe sebelum bisa join trip | ⏳ pending | Banner Phase A2 cuma soft nudge. Hard-gate saat user pertama kali pencet "Join". |
|
||
| C8 | Referral / invite-with-link | ⏳ pending | Loop pertumbuhan komunitas. |
|
||
|
||
---
|
||
|
||
## ❌ Anti-list (yang harus DITOLAK kalau muncul)
|
||
|
||
Fitur-fitur ini akan menarik Setrip ke arena OTA (Traveloka/Klook) yang tidak bisa dimenangkan:
|
||
|
||
- Booking hotel / tiket pesawat
|
||
- Tour massal tanpa interaksi (>30 orang, bus pariwisata)
|
||
- Mass listing dari travel agent (B2B aggregator)
|
||
- Filter & sort harga yang lebih agresif (price-low-to-high, dll.) — perkuat framing harga-dulu
|
||
- Affiliate/komisi dari pihak ketiga yang bukan organizer terverifikasi
|
||
- SEO-driven mass content untuk destinasi (artikel "10 Gunung Terbaik di Jawa") tanpa angle social
|
||
- Integrasi pembayaran kompleks (split-bill, escrow rumit) sebelum chat dasar (C1) ada — prioritas terbalik
|
||
|
||
Kalau muncul request ke arah ini, tanya: "ini meningkatkan kemungkinan dua orang asing kenalan, atau cuma memudahkan transaksi?" Kalau jawabannya yang kedua → tolak / tunda.
|
||
|
||
---
|
||
|
||
## Konteks positioning (referensi cepat)
|
||
|
||
**Untuk siapa:** orang yang ingin pergi tapi tidak punya teman, ingin kenalan baru lewat aktivitas bareng.
|
||
|
||
**Bukan untuk:** orang yang sudah punya grup dan tinggal cari paket trip termurah.
|
||
|
||
**Categories yang valid** (semua harus punya: organizer, group kecil, interaksi sosial):
|
||
- Core: hiking, camping
|
||
- Natural expansion: snorkeling, diving, island hopping
|
||
- Social activity: city trip, kulineran, konser bareng
|
||
- Semi-professional: workshop, kelas outdoor, retreat
|
||
|
||
**Tagline:** "Pergi bareng, bukan sendiri" / "From strangers to travel buddies".
|