TIF_NGANJUK_E41212036/lib/presentation/controllers/transaction_porter_controll...

543 lines
18 KiB
Dart

import 'dart:async';
import 'dart:developer';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:e_porter/_core/service/preferences_service.dart';
import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart';
import 'package:e_porter/domain/models/porter_queue_model.dart';
import 'package:e_porter/presentation/screens/boarding_pass/provider/porter_service_provider.dart';
import 'package:get/get.dart';
import '../../domain/models/transaction_porter_model.dart';
import '../../domain/usecases/transaction_porter_usecase.dart';
class TransactionPorterController extends GetxController {
final TransactionPorterUsecase _useCase;
TransactionPorterController(this._useCase);
final RxList<PorterTransactionModel> transactions = <PorterTransactionModel>[].obs;
final RxList<PorterTransactionModel> rejectedTransactions = <PorterTransactionModel>[].obs;
final Rx<PorterTransactionModel?> currentTransaction = Rx<PorterTransactionModel?>(null);
final Map<String, StreamSubscription<PorterTransactionModel?>> _transactionWatchers = {};
final RxString currentPorterId = ''.obs;
final RxBool isLoading = false.obs;
final RxString error = ''.obs;
final RxBool isRejectionDialogVisible = false.obs;
final RxString rejectionReason = ''.obs;
final RxString statusFilter = 'pending'.obs;
final RxBool isRejecting = false.obs;
final RxBool needsRefresh = false.obs;
StreamSubscription<List<PorterTransactionModel>>? _subscription;
StreamSubscription<PorterQueueModel?>? _porterSubscription;
StreamSubscription<List<PorterTransactionModel>>? _rejectedSubscription;
@override
void onInit() {
super.onInit();
_loadPorterData();
_watchRejectedTransactions();
}
Future<void> _loadPorterData() async {
try {
final userData = await PreferencesService.getUserData();
if (userData == null || userData.uid.isEmpty) {
error.value = 'Data user tidak tersedia';
return;
}
String userId = userData.uid;
currentPorterId.value = userId; // Langsung gunakan userId
log('Menggunakan userId dari preferences: $userId');
loadTransactionsFromUserId(userId); // Langsung gunakan userId
} catch (e) {
error.value = 'Error inisialisasi: $e';
}
}
void loadTransactionsFromUserId(String userId) {
if (userId.isEmpty) {
error.value = 'User ID tidak boleh kosong';
return;
}
isLoading.value = true;
error.value = '';
_subscription?.cancel();
_subscription = _useCase.watchPorterTransactionsByUserId(userId).listen(
(transactionList) {
log('[TransactionPorterController] Menerima ${transactionList.length} transaksi dari userId: $userId');
if (transactionList.isEmpty) {
log('[TransactionPorterController] Tidak ada transaksi ditemukan untuk userId: $userId');
if (transactions.isNotEmpty) {
transactions.clear();
}
} else {
transactions.assignAll(transactionList);
// Mulai memantau setiap transaksi individual untuk real-time updates
for (var transaction in transactionList) {
watchTransaction(transaction.id);
}
}
isLoading.value = false;
},
onError: (e) {
log('[TransactionPorterController] Error streaming transaksi: $e');
error.value = 'Gagal memuat transaksi: $e';
isLoading.value = false;
},
);
}
void watchTransaction(String transactionId) {
_transactionWatchers[transactionId]?.cancel();
_transactionWatchers[transactionId] = _useCase.watchTransactionById(transactionId).listen(
(updatedTransaction) {
if (updatedTransaction != null) {
if (currentTransaction.value?.id == transactionId) {
currentTransaction.value = updatedTransaction;
}
final index = transactions.indexWhere((tx) => tx.id == transactionId);
if (index >= 0) {
transactions[index] = updatedTransaction;
log('Transaksi diperbarui secara real-time: $transactionId, status: ${updatedTransaction.status}');
}
}
},
onError: (error) {
log('Error memantau transaksi: $error');
},
);
}
void loadTransactionsFromPorterId(String porterId) {
if (porterId.isEmpty) {
error.value = 'Porter ID tidak boleh kosong';
return;
}
isLoading.value = true;
error.value = '';
_subscription?.cancel();
_subscription = _useCase.watchPorterTransactions(porterId).listen((transactionList) {
log('Menerima ${transactionList.length} transaksi');
transactions.assignAll(transactionList);
for (var transaction in transactionList) {
watchTransaction(transaction.id);
}
isLoading.value = false;
}, onError: (e) {
log('Error streaming transaksi: $e');
error.value = 'Gagal memuat transaksi: $e';
isLoading.value = false;
});
}
Future<PorterTransactionModel?> getTransactionById(String transactionId) async {
try {
log('Getting transaction by ID: $transactionId');
isLoading.value = true;
error.value = '';
currentTransaction.value = null;
final transaction = await _useCase.getTransactionById(transactionId);
if (transaction != null) {
log('Transaction found and set to current: ${transaction.id}');
currentTransaction.value = transaction;
watchTransaction(transactionId);
} else {
log('Transaction not found with ID: $transactionId');
error.value = 'Transaksi tidak ditemukan';
}
return transaction;
} catch (e) {
log('Error getting transaction by ID: $e');
error.value = 'Gagal mendapatkan detail transaksi: $e';
return null;
} finally {
isLoading.value = false;
}
}
void _watchRejectedTransactions() async {
try {
final userData = await PreferencesService.getUserData();
if (userData == null || userData.uid.isEmpty) {
log('[Controller] User data tidak ditemukan untuk watching rejected transactions');
return;
}
String porterUserId = userData.uid;
log('[Controller] Watching rejected transactions for porterUserId: $porterUserId');
_rejectedSubscription?.cancel();
_rejectedSubscription = _useCase.watchRejectedTransactionsByPorter(porterUserId).listen(
(transactions) {
log('[Controller] Received ${transactions.length} rejected transactions');
final uniqueTransactions = <String, PorterTransactionModel>{};
for (var tx in transactions) {
uniqueTransactions[tx.id] = tx;
}
rejectedTransactions.assignAll(uniqueTransactions.values.toList());
},
onError: (error) {
log('[Controller] Error watching rejected transactions: $error');
},
);
} catch (e) {
log('[Controller] Error setting up rejected transactions watcher: $e');
}
}
Future<PorterTransactionModel?> getRejectedTransactionById(String transactionId) async {
try {
log('[TransactionPorterController] Mencari transaksi ditolak dengan ID: $transactionId');
PorterTransactionModel? rejectedTransaction;
try {
rejectedTransaction = rejectedTransactions.firstWhere(
(tx) => tx.id == transactionId,
);
} catch (e) {
rejectedTransaction = null;
}
if (rejectedTransaction != null) {
// log('[TransactionPorterController] Transaksi ditolak ditemukan di cache');
currentTransaction.value = rejectedTransaction;
return rejectedTransaction;
}
final userData = await PreferencesService.getUserData();
if (userData != null && userData.uid.isNotEmpty) {
String porterUserId = userData.uid;
final completer = Completer<PorterTransactionModel?>();
final subscription = _useCase.watchRejectedTransactionsByPorter(porterUserId).listen(
(transactions) {
PorterTransactionModel? foundTransaction;
try {
foundTransaction = transactions.firstWhere(
(tx) => tx.id == transactionId,
);
} catch (e) {
foundTransaction = null;
}
if (foundTransaction != null) {
completer.complete(foundTransaction);
} else {
completer.complete(null);
}
},
onError: (error) {
log('[TransactionPorterController] Error mencari transaksi ditolak: $error');
completer.complete(null);
},
);
final transaction = await completer.future.timeout(
Duration(seconds: 3),
onTimeout: () => null,
);
subscription.cancel();
if (transaction != null) {
currentTransaction.value = transaction;
// log('[TransactionPorterController] Transaksi ditolak ditemukan: ${transaction.id}');
} else {
// log('[TransactionPorterController] Transaksi ditolak tidak ditemukan: $transactionId');
}
return transaction;
}
return null;
} catch (e) {
log('[TransactionPorterController] Error mendapatkan transaksi ditolak: $e');
return null;
}
}
Future<void> updateTransactionStatus({
required String transactionId,
required String status,
}) async {
try {
isLoading.value = true;
error.value = '';
log('Memperbarui status transaksi: $transactionId menjadi $status');
await _useCase.updateTransactionStatus(
transactionId: transactionId,
status: status,
);
final updatedTransaction = await getTransactionById(transactionId);
if (updatedTransaction != null) {
final index = transactions.indexWhere((tx) => tx.id == transactionId);
if (index >= 0) {
transactions[index] = updatedTransaction;
log('Transaksi di daftar utama diperbarui: $transactionId dengan status: $status');
} else {
log('Transaksi tidak ditemukan di daftar, menyegarkan seluruh daftar');
refreshTransactions();
}
}
SnackbarHelper.showSuccess('Berhasil', 'Status transaksi berhasil diperbarui');
} catch (e) {
log('Error memperbarui status: $e');
error.value = 'Gagal memperbarui status transaksi: $e';
SnackbarHelper.showError('Terjadi Kesalahan', 'Status transaksi gagal diperbarui');
} finally {
isLoading.value = false;
}
}
Future<void> rejectTransaction({
required String transactionId,
required String reason,
}) async {
try {
isLoading.value = true;
isRejecting.value = true;
error.value = '';
log('Menolak transaksi: $transactionId dengan alasan: $reason');
await _useCase.rejectTransaction(
transactionId: transactionId,
reason: reason.isEmpty ? 'Tidak ada alasan' : reason,
);
needsRefresh.value = true;
final updatedTransaction = await getTransactionById(transactionId);
if (updatedTransaction != null) {
final index = transactions.indexWhere((tx) => tx.id == transactionId);
if (index >= 0) {
transactions[index] = updatedTransaction;
log('Transaksi di daftar utama diperbarui menjadi ditolak: $transactionId');
} else {
refreshTransactions();
}
}
await Future.delayed(Duration(milliseconds: 500));
try {
log('[TransactionPorterController] Memaksa pengecekan reassignment...');
await PorterServiceProvider.forceReassignmentCheck();
} catch (reassignError) {
log('[TransactionPorterController] Error saat memaksa reassignment: $reassignError');
}
SnackbarHelper.showSuccess('Berhasil', 'Transaksi berhasil ditolak');
} catch (e) {
log('Error menolak transaksi: $e');
error.value = 'Gagal menolak transaksi: $e';
SnackbarHelper.showError('Gagal', 'Transaksi gagal ditolak: ${e.toString()}');
} finally {
isLoading.value = false;
isRejecting.value = false;
if (needsRefresh.value) {
await refreshTransactions();
needsRefresh.value = false;
}
}
}
Future<void> completePorterTransaction({
required String transactionId,
}) async {
try {
isLoading.value = true;
error.value = '';
final transaction = currentTransaction.value;
if (transaction == null) {
throw Exception('Tidak dapat menemukan data transaksi');
}
final porterOnlineId = transaction.porterOnlineId;
if (porterOnlineId.isEmpty) {
throw Exception('ID Porter Online tidak ditemukan');
}
log('Menyelesaikan transaksi: $transactionId dengan porter: $porterOnlineId');
await _useCase.completePorterTransaction(
transactionId: transactionId,
porterOnlineId: porterOnlineId,
);
final updatedTransaction = await getTransactionById(transactionId);
if (updatedTransaction != null) {
final index = transactions.indexWhere((tx) => tx.id == transactionId);
if (index >= 0) {
transactions[index] = updatedTransaction;
log('Transaksi di daftar utama diperbarui menjadi selesai: $transactionId');
} else {
refreshTransactions();
}
}
SnackbarHelper.showSuccess('Transaksi Selesai', 'Transaksi porter berhasil diselesaikan');
} catch (e) {
log('Error menyelesaikan transaksi: $e');
error.value = 'Gagal menyelesaikan transaksi: $e';
SnackbarHelper.showError('Terjadi Kesalahan', 'Gagal menyelesaikan transaksi');
} finally {
isLoading.value = false;
}
}
Future<void> refreshTransactions() async {
final id = currentPorterId.value;
if (id.isEmpty) {
try {
final userData = await PreferencesService.getUserData();
if (userData != null && userData.uid.isNotEmpty) {
currentPorterId.value = userData.uid;
log('Retrieved porter ID from preferences during refresh: ${userData.uid}');
} else {
error.value = 'ID porter tidak tersedia';
return;
}
} catch (e) {
log('Error getting user data for refresh: $e');
error.value = 'Gagal memperoleh ID porter: $e';
return;
}
}
log('Refreshing transactions for porter: ${currentPorterId.value}');
isLoading.value = true;
loadTransactionsFromPorterId(currentPorterId.value);
}
Future<void> refreshRejectedTransactions() async {
try {
final userData = await PreferencesService.getUserData();
if (userData == null || userData.uid.isEmpty) {
log('[TransactionPorterController] User data tidak ditemukan untuk refresh rejected transactions');
return;
}
String porterUserId = userData.uid;
log('[TransactionPorterController] Refreshing rejected transactions untuk porterUserId: $porterUserId');
_rejectedSubscription?.cancel();
_rejectedSubscription = _useCase.watchRejectedTransactionsByPorter(porterUserId).listen(
(transactions) {
log('[TransactionPorterController] Refreshed: ${transactions.length} rejected transactions');
rejectedTransactions.assignAll(transactions);
},
onError: (error) {
log('[Controller] Error watching rejected transactions: $error');
if (error is FirebaseException) {
this.error.value = 'Error loading rejected transactions: ${error.message}';
} else {
this.error.value = 'Error loading rejected transactions: $error';
}
},
);
} catch (e) {
log('[TransactionPorterController] Error refresh rejected transactions: $e');
}
}
Future<void> forceReassignTransaction(String transactionId) async {
try {
isLoading.value = true;
error.value = '';
log('[TransactionPorterController] Mencoba mengalihkan transaksi: $transactionId');
final result = await _useCase.reassignRejectedTransaction(
transactionId: transactionId,
);
if (result != null) {
await Future.delayed(Duration(milliseconds: 500));
final updatedTransaction = await getTransactionById(transactionId);
if (updatedTransaction != null) {
final index = transactions.indexWhere((tx) => tx.id == transactionId);
if (index >= 0) {
transactions[index] = updatedTransaction;
}
}
await refreshTransactions();
SnackbarHelper.showSuccess('Berhasil', 'Transaksi berhasil dialihkan ke porter lain');
} else {
SnackbarHelper.showInfo('Info', 'Tidak ada porter tersedia saat ini, akan dicoba lagi nanti');
}
} catch (e) {
error.value = 'Gagal mengalihkan transaksi: $e';
SnackbarHelper.showError('Gagal', 'Transaksi gagal dialihkan: ${e.toString()}');
} finally {
isLoading.value = false;
}
}
List<PorterTransactionModel> getFilteredTransactions() {
return transactions.where((tx) {
if (statusFilter.value == 'pending') {
return tx.status == 'pending' && tx.porterOnlineId == currentPorterId.value;
}
if (statusFilter.value == 'rejected') {
return tx.status == 'rejected' && tx.porterOnlineId == currentPorterId.value;
}
return tx.normalizedStatus == statusFilter.value && tx.porterOnlineId == currentPorterId.value;
}).toList();
}
void updateStatusFilter(String status) {
statusFilter.value = status;
}
void showRejectDialog() {
rejectionReason.value = '';
isRejectionDialogVisible.value = true;
}
void hideRejectDialog() {
isRejectionDialogVisible.value = false;
}
@override
void onClose() {
_porterSubscription?.cancel();
_subscription?.cancel();
_rejectedSubscription?.cancel();
for (var subscription in _transactionWatchers.values) {
subscription.cancel();
}
_transactionWatchers.clear();
super.onClose();
}
}