Feat: done logic transaction with upload file
This commit is contained in:
parent
ea3e1080e0
commit
7b40cbace4
|
@ -632,7 +632,7 @@
|
|||
"languageVersion": "3.4"
|
||||
}
|
||||
],
|
||||
"generated": "2025-04-01T21:50:03.833642Z",
|
||||
"generated": "2025-04-08T08:36:36.076309Z",
|
||||
"generator": "pub",
|
||||
"generatorVersion": "3.5.0",
|
||||
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
||||
|
|
|
@ -73,54 +73,43 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
|
||||
if (seatData.exists) {
|
||||
Map<String, dynamic>? data = seatData.data() as Map<String, dynamic>?;
|
||||
|
||||
// Fungsi untuk mendapatkan kelas seat dari nomor kursi (misalnya "A2" -> "a")
|
||||
String getSeatClass(String seatNumber) {
|
||||
// Ambil huruf pertama dan ubah ke lowercase
|
||||
if (seatNumber.isNotEmpty) {
|
||||
return seatNumber[0].toLowerCase();
|
||||
}
|
||||
return "a"; // Default ke "a" jika format tidak sesuai
|
||||
return "a";
|
||||
}
|
||||
|
||||
// Fungsi untuk mendapatkan indeks seat dari nomor kursi (misalnya "A2" -> 2)
|
||||
int getSeatIndex(String seatNumber) {
|
||||
if (seatNumber.length > 1) {
|
||||
try {
|
||||
return int.parse(seatNumber.substring(1)) - 1; // Konversi ke berbasis-0
|
||||
return int.parse(seatNumber.substring(1)) - 1;
|
||||
} catch (e) {
|
||||
log("Error parsing seat index: $e");
|
||||
}
|
||||
}
|
||||
return 0; // Default ke 0 jika format tidak sesuai
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Perbarui status kursi untuk setiap seat yang dipilih
|
||||
for (String seatNumber in numberSeat) {
|
||||
String seatClass = getSeatClass(seatNumber);
|
||||
int seatIndex = getSeatIndex(seatNumber);
|
||||
|
||||
log("Processing seat: $seatNumber, class: $seatClass, index: $seatIndex");
|
||||
|
||||
// Pastikan struktur data seat tersedia
|
||||
if (data != null && data.containsKey('seat')) {
|
||||
Map<String, dynamic> seatData = Map<String, dynamic>.from(data['seat']);
|
||||
|
||||
// Pastikan kelas kursi (a-f) tersedia
|
||||
if (seatData.containsKey(seatClass)) {
|
||||
Map<String, dynamic> classSeatData = Map<String, dynamic>.from(seatData[seatClass]);
|
||||
|
||||
// Pastikan array isTaken tersedia
|
||||
if (classSeatData.containsKey('isTaken')) {
|
||||
List<dynamic> isTaken = List<dynamic>.from(classSeatData['isTaken'] ?? []);
|
||||
|
||||
// Perbarui array isTaken
|
||||
while (isTaken.length <= seatIndex) {
|
||||
isTaken.add(false); // Tambahkan seat yang belum ada dengan false
|
||||
isTaken.add(false);
|
||||
}
|
||||
isTaken[seatIndex] = true; // Set kursi yang dipilih sebagai 'taken'
|
||||
isTaken[seatIndex] = true;
|
||||
|
||||
// Update Firestore untuk kelas kursi tertentu
|
||||
await _firestore
|
||||
.collection('tickets')
|
||||
.doc(ticketId)
|
||||
|
@ -130,7 +119,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
|
||||
log("Successfully updated seat $seatNumber in class $seatClass at index $seatIndex");
|
||||
} else {
|
||||
// Jika 'isTaken' tidak ada, buat array baru
|
||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||
isTaken[seatIndex] = true;
|
||||
|
||||
|
@ -144,7 +132,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
log("Created new isTaken array for seat $seatNumber in class $seatClass");
|
||||
}
|
||||
} else {
|
||||
// Jika kelas kursi tidak ada, buat struktur baru
|
||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||
isTaken[seatIndex] = true;
|
||||
|
||||
|
@ -155,7 +142,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
log("Created new seat class $seatClass for seat $seatNumber");
|
||||
}
|
||||
} else {
|
||||
// Jika struktur 'seat' tidak ada, buat struktur lengkap
|
||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||
isTaken[seatIndex] = true;
|
||||
|
||||
|
@ -221,13 +207,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
'updatedAt': now,
|
||||
});
|
||||
|
||||
// Update status dokumen tiket utama
|
||||
await _firestore.collection('tickets').doc(ticketId).update({
|
||||
'status': status == 'paid' ? 'awaiting_verification' : status,
|
||||
'lastUpdated': now,
|
||||
});
|
||||
|
||||
// Update status di Realtime Database
|
||||
final databaseRef = FirebaseDatabase.instance.ref();
|
||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||
'status': status,
|
||||
|
@ -239,36 +218,38 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> uploadPaymentProof(
|
||||
{required String ticketId, required String transactionId, required File proofImage}) async {
|
||||
Future<void> uploadPaymentProof({
|
||||
required String ticketId,
|
||||
required String transactionId,
|
||||
required File proofImage,
|
||||
required String userId,
|
||||
}) async {
|
||||
try {
|
||||
final fileName =
|
||||
'payment_proof_${transactionId}_${DateTime.now().millisecondsSinceEpoch}${path.extension(proofImage.path)}';
|
||||
final storageRef = _storage.ref().child('payment/$fileName');
|
||||
|
||||
// Upload file ke Firebase Storage
|
||||
final uploadTask = await storageRef.putFile(proofImage);
|
||||
final downloadUrl = await uploadTask.ref.getDownloadURL();
|
||||
// Tambahkan listener untuk progress upload
|
||||
final uploadTask = storageRef.putFile(proofImage);
|
||||
|
||||
// Update di Firestore dengan URL bukti pembayaran
|
||||
// Upload file ke Firebase Storage
|
||||
final snapshot = await uploadTask;
|
||||
final downloadUrl = await snapshot.ref.getDownloadURL();
|
||||
final now = DateTime.now();
|
||||
|
||||
// Update Firestore
|
||||
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
||||
'proofUrl': downloadUrl,
|
||||
'status': 'paid',
|
||||
'paidAt': DateTime.now(),
|
||||
'status': 'active',
|
||||
'paidAt': now,
|
||||
});
|
||||
|
||||
// Update status dokumen tiket utama
|
||||
await _firestore.collection('tickets').doc(ticketId).update({
|
||||
'status': 'awaiting_verification',
|
||||
'lastUpdated': DateTime.now(),
|
||||
});
|
||||
|
||||
// Update di Realtime Database
|
||||
// Update Realtime Database
|
||||
final databaseRef = FirebaseDatabase.instance.ref();
|
||||
await databaseRef.child('transactions/$ticketId/payment').update({
|
||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||
'proofUrl': downloadUrl,
|
||||
'status': 'paid',
|
||||
'paidAt': DateTime.now().millisecondsSinceEpoch,
|
||||
'status': 'active',
|
||||
'paidAt': now.millisecondsSinceEpoch,
|
||||
});
|
||||
} catch (e) {
|
||||
throw Exception('Failed to upload payment proof: $e');
|
||||
|
@ -278,12 +259,9 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
@override
|
||||
Future<List<TransactionModel>> getTransactionsByUserId(String userId) async {
|
||||
try {
|
||||
// Mencari tiket berdasarkan userId
|
||||
final ticketQuerySnapshot = await _firestore.collection('tickets').where('userId', isEqualTo: userId).get();
|
||||
|
||||
final List<TransactionModel> transactions = [];
|
||||
|
||||
// Untuk setiap tiket, ambil data payment
|
||||
for (var ticketDoc in ticketQuerySnapshot.docs) {
|
||||
final ticketId = ticketDoc.id;
|
||||
final paymentsSnapshot = await _firestore.collection('tickets').doc(ticketId).collection('payments').get();
|
||||
|
@ -368,40 +346,194 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
|||
|
||||
Future<void> checkAndCancelExpiredTransactions() async {
|
||||
try {
|
||||
log('[TransactionRepository] Mulai memeriksa transaksi kedaluwarsa');
|
||||
final now = DateTime.now();
|
||||
final pendingTransactionsSnapshot =
|
||||
await _firestore.collectionGroup('payments').where('status', isEqualTo: 'pending').get();
|
||||
|
||||
final ticketsCheck = await _firestore.collection('tickets').limit(1).get();
|
||||
if (ticketsCheck.docs.isEmpty) {
|
||||
log('[TransactionRepository] Tidak ada tiket untuk diperiksa, melewati pengecekan');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final pendingTransactionsSnapshot = await _firestore
|
||||
.collectionGroup('payments')
|
||||
.where('status', isEqualTo: 'pending')
|
||||
.orderBy('createdAt', descending: false)
|
||||
.get();
|
||||
|
||||
for (var doc in pendingTransactionsSnapshot.docs) {
|
||||
final data = doc.data();
|
||||
final expiryTime = (data['expiryTime'] as Timestamp).toDate();
|
||||
|
||||
// Jika sudah melewati waktu pembayaran
|
||||
if (expiryTime.isBefore(now)) {
|
||||
final ticketId = data['ticketId'];
|
||||
final transactionId = doc.id;
|
||||
final userId = data['userDetails']['uid'];
|
||||
final flightId = data['flightId'];
|
||||
final numberSeat = (data['numberSeat'] as List).map((e) => e.toString()).toList();
|
||||
|
||||
// Update status di Firestore
|
||||
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
||||
'status': 'cancelled',
|
||||
'updatedAt': now,
|
||||
});
|
||||
|
||||
await _firestore.collection('tickets').doc(ticketId).update({
|
||||
'status': 'cancelled',
|
||||
'lastUpdated': now,
|
||||
});
|
||||
// Reset status kursi
|
||||
await _resetSeatStatus(ticketId, flightId, numberSeat);
|
||||
|
||||
// Update status di Realtime Database
|
||||
final databaseRef = FirebaseDatabase.instance.ref();
|
||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||
'status': 'cancelled',
|
||||
'updatedAt': now.millisecondsSinceEpoch,
|
||||
});
|
||||
|
||||
log('Transaction $transactionId cancelled due to expiry');
|
||||
log('Transaksi $transactionId dibatalkan karena kedaluwarsa');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log('Error checking expired transactions: $e');
|
||||
if (e.toString().contains('failed-precondition') || e.toString().contains('requires an index')) {
|
||||
log('[TransactionRepository] Indeks belum tersedia. Menggunakan metode alternatif.');
|
||||
// Gunakan metode alternatif yang tidak memerlukan indeks khusus
|
||||
await checkAndCancelExpiredTransactionsAlternative();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log('[TransactionRepository] Error memeriksa transaksi kedaluwarsa: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Metode helper untuk mereset status kursi
|
||||
Future<void> _resetSeatStatus(String ticketId, String flightId, List<String> numberSeat) async {
|
||||
try {
|
||||
DocumentSnapshot seatData =
|
||||
await _firestore.collection('tickets').doc(ticketId).collection('flights').doc(flightId).get();
|
||||
|
||||
if (seatData.exists) {
|
||||
Map<String, dynamic>? data = seatData.data() as Map<String, dynamic>?;
|
||||
|
||||
for (String seatNumber in numberSeat) {
|
||||
String seatClass = getSeatClass(seatNumber);
|
||||
int seatIndex = getSeatIndex(seatNumber);
|
||||
|
||||
if (data != null && data.containsKey('seat')) {
|
||||
Map<String, dynamic> seatData = Map<String, dynamic>.from(data['seat']);
|
||||
|
||||
if (seatData.containsKey(seatClass)) {
|
||||
Map<String, dynamic> classSeatData = Map<String, dynamic>.from(seatData[seatClass]);
|
||||
|
||||
if (classSeatData.containsKey('isTaken')) {
|
||||
List<dynamic> isTaken = List<dynamic>.from(classSeatData['isTaken'] ?? []);
|
||||
|
||||
if (seatIndex < isTaken.length) {
|
||||
isTaken[seatIndex] = false; // Reset status kursi
|
||||
|
||||
await _firestore
|
||||
.collection('tickets')
|
||||
.doc(ticketId)
|
||||
.collection('flights')
|
||||
.doc(flightId)
|
||||
.update({'seat.$seatClass.isTaken': isTaken});
|
||||
|
||||
log("Reset kursi $seatNumber di kelas $seatClass pada indeks $seatIndex");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error mereset status kursi: $e");
|
||||
}
|
||||
}
|
||||
|
||||
String getSeatClass(String seatNumber) {
|
||||
if (seatNumber.isNotEmpty) {
|
||||
return seatNumber[0].toLowerCase();
|
||||
}
|
||||
return "a";
|
||||
}
|
||||
|
||||
int getSeatIndex(String seatNumber) {
|
||||
if (seatNumber.length > 1) {
|
||||
try {
|
||||
return int.parse(seatNumber.substring(1)) - 1;
|
||||
} catch (e) {
|
||||
log("Error parsing seat index: $e");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Future<void> checkAndCancelExpiredTransactionsAlternative() async {
|
||||
try {
|
||||
final now = DateTime.now();
|
||||
log('[TransactionRepository] Menggunakan metode alternatif untuk memeriksa transaksi');
|
||||
|
||||
final ticketsSnapshot = await _firestore.collection('tickets').get();
|
||||
|
||||
int count = 0;
|
||||
for (var ticketDoc in ticketsSnapshot.docs) {
|
||||
final ticketId = ticketDoc.id;
|
||||
|
||||
final paymentsSnapshot = await _firestore
|
||||
.collection('tickets')
|
||||
.doc(ticketId)
|
||||
.collection('payments')
|
||||
.where('status', isEqualTo: 'pending')
|
||||
.get();
|
||||
|
||||
for (var doc in paymentsSnapshot.docs) {
|
||||
final data = doc.data();
|
||||
if (data.containsKey('expiryTime')) {
|
||||
final expiryTime = (data['expiryTime'] as Timestamp).toDate();
|
||||
|
||||
if (expiryTime.isBefore(now)) {
|
||||
final transactionId = doc.id;
|
||||
final userId = data['userDetails']?['uid'];
|
||||
final flightId = data['flightId'];
|
||||
final List<String> numberSeat = [];
|
||||
|
||||
if (data.containsKey('numberSeat')) {
|
||||
numberSeat.addAll((data['numberSeat'] as List).map((e) => e.toString()));
|
||||
}
|
||||
|
||||
log('[TransactionRepository] Memproses transaksi kedaluwarsa alternatif: $transactionId');
|
||||
|
||||
// Update status transaksi
|
||||
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
||||
'status': 'cancelled',
|
||||
'updatedAt': now,
|
||||
});
|
||||
|
||||
// Reset seat status
|
||||
if (numberSeat.isNotEmpty) {
|
||||
await _resetSeatStatus(ticketId, flightId, numberSeat);
|
||||
}
|
||||
|
||||
// Update Realtime Database jika userId tersedia
|
||||
if (userId != null) {
|
||||
final databaseRef = FirebaseDatabase.instance.ref();
|
||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||
'status': 'cancelled',
|
||||
'updatedAt': now.millisecondsSinceEpoch,
|
||||
});
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log('[TransactionRepository] Selesai memeriksa transaksi (metode alternatif), $count transaksi dibatalkan');
|
||||
} catch (e) {
|
||||
log('[TransactionRepository] Error pada metode alternatif: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:e_porter/data/repositories/transaction_repository_impl.dart';
|
||||
import 'package:e_porter/_core/service/transaction_expiry_service.dart';
|
||||
|
||||
class AppBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
// Inisialisasi TransactionExpiryService
|
||||
Get.put<TransactionRepositoryImpl>(TransactionRepositoryImpl(), permanent: true);
|
||||
|
||||
// Inisialisasi dan mulai service
|
||||
final repository = Get.find<TransactionRepositoryImpl>();
|
||||
TransactionExpiryService().initialize(repository);
|
||||
}
|
||||
}
|
|
@ -4,22 +4,29 @@ import 'package:e_porter/data/repositories/transaction_repository_impl.dart';
|
|||
import 'package:e_porter/domain/usecases/transaction_usecase.dart';
|
||||
import 'package:e_porter/presentation/controllers/transaction_controller.dart';
|
||||
|
||||
import '../../_core/service/transaction_expiry_service.dart';
|
||||
|
||||
class TransactionBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
// Repository
|
||||
Get.lazyPut<TransactionRepository>(
|
||||
() => TransactionRepositoryImpl(),
|
||||
fenix: true
|
||||
);
|
||||
|
||||
Get.lazyPut<TransactionRepositoryImpl>(
|
||||
() => TransactionRepositoryImpl(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// UseCase
|
||||
Get.lazyPut<TransactionUseCase>(
|
||||
() => TransactionUseCase(Get.find<TransactionRepository>()),
|
||||
);
|
||||
|
||||
// Controller
|
||||
Get.lazyPut<TransactionController>(
|
||||
() => TransactionController(Get.find<TransactionUseCase>()),
|
||||
);
|
||||
|
||||
TransactionExpiryService().initialize(Get.find<TransactionRepositoryImpl>());
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ abstract class TransactionRepository {
|
|||
required String ticketId,
|
||||
required String transactionId,
|
||||
required File proofImage,
|
||||
required String userId,
|
||||
});
|
||||
|
||||
Future<List<TransactionModel>> getTransactionsByUserId(String userId);
|
||||
|
|
|
@ -55,11 +55,13 @@ class TransactionUseCase {
|
|||
required String ticketId,
|
||||
required String transactionId,
|
||||
required File proofImage,
|
||||
required String userId,
|
||||
}) {
|
||||
return _repository.uploadPaymentProof(
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
proofImage: proofImage,
|
||||
userId: userId,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
import 'dart:developer';
|
||||
import 'package:e_porter/data/repositories/transaction_repository_impl.dart';
|
||||
import 'package:e_porter/domain/bindings/app_binding.dart';
|
||||
import 'package:e_porter/presentation/screens/routes/app_rountes.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '_core/service/transaction_expiry_service.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp();
|
||||
|
||||
final transactionRepo = Get.put(TransactionRepositoryImpl());
|
||||
TransactionExpiryService().initialize(transactionRepo);
|
||||
|
||||
log("Firebase Initialized Successfully!");
|
||||
runApp(MyApp(initialRoute: Routes.SPLASH));
|
||||
}
|
||||
|
@ -32,6 +27,7 @@ class MyApp extends StatelessWidget {
|
|||
return GetMaterialApp(
|
||||
debugShowCheckedModeBanner: true,
|
||||
initialRoute: initialRoute,
|
||||
initialBinding: AppBinding(),
|
||||
getPages: AppRoutes.routes,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:e_porter/domain/models/transaction_model.dart';
|
||||
import 'package:e_porter/domain/usecases/transaction_usecase.dart';
|
||||
import 'package:e_porter/_core/service/logger_service.dart';
|
||||
|
||||
import '../../data/repositories/transaction_repository_impl.dart';
|
||||
|
||||
class TransactionController extends GetxController {
|
||||
final TransactionUseCase _transactionUseCase;
|
||||
|
@ -12,6 +14,8 @@ class TransactionController extends GetxController {
|
|||
final Rx<TransactionModel?> currentTransaction = Rx<TransactionModel?>(null);
|
||||
final RxBool isLoading = false.obs;
|
||||
final RxString error = ''.obs;
|
||||
final RxBool isUploading = false.obs;
|
||||
final RxDouble uploadProgress = 0.0.obs;
|
||||
|
||||
Future<String> createTransaction({
|
||||
required String ticketId,
|
||||
|
@ -46,7 +50,6 @@ class TransactionController extends GetxController {
|
|||
numberSeat: numberSeat,
|
||||
);
|
||||
|
||||
// Ambil data transaksi setelah berhasil dibuat
|
||||
final transaction = await _transactionUseCase.getTransactionById(
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
|
@ -55,7 +58,7 @@ class TransactionController extends GetxController {
|
|||
currentTransaction.value = transaction;
|
||||
return transactionId;
|
||||
} catch (e) {
|
||||
logger.e('Gagal membuat transaksi: $e');
|
||||
log('Gagal membuat transaksi: $e');
|
||||
error.value = 'Gagal membuat transaksi: $e';
|
||||
throw Exception('Gagal membuat transaksi: $e');
|
||||
} finally {
|
||||
|
@ -63,26 +66,45 @@ class TransactionController extends GetxController {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> uploadPaymentProof(
|
||||
{required String ticketId, required String transactionId, required File proofImage}) async {
|
||||
Future<void> uploadPaymentProof({
|
||||
required String ticketId,
|
||||
required String transactionId,
|
||||
required File proofImage,
|
||||
required String userId,
|
||||
}) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
isUploading.value = true;
|
||||
error.value = '';
|
||||
uploadProgress.value = 0.0;
|
||||
|
||||
await _transactionUseCase.uploadPaymentProof(
|
||||
ticketId: ticketId, transactionId: transactionId, proofImage: proofImage);
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
proofImage: proofImage,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
// Refresh data transaksi setelah bukti pembayaran diunggah
|
||||
final transaction =
|
||||
await _transactionUseCase.getTransactionById(ticketId: ticketId, transactionId: transactionId);
|
||||
// Update status transaksi setelah upload berhasil
|
||||
await _transactionUseCase.updateTransactionStatus(
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
status: 'active',
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
// Refresh data transaksi
|
||||
final transaction = await _transactionUseCase.getTransactionById(
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
);
|
||||
|
||||
currentTransaction.value = transaction;
|
||||
|
||||
} catch (e) {
|
||||
logger.e('Gagal mengunggah bukti pembayaran: $e');
|
||||
error.value = 'Gagal mengunggah bukti pembayaran: $e';
|
||||
throw Exception('Gagal mengunggah bukti pembayaran: $e');
|
||||
error.value = 'Gagal mengupload bukti pembayaran: $e';
|
||||
throw Exception('Gagal mengupload bukti pembayaran: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
isUploading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +116,7 @@ class TransactionController extends GetxController {
|
|||
final transactions = await _transactionUseCase.getTransactionsByUserId(userId);
|
||||
return transactions;
|
||||
} catch (e) {
|
||||
logger.e('Gagal mendapatkan daftar transaksi: $e');
|
||||
log('Gagal mendapatkan daftar transaksi: $e');
|
||||
error.value = 'Gagal mendapatkan daftar transaksi: $e';
|
||||
throw Exception('Gagal mendapatkan daftar transaksi: $e');
|
||||
} finally {
|
||||
|
@ -112,7 +134,7 @@ class TransactionController extends GetxController {
|
|||
|
||||
currentTransaction.value = transaction;
|
||||
} catch (e) {
|
||||
logger.e('Gagal mendapatkan detail transaksi: $e');
|
||||
log('Gagal mendapatkan detail transaksi: $e');
|
||||
error.value = 'Gagal mendapatkan detail transaksi: $e';
|
||||
throw Exception('Gagal mendapatkan detail transaksi: $e');
|
||||
} finally {
|
||||
|
@ -127,12 +149,12 @@ class TransactionController extends GetxController {
|
|||
currentTransaction.value = transaction;
|
||||
},
|
||||
onError: (e) {
|
||||
logger.e('Error mendengarkan perubahan transaksi: $e');
|
||||
log('Error mendengarkan perubahan transaksi: $e');
|
||||
error.value = 'Error mendengarkan perubahan transaksi: $e';
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
logger.e('Gagal memantau transaksi: $e');
|
||||
log('Gagal memantau transaksi: $e');
|
||||
error.value = 'Gagal memantau transaksi: $e';
|
||||
}
|
||||
}
|
||||
|
@ -162,11 +184,28 @@ class TransactionController extends GetxController {
|
|||
|
||||
currentTransaction.value = transaction;
|
||||
} catch (e) {
|
||||
logger.e('Gagal mengupdate status transaksi: $e');
|
||||
log('Gagal mengupdate status transaksi: $e');
|
||||
error.value = 'Gagal mengupdate status transaksi: $e';
|
||||
throw Exception('Gagal mengupdate status transaksi: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkExpiredTransactions() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = '';
|
||||
|
||||
final repository = Get.find<TransactionRepositoryImpl>();
|
||||
await repository.checkAndCancelExpiredTransactions();
|
||||
|
||||
log('Berhasil memeriksa transaksi kedaluwarsa');
|
||||
} catch (e) {
|
||||
log('Gagal memeriksa transaksi kedaluwarsa: $e');
|
||||
error.value = 'Gagal memeriksa transaksi kedaluwarsa: $e';
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:e_porter/_core/component/appbar/appbar_component.dart';
|
||||
import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
|
||||
import 'package:e_porter/_core/constants/colors.dart';
|
||||
|
@ -125,7 +127,14 @@ class _PaymentScreenState extends State<PaymentScreen> {
|
|||
text: 'Lanjutkan',
|
||||
textColor: Colors.white,
|
||||
onTap: () {
|
||||
Get.toNamed(Routes.UPLOADFILE);
|
||||
final argument = {
|
||||
'ticketId': ticketId,
|
||||
'transactionId': Get.arguments['transactionId'], // Ambil dari argumen yang diteruskan
|
||||
'flightData': flightData,
|
||||
'totalAll': totalAll,
|
||||
};
|
||||
log('Transaction ID: $argument');
|
||||
Get.toNamed(Routes.UPLOADFILE, arguments: argument);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -65,6 +65,7 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
|
|||
selectedPorterServices = args['selectedPorterServices'] ?? {};
|
||||
|
||||
fetchDataFlight();
|
||||
transactionController.checkExpiredTransactions();
|
||||
}
|
||||
|
||||
Future<void> fetchDataFlight() async {
|
||||
|
@ -238,7 +239,7 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
|
|||
|
||||
// Persiapkan data expiry time
|
||||
final DateTime currentTime = DateTime.now();
|
||||
final DateTime expiryTime = currentTime.add(Duration(seconds: 5));
|
||||
final DateTime expiryTime = currentTime.add(Duration(days: 1));
|
||||
|
||||
// Persiapkan data bandara
|
||||
final bandaraData = {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:e_porter/_core/constants/colors.dart';
|
||||
import 'package:e_porter/_core/constants/typography.dart';
|
||||
import 'package:e_porter/_core/service/permission_service.dart';
|
||||
|
@ -11,7 +13,10 @@ import 'package:zoom_tap_animation/zoom_tap_animation.dart';
|
|||
import '../../../../_core/component/appbar/appbar_component.dart';
|
||||
import '../../../../_core/component/button/button_fill.dart';
|
||||
import '../../../../_core/component/card/custome_shadow_cotainner.dart';
|
||||
import '../../../../_core/service/preferences_service.dart';
|
||||
import '../../../../domain/models/upload_file_model.dart';
|
||||
import '../../../controllers/transaction_controller.dart';
|
||||
import '../../routes/app_rountes.dart';
|
||||
|
||||
class UploadFileScreen extends StatefulWidget {
|
||||
const UploadFileScreen({super.key});
|
||||
|
@ -22,8 +27,100 @@ class UploadFileScreen extends StatefulWidget {
|
|||
|
||||
class _UploadFileScreenState extends State<UploadFileScreen> {
|
||||
final List<UploadFileModel> uploadedFiles = [];
|
||||
final TransactionController _transactionController = Get.find<TransactionController>();
|
||||
bool isUploading = false;
|
||||
|
||||
late String ticketId;
|
||||
late String transactionId;
|
||||
late String userId = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final args = Get.arguments as Map<String, dynamic>;
|
||||
ticketId = args['ticketId'] ?? '';
|
||||
transactionId = args['transactionId'] ?? '';
|
||||
|
||||
_loadUserData();
|
||||
}
|
||||
|
||||
Future<void> _loadUserData() async {
|
||||
final userData = await PreferencesService.getUserData();
|
||||
if (userData != null) {
|
||||
setState(() {
|
||||
userId = userData.uid;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _uploadToServer() async {
|
||||
try {
|
||||
// Validasi data transaksi
|
||||
if (ticketId.isEmpty || transactionId.isEmpty || userId.isEmpty) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Data transaksi tidak lengkap',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (uploadedFiles.isEmpty || uploadedFiles.first.status != FileUploadStatus.completed) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Silakan pilih dan selesaikan proses file terlebih dahulu',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isUploading = true;
|
||||
});
|
||||
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Mengupload bukti pembayaran...',
|
||||
backgroundColor: PrimaryColors.primary600,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
||||
final fileToUpload = uploadedFiles.first;
|
||||
final File proofImage = File(fileToUpload.filePath);
|
||||
|
||||
await _transactionController.uploadPaymentProof(
|
||||
ticketId: ticketId,
|
||||
transactionId: transactionId,
|
||||
proofImage: proofImage,
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Bukti pembayaran berhasil diupload',
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
||||
Get.offAllNamed(Routes.NAVBAR);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengupload bukti pembayaran: $e',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isUploading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -239,7 +336,7 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
|||
try {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf', 'zip', 'mp4'],
|
||||
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf'],
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
|
@ -271,7 +368,6 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void _simulateLocalLoading(UploadFileModel file) {
|
||||
setState(() {
|
||||
file.status = FileUploadStatus.uploading;
|
||||
|
@ -349,31 +445,4 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
|||
_uploadToServer();
|
||||
}
|
||||
}
|
||||
|
||||
void _uploadToServer() {
|
||||
// Simulate uploading to server
|
||||
Get.snackbar(
|
||||
'Info',
|
||||
'Mengupload bukti pembayaran...',
|
||||
backgroundColor: PrimaryColors.primary600,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
||||
// Here you would actually send the files to your server
|
||||
// For now, let's just simulate a successful upload
|
||||
Future.delayed(Duration(seconds: 2), () {
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Bukti pembayaran berhasil diupload',
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
|
||||
// Navigate back or to a success screen
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
// Get.offNamed(Routes.SUCCESS_SCREEN);
|
||||
Get.back();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ dependencies:
|
|||
path: ^1.9.0
|
||||
permission_handler: ^11.4.0
|
||||
device_info_plus: ^11.3.0
|
||||
# workmanager: ^0.5.2
|
||||
|
||||
# pin_code_fields: ^8.0.1
|
||||
# dio: ^5.8.0+1
|
||||
|
|
Loading…
Reference in New Issue