Files
halobestie-clone/mitra_app/lib/features/profile/support_handles_provider.dart
Ramadhan Sjamsani 10699d1ad1 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>
2026-05-21 16:32:21 +08:00

44 lines
1.5 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/api/api_client_provider.dart';
class SupportHandle {
final String label;
final String deeplink;
const SupportHandle({required this.label, required this.deeplink});
factory SupportHandle.fromJson(Map<String, dynamic> json) =>
SupportHandle(
label: json['label'] as String? ?? '',
deeplink: json['deeplink'] as String? ?? '',
);
/// Display form for the menu subtitle: strip the URL scheme so
/// "https://wa.me/6285173310010" → "wa.me/6285173310010" and
/// "https://t.me/halobestie" → "t.me/halobestie". Falls back to the
/// raw deeplink when there's no scheme to strip.
String get displayHandle {
if (deeplink.isEmpty) return '';
final stripped = deeplink.replaceFirst(RegExp(r'^https?://'), '');
return stripped;
}
}
class SupportHandles {
final SupportHandle? wa;
final SupportHandle? telegram;
const SupportHandles({this.wa, this.telegram});
factory SupportHandles.fromJson(Map<String, dynamic> json) {
SupportHandle? parse(dynamic v) =>
v is Map<String, dynamic> ? SupportHandle.fromJson(v) : null;
return SupportHandles(wa: parse(json['wa']), telegram: parse(json['telegram']));
}
}
final supportHandlesProvider = FutureProvider<SupportHandles>((ref) async {
final api = ref.read(apiClientProvider);
final response = await api.get('/api/shared/support-handles');
final data = response['data'] as Map<String, dynamic>? ?? const {};
return SupportHandles.fromJson(data);
});