Files
halobestie-clone/requirement/flow_customer.mermaid.md
ramadhan sjamsani 8c212cb464 Phase 4 PRD + plan: customer-flow redesign (Figma alignment)
Adds the Phase 4 requirement docs that align the customer app with the new
HaloBestie Figma design dump.

- requirement/flow_customer.md: source-of-truth numbered flow (input)
- requirement/flow_customer.mermaid.md: 6 mermaid diagrams + Figma cross-ref
- requirement/phase4-customer-flow.md: PRD (15 functional sections)
- requirement/phase4-customer-flow-plan.md: 10-stage implementation plan
- .gitignore: exclude requirement/Figma.zip + extracted Figma/ folder

Resolved product decisions: no free trial (replaced by configurable
first-session discount), pricing has independent chat/call groups,
voice-call mode is chat-with-badge (mitra shares Meet link manually),
social login is server-driven via /api/shared/auth-providers, ESP tags
are info-only (not used for matching).

No code changes; implementation starts at plan stage 0 (design system).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 23:21:26 +08:00

11 KiB
Raw Blame History

Customer App Flow — Mermaid Diagrams

Generated from requirement/flow_customer.md and cross-checked against the Figma handoff package (requirement/Figma/ — git-ignored).

Legend (status pulled from a client_app audit, see phase4-customer-flow.md):

  • 🟢 EXISTS — already shipped in client_app
  • 🟡 PARTIAL — close, but a key piece (UX, copy, or sub-state) is missing
  • 🔴 MISSING — no implementation in client_app yet

