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:
@@ -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 Norrbotten’s 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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user