Merge pull request 'filters in your tenders fixed' (#93) from your_tenders_filters into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/93
This commit is contained in:
a.ghabeli
2025-09-03 13:26:06 +03:30
12 changed files with 363 additions and 252 deletions
@@ -1,4 +1,5 @@
import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart'; import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/data/services/your_tenders_service.dart'; import 'package:tm_app/data/services/your_tenders_service.dart';
@@ -7,8 +8,18 @@ class YourTendersRepository {
YourTendersRepository({required YourTendersService yourTendersService}) YourTendersRepository({required YourTendersService yourTendersService})
: _yourTendersService = yourTendersService; : _yourTendersService = yourTendersService;
Future<Result<TenderApprovalsResponse>> getApprovedTenders() { Future<Result<TenderApprovalsResponse>> getApprovedTenders({
return _yourTendersService.getApprovedTenders(); required int limit,
required int offset,
required String createdFrom,
required String createdTo,
}) {
return _yourTendersService.getApprovedTenders(
limit: limit,
offset: offset,
createdFrom: createdFrom,
createdTo: createdTo,
);
} }
Future<Result<TenderApprovalsResponse>> getTenders({ Future<Result<TenderApprovalsResponse>> getTenders({
@@ -24,4 +35,20 @@ class YourTendersRepository {
createdTo: createdTo, createdTo: createdTo,
); );
} }
Future<Result<LikedTendersResponse>> getTendersFeedback({
required int limit,
required int offset,
required String feedbackType,
required String dateFrom,
required String dateTo,
}) {
return _yourTendersService.getTendersFeedback(
limit: limit,
offset: offset,
feedbackType: feedbackType,
dateFrom: dateFrom,
dateTo: dateTo,
);
}
} }
@@ -2,6 +2,19 @@ class YourTendersApi {
static const String tenderApprovalsStatus = static const String tenderApprovalsStatus =
'/api/v1/tender-approvals?status=submitted'; '/api/v1/tender-approvals?status=submitted';
static String getTenderApprovalsStatus({
required int limit,
required int offset,
required String createdFrom,
required String createdTo,
}) {
if (createdFrom.isNotEmpty && createdTo.isNotEmpty) {
return '$tenderApprovalsStatus&created_from=$createdFrom&created_to=$createdTo';
}
return tenderApprovalsStatus;
}
// return '$tenderApprovalsStatus?limit=$limit&offset=$offset';
static const String tenderApproval = '/api/v1/tender-approvals'; static const String tenderApproval = '/api/v1/tender-approvals';
static String getTenders({ static String getTenders({
@@ -15,4 +28,19 @@ class YourTendersApi {
} }
return '$tenderApproval?limit=$limit&offset=$offset'; return '$tenderApproval?limit=$limit&offset=$offset';
} }
static const String tendersFeedback = '/api/v1/feedback';
static String getTendersFeedback({
required int limit,
required int offset,
required String feedbackType,
required String dateFrom,
required String dateTo,
}) {
if (dateFrom.isNotEmpty && dateTo.isNotEmpty) {
return '$tendersFeedback?feedback_type=$feedbackType&limit=$limit&offset=$offset&date_from=$dateFrom&date_to=$dateTo';
}
return '$tendersFeedback?feedback_type=$feedbackType&limit=$limit&offset=$offset';
}
} }
@@ -1,51 +0,0 @@
const String homeMockData = '''
{
"data": {
"partnership": "0.75",
"self_apply": "12",
"contracting": "36",
"tender_submitted": "12",
"approved_tenders": "03",
"tender_value": "\$250,000",
"thunder_status": "23",
"your_tenders": [
{
"created_time": "2025-01-01",
"tender_id": "1234567890",
"status": "Completed",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.Lorem ipsum dolor sit amet consectetur",
"location": "Sweden",
"type": "Self Apply"
},
{
"created_time": "2025-01-01",
"tender_id": "1234567890",
"status": "Completed",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.Lorem ipsum dolor sit amet consectetur",
"location": "Sweden",
"type": "Partner Ship"
},
{
"created_time": "2025-01-01",
"tender_id": "1234567890",
"status": "Completed",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.Lorem ipsum dolor sit amet consectetur",
"location": "Sweden",
"type": "Self Apply"
},
{
"created_time": "2025-01-01",
"tender_id": "1234567890",
"status": "Completed",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.Lorem ipsum dolor sit amet consectetur",
"location": "Sweden",
"type": "Partner Ship"
}
]
}
}
''';
@@ -1,62 +0,0 @@
const String mockYourTendersData = '''
{
"approvedTenders": [
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. Suscipit ...",
"location": "Sweden",
"countryFlag": "assets/icons/SE.png",
"status": "Completed",
"projectStatus": "Self Apply"
},
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor.",
"location": "Thailand",
"countryFlag": "assets/icons/SE.png",
"status": "Completed",
"projectStatus": "Partner Ship"
},
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis.",
"location": "Italy",
"countryFlag": "assets/icons/SE.png",
"status": "Completed",
"projectStatus": "Self Apply"
}
],
"tendersSubmitted": [
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis.",
"location": "Sweden",
"countryFlag": "assets/icons/SE.png",
"status": "Won",
"projectStatus": "Self Apply"
},
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor.",
"location": "Thailand",
"countryFlag": "assets/icons/SE.png",
"status": "Reject",
"projectStatus": "Partner Ship"
},
{
"date": "2025-05-21",
"title": "Lorem ipsum dolor sit amet consectetur.",
"description": "Lorem ipsum dolor sit amet consectetur. Volutpat facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis.",
"location": "Italy",
"countryFlag": "assets/icons/SE.png",
"status": "Won",
"projectStatus": "Self Apply"
}
]
}
''';
@@ -1,24 +0,0 @@
const tenderDetailMockData = '''
{
"data": {
"date": "2025-05-21",
"status": "Open",
"title": "Operation, support and further development of open e-platform Operation, support and further development of open e-platform",
"id": "JNDFKMDV-100JF",
"approvalDate": "2025-05-21",
"submissionDate": "2025-05-21",
"client": "Procurement Notice Procurement NoticeProcurement Notice",
"deliveryLocation": "Norrbotten County",
"referenceNumber": "KLF 2025/120",
"country": "UK",
"flagAsset": "assets/icons/SE.png",
"locationTitle": "Lorem ipsum is amet",
"locationDescription": "Luleå Municipality is procuring on behalf of Norrbottens e-board (all municipalities in Norrbotten). The procurement includes operation, support, and further development of the existing open e-platform. No additional information, amendments, or answers to questions will be provided after June 16.",
"documentName": "PDF, Document",
"documentUrl": "https://www.tenders.com",
"profileMatch": 0.75,
"incompleteResume": true,
"incompleteResumeReason": "No experience in e-platform development"
}
}
''';
@@ -1,76 +0,0 @@
final String tendersMockData = '''
{
"tenders": [
{
"tender_id": "KLF 2025/120",
"title": "Tender 1",
"description": "Description 1",
"created_time": "2021-01-01",
"deadline": "2025-06-15",
"location": "Sweden",
"type": "Self Apply",
"profile_match": 75,
"liked": false,
"disliked": false,
"rejected": false,
"submitted": false
},
{
"tender_id": "KLF 2025/121",
"title": "Tender 2",
"description": "Description 2",
"created_time": "2021-02-01",
"deadline": "2025-07-20",
"location": "Norway",
"type": "Invitation",
"profile_match": 85,
"liked": false,
"disliked": false,
"rejected": false,
"submitted": false
},
{
"tender_id": "KLF 2025/122",
"title": "Tender 3",
"description": "Description 3",
"created_time": "2021-03-01",
"deadline": "2025-08-10",
"location": "Finland",
"type": "Self Apply",
"profile_match": 65,
"liked": false,
"disliked": false,
"rejected": false,
"submitted": false
},
{
"tender_id": "KLF 2025/123",
"title": "Tender 4",
"description": "Description 4",
"created_time": "2021-04-01",
"deadline": "2025-09-05",
"location": "Denmark",
"type": "Invitation",
"profile_match": 90,
"liked": false,
"disliked": false,
"rejected": false,
"submitted": false
},
{
"tender_id": "KLF 2025/124",
"title": "Tender 5",
"description": "Description 5",
"created_time": "2021-05-01",
"deadline": "2025-10-12",
"location": "Iceland",
"type": "Self Apply",
"profile_match": 70,
"liked": false,
"disliked": false,
"rejected": false,
"submitted": false
}
]
}
''';
+34 -2
View File
@@ -1,5 +1,6 @@
import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/api/your_tenders_api.dart'; import 'package:tm_app/data/services/api/your_tenders_api.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart'; import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import '../../../core/network/network_manager.dart'; import '../../../core/network/network_manager.dart';
@@ -10,9 +11,19 @@ class YourTendersService {
final NetworkManager _networkManager; final NetworkManager _networkManager;
Future<Result<TenderApprovalsResponse>> getApprovedTenders() async { Future<Result<TenderApprovalsResponse>> getApprovedTenders({
required int limit,
required int offset,
required String createdFrom,
required String createdTo,
}) async {
final result = await _networkManager.makeRequest( final result = await _networkManager.makeRequest(
YourTendersApi.tenderApprovalsStatus, YourTendersApi.getTenderApprovalsStatus(
limit: limit,
offset: offset,
createdFrom: createdFrom,
createdTo: createdTo,
),
(json) => TenderApprovalsResponse.fromJson(json), (json) => TenderApprovalsResponse.fromJson(json),
method: 'GET', method: 'GET',
); );
@@ -38,4 +49,25 @@ class YourTendersService {
); );
return result; return result;
} }
Future<Result<LikedTendersResponse>> getTendersFeedback({
required int limit,
required int offset,
required String feedbackType,
required String dateFrom,
required String dateTo,
}) async {
final result = await _networkManager.makeRequest(
YourTendersApi.getTendersFeedback(
limit: limit,
offset: offset,
feedbackType: feedbackType,
dateFrom: dateFrom,
dateTo: dateTo,
),
(json) => LikedTendersResponse.fromJson(json),
method: 'GET',
);
return result;
}
} }
+104 -15
View File
@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/repositories/your_tenders_repository.dart'; import 'package:tm_app/data/repositories/your_tenders_repository.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart'; import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart';
class YourTendersViewModel with ChangeNotifier { class YourTendersViewModel with ChangeNotifier {
final YourTendersRepository yourTendersRepository; final YourTendersRepository yourTendersRepository;
@@ -11,7 +13,13 @@ class YourTendersViewModel with ChangeNotifier {
bool isLoading = false; bool isLoading = false;
String? errorMessage; String? errorMessage;
TenderApprovalsResponse? data; TenderApprovalsResponse? data;
LikedTendersResponse? likedTendersData;
// Pagination state for feedback lists
bool isLoadingMore = false;
bool hasMore = true;
int _offset = 0;
final int _limit = 10;
String? _currentFeedbackType;
// Filter States // Filter States
DateTimeRange? selectedDateRange; DateTimeRange? selectedDateRange;
String selectedStatus = 'All'; String selectedStatus = 'All';
@@ -27,21 +35,29 @@ class YourTendersViewModel with ChangeNotifier {
? _fmt.format(selectedDateRange!.end) ? _fmt.format(selectedDateRange!.end)
: 'YYYY/MM/DD'; : 'YYYY/MM/DD';
int get startDateUnix => String get startDateUnix =>
selectedDateRange?.start != null selectedDateRange?.start != null
? selectedDateRange!.start.millisecondsSinceEpoch ~/ 1000 ? (selectedDateRange!.start.millisecondsSinceEpoch ~/ 1000).toString()
: 0; : '';
int get endDateUnix => String get endDateUnix =>
selectedDateRange?.end != null selectedDateRange?.end != null
? selectedDateRange!.end.millisecondsSinceEpoch ~/ 1000 ? (selectedDateRange!.end.millisecondsSinceEpoch ~/ 1000).toString()
: 0; : '';
Future<void> getApprovedTenders() async { Future<void> getApprovedTenders({
required int limit,
required int offset,
}) async {
isLoading = true; isLoading = true;
notifyListeners(); notifyListeners();
final result = await yourTendersRepository.getApprovedTenders(); final result = await yourTendersRepository.getApprovedTenders(
limit: limit,
offset: offset,
createdFrom: startDateUnix,
createdTo: endDateUnix,
);
switch (result) { switch (result) {
case Ok<TenderApprovalsResponse>(): case Ok<TenderApprovalsResponse>():
@@ -86,6 +102,75 @@ class YourTendersViewModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> getTendersFeedback({
required String feedbackType,
bool reset = false,
}) async {
if (reset) {
_offset = 0;
hasMore = true;
_currentFeedbackType = feedbackType;
}
if (!reset && (isLoadingMore || !hasMore)) {
return;
}
if (_offset == 0) {
isLoading = true;
} else {
isLoadingMore = true;
}
notifyListeners();
final result = await yourTendersRepository.getTendersFeedback(
limit: _limit,
offset: _offset,
feedbackType: _currentFeedbackType ?? feedbackType,
dateFrom: startDateUnix,
dateTo: endDateUnix,
);
switch (result) {
case Ok<LikedTendersResponse>():
final incoming = result.value;
final incomingList = incoming.data?.feedback ?? [];
if (reset || likedTendersData == null) {
likedTendersData = incoming;
} else {
final existingList = likedTendersData!.data?.feedback ?? [];
final combined = List.of(existingList)..addAll(incomingList);
final newMeta = incoming.data?.meta ?? likedTendersData!.data?.meta;
likedTendersData = likedTendersData!.copyWith(
data: likedTendersData!.data!.copyWith(
feedback: combined,
meta: newMeta,
),
);
}
hasMore = incomingList.length == _limit;
_offset += _limit;
errorMessage = null;
break;
case Error<LikedTendersResponse>():
errorMessage = result.error.toString();
break;
}
isLoading = false;
isLoadingMore = false;
notifyListeners();
}
Future<void> loadMoreFeedback() async {
if (_currentFeedbackType == null) {
return;
}
await getTendersFeedback(feedbackType: _currentFeedbackType!, reset: false);
}
// --- Filter Actions --- // --- Filter Actions ---
void setDateRange(DateTimeRange? range) { void setDateRange(DateTimeRange? range) {
selectedDateRange = range; selectedDateRange = range;
@@ -99,20 +184,24 @@ class YourTendersViewModel with ChangeNotifier {
void clearFilter() { void clearFilter() {
selectedDateRange = null; selectedDateRange = null;
selectedStatus = 'All'; selectedStatus = YourTendersStrings.all;
notifyListeners(); notifyListeners();
} }
Future<void> callWithFilter() async { Future<void> callWithFilter() async {
if (selectedStatus == 'All') { if (selectedStatus == YourTendersStrings.all) {
await getTenders( await getTenders(
limit: 10, limit: 10,
offset: 0, offset: 0,
createdFrom: startDateUnix.toString(), createdFrom: startDateUnix,
createdTo: endDateUnix.toString(), createdTo: endDateUnix,
); );
} else if (selectedStatus == 'Tenders submitted') { } else if (selectedStatus == YourTendersStrings.tendersSubmitted) {
await getApprovedTenders(); await getApprovedTenders(limit: 10, offset: 0);
} else if (selectedStatus == YourTendersStrings.likeTenders) {
await getTendersFeedback(feedbackType: 'like', reset: true);
} else if (selectedStatus == YourTendersStrings.dislikeTenders) {
await getTendersFeedback(feedbackType: 'dislike', reset: true);
} }
} }
} }
@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart'; import 'package:tm_app/view_models/your_tenders_view_model.dart';
import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart';
import 'package:tm_app/views/your_tenders/widgets/filter_button.dart'; import 'package:tm_app/views/your_tenders/widgets/filter_button.dart';
import 'package:tm_app/views/your_tenders/widgets/liked_disliked_tenders_list.dart';
import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart'; import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
@@ -24,10 +26,6 @@ class _YourTendersDesktopPageState extends State<YourTendersDesktopPage>
void initState() { void initState() {
super.initState(); super.initState();
controller = TabController(length: 2, vsync: this); controller = TabController(length: 2, vsync: this);
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<YourTendersViewModel>().getApprovedTenders();
});
} }
@override @override
@@ -94,9 +92,28 @@ class _YourTendersDesktopPageState extends State<YourTendersDesktopPage>
if (viewModel.errorMessage != null) { if (viewModel.errorMessage != null) {
return Center(child: Text(viewModel.errorMessage!)); return Center(child: Text(viewModel.errorMessage!));
} }
return TendersSubmitted(
approvedTenders: viewModel.data!, if (viewModel.selectedStatus ==
); YourTendersStrings.likeTenders &&
viewModel.likedTendersData != null) {
return LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'liked',
);
} else if (viewModel.selectedStatus ==
YourTendersStrings.dislikeTenders &&
viewModel.likedTendersData != null) {
return LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'disliked',
);
} else if (viewModel.data != null) {
return TendersSubmitted(
approvedTenders: viewModel.data!,
);
} else {
return const Center(child: Text('No data available'));
}
// return TabBarView( // return TabBarView(
// controller: controller, // controller: controller,
// children: [ // children: [
@@ -5,6 +5,7 @@ import 'package:tm_app/view_models/your_tenders_view_model.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart'; import 'package:tm_app/views/shared/tender_app_bar.dart';
import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart'; import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart';
import 'package:tm_app/views/your_tenders/widgets/filter_button.dart'; import 'package:tm_app/views/your_tenders/widgets/filter_button.dart';
import 'package:tm_app/views/your_tenders/widgets/liked_disliked_tenders_list.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
class YourTendersMobilePage extends StatefulWidget { class YourTendersMobilePage extends StatefulWidget {
@@ -55,10 +56,34 @@ class _YourTendersMobilePageState extends State<YourTendersMobilePage> {
return Column( return Column(
children: [ children: [
// Expanded(child: ApprovedTenders(tendersSubmitted: [])), if (viewModel.selectedStatus ==
Expanded( YourTendersStrings.likeTenders &&
child: TendersSubmitted(approvedTenders: viewModel.data!), viewModel.likedTendersData != null)
), Expanded(
child: LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'liked tenders',
),
)
else if (viewModel.selectedStatus ==
YourTendersStrings.dislikeTenders &&
viewModel.likedTendersData != null)
Expanded(
child: LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'disliked',
),
)
else if (viewModel.data != null)
Expanded(
child: TendersSubmitted(
approvedTenders: viewModel.data!,
),
)
else
const Expanded(
child: Center(child: Text('No data available')),
),
], ],
); );
}, },
@@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart'; import 'package:tm_app/view_models/your_tenders_view_model.dart';
import 'package:tm_app/views/your_tenders/widgets/filter_button.dart'; import 'package:tm_app/views/your_tenders/widgets/filter_button.dart';
import 'package:tm_app/views/your_tenders/widgets/liked_disliked_tenders_list.dart';
import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart'; import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
@@ -28,10 +29,6 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
void initState() { void initState() {
super.initState(); super.initState();
controller = TabController(length: 2, vsync: this); controller = TabController(length: 2, vsync: this);
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<YourTendersViewModel>().getApprovedTenders();
});
} }
@override @override
@@ -46,10 +43,7 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
return Scaffold( return Scaffold(
key: key, key: key,
backgroundColor: AppColors.backgroundColor, backgroundColor: AppColors.backgroundColor,
appBar: tabletAppBar( appBar: tabletAppBar(title: YourTendersStrings.yourTenders, key: key),
title: YourTendersStrings.yourTenders,
key: key,
),
drawer: TabletNavigationWidget( drawer: TabletNavigationWidget(
currentIndex: 1, // Home is index 0 currentIndex: 1, // Home is index 0
onTabChanged: (index) { onTabChanged: (index) {
@@ -102,7 +96,26 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
if (viewModel.errorMessage != null) { if (viewModel.errorMessage != null) {
return Center(child: Text(viewModel.errorMessage!)); return Center(child: Text(viewModel.errorMessage!));
} }
return TendersSubmitted(approvedTenders: viewModel.data!);
if (viewModel.selectedStatus ==
YourTendersStrings.likeTenders &&
viewModel.likedTendersData != null) {
return LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'liked',
);
} else if (viewModel.selectedStatus ==
YourTendersStrings.dislikeTenders &&
viewModel.likedTendersData != null) {
return LikedDislikedTendersList(
data: viewModel.likedTendersData!,
status: 'disliked',
);
} else if (viewModel.data != null) {
return TendersSubmitted(approvedTenders: viewModel.data!);
} else {
return const Center(child: Text('No data available'));
}
}, },
), ),
), ),
@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.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/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import 'package:tm_app/views/your_tenders/widgets/tender_card.dart';
class LikedDislikedTendersList extends StatefulWidget {
final LikedTendersResponse data;
final String status;
const LikedDislikedTendersList({
required this.data,
required this.status,
super.key,
});
@override
State<LikedDislikedTendersList> createState() =>
_LikedDislikedTendersListState();
}
class _LikedDislikedTendersListState extends State<LikedDislikedTendersList> {
final ScrollController _controller = ScrollController();
@override
void initState() {
super.initState();
_controller.addListener(_onScroll);
}
void _onScroll() {
final vm = context.read<YourTendersViewModel>();
if (_controller.position.pixels >=
_controller.position.maxScrollExtent - 200) {
vm.loadMoreFeedback();
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final vm = context.watch<YourTendersViewModel>();
final data = widget.data;
if (data.data?.feedback == null || data.data!.feedback!.isEmpty) {
return Center(
child: Text(
'No ${widget.status} found',
style: TextStyle(fontSize: 16.0.sp(), color: AppColors.grey70),
),
);
}
final items = data.data!.feedback!;
final itemCount = items.length + (vm.isLoadingMore ? 1 : 0);
return ListView.builder(
controller: _controller,
itemCount: itemCount,
itemBuilder: (context, index) {
if (index >= items.length) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.0.h()),
child: const Center(
child: CircularProgressIndicator(color: AppColors.secondary50),
),
);
}
final feedback = items[index];
return Padding(
padding: EdgeInsets.only(bottom: 16.0.h()),
child: TenderCard(
date: feedback.tender!.publicationDate?.toString() ?? '',
title: feedback.tender!.title ?? '',
description: feedback.tender!.description ?? '',
location: feedback.tender!.countryCode ?? '',
countryCode: feedback.tender!.countryCode ?? '',
status: feedback.tender!.status ?? '',
projectStatus: feedback.tender!.procurementTypeCode!,
),
);
},
);
}
}