Files
tm_app/lib/view_models/liked_tenders_view_model.dart
T
2025-09-08 10:48:05 +03:30

389 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/repositories/liked_tenders_repository.dart';
import 'package:tm_app/data/repositories/tenders_repository.dart';
import 'package:tm_app/data/services/model/liked_tender_feedback/liked_tender_feedback.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_toggle_response/tender_approvals_toggle_response.dart';
import 'package:tm_app/view_models/home_view_model.dart';
import '../data/services/model/feedback_response/feedback_response.dart';
import '../data/services/model/request_models/tender_feedback_request_model/tender_feedback_request_model.dart';
import '../data/services/model/tender_approvals_by_id_response/tender_approvals_by_id_response.dart';
import '../data/services/model/tender_approvals_data/tender_approvals_data.dart';
class LikedTendersViewModel with ChangeNotifier {
final LikedTendersRepository _likedTendersRepository;
final TendersRepository _tendersRepository;
final HomeViewModel _homeViewModel;
LikedTendersViewModel({
required LikedTendersRepository likedTendersRepository,
required TendersRepository tendersRepository,
required HomeViewModel homeViewModel,
}) : _likedTendersRepository = likedTendersRepository,
_homeViewModel = homeViewModel,
_tendersRepository = tendersRepository;
bool isLoading = false;
bool isLoadingMore = false;
bool hasMore = true;
String? errorMessage;
LikedTendersResponse? data;
TenderApprovalsData? _tenderApprovalData;
TenderApprovalsData? get tenderApprovalData => _tenderApprovalData;
List<TenderApprovalsData> tenderApprovals = [];
int _offset = 0;
final int _limit = 7;
int currentPage = 1;
// Approval status properties
bool approvalStatus = false;
String _status = '';
String? _errorMessage;
bool _isSubmitApprovalLoading = false;
bool _isRejectApprovalLoading = false;
String get status => _status;
String? get approvalErrorMessage => _errorMessage;
bool get isSubmitApprovalLoading => _isSubmitApprovalLoading;
bool get isRejectApprovalLoading => _isRejectApprovalLoading;
void updateHome() {
_homeViewModel.init();
}
void clearApprovalErrorMessage() {
_errorMessage = null;
notifyListeners();
}
Future<void> getLikedTenders({bool reset = false}) async {
if (!reset && (isLoadingMore || !hasMore)) {
return;
}
if (reset) {
_offset = (currentPage - 1) * _limit;
hasMore = true;
data = null;
isLoading = true;
notifyListeners();
} else {
if (_offset == 0) {
isLoading = true;
} else {
isLoadingMore = true;
}
notifyListeners();
}
errorMessage = null;
final requestModel = TenderFeedbackRequestModel(
offset: _offset,
limit: _limit,
feedbackType: 'like',
);
final result = await _likedTendersRepository.getLikedTenders(
requestModel: requestModel,
);
switch (result) {
case Ok<LikedTendersResponse>():
final incoming = result.value;
final incomingList = incoming.data?.feedback ?? [];
for (var element in incomingList) {
await getTenderApproval(element.tenderId!);
}
if (reset) {
data = incoming;
} else {
final existingList = data?.data?.feedback ?? [];
final combined = List<LikedTenderFeedback>.of(existingList)
..addAll(incomingList);
data = data?.copyWith(
data: data?.data?.copyWith(
feedback: combined,
meta: incoming.data?.meta ?? data!.data!.meta,
),
);
}
hasMore = incomingList.length == _limit;
_offset += _limit;
errorMessage = null;
break;
case Error<LikedTendersResponse>():
errorMessage = result.error.toString();
break;
}
isLoading = false;
isLoadingMore = false;
notifyListeners();
}
Future<void> loadMoreIfNeeded() async {
if (isLoadingMore || !hasMore) {
return;
}
await getLikedTenders(reset: false);
}
Future<void> jumpToPage(int page) async {
currentPage = page;
_offset = (page - 1) * _limit;
hasMore = true;
await getLikedTenders(reset: true);
}
void removeTenderById(String id) async {
final current = data;
final list = current?.data?.feedback;
if (current == null || list == null) {
return;
}
// Store original list to restore if error occurs
final originalList = List<LikedTenderFeedback>.of(list);
final updatedList = List<LikedTenderFeedback>.of(list)
..removeWhere((e) => e.tenderId == id);
// Temporarily update UI
data = current.copyWith(
data: current.data?.copyWith(feedback: updatedList),
);
notifyListeners();
final result = await _tendersRepository.toggleFeedback(
tenderId: id,
feedbackType: 'like',
);
switch (result) {
case Ok<FeedbackResponse>():
final updatedMeta = current.data?.meta?.copyWith(
total: updatedList.length,
);
data = current.copyWith(
data: current.data?.copyWith(
feedback: updatedList,
meta: updatedMeta,
),
);
break;
case Error<FeedbackResponse>():
// Restore original list if error occurs
data = current.copyWith(
data: current.data?.copyWith(feedback: originalList),
);
errorMessage = result.error.toString();
break;
}
notifyListeners();
}
Future<void> getTenderApproval(String tenderId) async {
errorMessage = null;
notifyListeners();
final result = await _tendersRepository.getTenderApprovals(
tenderId: tenderId,
);
switch (result) {
case Ok<TenderApprovalsByIdResponse>():
_tenderApprovalData = result.value.data;
tenderApprovals.add(_tenderApprovalData!);
break;
case Error<TenderApprovalsByIdResponse>():
errorMessage = result.error.toString();
break;
}
notifyListeners();
errorMessage = null;
notifyListeners();
}
TenderApprovalsData? getApprovalStatusForTender(String tenderId) {
try {
return tenderApprovals.firstWhere((a) => a.tenderId == tenderId);
} catch (e) {
return null;
}
}
/// Check if tender approval exists for a specific tender
bool hasTenderApprovalForTender(String tenderId) {
return tenderApprovals.any((a) => a.tenderId == tenderId);
}
/// Get approval status for a tender safely
String? getApprovalStatusForTenderId(String tenderId) {
try {
final approval = tenderApprovals.firstWhere(
(a) => a.tenderId == tenderId,
);
return approval.status;
} catch (e) {
return null;
}
}
/// Check if tender is submitted
bool isTenderSubmitted(String tenderId) {
return getApprovalStatusForTenderId(tenderId) == 'submitted';
}
/// Check if tender is rejected
bool isTenderRejected(String tenderId) {
return getApprovalStatusForTenderId(tenderId) == 'rejected';
}
Future<void> submitTenderApproval({
required String tenderId,
required String submissionMode,
}) async {
_isSubmitApprovalLoading = true;
_errorMessage = null;
notifyListeners();
// Store original state for rollback
final originalApprovals = List<TenderApprovalsData>.of(tenderApprovals);
// Check current approval status and update optimistically
final existingApproval = getApprovalStatusForTender(tenderId);
if (existingApproval != null) {
if (existingApproval.status == 'submitted') {
// Remove from list if already submitted
tenderApprovals.removeWhere((a) => a.tenderId == tenderId);
} else if (existingApproval.status == 'rejected') {
// Change rejected to submitted
final updatedApproval = existingApproval.copyWith(status: 'submitted');
final index = tenderApprovals.indexWhere((a) => a.tenderId == tenderId);
if (index != -1) {
tenderApprovals[index] = updatedApproval;
}
}
} else {
// Add new approval with submitted status
final newApproval = TenderApprovalsData(
id: null,
status: 'submitted',
tenderId: tenderId,
companyId: null,
submissionMode: submissionMode,
createdAt: null,
tender: null,
);
tenderApprovals.add(newApproval);
}
notifyListeners();
// Make server request
final result = await _tendersRepository.toggleApprovals(
tenderId: tenderId,
status: 'submitted',
submissionMode: submissionMode,
);
switch (result) {
case Ok<TenderApprovalsToggleResponse>():
approvalStatus = true;
_status = result.value.data?.status ?? '';
break;
case Error<TenderApprovalsToggleResponse>():
// Rollback changes on error
tenderApprovals = originalApprovals;
_errorMessage = result.error.toString();
approvalStatus = false;
break;
}
approvalStatus = false;
_isSubmitApprovalLoading = false;
notifyListeners();
}
Future<void> rejectTenderApproval({
required String tenderId,
required String submissionMode,
}) async {
_isRejectApprovalLoading = true;
_errorMessage = null;
notifyListeners();
// Store original state for rollback
final originalApprovals = List<TenderApprovalsData>.of(tenderApprovals);
// Check current approval status and update optimistically
final existingApproval = getApprovalStatusForTender(tenderId);
if (existingApproval != null) {
if (existingApproval.status == 'rejected') {
// Remove from list if already rejected
tenderApprovals.removeWhere((a) => a.tenderId == tenderId);
} else if (existingApproval.status == 'submitted') {
// Change submitted to rejected
final updatedApproval = existingApproval.copyWith(status: 'rejected');
final index = tenderApprovals.indexWhere((a) => a.tenderId == tenderId);
if (index != -1) {
tenderApprovals[index] = updatedApproval;
}
}
} else {
// Add new approval with rejected status
final newApproval = TenderApprovalsData(
id: null,
status: 'rejected',
tenderId: tenderId,
companyId: null,
submissionMode: submissionMode,
createdAt: null,
tender: null,
);
tenderApprovals.add(newApproval);
}
notifyListeners();
// Make server request
final result = await _tendersRepository.toggleApprovals(
tenderId: tenderId,
status: 'rejected',
submissionMode: submissionMode,
);
switch (result) {
case Ok<TenderApprovalsToggleResponse>():
approvalStatus = true;
_status = result.value.data?.status ?? '';
break;
case Error<TenderApprovalsToggleResponse>():
// Rollback changes on error
tenderApprovals = originalApprovals;
_errorMessage = result.error.toString();
approvalStatus = false;
break;
}
approvalStatus = false;
_isRejectApprovalLoading = false;
notifyListeners();
}
}