The flow is split into 6 sub-diagrams so each one stays readable. Screen IDs use the Figma handoff naming (S1, S6, S10, …); see Figma/handoff/png/ for the 909×540 renders and Figma/screens/*.jsx for the live source.


1. Boot + Home gating

flowchart TD
  S1["S1 · Splash 🟢"] --> Home{"JWT session?"}
  Home -->|"no"| Home1st["Home (1st time)<br/>+ login panel 🟡"]
  Home -->|"yes"| HomeRet["Home (returning)<br/>+ profile panel 🟢"]
  Home1st --> NotifCheck{"OS notif allowed?"}
  HomeRet --> NotifCheck
  NotifCheck -->|"no"| HomeBanner["Home + notif banner 🔴"]
  NotifCheck -->|"yes"| HomeReady["Home (ready) 🟢"]
  HomeBanner --> HomeReady

  HomeReady --> CTA{"CTA tapped?"}
  CTA -->|"'aku mau curhat'<br/>(1st time)"| NewUser["→ New User flow"]
  CTA -->|"'curhat sama bestie baru'<br/>(returning)"| ReturningUser["→ Returning flow"]

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class HomeBanner missing
  class Home1st partial

2. New-User onboarding (verified vs. anonymous)

flowchart TD
  Start["from Home — 'aku mau curhat' 🟢"] --> NameCheck{"call_sign exists?"}
  NameCheck -->|"no"| S2["S2 · Pengisian Nama 🟢"]
  NameCheck -->|"yes"| VerifChoice
  S2 --> VerifChoice["Verif vs Anon Choice Sheet<br/>(VerifChoiceSheet) 🔴"]

  VerifChoice -->|"verif WA · Rp2k"| ESPa["S5 · ESP screening<br/>(multi-select chips) 🟡"]
  VerifChoice -->|"tanpa verif · Rp5k+"| ESPb["S5 · ESP screening 🟡"]

  %% Verified path
  ESPa --> USPa["S5b · USP screen 🔴"]
  USPa --> S3a["S3a · WhatsApp input 🟡 (6→4 digit)"]
  S3a --> S3b["S3b · OTP 4-digit 🟡"]
  S3b --> OTPok{"OTP ok?"}
  OTPok -->|"too many retries"| OTPBlock["OTP Blocked Popup 🔴<br/>→ fallback to Anon"]
  OTPBlock --> ESPb
  OTPok -->|"verified"| S6["S6 · Paywall Rp2.000<br/>(12 menit, sekali seumur hidup) 🟡"]
  S6 --> Pay

  %% Anonymous path
  ESPb --> USPb["S5b · USP screen 🔴"]
  USPb --> PickMethod["Pilih cara curhat<br/>(chat / voice call) 🔴"]
  PickMethod --> PickDuration["Pemilihan harga<br/>(5 durations, full screen) 🟡"]
  PickDuration --> PayMethod["Cara bayar (QRIS-first) 🔴"]
  PayMethod --> Pay

  %% Shared payment exit
  Pay["Xendit checkout<br/>(QRIS / e-wallet) 🔴"] --> WaitPay["Waiting Payment<br/>(20-min QRIS clock) 🔴"]
  WaitPay --> PayStat{"payment status"}
  PayStat -->|"timeout 20 min"| PayExpired["Pembayaran expired 🔴<br/>→ retry"]
  PayExpired --> Pay
  PayStat -->|"paid"| NotifGate

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class VerifChoice,USPa,USPb,PickMethod,PayMethod,Pay,WaitPay,PayExpired,OTPBlock missing
  class ESPa,ESPb,S3a,S3b,S6,PickDuration partial

Anchor mismatch: flow_customer.md numbers ESP/USP under 5.1.2 Verification request (OTP) for both branches, but Figma puts VerifChoiceSheet before ESP. The mermaid above follows Figma; reconcile in phase4 spec.


3. Pre-pairing → Searching → Match (shared)

flowchart TD
  NotifGate["Notif Gate Screen 🔴<br/>(Aktifkan / Nanti Saja)"] --> NotifBranch{"OS allowed?"}
  NotifBranch -->|"no + ask"| EnableNotif["OS settings deeplink"] --> SoftPrompt
  NotifBranch -->|"yes / skipped"| SoftPrompt
  SoftPrompt["S7 · Soft-prompt<br/>(consent + warmup, CTA 'Aku ngerti, Lanjut') 🟡"] --> Blast
  Blast["Blast pair request<br/>S7 · Searching state 🟡"] --> BlastTimer{"5-min timer"}
  BlastTimer -->|"matched"| S9["S9 · Match Found<br/>(bestie name, age, hobi) 🟡"]
  BlastTimer -->|"timeout"| S7Timeout["S7 · Timeout 5 menit 🔴<br/>CTA 'Coba Cari Lagi'<br/>ghost CTA 'Coba cari lagi nanti' → Home"]
  S7Timeout -->|"retry"| Blast
  S7Timeout -->|"home"| HomeRet
  S9 --> S10
  S10["S10 · Chat Room"]
  HomeRet["→ Home (returning)"]

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class NotifGate,S7Timeout missing
  class SoftPrompt,Blast,S9 partial

4. Returning-User pairing (lama / baru)

flowchart TD
  CTA["'curhat sama bestie baru' 🟢"] --> Choice["Bestie Choice Sheet<br/>(BestieChoiceSheet) 🔴"]
  Choice -->|"bestie yang udah kenal"| HistList["Bestie History List<br/>(BestieHistoryList) 🟢"]
  Choice -->|"bestie baru"| BlastFlow["→ S7 Soft-prompt + Blast<br/>(see diagram 3)"]

  HistList --> PickBestie["pick bestie"]
  PickBestie --> CheckOnline{"bestie online?"}
  CheckOnline -->|"no"| OfflinePopup["Bestie Offline Popup<br/>(returning variant) 🟢"]
  OfflinePopup -->|"cari bestie lain"| BlastFlow
  OfflinePopup -->|"tanya admin"| AdminSheet["Sheet · tanya admin<br/>(WA / Telegram) 🔴"]
  CheckOnline -->|"yes"| Targeted["Request targeted pair<br/>'Menunggu bestie tertentu' 🟡<br/>(20s countdown overlay)"]
  Targeted --> TargetedRes{"mitra answers?"}
  TargetedRes -->|"accept"| S10["→ S10 Chat Room"]
  TargetedRes -->|"reject / timeout"| OfflinePopup

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class Choice,AdminSheet missing
  class Targeted partial

5. Chat Room (S10) — countdown UX

flowchart TD
  Enter["enter S10 · Chat Room<br/>(WebSocket open, timer running) 🟡"] --> T3{"3-minutes-left tick"}
  T3 -->|"fired"| Snackbar["S10 · Snackbar reminder<br/>'sisa 3 menit lagi ya' 🔴"]
  Snackbar --> T2
  T3 -->|"not yet"| T2{"2-minutes-left tick"}
  T2 -->|"fired"| LowTime["S10 · Last 2 Minutes<br/>(timer turns danger color) 🔴"]
  LowTime --> Expire
  T2 -->|"not yet"| Expire{"timer hits 0"}
  Expire -->|"fired"| ExpiredBanner["S10 · Floating Expired Banner<br/>'habis nih... mau lanjutin?' 🔴"]
  ExpiredBanner --> CTAExt{"perpanjang CTA?"}
  CTAExt -->|"yes"| TimeUp
  CTAExt -->|"close / ignore"| EndFlow["→ end-session flow"]

  Expire -->|"not yet · user taps perpanjang"| TimeUp
  TimeUp["Time-up Bottom Sheet<br/>(5 durations · chat/call toggle) 🟡"]
  TimeUp -->|"perpanjang"| AskMitra["Targeted re-pay request<br/>(same mitra, no blast) 🔴"]
  TimeUp -->|"cukup, akhiri sesi"| EndFlow

  AskMitra --> MitraRes{"mitra approves?"}
  MitraRes -->|"yes + paid"| Enter
  MitraRes -->|"reject"| OfflinePopup["Bestie Offline Popup<br/>(returning variant) 🟢"]

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class Snackbar,LowTime,ExpiredBanner,AskMitra missing
  class Enter,TimeUp partial

6. End-of-session sequence (2-step confirm + closing message)

flowchart TD
  EndStart["End-session entry<br/>(from S10 or Time-up sheet 'Cukup, Akhiri')"] --> Confirm1["Popup · Konfirmasi Akhiri (1)<br/>'beneran udah cukup?' 🟡"]
  Confirm1 -->|"Gak Jadi, Balik"| TimeUp["Time-up Bottom Sheet 🟡"]
  Confirm1 -->|"Lanjut Akhiri"| Confirm2["Popup · Konfirmasi Akhiri (2)<br/>'mau tinggalin pesan penutup?' 🔴"]

  Confirm2 -->|"Tulis Pesan Penutup"| ClosingSheet["Pesan Penutup Bottom Sheet<br/>(textarea) 🟡"]
  Confirm2 -->|"Lewati Saja"| ThankYou

  ClosingSheet -->|"Kirim & Akhiri"| MitraReceipt{"mitra rejects close?"}
  ClosingSheet -->|"Lewat — Langsung Akhiri"| ThankYou
  MitraReceipt -->|"no"| ThankYou
  MitraReceipt -->|"yes (rare)"| OfflinePopup["Bestie Offline Popup 🟢"]

  ThankYou["S11 · Terima Kasih Udah Cerita 🔴"] --> Home["→ Home (returning) 🟢"]

  classDef missing fill:#ffe5e5,stroke:#c44979
  classDef partial fill:#fff4d6,stroke:#c69b3f
  class Confirm2,ThankYou missing
  class Confirm1,ClosingSheet,TimeUp partial

Cross-reference: Figma → flow_customer.md

Figma artifact (file) Source flow_customer.md ref
S1 Splash screens/onboarding.jsx::S1Splash §1
Home 1st / Returning screens/v3.jsx::SHome1st / SHomeReturning + screens/session.jsx::S12Home §23
Notif banner on home screens/v3.jsx::HBNotifBanner §4.1
S2 Nama screens/onboarding.jsx::S2Name (and v4 variant) §5.1.1
VerifChoiceSheet screens/v4.jsx::VerifChoiceSheet implied between §5.1.1 ↔ §5.1.2
S5 ESP screening screens/onboarding.jsx::S5ESP §5.1.2.1.1 / §5.1.2.2.1
S5b USP screens/onboarding.jsx::S5USP §5.1.2.1.2 / §5.1.2.2.2
S3a WA / S3b OTP screens/onboarding.jsx::S3Phone (+ screens/v4.jsx::S3OTPV4) §5.1.2.1.3-4
OTPBlockedPopup screens/v4.jsx::OTPBlockedPopup (gap — not in flow doc)
S6 Paywall Rp2k screens/onboarding.jsx::S6Paywall §5.1.2.1.5
Pilih cara curhat screens/v3.jsx::SPickMethod §5.1.2.2.3
Pemilihan harga screens/v3.jsx::SPickDuration (+ v4::InitialDurationPicker) §5.1.2.2.4
Cara bayar screens/extras.jsx::SPaymentMethod §5.1.2.2.5
Waiting Payment screens/extras.jsx::SWaitingPayment §5.1.4
Pembayaran expired screens/v4.jsx::PaymentExpiredV4 (+ extras SWaitingPayment expired) §5.1.4.1
Notif Gate (full screen) screens/extras.jsx::SNotifGate (+ v4::NotifGateV4) §5.1.5.1
S7 Soft-prompt + Searching screens/session.jsx::S7Prompt + S8Searching + screens/v3.jsx::SSearchPrompt §5.1.6-8
S7 Timeout 5 menit screens/v3.jsx::SSearchPrompt(state='timeout') §5.1.8.2
S9 Match screens/session.jsx::S9Match (+ v4::S9MatchV4) §5.1.9
Bestie Choice Sheet screens/v4.jsx::BestieChoiceSheet §5.2.1
Bestie History List screens/v4.jsx::BestieHistoryList §5.2.1.1
Bestie Offline Popup screens/v4.jsx::BestieOfflinePopup §5.2.1.1.1 / §5.7.8.1.1.1
Tanya Admin Sheet screens/v3.jsx::HBContactAdminSheet §5.2.1.1.1.2
Menunggu Bestie screens/extras.jsx::SWaitingBestie §5.2.1.1.2.1
S10 Chat screens/session.jsx::S10Chat §5.3
3-min Snackbar reminder screens/v3.jsx::HBSnackbar (configured for "sisa 3 menit") §5.4
Last-2-min visuals screens/session.jsx::S10Chat (lowTime branch) §5.5
Floating expired banner screens/v3.jsx::HBChatExpiredBanner §5.6
Time-up Bottom Sheet screens/extras.jsx::STimeUpSheet §5.7-8
Confirm akhiri (2 popups) screens/v3.jsx::HBConfirmEndPopup (step 1 + 2) §5.8.2.1 / §5.8.2.1.1
Closing Message Sheet screens/extras.jsx::SClosingSheet §5.8.2.1.1.1
S11 Thank-you screens/session.jsx::S11Post §5.8.2.1.1.1.1

Anything Figma describes that flow_customer.md doesn't mention is captured as a gap in phase4-customer-flow.md (next-phase doc).