6d674a4a1b
- Updated `pubspec.lock` to reflect new versions for several packages, including `async`, `fake_async`, `leak_tracker`, and `vm_service`. - Modified `TenderApprovalStatus` enum to include a new status `approved`. - Updated routing for `YourTendersRouteData` to accept a status parameter and pass it to the `YourTendersScreen`. - Adjusted API calls and view models to utilize the new status handling, ensuring proper filtering and display of tender data based on approval status. - Enhanced filter dialog to support new status options and improved user feedback in the UI for submitted and approved tenders.
389 lines
11 KiB
Dart
389 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:tm_app/core/theme/colors.dart';
|
|
import 'package:tm_app/core/utils/error_utils.dart';
|
|
import 'package:tm_app/core/utils/result.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/views/your_tenders/strings/your_tenders_strings.dart';
|
|
|
|
import '../core/constants/assets.dart';
|
|
import '../core/constants/tender_approval_status.dart';
|
|
import '../core/constants/tender_feedback.dart';
|
|
import '../data/services/model/request_models/get_tenders_request_model/get_tenders_request_model.dart';
|
|
import '../data/services/model/request_models/tender_feedback_request_model/tender_feedback_request_model.dart';
|
|
|
|
class YourTendersViewModel with ChangeNotifier {
|
|
final YourTendersRepository yourTendersRepository;
|
|
YourTendersViewModel({required this.yourTendersRepository});
|
|
|
|
bool isLoading = false;
|
|
String? errorMessage;
|
|
TenderApprovalsResponse? data;
|
|
LikedTendersResponse? likedTendersData;
|
|
|
|
// Pagination state for feedback lists
|
|
bool isLoadingMore = false;
|
|
bool hasMore = true;
|
|
int _offset = 0;
|
|
final int _limit = 10;
|
|
String? _currentFeedbackType;
|
|
|
|
// Pagination state for tenders
|
|
bool isLoadingMoreTenders = false;
|
|
bool hasMoreTenders = true;
|
|
int _tendersOffset = 0;
|
|
final int _tendersLimit = 10;
|
|
|
|
// Filter States
|
|
DateTimeRange? selectedDateRange;
|
|
String selectedStatus = 'All';
|
|
final _fmt = DateFormat('yyyy/MM/dd');
|
|
|
|
String get startDateFormatted =>
|
|
selectedDateRange?.start != null
|
|
? _fmt.format(selectedDateRange!.start)
|
|
: 'YYYY/MM/DD';
|
|
|
|
String get endDateFormatted =>
|
|
selectedDateRange?.end != null
|
|
? _fmt.format(selectedDateRange!.end)
|
|
: 'YYYY/MM/DD';
|
|
|
|
String get startDateUnix =>
|
|
selectedDateRange?.start != null
|
|
? (selectedDateRange!.start.millisecondsSinceEpoch ~/ 1000).toString()
|
|
: '';
|
|
|
|
String get endDateUnix =>
|
|
selectedDateRange?.end != null
|
|
? (selectedDateRange!.end.millisecondsSinceEpoch ~/ 1000).toString()
|
|
: '';
|
|
|
|
Future<void> getTenders({
|
|
required GetTendersRequestModel requestModel,
|
|
bool reset = false,
|
|
}) async {
|
|
if (!reset && (isLoadingMoreTenders || !hasMoreTenders)) {
|
|
return;
|
|
}
|
|
|
|
if (reset) {
|
|
_tendersOffset = 0;
|
|
hasMoreTenders = true;
|
|
data = null;
|
|
}
|
|
|
|
if (_tendersOffset == 0) {
|
|
isLoading = true;
|
|
} else {
|
|
isLoadingMoreTenders = true;
|
|
}
|
|
notifyListeners();
|
|
|
|
final result = await yourTendersRepository.getTenders(
|
|
requestModel: requestModel,
|
|
);
|
|
|
|
switch (result) {
|
|
case Ok<TenderApprovalsResponse>():
|
|
final incoming = result.value;
|
|
final incomingList = incoming.data?.tenders ?? [];
|
|
|
|
if (reset || data == null) {
|
|
data = incoming;
|
|
} else {
|
|
final existingList = data!.data?.tenders ?? [];
|
|
final combined = List.of(existingList)..addAll(incomingList);
|
|
final newMetadata = incoming.data?.metadata ?? data!.data?.metadata;
|
|
data = data!.copyWith(
|
|
data: data!.data!.copyWith(
|
|
tenders: combined,
|
|
metadata: newMetadata,
|
|
),
|
|
);
|
|
}
|
|
|
|
hasMoreTenders = incomingList.length == _tendersLimit;
|
|
_tendersOffset += _tendersLimit;
|
|
errorMessage = null;
|
|
break;
|
|
case Error<TenderApprovalsResponse>():
|
|
errorMessage = ErrorUtils.getErrorMessage(result.error);
|
|
break;
|
|
}
|
|
|
|
isLoading = false;
|
|
isLoadingMoreTenders = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> getTendersFeedback({
|
|
required String feedbackType,
|
|
bool reset = false,
|
|
}) async {
|
|
if (reset) {
|
|
_offset = (currentPage - 1) * _limit;
|
|
hasMore = true;
|
|
_currentFeedbackType = feedbackType;
|
|
}
|
|
|
|
if (!reset && (isLoadingMore || !hasMore)) {
|
|
return;
|
|
}
|
|
|
|
if (_offset == 0) {
|
|
isLoading = true;
|
|
} else {
|
|
isLoadingMore = true;
|
|
}
|
|
notifyListeners();
|
|
|
|
final requestModel = TenderFeedbackRequestModel(
|
|
offset: _offset,
|
|
limit: _limit,
|
|
feedbackType: _currentFeedbackType ?? feedbackType,
|
|
dateFrom: startDateUnix,
|
|
dateTo: endDateUnix,
|
|
);
|
|
|
|
final result = await yourTendersRepository.getTendersFeedback(
|
|
requestModel: requestModel,
|
|
);
|
|
|
|
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 = ErrorUtils.getErrorMessage(result.error);
|
|
break;
|
|
}
|
|
|
|
isLoading = false;
|
|
isLoadingMore = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> loadMoreFeedback() async {
|
|
if (_currentFeedbackType == null) {
|
|
return;
|
|
}
|
|
await getTendersFeedback(feedbackType: _currentFeedbackType!, reset: false);
|
|
}
|
|
|
|
Future<void> loadMoreTenders() async {
|
|
if (isLoadingMoreTenders || !hasMoreTenders) {
|
|
return;
|
|
}
|
|
|
|
if (selectedStatus == YourTendersStrings.all) {
|
|
final requestModel = GetTendersRequestModel(
|
|
status: '',
|
|
createdFrom: startDateUnix,
|
|
createdTo: endDateUnix,
|
|
limit: _tendersLimit,
|
|
offset: _tendersOffset,
|
|
);
|
|
await getTenders(requestModel: requestModel);
|
|
} else if (selectedStatus == YourTendersStrings.tendersSubmitted) {
|
|
final requestModel = GetTendersRequestModel(
|
|
status: TenderApprovalStatus.submitted.value,
|
|
createdFrom: startDateUnix,
|
|
createdTo: endDateUnix,
|
|
limit: _tendersLimit,
|
|
offset: _tendersOffset,
|
|
);
|
|
await getTenders(requestModel: requestModel);
|
|
}
|
|
}
|
|
|
|
// --- Filter Actions ---
|
|
void setDateRange(DateTimeRange? range) {
|
|
selectedDateRange = range;
|
|
notifyListeners();
|
|
}
|
|
|
|
void setStatus(String status) {
|
|
// Clear data when status changes
|
|
_clearAllData();
|
|
selectedStatus = status;
|
|
currentPage = 1;
|
|
notifyListeners();
|
|
}
|
|
|
|
void clearFilter() {
|
|
// Clear data when clearing filter
|
|
_clearAllData();
|
|
selectedDateRange = null;
|
|
selectedStatus = YourTendersStrings.all;
|
|
notifyListeners();
|
|
}
|
|
|
|
// Clear all data and reset states
|
|
void _clearAllData() {
|
|
// Clear main data
|
|
data = null;
|
|
likedTendersData = null;
|
|
|
|
// Clear loading states
|
|
isLoading = false;
|
|
isLoadingMore = false;
|
|
isLoadingMoreTenders = false;
|
|
|
|
// Clear error messages
|
|
errorMessage = null;
|
|
|
|
// Reset pagination states
|
|
_offset = 0;
|
|
_tendersOffset = 0;
|
|
hasMore = true;
|
|
hasMoreTenders = true;
|
|
_currentFeedbackType = null;
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> callWithFilter() async {
|
|
likedTendersData = null;
|
|
data = null;
|
|
isLoading = true;
|
|
notifyListeners();
|
|
|
|
if (selectedStatus == YourTendersStrings.all) {
|
|
final requestModel = GetTendersRequestModel(
|
|
status: '',
|
|
createdFrom: startDateUnix,
|
|
createdTo: endDateUnix,
|
|
limit: _tendersLimit,
|
|
offset: _tendersOffset,
|
|
);
|
|
await getTenders(requestModel: requestModel, reset: true);
|
|
} else if (selectedStatus == TenderApprovalStatus.submitted.value) {
|
|
final requestModel = GetTendersRequestModel(
|
|
status: TenderApprovalStatus.submitted.value,
|
|
createdFrom: startDateUnix,
|
|
createdTo: endDateUnix,
|
|
limit: _tendersLimit,
|
|
offset: _tendersOffset,
|
|
);
|
|
await getTenders(requestModel: requestModel, reset: true);
|
|
} else if (selectedStatus == TenderFeedback.liked.value) {
|
|
await getTendersFeedback(
|
|
feedbackType: TenderFeedback.liked.value,
|
|
reset: true,
|
|
);
|
|
} else if (selectedStatus == TenderFeedback.disliked.value) {
|
|
await getTendersFeedback(
|
|
feedbackType: TenderFeedback.disliked.value,
|
|
reset: true,
|
|
);
|
|
} else if (selectedStatus == TenderApprovalStatus.approved.value) {
|
|
await getTendersFeedback(
|
|
feedbackType: TenderApprovalStatus.approved.value,
|
|
reset: true,
|
|
);
|
|
}
|
|
}
|
|
|
|
int currentPage = 1;
|
|
|
|
int get totalPages {
|
|
if (likedTendersData?.data?.meta?.pages != null) {
|
|
return likedTendersData!.data!.meta!.pages!;
|
|
}
|
|
if (data?.data?.metadata?.pages != null) {
|
|
return data!.data!.metadata!.pages!;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
Future<void> jumpToPage(int page) async {
|
|
currentPage = page;
|
|
_offset = (page - 1) * _limit;
|
|
_tendersOffset = (page - 1) * _tendersLimit;
|
|
|
|
likedTendersData = null;
|
|
data = null;
|
|
hasMore = true;
|
|
hasMoreTenders = true;
|
|
|
|
await callWithFilter();
|
|
}
|
|
|
|
Color getBackGroundColorByStatus(String status) {
|
|
if (status == TenderFeedback.liked.value ||
|
|
status == TenderApprovalStatus.submitted.value) {
|
|
return AppColors.green0;
|
|
} else if (status == TenderFeedback.disliked.value ||
|
|
status == TenderApprovalStatus.rejected.value) {
|
|
return AppColors.red0;
|
|
}
|
|
return AppColors.grey0;
|
|
}
|
|
|
|
Color getBorderColorByStatus(String status) {
|
|
if (status == TenderFeedback.liked.value ||
|
|
status == TenderApprovalStatus.submitted.value) {
|
|
return AppColors.green10;
|
|
} else if (status == TenderFeedback.disliked.value ||
|
|
status == TenderApprovalStatus.rejected.value) {
|
|
return AppColors.red10;
|
|
}
|
|
return AppColors.grey30;
|
|
}
|
|
|
|
Color getStatusCardColorByStatus(String status) {
|
|
if (status == TenderFeedback.liked.value ||
|
|
status == TenderApprovalStatus.submitted.value) {
|
|
return AppColors.green20;
|
|
} else if (status == TenderFeedback.disliked.value ||
|
|
status == TenderApprovalStatus.rejected.value) {
|
|
return AppColors.red10;
|
|
}
|
|
return AppColors.grey0;
|
|
}
|
|
|
|
Color getStatusTextColorByStatus(String status) {
|
|
if (status == TenderFeedback.liked.value ||
|
|
status == TenderApprovalStatus.submitted.value) {
|
|
return AppColors.green30;
|
|
} else if (status == TenderFeedback.disliked.value ||
|
|
status == TenderApprovalStatus.rejected.value) {
|
|
return AppColors.red20;
|
|
}
|
|
return AppColors.grey60;
|
|
}
|
|
|
|
String? getStatusIconByStatus(String status) {
|
|
if (status == TenderFeedback.liked.value ||
|
|
status == TenderApprovalStatus.submitted.value) {
|
|
return AssetsManager.tickCircle;
|
|
} else if (status == TenderFeedback.disliked.value ||
|
|
status == TenderApprovalStatus.rejected.value) {
|
|
return AssetsManager.closeCircle;
|
|
}
|
|
return null;
|
|
}
|
|
}
|