import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:nhance_app_pwa/customAppBar/responsive.dart'; import 'package:nhance_app_pwa/features/chatbot/application/chatbot_controller.dart'; import 'package:nhance_app_pwa/features/chatbot/domain/models/chatbot_models.dart'; import 'package:nhance_app_pwa/features/chatbot/presentation/widgets/chatbot_message_card.dart'; import 'package:nhance_app_pwa/features/chatbot/presentation/widgets/chatbot_option_button.dart'; import 'package:nhance_app_pwa/features/chatbot/presentation/widgets/chatbot_typing_indicator.dart'; class ChatbotWindow extends ConsumerStatefulWidget { const ChatbotWindow({super.key}); @override ConsumerState createState() => _ChatbotWindowState(); } class _ChatbotWindowState extends ConsumerState { final ScrollController _messagesScrollController = ScrollController(); final Map statusColors = { 'Claim Received': Colors.blueGrey, 'Under Process': Colors.orangeAccent, 'Information Required': Colors.deepOrange, 'Approved': Colors.green, 'Settled': Colors.teal, 'Denial Review Awaited': Colors.redAccent, 'Rejected': Colors.red, }; @override void dispose() { _messagesScrollController.dispose(); super.dispose(); } void _scrollToLatestMessage() { if (!_messagesScrollController.hasClients) return; WidgetsBinding.instance.addPostFrameCallback((_) { if (!_messagesScrollController.hasClients) return; _messagesScrollController.animateTo( _messagesScrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 220), curve: Curves.easeOut, ); }); } Color _statusColor(String status) { return statusColors[status.trim()] ?? const Color(0xFF6B7280); } Widget _buildClaimStatusCard(ClaimStatusItem item) { final badgeColor = _statusColor(item.status); return Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(bottom: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( padding: EdgeInsets.only(left: 4, right: 10, top: 2), child: ClipOval( child: Image( image: AssetImage('assets/chatbot_icon.png'), width: 30, height: 30, fit: BoxFit.cover, ), ), ), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 280), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: const Color(0xFFFFFCE5), borderRadius: BorderRadius.circular(14), boxShadow: const [ BoxShadow( color: Color(0x22000000), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( item.claimNumber, style: const TextStyle( fontWeight: FontWeight.w700, fontSize: 18, color: Color(0xFF111827), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 5), decoration: BoxDecoration( color: badgeColor, borderRadius: BorderRadius.circular(16), ), child: Text( item.status, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, fontSize: 12, ), ), ), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: Text( item.amount.isNotEmpty ? 'Amount: ${item.amount}' : '-', textAlign: TextAlign.start, style: const TextStyle( color: Color(0xFF374151), fontSize: 14, ), ), ), const SizedBox(width: 8), Text( item.date, textAlign: TextAlign.right, style: const TextStyle( color: Color(0xFF6B7280), fontSize: 13, ), ), ], ), ], ), ), ), ], ), ), ); } @override Widget build(BuildContext context) { final state = ref.watch(chatbotControllerProvider); final controller = ref.read(chatbotControllerProvider.notifier); _scrollToLatestMessage(); final isMobile = Responsive.isMobile(context); final isTablet = Responsive.isTablet(context); final topPadding = MediaQuery.of(context).padding.top; final bottomPadding = MediaQuery.of(context).padding.bottom; final window = Material( elevation: isMobile ? 0 : 14, color: const Color(0xFFF3F7FB), borderRadius: BorderRadius.circular(isMobile ? 0 : 14), child: SizedBox( width: isMobile ? MediaQuery.of(context).size.width : isTablet ? 420 : 420, height: isMobile ? MediaQuery.of(context).size.height : 560, child: Column( children: [ Container( decoration: BoxDecoration( color: Color(0xFF00999E), borderRadius: BorderRadius.vertical( top: Radius.circular(isMobile ? 0 : 14), ), ), padding: EdgeInsets.fromLTRB(14, isMobile ? topPadding + 8 : 12, 14, 12), child: Row( children: [ Expanded( child: Row( children: [ ClipOval( child: Container( width: 28, height: 28, color: Colors.white, alignment: Alignment.center, child: Image.asset( 'assets/chatbot_icon.png', width: 24, height: 24, fit: BoxFit.cover, ), ), ), const SizedBox(width: 8), const Text( 'Nhance Care', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600, fontSize: 16, ), ), ], ), ), InkWell( onTap: controller.close, child: const Icon(Icons.close, color: Colors.white), ), ], ), ), Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(12, 12, 12, 0), child: ListView( controller: _messagesScrollController, children: [ ...state.messages.map( (item) => ChatbotMessageCard( text: item.text, isBot: item.isBot, createdAt: item.createdAt, ), ), if (state.step == ChatbotFlowStep.claimStatusList && state.claimStatuses.isNotEmpty) ...state.claimStatuses.map(_buildClaimStatusCard), if (state.isLoading) const ChatbotTypingIndicator(), ], ), ), ), Padding( padding: EdgeInsets.fromLTRB(12, 8, 12, 12 + (isMobile ? bottomPadding : 0)), child: Column( children: state.options .map( (option) => ChatbotOptionButton( label: option.label, onTap: state.isLoading ? () {} : () => controller.onOptionSelected(context, option), ), ) .toList(), ), ), ], ), ), ); if (isMobile) { return AnimatedSlide( duration: const Duration(milliseconds: 220), curve: Curves.easeOut, offset: state.isOpen ? Offset.zero : const Offset(0, 1), child: window, ); } return AnimatedScale( duration: const Duration(milliseconds: 180), scale: state.isOpen ? 1 : 0.9, alignment: Alignment.bottomRight, child: window, ); } }