Mitra Profil: WA/TG contacts + Keluar-only (no Hapus Akun)
Replaces the placeholder "Hubungi Koordinator" row with two real
contacts pulled from backend config (support_handles_json), and drops
the "Hapus Akun" CTA. Mirrors the figma BestieProfile design but uses
the same WA/TG channel as the customer Tanya Admin sheet — business
decided the same ops team triages both audiences.
Backend:
- Promote support-handles route from /api/client to /api/shared
(renamed file + export). Both apps now consume the same endpoint;
hitting /api/client/* from mitra would violate the per-app
convention in mitra_app/CLAUDE.md.
- client_app provider updated to /api/shared/support-handles.
Mitra app:
- New support_handles_provider mirroring the client_app one. Adds a
`displayHandle` getter that strips the URL scheme for the subtitle
("https://wa.me/X" → "wa.me/X", "https://t.me/Y" → "t.me/Y") so the
row looks like the figma without exposing raw URLs.
- Profil screen now lists: Chat WhatsApp Kami, Chat Telegram Kami,
Syarat & Ketentuan, Kebijakan Privasi. Danger zone simplified to
Keluar only — mitras request account deletion through the same
WA/TG channels (no separate self-service path).
- url_launcher added as a runtime dep, launches deeplinks in
externalApplication mode with graceful snackbar fallback when
parsing or launching fails.
Updates [[feedback-mitra-internal-audience]] — pre-login rule still
holds (no admin CTAs on S3a/S3b/AccountInactive), but the post-login
Profil tab now does surface WA/TG. Overrides decided 2026-05-21.
Verified on emulator-5556: Profil tab renders both rows with handles
from `wa.me/6285173310010` + `t.me/halobestie`, Keluar present, no
Hapus Akun button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import { clientPaymentRoutes } from './routes/public/client.payment.routes.js'
|
||||
import { clientMitraAvailabilityRoutes } from './routes/public/client.mitra-availability.routes.js'
|
||||
import { publicBestieAvailabilityRoutes } from './routes/public/public.bestie-availability.routes.js'
|
||||
import { clientOnboardingRoutes } from './routes/public/client.onboarding.routes.js'
|
||||
import { clientSupportRoutes } from './routes/public/client.support.routes.js'
|
||||
import { sharedSupportRoutes } from './routes/public/shared.support.routes.js'
|
||||
import { sharedChatRoutes } from './routes/public/shared.chat.routes.js'
|
||||
import { errorHandler } from './plugins/error-handler.js'
|
||||
import { registerWebSocketPlugin, registerWebSocketRoute } from './plugins/websocket.js'
|
||||
@@ -38,10 +38,10 @@ export const buildPublicApp = async () => {
|
||||
app.register(clientPaymentRoutes, { prefix: '/api/client/payment-sessions' })
|
||||
app.register(clientMitraAvailabilityRoutes, { prefix: '/api/client/mitra-availability' })
|
||||
app.register(publicBestieAvailabilityRoutes, { prefix: '/api/public/bestie' })
|
||||
// Phase 4: onboarding-state + support handles. Both are tiny so they live in their
|
||||
// own files rather than bloating client.auth.routes / shared.config.routes.
|
||||
// Onboarding-state stays client-only (anonymous customer flow). Support
|
||||
// handles are shared — both client and mitra apps link the same WA/TG.
|
||||
app.register(clientOnboardingRoutes, { prefix: '/api/client' })
|
||||
app.register(clientSupportRoutes, { prefix: '/api/client' })
|
||||
app.register(sharedSupportRoutes, { prefix: '/api/shared' })
|
||||
|
||||
// WebSocket route (registered at app level, not prefixed)
|
||||
registerWebSocketRoute(app)
|
||||
|
||||
@@ -2,11 +2,15 @@ import { authenticate } from '../../plugins/auth.js'
|
||||
import { getSupportHandles } from '../../services/config.service.js'
|
||||
|
||||
/**
|
||||
* Phase 4 — Tanya Admin sheet handles. Sourced from `app_config.support_handles_json`,
|
||||
* Support channels (WA + Telegram). Sourced from `app_config.support_handles_json`,
|
||||
* editable by CC. Authenticated so unauthenticated callers can't enumerate the
|
||||
* support channels (rate-limit hardening, not a secret).
|
||||
* channels (rate-limit hardening, not a secret).
|
||||
*
|
||||
* Originally registered under /api/client (Phase 4 Tanya Admin sheet). Promoted
|
||||
* to /api/shared when the mitra Profil screen started linking the same WA/TG
|
||||
* contacts — same data, both audiences.
|
||||
*/
|
||||
export const clientSupportRoutes = async (app) => {
|
||||
export const sharedSupportRoutes = async (app) => {
|
||||
app.get('/support-handles', { preHandler: authenticate }, async (_request, reply) => {
|
||||
const handles = await getSupportHandles()
|
||||
return reply.send({ success: true, data: handles })
|
||||
Reference in New Issue
Block a user