Feat: done logic transaction with upload file
This commit is contained in:
parent
ea3e1080e0
commit
7b40cbace4
|
@ -632,7 +632,7 @@
|
||||||
"languageVersion": "3.4"
|
"languageVersion": "3.4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"generated": "2025-04-01T21:50:03.833642Z",
|
"generated": "2025-04-08T08:36:36.076309Z",
|
||||||
"generator": "pub",
|
"generator": "pub",
|
||||||
"generatorVersion": "3.5.0",
|
"generatorVersion": "3.5.0",
|
||||||
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
||||||
|
|
|
@ -73,54 +73,43 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
|
|
||||||
if (seatData.exists) {
|
if (seatData.exists) {
|
||||||
Map<String, dynamic>? data = seatData.data() as Map<String, dynamic>?;
|
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) {
|
String getSeatClass(String seatNumber) {
|
||||||
// Ambil huruf pertama dan ubah ke lowercase
|
|
||||||
if (seatNumber.isNotEmpty) {
|
if (seatNumber.isNotEmpty) {
|
||||||
return seatNumber[0].toLowerCase();
|
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) {
|
int getSeatIndex(String seatNumber) {
|
||||||
if (seatNumber.length > 1) {
|
if (seatNumber.length > 1) {
|
||||||
try {
|
try {
|
||||||
return int.parse(seatNumber.substring(1)) - 1; // Konversi ke berbasis-0
|
return int.parse(seatNumber.substring(1)) - 1;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log("Error parsing seat index: $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) {
|
for (String seatNumber in numberSeat) {
|
||||||
String seatClass = getSeatClass(seatNumber);
|
String seatClass = getSeatClass(seatNumber);
|
||||||
int seatIndex = getSeatIndex(seatNumber);
|
int seatIndex = getSeatIndex(seatNumber);
|
||||||
|
|
||||||
log("Processing seat: $seatNumber, class: $seatClass, index: $seatIndex");
|
log("Processing seat: $seatNumber, class: $seatClass, index: $seatIndex");
|
||||||
|
|
||||||
// Pastikan struktur data seat tersedia
|
|
||||||
if (data != null && data.containsKey('seat')) {
|
if (data != null && data.containsKey('seat')) {
|
||||||
Map<String, dynamic> seatData = Map<String, dynamic>.from(data['seat']);
|
Map<String, dynamic> seatData = Map<String, dynamic>.from(data['seat']);
|
||||||
|
|
||||||
// Pastikan kelas kursi (a-f) tersedia
|
|
||||||
if (seatData.containsKey(seatClass)) {
|
if (seatData.containsKey(seatClass)) {
|
||||||
Map<String, dynamic> classSeatData = Map<String, dynamic>.from(seatData[seatClass]);
|
Map<String, dynamic> classSeatData = Map<String, dynamic>.from(seatData[seatClass]);
|
||||||
|
|
||||||
// Pastikan array isTaken tersedia
|
|
||||||
if (classSeatData.containsKey('isTaken')) {
|
if (classSeatData.containsKey('isTaken')) {
|
||||||
List<dynamic> isTaken = List<dynamic>.from(classSeatData['isTaken'] ?? []);
|
List<dynamic> isTaken = List<dynamic>.from(classSeatData['isTaken'] ?? []);
|
||||||
|
|
||||||
// Perbarui array isTaken
|
|
||||||
while (isTaken.length <= seatIndex) {
|
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
|
await _firestore
|
||||||
.collection('tickets')
|
.collection('tickets')
|
||||||
.doc(ticketId)
|
.doc(ticketId)
|
||||||
|
@ -130,7 +119,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
|
|
||||||
log("Successfully updated seat $seatNumber in class $seatClass at index $seatIndex");
|
log("Successfully updated seat $seatNumber in class $seatClass at index $seatIndex");
|
||||||
} else {
|
} else {
|
||||||
// Jika 'isTaken' tidak ada, buat array baru
|
|
||||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||||
isTaken[seatIndex] = true;
|
isTaken[seatIndex] = true;
|
||||||
|
|
||||||
|
@ -144,7 +132,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
log("Created new isTaken array for seat $seatNumber in class $seatClass");
|
log("Created new isTaken array for seat $seatNumber in class $seatClass");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Jika kelas kursi tidak ada, buat struktur baru
|
|
||||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||||
isTaken[seatIndex] = true;
|
isTaken[seatIndex] = true;
|
||||||
|
|
||||||
|
@ -155,7 +142,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
log("Created new seat class $seatClass for seat $seatNumber");
|
log("Created new seat class $seatClass for seat $seatNumber");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Jika struktur 'seat' tidak ada, buat struktur lengkap
|
|
||||||
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
List<dynamic> isTaken = List.filled(seatIndex + 1, false);
|
||||||
isTaken[seatIndex] = true;
|
isTaken[seatIndex] = true;
|
||||||
|
|
||||||
|
@ -221,13 +207,6 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
'updatedAt': now,
|
'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();
|
final databaseRef = FirebaseDatabase.instance.ref();
|
||||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||||
'status': status,
|
'status': status,
|
||||||
|
@ -239,36 +218,38 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> uploadPaymentProof(
|
Future<void> uploadPaymentProof({
|
||||||
{required String ticketId, required String transactionId, required File proofImage}) async {
|
required String ticketId,
|
||||||
|
required String transactionId,
|
||||||
|
required File proofImage,
|
||||||
|
required String userId,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final fileName =
|
final fileName =
|
||||||
'payment_proof_${transactionId}_${DateTime.now().millisecondsSinceEpoch}${path.extension(proofImage.path)}';
|
'payment_proof_${transactionId}_${DateTime.now().millisecondsSinceEpoch}${path.extension(proofImage.path)}';
|
||||||
final storageRef = _storage.ref().child('payment/$fileName');
|
final storageRef = _storage.ref().child('payment/$fileName');
|
||||||
|
|
||||||
// Upload file ke Firebase Storage
|
// Tambahkan listener untuk progress upload
|
||||||
final uploadTask = await storageRef.putFile(proofImage);
|
final uploadTask = storageRef.putFile(proofImage);
|
||||||
final downloadUrl = await uploadTask.ref.getDownloadURL();
|
|
||||||
|
|
||||||
// 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({
|
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
||||||
'proofUrl': downloadUrl,
|
'proofUrl': downloadUrl,
|
||||||
'status': 'paid',
|
'status': 'active',
|
||||||
'paidAt': DateTime.now(),
|
'paidAt': now,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update status dokumen tiket utama
|
// Update Realtime Database
|
||||||
await _firestore.collection('tickets').doc(ticketId).update({
|
|
||||||
'status': 'awaiting_verification',
|
|
||||||
'lastUpdated': DateTime.now(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update di Realtime Database
|
|
||||||
final databaseRef = FirebaseDatabase.instance.ref();
|
final databaseRef = FirebaseDatabase.instance.ref();
|
||||||
await databaseRef.child('transactions/$ticketId/payment').update({
|
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
||||||
'proofUrl': downloadUrl,
|
'proofUrl': downloadUrl,
|
||||||
'status': 'paid',
|
'status': 'active',
|
||||||
'paidAt': DateTime.now().millisecondsSinceEpoch,
|
'paidAt': now.millisecondsSinceEpoch,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to upload payment proof: $e');
|
throw Exception('Failed to upload payment proof: $e');
|
||||||
|
@ -278,12 +259,9 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
@override
|
@override
|
||||||
Future<List<TransactionModel>> getTransactionsByUserId(String userId) async {
|
Future<List<TransactionModel>> getTransactionsByUserId(String userId) async {
|
||||||
try {
|
try {
|
||||||
// Mencari tiket berdasarkan userId
|
|
||||||
final ticketQuerySnapshot = await _firestore.collection('tickets').where('userId', isEqualTo: userId).get();
|
final ticketQuerySnapshot = await _firestore.collection('tickets').where('userId', isEqualTo: userId).get();
|
||||||
|
|
||||||
final List<TransactionModel> transactions = [];
|
final List<TransactionModel> transactions = [];
|
||||||
|
|
||||||
// Untuk setiap tiket, ambil data payment
|
|
||||||
for (var ticketDoc in ticketQuerySnapshot.docs) {
|
for (var ticketDoc in ticketQuerySnapshot.docs) {
|
||||||
final ticketId = ticketDoc.id;
|
final ticketId = ticketDoc.id;
|
||||||
final paymentsSnapshot = await _firestore.collection('tickets').doc(ticketId).collection('payments').get();
|
final paymentsSnapshot = await _firestore.collection('tickets').doc(ticketId).collection('payments').get();
|
||||||
|
@ -368,40 +346,194 @@ class TransactionRepositoryImpl implements TransactionRepository {
|
||||||
|
|
||||||
Future<void> checkAndCancelExpiredTransactions() async {
|
Future<void> checkAndCancelExpiredTransactions() async {
|
||||||
try {
|
try {
|
||||||
|
log('[TransactionRepository] Mulai memeriksa transaksi kedaluwarsa');
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final pendingTransactionsSnapshot =
|
|
||||||
await _firestore.collectionGroup('payments').where('status', isEqualTo: 'pending').get();
|
|
||||||
|
|
||||||
for (var doc in pendingTransactionsSnapshot.docs) {
|
final ticketsCheck = await _firestore.collection('tickets').limit(1).get();
|
||||||
final data = doc.data();
|
if (ticketsCheck.docs.isEmpty) {
|
||||||
final expiryTime = (data['expiryTime'] as Timestamp).toDate();
|
log('[TransactionRepository] Tidak ada tiket untuk diperiksa, melewati pengecekan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (expiryTime.isBefore(now)) {
|
try {
|
||||||
final ticketId = data['ticketId'];
|
final pendingTransactionsSnapshot = await _firestore
|
||||||
final transactionId = doc.id;
|
.collectionGroup('payments')
|
||||||
final userId = data['userDetails']['uid'];
|
.where('status', isEqualTo: 'pending')
|
||||||
|
.orderBy('createdAt', descending: false)
|
||||||
|
.get();
|
||||||
|
|
||||||
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
for (var doc in pendingTransactionsSnapshot.docs) {
|
||||||
'status': 'cancelled',
|
final data = doc.data();
|
||||||
'updatedAt': now,
|
final expiryTime = (data['expiryTime'] as Timestamp).toDate();
|
||||||
});
|
|
||||||
|
|
||||||
await _firestore.collection('tickets').doc(ticketId).update({
|
// Jika sudah melewati waktu pembayaran
|
||||||
'status': 'cancelled',
|
if (expiryTime.isBefore(now)) {
|
||||||
'lastUpdated': 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();
|
||||||
|
|
||||||
final databaseRef = FirebaseDatabase.instance.ref();
|
// Update status di Firestore
|
||||||
await databaseRef.child('transactions/$userId/$ticketId/$transactionId/payment').update({
|
await _firestore.collection('tickets').doc(ticketId).collection('payments').doc(transactionId).update({
|
||||||
'status': 'cancelled',
|
'status': 'cancelled',
|
||||||
'updatedAt': now.millisecondsSinceEpoch,
|
'updatedAt': now,
|
||||||
});
|
});
|
||||||
|
|
||||||
log('Transaction $transactionId cancelled due to expiry');
|
// 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('Transaksi $transactionId dibatalkan karena kedaluwarsa');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (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) {
|
} catch (e) {
|
||||||
log('Error checking expired transactions: $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/domain/usecases/transaction_usecase.dart';
|
||||||
import 'package:e_porter/presentation/controllers/transaction_controller.dart';
|
import 'package:e_porter/presentation/controllers/transaction_controller.dart';
|
||||||
|
|
||||||
|
import '../../_core/service/transaction_expiry_service.dart';
|
||||||
|
|
||||||
class TransactionBinding extends Bindings {
|
class TransactionBinding extends Bindings {
|
||||||
@override
|
@override
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
// Repository
|
|
||||||
Get.lazyPut<TransactionRepository>(
|
Get.lazyPut<TransactionRepository>(
|
||||||
() => TransactionRepositoryImpl(),
|
() => TransactionRepositoryImpl(),
|
||||||
|
fenix: true
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.lazyPut<TransactionRepositoryImpl>(
|
||||||
|
() => TransactionRepositoryImpl(),
|
||||||
|
fenix: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// UseCase
|
|
||||||
Get.lazyPut<TransactionUseCase>(
|
Get.lazyPut<TransactionUseCase>(
|
||||||
() => TransactionUseCase(Get.find<TransactionRepository>()),
|
() => TransactionUseCase(Get.find<TransactionRepository>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Controller
|
|
||||||
Get.lazyPut<TransactionController>(
|
Get.lazyPut<TransactionController>(
|
||||||
() => TransactionController(Get.find<TransactionUseCase>()),
|
() => TransactionController(Get.find<TransactionUseCase>()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TransactionExpiryService().initialize(Get.find<TransactionRepositoryImpl>());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,7 @@ abstract class TransactionRepository {
|
||||||
required String ticketId,
|
required String ticketId,
|
||||||
required String transactionId,
|
required String transactionId,
|
||||||
required File proofImage,
|
required File proofImage,
|
||||||
|
required String userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<TransactionModel>> getTransactionsByUserId(String userId);
|
Future<List<TransactionModel>> getTransactionsByUserId(String userId);
|
||||||
|
|
|
@ -55,11 +55,13 @@ class TransactionUseCase {
|
||||||
required String ticketId,
|
required String ticketId,
|
||||||
required String transactionId,
|
required String transactionId,
|
||||||
required File proofImage,
|
required File proofImage,
|
||||||
|
required String userId,
|
||||||
}) {
|
}) {
|
||||||
return _repository.uploadPaymentProof(
|
return _repository.uploadPaymentProof(
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
transactionId: transactionId,
|
transactionId: transactionId,
|
||||||
proofImage: proofImage,
|
proofImage: proofImage,
|
||||||
|
userId: userId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
import 'dart:developer';
|
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:e_porter/presentation/screens/routes/app_rountes.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '_core/service/transaction_expiry_service.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp();
|
await Firebase.initializeApp();
|
||||||
|
|
||||||
final transactionRepo = Get.put(TransactionRepositoryImpl());
|
|
||||||
TransactionExpiryService().initialize(transactionRepo);
|
|
||||||
|
|
||||||
log("Firebase Initialized Successfully!");
|
log("Firebase Initialized Successfully!");
|
||||||
runApp(MyApp(initialRoute: Routes.SPLASH));
|
runApp(MyApp(initialRoute: Routes.SPLASH));
|
||||||
}
|
}
|
||||||
|
@ -32,6 +27,7 @@ class MyApp extends StatelessWidget {
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
debugShowCheckedModeBanner: true,
|
debugShowCheckedModeBanner: true,
|
||||||
initialRoute: initialRoute,
|
initialRoute: initialRoute,
|
||||||
|
initialBinding: AppBinding(),
|
||||||
getPages: AppRoutes.routes,
|
getPages: AppRoutes.routes,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:e_porter/domain/models/transaction_model.dart';
|
import 'package:e_porter/domain/models/transaction_model.dart';
|
||||||
import 'package:e_porter/domain/usecases/transaction_usecase.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 {
|
class TransactionController extends GetxController {
|
||||||
final TransactionUseCase _transactionUseCase;
|
final TransactionUseCase _transactionUseCase;
|
||||||
|
@ -12,6 +14,8 @@ class TransactionController extends GetxController {
|
||||||
final Rx<TransactionModel?> currentTransaction = Rx<TransactionModel?>(null);
|
final Rx<TransactionModel?> currentTransaction = Rx<TransactionModel?>(null);
|
||||||
final RxBool isLoading = false.obs;
|
final RxBool isLoading = false.obs;
|
||||||
final RxString error = ''.obs;
|
final RxString error = ''.obs;
|
||||||
|
final RxBool isUploading = false.obs;
|
||||||
|
final RxDouble uploadProgress = 0.0.obs;
|
||||||
|
|
||||||
Future<String> createTransaction({
|
Future<String> createTransaction({
|
||||||
required String ticketId,
|
required String ticketId,
|
||||||
|
@ -46,7 +50,6 @@ class TransactionController extends GetxController {
|
||||||
numberSeat: numberSeat,
|
numberSeat: numberSeat,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ambil data transaksi setelah berhasil dibuat
|
|
||||||
final transaction = await _transactionUseCase.getTransactionById(
|
final transaction = await _transactionUseCase.getTransactionById(
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
transactionId: transactionId,
|
transactionId: transactionId,
|
||||||
|
@ -55,7 +58,7 @@ class TransactionController extends GetxController {
|
||||||
currentTransaction.value = transaction;
|
currentTransaction.value = transaction;
|
||||||
return transactionId;
|
return transactionId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal membuat transaksi: $e');
|
log('Gagal membuat transaksi: $e');
|
||||||
error.value = 'Gagal membuat transaksi: $e';
|
error.value = 'Gagal membuat transaksi: $e';
|
||||||
throw Exception('Gagal membuat transaksi: $e');
|
throw Exception('Gagal membuat transaksi: $e');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -63,26 +66,45 @@ class TransactionController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadPaymentProof(
|
Future<void> uploadPaymentProof({
|
||||||
{required String ticketId, required String transactionId, required File proofImage}) async {
|
required String ticketId,
|
||||||
|
required String transactionId,
|
||||||
|
required File proofImage,
|
||||||
|
required String userId,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isUploading.value = true;
|
||||||
error.value = '';
|
error.value = '';
|
||||||
|
uploadProgress.value = 0.0;
|
||||||
|
|
||||||
await _transactionUseCase.uploadPaymentProof(
|
await _transactionUseCase.uploadPaymentProof(
|
||||||
ticketId: ticketId, transactionId: transactionId, proofImage: proofImage);
|
ticketId: ticketId,
|
||||||
|
transactionId: transactionId,
|
||||||
|
proofImage: proofImage,
|
||||||
|
userId: userId,
|
||||||
|
);
|
||||||
|
|
||||||
// Refresh data transaksi setelah bukti pembayaran diunggah
|
// Update status transaksi setelah upload berhasil
|
||||||
final transaction =
|
await _transactionUseCase.updateTransactionStatus(
|
||||||
await _transactionUseCase.getTransactionById(ticketId: ticketId, transactionId: transactionId);
|
ticketId: ticketId,
|
||||||
|
transactionId: transactionId,
|
||||||
|
status: 'active',
|
||||||
|
userId: userId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Refresh data transaksi
|
||||||
|
final transaction = await _transactionUseCase.getTransactionById(
|
||||||
|
ticketId: ticketId,
|
||||||
|
transactionId: transactionId,
|
||||||
|
);
|
||||||
|
|
||||||
currentTransaction.value = transaction;
|
currentTransaction.value = transaction;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal mengunggah bukti pembayaran: $e');
|
error.value = 'Gagal mengupload bukti pembayaran: $e';
|
||||||
error.value = 'Gagal mengunggah bukti pembayaran: $e';
|
throw Exception('Gagal mengupload bukti pembayaran: $e');
|
||||||
throw Exception('Gagal mengunggah bukti pembayaran: $e');
|
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isUploading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +116,7 @@ class TransactionController extends GetxController {
|
||||||
final transactions = await _transactionUseCase.getTransactionsByUserId(userId);
|
final transactions = await _transactionUseCase.getTransactionsByUserId(userId);
|
||||||
return transactions;
|
return transactions;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal mendapatkan daftar transaksi: $e');
|
log('Gagal mendapatkan daftar transaksi: $e');
|
||||||
error.value = 'Gagal mendapatkan daftar transaksi: $e';
|
error.value = 'Gagal mendapatkan daftar transaksi: $e';
|
||||||
throw Exception('Gagal mendapatkan daftar transaksi: $e');
|
throw Exception('Gagal mendapatkan daftar transaksi: $e');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -112,7 +134,7 @@ class TransactionController extends GetxController {
|
||||||
|
|
||||||
currentTransaction.value = transaction;
|
currentTransaction.value = transaction;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal mendapatkan detail transaksi: $e');
|
log('Gagal mendapatkan detail transaksi: $e');
|
||||||
error.value = 'Gagal mendapatkan detail transaksi: $e';
|
error.value = 'Gagal mendapatkan detail transaksi: $e';
|
||||||
throw Exception('Gagal mendapatkan detail transaksi: $e');
|
throw Exception('Gagal mendapatkan detail transaksi: $e');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -127,12 +149,12 @@ class TransactionController extends GetxController {
|
||||||
currentTransaction.value = transaction;
|
currentTransaction.value = transaction;
|
||||||
},
|
},
|
||||||
onError: (e) {
|
onError: (e) {
|
||||||
logger.e('Error mendengarkan perubahan transaksi: $e');
|
log('Error mendengarkan perubahan transaksi: $e');
|
||||||
error.value = 'Error mendengarkan perubahan transaksi: $e';
|
error.value = 'Error mendengarkan perubahan transaksi: $e';
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal memantau transaksi: $e');
|
log('Gagal memantau transaksi: $e');
|
||||||
error.value = 'Gagal memantau transaksi: $e';
|
error.value = 'Gagal memantau transaksi: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,11 +184,28 @@ class TransactionController extends GetxController {
|
||||||
|
|
||||||
currentTransaction.value = transaction;
|
currentTransaction.value = transaction;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('Gagal mengupdate status transaksi: $e');
|
log('Gagal mengupdate status transaksi: $e');
|
||||||
error.value = 'Gagal mengupdate status transaksi: $e';
|
error.value = 'Gagal mengupdate status transaksi: $e';
|
||||||
throw Exception('Gagal mengupdate status transaksi: $e');
|
throw Exception('Gagal mengupdate status transaksi: $e');
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
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/appbar/appbar_component.dart';
|
||||||
import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
|
import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
|
@ -125,7 +127,14 @@ class _PaymentScreenState extends State<PaymentScreen> {
|
||||||
text: 'Lanjutkan',
|
text: 'Lanjutkan',
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
onTap: () {
|
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'] ?? {};
|
selectedPorterServices = args['selectedPorterServices'] ?? {};
|
||||||
|
|
||||||
fetchDataFlight();
|
fetchDataFlight();
|
||||||
|
transactionController.checkExpiredTransactions();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchDataFlight() async {
|
Future<void> fetchDataFlight() async {
|
||||||
|
@ -238,7 +239,7 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
|
||||||
|
|
||||||
// Persiapkan data expiry time
|
// Persiapkan data expiry time
|
||||||
final DateTime currentTime = DateTime.now();
|
final DateTime currentTime = DateTime.now();
|
||||||
final DateTime expiryTime = currentTime.add(Duration(seconds: 5));
|
final DateTime expiryTime = currentTime.add(Duration(days: 1));
|
||||||
|
|
||||||
// Persiapkan data bandara
|
// Persiapkan data bandara
|
||||||
final bandaraData = {
|
final bandaraData = {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
import 'package:e_porter/_core/constants/typography.dart';
|
import 'package:e_porter/_core/constants/typography.dart';
|
||||||
import 'package:e_porter/_core/service/permission_service.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/appbar/appbar_component.dart';
|
||||||
import '../../../../_core/component/button/button_fill.dart';
|
import '../../../../_core/component/button/button_fill.dart';
|
||||||
import '../../../../_core/component/card/custome_shadow_cotainner.dart';
|
import '../../../../_core/component/card/custome_shadow_cotainner.dart';
|
||||||
|
import '../../../../_core/service/preferences_service.dart';
|
||||||
import '../../../../domain/models/upload_file_model.dart';
|
import '../../../../domain/models/upload_file_model.dart';
|
||||||
|
import '../../../controllers/transaction_controller.dart';
|
||||||
|
import '../../routes/app_rountes.dart';
|
||||||
|
|
||||||
class UploadFileScreen extends StatefulWidget {
|
class UploadFileScreen extends StatefulWidget {
|
||||||
const UploadFileScreen({super.key});
|
const UploadFileScreen({super.key});
|
||||||
|
@ -22,8 +27,100 @@ class UploadFileScreen extends StatefulWidget {
|
||||||
|
|
||||||
class _UploadFileScreenState extends State<UploadFileScreen> {
|
class _UploadFileScreenState extends State<UploadFileScreen> {
|
||||||
final List<UploadFileModel> uploadedFiles = [];
|
final List<UploadFileModel> uploadedFiles = [];
|
||||||
|
final TransactionController _transactionController = Get.find<TransactionController>();
|
||||||
bool isUploading = false;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -225,52 +322,51 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickFile() async {
|
Future<void> _pickFile() async {
|
||||||
bool permissionGranted = await PermissionHelper.requestStoragePermission();
|
bool permissionGranted = await PermissionHelper.requestStoragePermission();
|
||||||
if (!permissionGranted) {
|
if (!permissionGranted) {
|
||||||
Get.snackbar(
|
Get.snackbar(
|
||||||
'Permission Denied',
|
'Permission Denied',
|
||||||
'Storage permission is required to select files.',
|
'Storage permission is required to select files.',
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
colorText: Colors.white,
|
colorText: Colors.white,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf', 'zip', 'mp4'],
|
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf'],
|
||||||
);
|
|
||||||
|
|
||||||
if (result != null) {
|
|
||||||
final file = result.files.first;
|
|
||||||
final newFile = UploadFileModel(
|
|
||||||
fileName: file.name,
|
|
||||||
filePath: file.path!,
|
|
||||||
fileSize: file.size,
|
|
||||||
progress: 0,
|
|
||||||
remainingTime: '0',
|
|
||||||
status: FileUploadStatus.pending,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
setState(() {
|
if (result != null) {
|
||||||
uploadedFiles.add(newFile);
|
final file = result.files.first;
|
||||||
});
|
final newFile = UploadFileModel(
|
||||||
|
fileName: file.name,
|
||||||
|
filePath: file.path!,
|
||||||
|
fileSize: file.size,
|
||||||
|
progress: 0,
|
||||||
|
remainingTime: '0',
|
||||||
|
status: FileUploadStatus.pending,
|
||||||
|
);
|
||||||
|
|
||||||
// Simulasikan proses loading lokal
|
setState(() {
|
||||||
_simulateLocalLoading(newFile);
|
uploadedFiles.add(newFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulasikan proses loading lokal
|
||||||
|
_simulateLocalLoading(newFile);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error picking file: $e');
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'Terjadi kesalahan saat memilih file',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print('Error picking file: $e');
|
|
||||||
Get.snackbar(
|
|
||||||
'Error',
|
|
||||||
'Terjadi kesalahan saat memilih file',
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void _simulateLocalLoading(UploadFileModel file) {
|
void _simulateLocalLoading(UploadFileModel file) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -349,31 +445,4 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
||||||
_uploadToServer();
|
_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
|
path: ^1.9.0
|
||||||
permission_handler: ^11.4.0
|
permission_handler: ^11.4.0
|
||||||
device_info_plus: ^11.3.0
|
device_info_plus: ^11.3.0
|
||||||
|
# workmanager: ^0.5.2
|
||||||
|
|
||||||
# pin_code_fields: ^8.0.1
|
# pin_code_fields: ^8.0.1
|
||||||
# dio: ^5.8.0+1
|
# dio: ^5.8.0+1
|
||||||
|
|
Loading…
Reference in New Issue