import 'package:flutter/material.dart'; import '../../../core/theme/halo_tokens.dart'; import '../../../core/theme/widgets/widgets.dart'; /// Generic row used by all three Chat-Tab sub-tabs (aktif / pembayaran / /// selesai). The owning sub-tab supplies the right-side trailing widget and /// any chips below the preview so this widget stays presentation-only. /// /// Visual reference: `requirement/Figma/screens/extras.jsx::SChatList` rows. class ChatRow extends StatelessWidget { final int seed; final String name; final String preview; final bool isLive; final String? trailing; final List chips; final bool isCall; final VoidCallback? onTap; const ChatRow({ super.key, required this.seed, required this.name, required this.preview, this.isLive = false, this.trailing, this.chips = const [], this.isCall = false, this.onTap, }); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 10), child: Material( color: HaloTokens.surface, borderRadius: BorderRadius.circular(14), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(14), child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all(color: HaloTokens.border), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ _Avatar(seed: seed, isLive: isLive), const SizedBox(width: 12), Expanded(child: _Body( name: name, preview: preview, trailing: trailing, isLive: isLive, isCall: isCall, chips: chips, )), ], ), ), ), ), ); } } class _Avatar extends StatelessWidget { final int seed; final bool isLive; const _Avatar({required this.seed, required this.isLive}); @override Widget build(BuildContext context) { return SizedBox( width: 44, height: 44, child: Stack( clipBehavior: Clip.none, children: [ HaloOrb(seed: seed, size: 44), if (isLive) Positioned( right: -2, bottom: -2, child: Container( width: 14, height: 14, decoration: BoxDecoration( color: HaloTokens.success, shape: BoxShape.circle, border: Border.all(color: HaloTokens.surface, width: 2.5), ), ), ), ], ), ); } } class _Body extends StatelessWidget { final String name; final String preview; final String? trailing; final bool isLive; final bool isCall; final List chips; const _Body({ required this.name, required this.preview, required this.trailing, required this.isLive, required this.isCall, required this.chips, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Expanded( child: Text( name, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 14, fontWeight: FontWeight.w600, color: HaloTokens.ink, ), ), ), if (trailing != null || isLive) Text( isLive ? '● live' : (trailing ?? ''), style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 10.5, fontWeight: isLive ? FontWeight.w600 : FontWeight.w400, color: isLive ? HaloTokens.success : HaloTokens.inkMuted, ), ), ], ), const SizedBox(height: 2), Text( preview, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 12, color: HaloTokens.inkSoft, height: 1.4, ), ), if (isCall || chips.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 6), child: Wrap( spacing: 6, runSpacing: 4, children: [ if (isCall) const _CallChip(), ...chips, ], ), ), ], ); } } /// Voice-call indicator. Stage 6.0 introduced the header pill for the in-call /// screen; this is its row-level companion in the Chat tab. class _CallChip extends StatelessWidget { const _CallChip(); @override Widget build(BuildContext context) => const _Chip( text: '📞 Call', textColor: HaloTokens.brandDark, background: HaloTokens.brandSoft, ); } /// Amber chip used on Pembayaran rows: `bayar Rp X.XXX`. class PaymentAmountChip extends StatelessWidget { final int amount; const PaymentAmountChip({super.key, required this.amount}); @override Widget build(BuildContext context) => _Chip( text: 'bayar ${_formatRupiah(amount)}', textColor: const Color(0xFFA8410E), background: const Color(0xFFFFF0E5), ); } /// Subtle duration suffix on Selesai rows: `X menit`. class DurationChip extends StatelessWidget { final int minutes; const DurationChip({super.key, required this.minutes}); @override Widget build(BuildContext context) => Text( '$minutes menit', style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 10.5, color: HaloTokens.inkMuted, ), ); } class _Chip extends StatelessWidget { final String text; final Color textColor; final Color background; const _Chip({ required this.text, required this.textColor, required this.background, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: background, borderRadius: BorderRadius.circular(6), ), child: Text( text, style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 10.5, fontWeight: FontWeight.w600, color: textColor, ), ), ); } } String _formatRupiah(int amount) { // Locale-agnostic, no intl dep. Render with `.` group separators per id_ID. final s = amount.toString(); final buf = StringBuffer(); for (int i = 0; i < s.length; i++) { if (i != 0 && (s.length - i) % 3 == 0) buf.write('.'); buf.write(s[i]); } return 'Rp${buf.toString()}'; }