Feat: completed features porter queue and history transaction porter
This commit is contained in:
parent
0b381d2571
commit
08331d69ca
|
@ -638,7 +638,7 @@
|
|||
"languageVersion": "3.4"
|
||||
}
|
||||
],
|
||||
"generated": "2025-04-23T15:39:37.293764Z",
|
||||
"generated": "2025-04-27T19:47:24.712851Z",
|
||||
"generator": "pub",
|
||||
"generatorVersion": "3.5.0",
|
||||
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '2.1.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
@ -6,7 +7,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:8.2.0"
|
||||
classpath "com.android.tools.build:gradle:8.4.0"
|
||||
classpath 'com.google.gms:google-services:4.4.2' // firebase
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'dart:math' hide log;
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:e_porter/domain/repositories/porter_queue_repository.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
|
||||
import '../../domain/models/porter_queue_model.dart';
|
||||
|
||||
|
@ -12,27 +14,41 @@ class PorterQueueRepositoryImpl implements PorterQueueRepository {
|
|||
FirebaseFirestore? firestore,
|
||||
}) : _firestore = firestore ?? FirebaseFirestore.instance;
|
||||
|
||||
String _generateId() {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
final rand = Random();
|
||||
return List.generate(7, (index) => chars[rand.nextInt(chars.length)]).join();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> createPorterQueue(String userId) async {
|
||||
Future<String> createPorterQueue(String userId, String locationPorter) async {
|
||||
try {
|
||||
log('[PorterRepository] Membuat antrian porter untuk userId: $userId');
|
||||
|
||||
// Periksa apakah porter dengan userId ini sudah ada
|
||||
final existingPorter = await getPorterByUserId(userId);
|
||||
if (existingPorter != null) {
|
||||
log('[PorterRepository] Porter dengan userId: $userId sudah ada di antrian');
|
||||
|
||||
if (!existingPorter.isAvailable) {
|
||||
await _firestore.collection('porterOnline').doc(existingPorter.id).update({
|
||||
'isAvailable': true,
|
||||
'idUser': null,
|
||||
'idTransaction': null,
|
||||
'onlineAt': FieldValue.serverTimestamp(),
|
||||
});
|
||||
log('[PorterRepository] Status porter diperbarui menjadi available');
|
||||
}
|
||||
|
||||
return existingPorter.id!;
|
||||
}
|
||||
|
||||
// Buat data porter baru
|
||||
final now = DateTime.now();
|
||||
final porterData = PorterQueueModel(
|
||||
userId: userId,
|
||||
isTaken: true,
|
||||
onlineAt: now,
|
||||
isAvailable: true,
|
||||
onlineAt: DateTime.now(),
|
||||
locationPorter: locationPorter,
|
||||
).toJson();
|
||||
|
||||
// Simpan ke Firestore
|
||||
final docRef = await _firestore.collection('porterOnline').add(porterData);
|
||||
|
||||
log('[PorterRepository] Berhasil membuat antrian porter dengan ID: ${docRef.id}');
|
||||
|
@ -63,6 +79,16 @@ class PorterQueueRepositoryImpl implements PorterQueueRepository {
|
|||
try {
|
||||
log('[PorterRepository] Menghapus antrian porter dengan ID: $porterId');
|
||||
|
||||
// Periksa apakah porter sedang memiliki transaksi aktif
|
||||
final porterDoc = await _firestore.collection('porterOnline').doc(porterId).get();
|
||||
if (porterDoc.exists) {
|
||||
final data = porterDoc.data();
|
||||
if (data != null && data['idTransaction'] != null) {
|
||||
log('[PorterRepository] Porter memiliki transaksi aktif, tidak dapat dihapus');
|
||||
throw Exception('Porter sedang menangani transaksi, selesaikan transaksi terlebih dahulu');
|
||||
}
|
||||
}
|
||||
|
||||
await _firestore.collection('porterOnline').doc(porterId).delete();
|
||||
|
||||
log('[PorterRepository] Berhasil menghapus antrian porter dengan ID: $porterId');
|
||||
|
@ -71,4 +97,363 @@ class PorterQueueRepositoryImpl implements PorterQueueRepository {
|
|||
throw Exception('Gagal menghapus antrian porter: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PorterQueueModel?> getNextAvailablePorter() async {
|
||||
try {
|
||||
log('[PorterRepository] Mencari porter yang tersedia...');
|
||||
|
||||
final snapshot = await _firestore
|
||||
.collection('porterOnline')
|
||||
.where('isAvailable', isEqualTo: true)
|
||||
.orderBy('onlineAt', descending: false)
|
||||
.limit(1)
|
||||
.get()
|
||||
.timeout(Duration(seconds: 10), onTimeout: () {
|
||||
throw TimeoutException('Waktu pencarian porter habis');
|
||||
});
|
||||
|
||||
log('[PorterRepository] Query result: ${snapshot.docs.length} porters available');
|
||||
|
||||
if (snapshot.docs.isNotEmpty) {
|
||||
final porter = PorterQueueModel.fromJson(snapshot.docs.first.data(), docId: snapshot.docs.first.id);
|
||||
log('[PorterRepository] Porter tersedia: ${porter.id}');
|
||||
return porter;
|
||||
}
|
||||
log('[PorterRepository] Tidak ada porter yang tersedia');
|
||||
return null;
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error mendapatkan porter tersedia: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> assignPorterToUser(String porterId, String userId, String transactionId) async {
|
||||
try {
|
||||
return await _firestore.runTransaction((transaction) async {
|
||||
final porterDoc = await transaction.get(_firestore.collection('porterOnline').doc(porterId));
|
||||
|
||||
if (!porterDoc.exists) {
|
||||
log('[PorterRepository] Porter tidak ditemukan');
|
||||
return false;
|
||||
}
|
||||
|
||||
final porterData = porterDoc.data();
|
||||
if (porterData == null || porterData['isAvailable'] != true) {
|
||||
log('[PorterRepository] Porter tidak tersedia');
|
||||
return false;
|
||||
}
|
||||
|
||||
transaction.update(_firestore.collection('porterOnline').doc(porterId), {
|
||||
'isAvailable': false,
|
||||
'idUser': userId,
|
||||
'idTransaction': transactionId,
|
||||
});
|
||||
|
||||
log('[PorterRepository] Porter berhasil ditugaskan ke user: $userId');
|
||||
return true;
|
||||
});
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error menugaskan porter: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createPorterTransaction(
|
||||
{required String porterTransactionId,
|
||||
required String porterId,
|
||||
required String passengerId,
|
||||
required String ticketId,
|
||||
required String transactionId,
|
||||
required String locationPassenger,
|
||||
required String locationPorter}) async {
|
||||
try {
|
||||
log('[PorterRepository] Membuat transaksi porter: $porterTransactionId');
|
||||
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
final kodePorter = _generateId();
|
||||
|
||||
// Dapatkan userId porter dari dokumen porterOnline
|
||||
final porterDoc = await _firestore.collection('porterOnline').doc(porterId).get();
|
||||
String porterUserId = '';
|
||||
|
||||
if (porterDoc.exists && porterDoc.data() != null) {
|
||||
porterUserId = porterDoc.data()!['userId'] ?? '';
|
||||
log('[PorterRepository] Mendapatkan userId porter: $porterUserId');
|
||||
}
|
||||
|
||||
if (porterUserId.isEmpty) {
|
||||
log('[PorterRepository] PERINGATAN: userId porter tidak ditemukan, menggunakan porterId: $porterId');
|
||||
porterUserId = porterId; // Fallback ke porterId jika userId tidak ditemukan
|
||||
}
|
||||
|
||||
final data = {
|
||||
'kodePorter': kodePorter,
|
||||
'porterOnlineId': porterId,
|
||||
'porterUserId': porterUserId, // Tambahkan porterUserId dalam data transaksi
|
||||
'idPassenger': passengerId,
|
||||
'ticketId': ticketId,
|
||||
'transactionId': transactionId,
|
||||
'status': 'pending',
|
||||
'locationPassenger': locationPassenger,
|
||||
'locationPorter': locationPorter,
|
||||
'createdAt': now,
|
||||
};
|
||||
|
||||
// 1. Update Firestore
|
||||
final batch = _firestore.batch();
|
||||
final porterDocId = _firestore.collection('porterOnline').doc(porterId);
|
||||
final txDoc = _firestore.collection('porterTransactions').doc(porterTransactionId);
|
||||
|
||||
batch.update(porterDocId, {
|
||||
'isAvailable': false,
|
||||
'idUser': passengerId,
|
||||
'idTransaction': porterTransactionId,
|
||||
'locationPorter': locationPorter,
|
||||
});
|
||||
batch.set(txDoc, data);
|
||||
|
||||
await batch.commit();
|
||||
log('[PorterRepository] Dokumen Firestore berhasil diperbarui');
|
||||
|
||||
// 2. Simpan transaksi di Realtime Database
|
||||
final rtdb = FirebaseDatabase.instance.ref();
|
||||
|
||||
// 2.1 Data transaksi utama
|
||||
await rtdb.child('porterTransactions/$porterTransactionId').set(data);
|
||||
log('[PorterRepository] Data transaksi utama disimpan di RTDB');
|
||||
|
||||
// 2.2 Simpan referensi di porterHistory berdasarkan porterId (untuk kompatibilitas mundur)
|
||||
await rtdb.child('porterHistory/$porterId/$porterTransactionId').set({
|
||||
'timestamp': now,
|
||||
'transactionId': porterTransactionId,
|
||||
});
|
||||
log('[PorterRepository] Referensi disimpan di porterHistory/$porterId');
|
||||
|
||||
// 2.3 Simpan referensi di porterHistory berdasarkan userId porter (baru)
|
||||
if (porterUserId != porterId && porterUserId.isNotEmpty) {
|
||||
await rtdb.child('porterHistory/$porterUserId/$porterTransactionId').set({
|
||||
'timestamp': now,
|
||||
'transactionId': porterTransactionId,
|
||||
});
|
||||
log('[PorterRepository] Referensi disimpan di porterHistory/$porterUserId');
|
||||
}
|
||||
|
||||
// 2.4 Simpan referensi di passengerHistory
|
||||
await rtdb.child('passengerHistory/$passengerId/$porterTransactionId').set({
|
||||
'timestamp': now,
|
||||
'transactionId': porterTransactionId,
|
||||
});
|
||||
log('[PorterRepository] Referensi disimpan di passengerHistory/$passengerId');
|
||||
|
||||
log('[PorterRepository] Transaksi porter berhasil dibuat');
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error membuat transaksi porter: $e');
|
||||
throw Exception('Gagal membuat transaksi porter: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<List<String>> getPorterTransactionIds(String porterId) async {
|
||||
// try {
|
||||
// final rtdb = FirebaseDatabase.instance.ref();
|
||||
// final snapshot = await rtdb.child('porterHistory/$porterId').get();
|
||||
|
||||
// if (snapshot.exists && snapshot.value != null) {
|
||||
// final Map<dynamic, dynamic> data = snapshot.value as Map<dynamic, dynamic>;
|
||||
// return data.keys.map((key) => key.toString()).toList();
|
||||
// }
|
||||
|
||||
// return [];
|
||||
// } catch (e) {
|
||||
// log('[PorterRepository] Error getting porter transaction IDs: $e');
|
||||
// return [];
|
||||
// }
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Future<Map<String, dynamic>?> getPorterTransactionById(String transactionId) async {
|
||||
// try {
|
||||
// final rtdb = FirebaseDatabase.instance.ref();
|
||||
// final snapshot = await rtdb.child('porterTransactions/$transactionId').get();
|
||||
|
||||
// if (snapshot.exists && snapshot.value != null) {
|
||||
// return Map<String, dynamic>.from(snapshot.value as Map);
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// } catch (e) {
|
||||
// log('[PorterRepository] Error getting porter transaction: $e');
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
Future<void> completePorterAssignment(String porterId) async {
|
||||
try {
|
||||
// Dapatkan dokumen porter
|
||||
final porterDoc = await _firestore.collection('porterOnline').doc(porterId).get();
|
||||
if (porterDoc.exists) {
|
||||
final porterData = porterDoc.data();
|
||||
final transactionId = porterData?['idTransaction'];
|
||||
|
||||
if (transactionId != null) {
|
||||
// Update status transaksi di Firestore
|
||||
await _firestore.collection('porterTransactions').doc(transactionId).update({
|
||||
'status': 'selesai',
|
||||
'updatedAt': FieldValue.serverTimestamp(),
|
||||
});
|
||||
|
||||
// Update status transaksi di Realtime DB
|
||||
final rtdb = FirebaseDatabase.instance.ref();
|
||||
await rtdb.child('porterTransactions/$transactionId').update({
|
||||
'status': 'selesai',
|
||||
'updatedAt': DateTime.now().millisecondsSinceEpoch,
|
||||
});
|
||||
}
|
||||
|
||||
// Reset status porter
|
||||
await _firestore.collection('porterOnline').doc(porterId).update({
|
||||
'isAvailable': true,
|
||||
'idUser': null,
|
||||
'idTransaction': null,
|
||||
});
|
||||
|
||||
log('[PorterRepository] Tugas porter selesai, status diperbarui');
|
||||
}
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error menyelesaikan tugas porter: $e');
|
||||
throw Exception('Gagal menyelesaikan tugas porter: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<void> createPorterTransaction(
|
||||
// {required String porterTransactionId,
|
||||
// required String porterId,
|
||||
// required String passengerId,
|
||||
// required String ticketId,
|
||||
// required String transactionId,
|
||||
// required String locationPassenger,
|
||||
// required String locationPorter}) async {
|
||||
// final now = FieldValue.serverTimestamp();
|
||||
// final kodePorter = _generateId();
|
||||
|
||||
// final data = {
|
||||
// 'kodePorter': kodePorter,
|
||||
// 'porterOnlineId': porterId,
|
||||
// 'idPassenger': passengerId,
|
||||
// 'ticketId': ticketId,
|
||||
// 'transactionId': transactionId,
|
||||
// 'status': 'pending',
|
||||
// 'locationPassenger': locationPassenger,
|
||||
// 'locationPorter': locationPorter,
|
||||
// 'createdAt': now,
|
||||
// };
|
||||
|
||||
// final batch = _firestore.batch();
|
||||
// final porterDoc = _firestore.collection('porterOnline').doc(porterId);
|
||||
// final txDoc = _firestore.collection('porterTransactions').doc(porterTransactionId);
|
||||
|
||||
// batch.update(porterDoc, {
|
||||
// 'isAvailable': false,
|
||||
// 'idUser': passengerId,
|
||||
// 'idTransaction': porterTransactionId,
|
||||
// 'locationPorter': locationPorter,
|
||||
// });
|
||||
// batch.set(txDoc, data);
|
||||
|
||||
// await batch.commit();
|
||||
|
||||
// // tulis juga ke Realtime Database agar porter bisa listen secara real-time
|
||||
// final rtdb = FirebaseDatabase.instance.ref();
|
||||
// await rtdb.child('porterTransactions/$porterId/$porterTransactionId').set({
|
||||
// ...data,
|
||||
// 'createdAt': DateTime.now().millisecondsSinceEpoch,
|
||||
// });
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Future<void> completePorterAssignment(String porterId) async {
|
||||
// try {
|
||||
// await _firestore.collection('porterOnline').doc(porterId).update({
|
||||
// 'isAvailable': true,
|
||||
// 'idUser': null,
|
||||
// 'idTransaction': null,
|
||||
// });
|
||||
|
||||
// log('[PorterRepository] Tugas porter selesai, status diperbarui');
|
||||
// } catch (e) {
|
||||
// log('[PorterRepository] Error menyelesaikan tugas porter: $e');
|
||||
// throw Exception('Gagal menyelesaikan tugas porter: $e');
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
Future<PorterQueueModel?> getPorterById(String porterId) async {
|
||||
try {
|
||||
log('[PorterRepository] Mendapatkan porter by ID: $porterId');
|
||||
|
||||
final snapshot = await _firestore.collection('porterOnline').doc(porterId).get();
|
||||
|
||||
if (snapshot.exists && snapshot.data() != null) {
|
||||
return PorterQueueModel.fromJson(snapshot.data()!, docId: snapshot.id);
|
||||
}
|
||||
|
||||
log('[PorterRepository] Porter tidak ditemukan dengan ID: $porterId');
|
||||
return null;
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error mendapatkan porter by ID: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkConditionForPorter(String porterId) async {
|
||||
try {
|
||||
// Memeriksa apakah porter ada dan tidak memiliki transaksi aktif
|
||||
final porterDoc = await _firestore.collection('porterOnline').doc(porterId).get();
|
||||
|
||||
if (!porterDoc.exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final data = porterDoc.data();
|
||||
if (data != null && data['idTransaction'] != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
throw Exception('Gagal memeriksa kondisi porter: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<PorterQueueModel?> watchPorterByUserId(String userId) {
|
||||
try {
|
||||
log('[PorterRepository] Memulai stream porter dengan userId: $userId');
|
||||
|
||||
return _firestore
|
||||
.collection('porterOnline')
|
||||
.where('userId', isEqualTo: userId)
|
||||
.limit(1)
|
||||
.snapshots()
|
||||
.map((snapshot) {
|
||||
if (snapshot.docs.isEmpty) {
|
||||
log('[PorterRepository] Tidak ada porter dengan userId: $userId');
|
||||
return null;
|
||||
}
|
||||
|
||||
final doc = snapshot.docs.first;
|
||||
log('[PorterRepository] Porter ditemukan dengan ID: ${doc.id}');
|
||||
return PorterQueueModel.fromJson(doc.data(), docId: doc.id);
|
||||
});
|
||||
} catch (e) {
|
||||
log('[PorterRepository] Error watching porter by userId: $e');
|
||||
throw Exception('Gagal memantau data porter: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,32 +3,44 @@ import 'package:cloud_firestore/cloud_firestore.dart';
|
|||
class PorterQueueModel {
|
||||
final String? id;
|
||||
final String userId;
|
||||
final bool isTaken;
|
||||
final bool isAvailable;
|
||||
final DateTime onlineAt;
|
||||
final String? idUser;
|
||||
final String? idTransaction;
|
||||
final String? locationPorter;
|
||||
|
||||
PorterQueueModel({
|
||||
this.id,
|
||||
required this.userId,
|
||||
required this.isTaken,
|
||||
required this.isAvailable,
|
||||
required this.onlineAt,
|
||||
this.idUser,
|
||||
this.idTransaction,
|
||||
this.locationPorter,
|
||||
});
|
||||
|
||||
factory PorterQueueModel.fromJson(Map<String, dynamic> json, {String? docId}) {
|
||||
return PorterQueueModel(
|
||||
id: docId ?? json['id'],
|
||||
userId: json['userId'] ?? '',
|
||||
isTaken: json['isTaken'] ?? false,
|
||||
onlineAt: (json['onlineAt'] is Timestamp)
|
||||
? (json['onlineAt'] as Timestamp).toDate()
|
||||
isAvailable: json['isAvailable'] ?? true,
|
||||
onlineAt: (json['onlineAt'] is Timestamp)
|
||||
? (json['onlineAt'] as Timestamp).toDate()
|
||||
: DateTime.fromMillisecondsSinceEpoch(json['onlineAt'] ?? 0),
|
||||
idUser: json['idUser'],
|
||||
idTransaction: json['idTransaction'],
|
||||
locationPorter: json['locationPorter'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'userId': userId,
|
||||
'isTaken': isTaken,
|
||||
'isAvailable': isAvailable,
|
||||
'onlineAt': onlineAt,
|
||||
'idUser': idUser,
|
||||
'idTransaction': idTransaction,
|
||||
'locationPorter': locationPorter,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,27 @@
|
|||
import '../models/porter_queue_model.dart';
|
||||
|
||||
abstract class PorterQueueRepository {
|
||||
Future<String> createPorterQueue(String userId);
|
||||
Future<String> createPorterQueue(String userId, String locationPorter);
|
||||
Future<PorterQueueModel?> getPorterByUserId(String userId);
|
||||
Future<void> deletePorterQueue(String porterId);
|
||||
}
|
||||
|
||||
// Sisi Porter
|
||||
Future<PorterQueueModel?> getNextAvailablePorter();
|
||||
Future<bool> assignPorterToUser(String porterId, String userId, String transactionId);
|
||||
Future<void> createPorterTransaction(
|
||||
{required String porterTransactionId,
|
||||
required String porterId,
|
||||
required String passengerId,
|
||||
required String transactionId,
|
||||
required String ticketId,
|
||||
required String locationPassenger,
|
||||
required String locationPorter});
|
||||
|
||||
// Future<List<String>> getPorterTransactionIds(String porterId);
|
||||
// Future<Map<String, dynamic>?> getPorterTransactionById(String transactionId);
|
||||
|
||||
Future<void> completePorterAssignment(String porterId);
|
||||
Future<PorterQueueModel?> getPorterById(String porterId);
|
||||
Future<bool> checkConditionForPorter(String porterId);
|
||||
Stream<PorterQueueModel?> watchPorterByUserId(String userId);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ class PorterQueueUsecase {
|
|||
|
||||
PorterQueueUsecase(this._repository);
|
||||
|
||||
Future<String> createPorterQueue(String userId) {
|
||||
return _repository.createPorterQueue(userId);
|
||||
Future<String> createPorterQueue(String userId, String locationPorter) {
|
||||
return _repository.createPorterQueue(userId, locationPorter);
|
||||
}
|
||||
|
||||
Future<PorterQueueModel?> getPorterByUserId(String userId) {
|
||||
|
@ -17,4 +17,61 @@ class PorterQueueUsecase {
|
|||
Future<void> deletePorterQueue(String porterId) {
|
||||
return _repository.deletePorterQueue(porterId);
|
||||
}
|
||||
|
||||
// Sisi Porter
|
||||
Future<PorterQueueModel?> getNextAvailablePorter() {
|
||||
return _repository.getNextAvailablePorter();
|
||||
}
|
||||
|
||||
Future<bool> assignPorterToUser(String porterId, String userId, String transactionId) {
|
||||
return _repository.assignPorterToUser(porterId, userId, transactionId);
|
||||
}
|
||||
|
||||
Future<Map<String, String>> requestPorter({
|
||||
required String passengerId,
|
||||
required String ticketId,
|
||||
required String transactionId,
|
||||
required String locationPassenger,
|
||||
}) async {
|
||||
final porter = await _repository.getNextAvailablePorter();
|
||||
if (porter == null) throw Exception('Porter tidak tersedia');
|
||||
|
||||
final porterId = porter.id!;
|
||||
final txId = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final locPorter = porter.locationPorter!;
|
||||
|
||||
final ok = await _repository.assignPorterToUser(porterId, passengerId, txId);
|
||||
if (!ok) throw Exception('Gagal menugaskan porter');
|
||||
|
||||
await _repository.createPorterTransaction(
|
||||
porterTransactionId: txId,
|
||||
porterId: porterId,
|
||||
passengerId: passengerId,
|
||||
transactionId: transactionId,
|
||||
ticketId: ticketId,
|
||||
locationPassenger: locationPassenger,
|
||||
locationPorter: locPorter,
|
||||
);
|
||||
|
||||
return {
|
||||
'porterId': porterId,
|
||||
'transactionId': txId,
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> completePorterAssignment(String porterId) {
|
||||
return _repository.completePorterAssignment(porterId);
|
||||
}
|
||||
|
||||
Future<PorterQueueModel?> getPorterById(String porterId) {
|
||||
return _repository.getPorterById(porterId);
|
||||
}
|
||||
|
||||
Future<bool> checkConditionForPorter(String porterId) {
|
||||
return _repository.checkConditionForPorter(porterId);
|
||||
}
|
||||
|
||||
Stream<PorterQueueModel?> watchPorterByUserId(String userId) {
|
||||
return _repository.watchPorterByUserId(userId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart';
|
||||
import 'package:e_porter/domain/models/porter_queue_model.dart';
|
||||
import 'package:e_porter/domain/usecases/porter_queue_usecase.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
@ -11,12 +14,12 @@ class PorterQueueController {
|
|||
final RxString error = ''.obs;
|
||||
final Rx<PorterQueueModel?> currentPorter = Rx<PorterQueueModel?>(null);
|
||||
|
||||
Future<String> createPorterQueue(String userId) async {
|
||||
Future<String> createPorterQueue(String userId, String locationPorter) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
final porterId = await _porterQueueUsecase.createPorterQueue(userId);
|
||||
final porterId = await _porterQueueUsecase.createPorterQueue(userId, locationPorter);
|
||||
|
||||
final porter = await _porterQueueUsecase.getPorterByUserId(userId);
|
||||
currentPorter.value = porter;
|
||||
|
@ -32,7 +35,16 @@ class PorterQueueController {
|
|||
|
||||
bool validatePorterQueueForDeletion() {
|
||||
final porter = currentPorter.value;
|
||||
return porter != null && porter.id != null;
|
||||
if (porter == null || porter.id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (porter.idTransaction != null) {
|
||||
error.value = 'Porter sedang menangani transaksi, selesaikan transaksi terlebih dahulu';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> deletePorterQueue(String porterId) async {
|
||||
|
@ -40,13 +52,23 @@ class PorterQueueController {
|
|||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
// Gunakan metode cek kondisi yang telah diperbaiki
|
||||
final canProceed = await checkConditionForPorter(porterId);
|
||||
if (!canProceed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await _porterQueueUsecase.deletePorterQueue(porterId);
|
||||
currentPorter.value = null;
|
||||
currentPorter.value = null;
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
error.value = 'Gagal menghapus antrian porter: $e';
|
||||
return false;
|
||||
SnackbarHelper.showError(
|
||||
'Gagal',
|
||||
'Gagal menghapus antrian porter.',
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
@ -73,4 +95,140 @@ class PorterQueueController {
|
|||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Belum
|
||||
|
||||
Future<PorterQueueModel?> getNextAvailablePorter() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
return await _porterQueueUsecase.getNextAvailablePorter();
|
||||
} catch (e) {
|
||||
error.value = 'Gagal mendapatkan porter tersedia: $e';
|
||||
return null;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> assignPorterToUser(String porterId, String userId, String transactionId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
return await _porterQueueUsecase.assignPorterToUser(porterId, userId, transactionId);
|
||||
} catch (e) {
|
||||
error.value = 'Gagal menugaskan porter: $e';
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, String>> requestPorter({
|
||||
required String passengerId,
|
||||
required String ticketId,
|
||||
required String transactionId,
|
||||
required String location,
|
||||
}) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
// Panggil usecase dan dapatkan result berupa map
|
||||
final result = await _porterQueueUsecase.requestPorter(
|
||||
passengerId: passengerId,
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
locationPassenger: location,
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
// Simpan pesan error dan lempar ulang
|
||||
error.value = e.toString();
|
||||
rethrow;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> completePorterAssignment(String porterId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
await _porterQueueUsecase.completePorterAssignment(porterId);
|
||||
|
||||
if (currentPorter.value?.id == porterId) {
|
||||
final userId = currentPorter.value?.userId;
|
||||
if (userId != null) {
|
||||
await loadCurrentPorter(userId);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = 'Gagal menyelesaikan tugas porter: $e';
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> checkConditionForPorter(String porterId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
// 1. Dapatkan data terbaru porter langsung dari repository
|
||||
final freshPorterData = await _porterQueueUsecase.getPorterById(porterId);
|
||||
|
||||
if (freshPorterData == null) {
|
||||
error.value = 'Data porter tidak ditemukan.';
|
||||
SnackbarHelper.showError('Gagal', error.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Gunakan data terbaru untuk validasi
|
||||
if (freshPorterData.idTransaction != null) {
|
||||
error.value = 'Porter sedang menangani transaksi, selesaikan transaksi terlebih dahulu.';
|
||||
SnackbarHelper.showError('Gagal', error.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (freshPorterData.idUser != null) {
|
||||
error.value = 'Porter masih melayani penumpang, selesaikan tugas terlebih dahulu.';
|
||||
SnackbarHelper.showError('Gagal', error.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (freshPorterData.isAvailable != true) {
|
||||
error.value = 'Porter belum tersedia untuk keluar antrian.';
|
||||
SnackbarHelper.showError('Gagal', error.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
error.value = 'Gagal memeriksa kondisi porter: $e';
|
||||
SnackbarHelper.showError('Gagal', 'Gagal memeriksa kondisi porter.');
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Stream<PorterQueueModel?> watchPorter(String userId) {
|
||||
if (userId.isEmpty) {
|
||||
log('[PorterQueueController] Error: userId kosong');
|
||||
return Stream.value(null);
|
||||
}
|
||||
|
||||
log('[PorterQueueController] Memulai pemantauan porter dengan userId: $userId');
|
||||
return _porterQueueUsecase.watchPorterByUserId(userId);
|
||||
}
|
||||
|
||||
void setError(String message) {
|
||||
error.value = message;
|
||||
log('[PorterQueueController] Error: $message');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Periksa apakah ada porterOnline yang aktif
|
||||
if (_porterQueueController.currentPorter.value == null) {
|
||||
SnackbarHelper.showError(
|
||||
'Gagal',
|
||||
|
@ -127,9 +128,15 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
);
|
||||
return;
|
||||
}
|
||||
final porterId = _porterQueueController.currentPorter.value!.id!;
|
||||
final canProceed = await _porterQueueController.checkConditionForPorter(porterId);
|
||||
|
||||
// Dapatkan ID porter saat ini
|
||||
final porterId = _porterQueueController.currentPorter.value!.id!;
|
||||
|
||||
// Muat ulang data porter sebelum melakukan pengecekan (optional)
|
||||
await _porterQueueController.loadCurrentPorter(validUserId);
|
||||
|
||||
// Gunakan metode cek kondisi yang diperbaiki
|
||||
final canProceed = await _porterQueueController.checkConditionForPorter(porterId);
|
||||
if (!canProceed) {
|
||||
return;
|
||||
}
|
||||
|
@ -158,7 +165,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
}
|
||||
SnackbarHelper.showError(
|
||||
'Gagal',
|
||||
'Gagal menghentikan antrian porter.',
|
||||
'Gagal menghentikan antrian porter: ${e.toString()}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue