import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/auth/auth_notifier.dart'; import '../../core/status/status_notifier.dart'; import '../../core/chat/chat_request_notifier.dart'; import '../../core/theme/halo_tokens.dart'; import '../../core/theme/widgets/widgets.dart'; /// Bestie Home (mitra). Mirrors `figma-bestie/project/screens/v4.jsx::BestieHome` /// + `v5.jsx::BestieHomeOffline`. Bottom nav (BestieTabBar) is deferred until /// the Profil + Chat tabs have screen implementations. class HomeScreen extends ConsumerWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(mitraAuthProvider); final authData = authState.valueOrNull; final displayName = authData is MitraAuthAuthenticatedData ? (authData.profile['display_name'] as String? ?? 'Bestie') : 'Bestie'; final statusState = ref.watch(onlineStatusProvider); final isOnline = statusState is StatusLoadedData && statusState.isOnline; // Load pending requests if mitra is already online (existing logic). if (statusState is StatusLoadedData && statusState.isOnline) { final requestState = ref.watch(chatRequestProvider); if (requestState is ChatRequestIdleData) { Future.microtask(() { ref.read(chatRequestProvider.notifier).startListening(); ref.read(chatRequestProvider.notifier).loadPendingRequests(); }); } } ref.listen(onlineStatusProvider, (prev, next) { if (next is StatusLoadedData && next.isOnline) { ref.read(chatRequestProvider.notifier).startListening(); ref.read(chatRequestProvider.notifier).loadPendingRequests(); } else if (next is StatusLoadedData && !next.isOnline) { ref.read(chatRequestProvider.notifier).stopListening(); } }); return Scaffold( backgroundColor: HaloTokens.bg, body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 20, 20, 28), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _Header(displayName: displayName, isOnline: isOnline), const SizedBox(height: 18), const _TilesGrid(), const SizedBox(height: 14), _StatusCard(isOnline: isOnline), const SizedBox(height: 10), const _GantiStatusButton(), const SizedBox(height: 22), const _Pengingat(), const SizedBox(height: 16), // Functional shortcuts (no figma equivalent — kept until the // Chat tab is built so the user can still reach sessions / // history pages from home). const _ShortcutTile( icon: Icons.chat_bubble_outline, title: 'Sesi Aktif', route: '/sessions', ), const SizedBox(height: 8), const _ShortcutTile( icon: Icons.history, title: 'Riwayat Chat', route: '/chat/history', ), ], ), ), ), ); } } class _Header extends ConsumerWidget { final String displayName; final bool isOnline; const _Header({required this.displayName, required this.isOnline}); @override Widget build(BuildContext context, WidgetRef ref) { final greetingSuffix = isOnline ? '🌸' : '🌙'; return Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Hei,', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 13, color: HaloTokens.inkSoft, ), ), Text( 'Bestie $displayName $greetingSuffix', style: const TextStyle( fontFamily: HaloTokens.fontDisplay, fontSize: 24, fontWeight: FontWeight.w700, color: HaloTokens.brandDark, letterSpacing: -0.4, ), ), ], ), ), IconButton( icon: const Icon(Icons.more_horiz, color: HaloTokens.ink), style: IconButton.styleFrom( backgroundColor: HaloTokens.surface, shape: const CircleBorder(), ), onPressed: () => _showMenu(context, ref), ), ], ); } Future _showMenu(BuildContext context, WidgetRef ref) { return showModalBottomSheet( context: context, builder: (ctx) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.logout, color: HaloTokens.danger), title: const Text( 'Keluar', style: TextStyle( fontFamily: HaloTokens.fontBody, fontWeight: FontWeight.w600, ), ), onTap: () async { Navigator.of(ctx).pop(); await ref.read(mitraAuthProvider.notifier).logout(); }, ), ], ), ), ); } } class _TilesGrid extends ConsumerWidget { const _TilesGrid(); @override Widget build(BuildContext context, WidgetRef ref) { ref.watch(chatRequestProvider); final undanganCount = ref.read(chatRequestProvider.notifier).activeRequestCount; return Row( children: [ Expanded( child: _DarkTile( icon: '📨', label: 'Undangan', subtitle: undanganCount > 0 ? 'Menunggu: $undanganCount' : 'Belum ada', badgeCount: undanganCount, onTap: () => context.push('/chat/requests/history'), ), ), const SizedBox(width: 10), // Perpanjang tile — backend wiring (extension request count) isn't // exposed to the home yet, so render the static "Belum ada" state to // match the figma. Wire to the same notifier once an extension-count // provider exists. const Expanded( child: _DarkTile( icon: '⚡', label: 'Perpanjang', subtitle: 'Belum ada', badgeCount: 0, onTap: null, ), ), ], ); } } class _DarkTile extends StatelessWidget { final String icon; final String label; final String subtitle; final int badgeCount; final VoidCallback? onTap; const _DarkTile({ required this.icon, required this.label, required this.subtitle, required this.badgeCount, required this.onTap, }); @override Widget build(BuildContext context) { final card = Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF2A1820), borderRadius: HaloRadius.lg, ), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text(icon, style: const TextStyle(fontSize: 18)), const SizedBox(height: 6), Text( label, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white, ), ), const SizedBox(height: 2), Text( subtitle, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 11, color: Color(0xB3FFFFFF), ), ), ], ), if (badgeCount > 0) Positioned( top: 0, right: 0, child: Container( width: 18, height: 18, alignment: Alignment.center, decoration: const BoxDecoration( color: Color(0xFFFF4D6A), shape: BoxShape.circle, ), child: Text( '$badgeCount', style: const TextStyle( fontFamily: HaloTokens.fontBody, color: Colors.white, fontSize: 11, fontWeight: FontWeight.w700, ), ), ), ), ], ), ); return Material( color: Colors.transparent, child: InkWell( borderRadius: HaloRadius.lg, onTap: onTap, child: card, ), ); } } class _StatusCard extends StatelessWidget { final bool isOnline; const _StatusCard({required this.isOnline}); @override Widget build(BuildContext context) { final bgColor = isOnline ? const Color(0xFFE8F7EE) : const Color(0xFFFCE8E8); final borderColor = isOnline ? const Color(0xFF9DD9B1) : const Color(0xFFF5B5B5); final titleColor = isOnline ? const Color(0xFF1F6B3B) : const Color(0xFF7A2828); final subColor = isOnline ? const Color(0xFF3F8956) : const Color(0xFF9C4040); final dot = isOnline ? '🟢' : '🔴'; return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: bgColor, borderRadius: HaloRadius.md, border: Border.all(color: borderColor), ), child: Row( children: [ Text(dot, style: const TextStyle(fontSize: 14)), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Kamu lagi ${isOnline ? 'ONLINE' : 'OFFLINE'}', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 13, fontWeight: FontWeight.w700, color: titleColor, ), ), Text( isOnline ? 'siap menerima curhat baru' : 'gak terima curhat dulu', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 11, color: subColor, ), ), ], ), ), ], ), ); } } class _GantiStatusButton extends ConsumerWidget { const _GantiStatusButton(); @override Widget build(BuildContext context, WidgetRef ref) { final statusState = ref.watch(onlineStatusProvider); final isOnline = statusState is StatusLoadedData && statusState.isOnline; final isLoading = statusState is StatusLoadingData; return HaloButton( label: isLoading ? 'memproses...' : 'Ganti Status', fullWidth: true, onPressed: isLoading ? null : () { final notifier = ref.read(onlineStatusProvider.notifier); if (isOnline) { notifier.toggleOffline(); } else { notifier.toggleOnline(); } }, ); } } class _Pengingat extends StatelessWidget { const _Pengingat(); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(bottom: 8), child: Text( 'Pengingat', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 13, fontWeight: FontWeight.w700, color: HaloTokens.brandDark, ), ), ), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFEEE7F5), borderRadius: HaloRadius.md, ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('💜', style: TextStyle(fontSize: 16)), SizedBox(width: 10), Expanded( child: Text.rich( TextSpan( style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 12.5, color: HaloTokens.ink, height: 1.45, ), children: [ TextSpan( text: 'Opening protocol: ', style: TextStyle(fontWeight: FontWeight.w700), ), TextSpan( text: 'selalu mulai dengan pertanyaan terbuka yang hangat ya, Bestie.', ), ], ), ), ), ], ), ), ], ); } } class _ShortcutTile extends StatelessWidget { final IconData icon; final String title; final String route; const _ShortcutTile({ required this.icon, required this.title, required this.route, }); @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( borderRadius: HaloRadius.lg, onTap: () => context.push(route), child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( color: HaloTokens.surface, borderRadius: HaloRadius.lg, border: Border.all(color: HaloTokens.border), ), child: Row( children: [ Icon(icon, color: HaloTokens.brandDark, size: 20), const SizedBox(width: 12), Expanded( child: Text( title, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 14, fontWeight: FontWeight.w600, color: HaloTokens.ink, ), ), ), const Icon(Icons.chevron_right, color: HaloTokens.inkMuted, size: 20), ], ), ), ), ); } }