369 lines
11 KiB
Dart
369 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 '../data/services/model/feedback_response/feedback_response.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;
|
|
LikedTendersViewModel({
|
|
required LikedTendersRepository likedTendersRepository,
|
|
required TendersRepository tendersRepository,
|
|
}) : _likedTendersRepository = likedTendersRepository,
|
|
_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;
|
|
|
|
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 result = await _likedTendersRepository.getLikedTenders(
|
|
offset: _offset,
|
|
limit: _limit,
|
|
);
|
|
|
|
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;
|
|
}
|
|
|
|
_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;
|
|
}
|
|
|
|
_isRejectApprovalLoading = false;
|
|
notifyListeners();
|
|
}
|
|
}
|