refactor: use GET tenders/recommend and improve tender title UX
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
Load the Recommended tab from GET /api/v1/tenders/recommend instead of the AI POST flow with 503 fallback. Fix notification pagination offset, make tender titles selectable across list and detail views, and ignore .cursor. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,10 +7,9 @@ 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';
|
||||
import 'recommendation_card.dart';
|
||||
|
||||
/// The "Recommended" (AI) tab body. Handles loading, empty/"still learning",
|
||||
/// error+retry and the 503 fallback states for [AiRecommendationsViewModel].
|
||||
/// 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});
|
||||
|
||||
@@ -49,20 +48,16 @@ class _RecommendedTendersViewState extends State<RecommendedTendersView> {
|
||||
onRetry: vm.refresh,
|
||||
);
|
||||
}
|
||||
if (vm.usingFallback && vm.fallbackTenders.isNotEmpty) {
|
||||
return _FallbackList(tenders: vm.fallbackTenders, onRefresh: vm.refresh);
|
||||
}
|
||||
if (vm.items.isNotEmpty) {
|
||||
if (vm.recommendedTenders.isNotEmpty) {
|
||||
return RefreshIndicator(
|
||||
color: AppColors.jellyBean,
|
||||
onRefresh: vm.refresh,
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.all(16.0.h()),
|
||||
itemCount: vm.items.length,
|
||||
itemCount: vm.recommendedTenders.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.0.h()),
|
||||
itemBuilder:
|
||||
(context, index) =>
|
||||
RecommendationCard(item: vm.items[index]),
|
||||
itemBuilder: (context, index) =>
|
||||
_RecommendedTenderCard(tender: vm.recommendedTenders[index]),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -76,56 +71,8 @@ class _RecommendedTendersViewState extends State<RecommendedTendersView> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the legacy keyword-based fallback list with a notice banner.
|
||||
class _FallbackList extends StatelessWidget {
|
||||
const _FallbackList({required this.tenders, required this.onRefresh});
|
||||
|
||||
final List<TenderData> tenders;
|
||||
final Future<void> Function() onRefresh;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
color: AppColors.jellyBean,
|
||||
onRefresh: onRefresh,
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.all(16.0.h()),
|
||||
itemCount: tenders.length + 1,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 12.0.h()),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return _fallbackNotice();
|
||||
}
|
||||
return _FallbackTenderCard(tender: tenders[index - 1]);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _fallbackNotice() {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(12.0.h()),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.orange10,
|
||||
borderRadius: BorderRadius.circular(8.0.w()),
|
||||
),
|
||||
child: Text(
|
||||
TendersStrings.recommendedFallbackNotice,
|
||||
style: TextStyle(
|
||||
fontSize: 13.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.yellow4,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lightweight card for a fallback tender. Decoupled from [TendersViewModel]
|
||||
/// (no feedback/pagination) so it can render outside the All-tab flow.
|
||||
class _FallbackTenderCard extends StatelessWidget {
|
||||
const _FallbackTenderCard({required this.tender});
|
||||
class _RecommendedTenderCard extends StatelessWidget {
|
||||
const _RecommendedTenderCard({required this.tender});
|
||||
|
||||
final TenderData tender;
|
||||
|
||||
@@ -156,10 +103,9 @@ class _FallbackTenderCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (title.isNotEmpty)
|
||||
Text(
|
||||
SelectableText(
|
||||
title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15.0.sp(),
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@@ -198,7 +198,7 @@ class TenderCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _tenderTitle() {
|
||||
return Text(
|
||||
return SelectableText(
|
||||
tender.title ?? '',
|
||||
maxLines: isDesktop ? 2 : 4,
|
||||
style: TextStyle(
|
||||
|
||||
Reference in New Issue
Block a user