import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/data/services/model/tender_data/tender_data.dart'; import 'package:tm_app/view_models/ai_recommendations_view_model.dart'; import '../strings/tenders_strings.dart'; /// The "Recommended" tab body. Handles loading, empty/"still learning", /// error+retry and the recommended tender list for [AiRecommendationsViewModel]. class RecommendedTendersView extends StatefulWidget { const RecommendedTendersView({super.key}); @override State createState() => _RecommendedTendersViewState(); } class _RecommendedTendersViewState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { context.read().loadIfNeeded(); } }); } @override Widget build(BuildContext context) { return Consumer( builder: (context, vm, child) { if (vm.isLoading) { return const _LoadingSkeleton(); } if (vm.errorMessage != null) { return _MessageState( title: TendersStrings.recommendedError, onRetry: vm.refresh, ); } if (vm.stillLearning) { return _MessageState( title: TendersStrings.recommendedStillLearningTitle, body: TendersStrings.recommendedStillLearningBody, onRetry: vm.refresh, ); } if (vm.recommendedTenders.isNotEmpty) { return RefreshIndicator( color: AppColors.jellyBean, onRefresh: vm.refresh, child: ListView.separated( padding: EdgeInsets.all(16.0.h()), itemCount: vm.recommendedTenders.length, separatorBuilder: (_, __) => SizedBox(height: 12.0.h()), itemBuilder: (context, index) => _RecommendedTenderCard(tender: vm.recommendedTenders[index]), ), ); } return _MessageState( title: TendersStrings.recommendedEmptyTitle, body: TendersStrings.recommendedEmptyBody, onRetry: vm.refresh, ); }, ); } } class _RecommendedTenderCard extends StatelessWidget { const _RecommendedTenderCard({required this.tender}); final TenderData tender; void _openDetail(BuildContext context) { final id = tender.id; if (id == null || id.isEmpty) { return; } TenderDetailRouteData(tenderId: id).push(context); } @override Widget build(BuildContext context) { final title = tender.title?.trim() ?? ''; final buyer = tender.buyerOrganization?.name?.trim() ?? ''; return InkWell( borderRadius: BorderRadius.circular(12.0.w()), onTap: () => _openDetail(context), child: Container( width: double.infinity, padding: EdgeInsets.all(16.0.h()), decoration: BoxDecoration( color: AppColors.grey0, borderRadius: BorderRadius.circular(12.0.w()), border: Border.all(color: AppColors.grey30), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title.isNotEmpty) SelectableText( title, maxLines: 2, style: TextStyle( fontSize: 15.0.sp(), fontWeight: FontWeight.w600, color: AppColors.grey80, ), ), if (buyer.isNotEmpty) ...[ SizedBox(height: 8.0.h()), Text( buyer, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 13.0.sp(), fontWeight: FontWeight.w400, color: AppColors.grey60, ), ), ], ], ), ), ); } } /// Centered title/body message with an optional retry button. Wrapped in a /// scroll view so pull-to-refresh works even when empty. class _MessageState extends StatelessWidget { const _MessageState({required this.title, this.body, this.onRetry}); final String title; final String? body; final Future Function()? onRetry; @override Widget build(BuildContext context) { final content = Padding( padding: EdgeInsets.symmetric(horizontal: 32.0.w(), vertical: 48.0.h()), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( title, textAlign: TextAlign.center, style: TextStyle( fontSize: 16.0.sp(), fontWeight: FontWeight.w600, color: AppColors.grey70, ), ), if (body != null) ...[ SizedBox(height: 12.0.h()), Text( body!, textAlign: TextAlign.center, style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w400, color: AppColors.grey60, height: 1.5, ), ), ], if (onRetry != null) ...[ SizedBox(height: 24.0.h()), TextButton( onPressed: onRetry, child: Text( TendersStrings.retry, style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w600, color: AppColors.mainBlue, ), ), ), ], ], ), ); if (onRetry == null) { return Center(child: content); } return RefreshIndicator( color: AppColors.jellyBean, onRefresh: onRetry!, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints(minHeight: SizeConfig.screenHeight * 0.6), child: Center(child: content), ), ), ); } } /// Simple placeholder list shown while recommendations load. class _LoadingSkeleton extends StatelessWidget { const _LoadingSkeleton(); @override Widget build(BuildContext context) { return ListView.separated( padding: EdgeInsets.all(16.0.h()), itemCount: 4, separatorBuilder: (_, __) => SizedBox(height: 12.0.h()), itemBuilder: (context, index) => Container( height: 120.0.h(), decoration: BoxDecoration( color: AppColors.grey0, borderRadius: BorderRadius.circular(12.0.w()), border: Border.all(color: AppColors.grey30), ), ), ); } }