Perbarui dependensi dan tambahkan fungsionalitas laporan penyaluran. Tambahkan paket baru seperti file_picker, pdf, dan open_file ke dalam pubspec.yaml. Hapus model LaporanModel yang tidak digunakan dan ganti dengan LaporanPenyaluranModel. Modifikasi tampilan dan controller untuk mendukung pengelolaan laporan penyaluran, termasuk navigasi dan ekspor ke PDF. Perbarui rute aplikasi untuk mencakup halaman laporan penyaluran baru.
This commit is contained in:
@ -2,9 +2,11 @@ import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penyaluran_bantuan_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/skema_bantuan_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/laporan_penyaluran_model.dart';
|
||||
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
import 'package:penyaluran_app/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart';
|
||||
|
||||
class DetailPenyaluranController extends GetxController {
|
||||
final SupabaseService _supabaseService = Get.find<SupabaseService>();
|
||||
@ -14,10 +16,16 @@ class DetailPenyaluranController extends GetxController {
|
||||
final penyaluran = Rx<PenyaluranBantuanModel?>(null);
|
||||
final skemaBantuan = Rx<SkemaBantuanModel?>(null);
|
||||
final penerimaPenyaluran = <PenerimaPenyaluranModel>[].obs;
|
||||
final laporan = Rx<LaporanPenyaluranModel?>(null);
|
||||
final isLoadingLaporan = false.obs;
|
||||
|
||||
// Status untuk mengetahui apakah petugas desa
|
||||
final isPetugasDesa = false.obs;
|
||||
|
||||
// Tambahkan referensi ke controller laporan
|
||||
LaporanPenyaluranController? laporanController;
|
||||
final RxBool isExporting = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -432,6 +440,9 @@ class DetailPenyaluranController extends GetxController {
|
||||
}
|
||||
penerimaPenyaluran.assignAll(penerima);
|
||||
|
||||
// Periksa apakah ada laporan untuk penyaluran ini
|
||||
await checkLaporanPenyaluran(penyaluranId);
|
||||
|
||||
// if (penerima.isNotEmpty) {
|
||||
// print('DetailPenyaluranController - ID penerima: ${penerima[0].id}');
|
||||
// }
|
||||
@ -446,6 +457,57 @@ class DetailPenyaluranController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkLaporanPenyaluran(String penyaluranId) async {
|
||||
try {
|
||||
isLoadingLaporan.value = true;
|
||||
|
||||
final response = await _supabaseService.client
|
||||
.from('laporan_penyaluran')
|
||||
.select('*')
|
||||
.eq('penyaluran_bantuan_id', penyaluranId)
|
||||
.maybeSingle();
|
||||
|
||||
if (response != null) {
|
||||
// Laporan ditemukan
|
||||
laporan.value = LaporanPenyaluranModel.fromJson(response);
|
||||
} else {
|
||||
// Tidak ada laporan
|
||||
laporan.value = null;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error saat memeriksa laporan penyaluran: $e');
|
||||
laporan.value = null;
|
||||
} finally {
|
||||
isLoadingLaporan.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void navigateToLaporanCreate() {
|
||||
if (penyaluran.value?.id == null) return;
|
||||
|
||||
// Kirim ID penyaluran langsung sebagai argument (String), bukan dalam bentuk Map
|
||||
Get.toNamed('/laporan-penyaluran/create', arguments: penyaluran.value!.id)
|
||||
?.then((value) {
|
||||
if (value == true) {
|
||||
// Refresh data setelah membuat laporan
|
||||
checkLaporanPenyaluran(penyaluran.value!.id!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void navigateToLaporanDetail() {
|
||||
if (laporan.value?.id == null) return;
|
||||
|
||||
// Navigasi ke halaman detail dengan mengirimkan ID sebagai argument
|
||||
Get.toNamed('/laporan-penyaluran/detail', arguments: laporan.value!.id)
|
||||
?.then((value) {
|
||||
if (value == true) {
|
||||
// Refresh data setelah melihat detail laporan
|
||||
checkLaporanPenyaluran(penyaluran.value!.id!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Metode untuk verifikasi penerima berdasarkan QR code
|
||||
Future<bool> verifikasiPenerimaByQrCode(
|
||||
String penyaluranId, String qrHash) async {
|
||||
@ -498,4 +560,53 @@ class DetailPenyaluranController extends GetxController {
|
||||
isProcessing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Method untuk memuat controller laporan
|
||||
Future<void> loadLaporanPenyaluranController() async {
|
||||
if (laporanController == null) {
|
||||
// Cek apakah controller sudah ada di Get
|
||||
if (Get.isRegistered<LaporanPenyaluranController>()) {
|
||||
laporanController = Get.find<LaporanPenyaluranController>();
|
||||
} else {
|
||||
// Jika belum ada, buat instance baru
|
||||
laporanController = Get.put(LaporanPenyaluranController());
|
||||
}
|
||||
}
|
||||
|
||||
// Pastikan data laporan dimuat
|
||||
if (laporan.value != null && penyaluran.value != null) {
|
||||
await laporanController!.fetchLaporanDetail(laporan.value!.id!);
|
||||
}
|
||||
}
|
||||
|
||||
// Method untuk export PDF
|
||||
Future<void> exportToPdf() async {
|
||||
if (laporan.value == null || penyaluran.value == null) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Data laporan atau penyaluran tidak tersedia',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isExporting.value = true;
|
||||
try {
|
||||
await loadLaporanPenyaluranController();
|
||||
await laporanController!.exportToPdf(laporan.value!, penyaluran.value!);
|
||||
} catch (e) {
|
||||
print('Error saat export PDF: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengekspor laporan ke PDF',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
} finally {
|
||||
isExporting.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,197 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/laporan_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
||||
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
|
||||
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
||||
|
||||
class LaporanController extends GetxController {
|
||||
final AuthController _authController = Get.find<AuthController>();
|
||||
final SupabaseService _supabaseService = SupabaseService.to;
|
||||
|
||||
final RxBool isLoading = false.obs;
|
||||
|
||||
// Indeks kategori yang dipilih untuk filter
|
||||
final RxInt selectedCategoryIndex = 0.obs;
|
||||
|
||||
// Data untuk laporan
|
||||
final RxList<LaporanModel> daftarLaporan = <LaporanModel>[].obs;
|
||||
|
||||
// Filter tanggal
|
||||
final Rx<DateTime?> tanggalMulai = Rx<DateTime?>(null);
|
||||
final Rx<DateTime?> tanggalSelesai = Rx<DateTime?>(null);
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// Set default tanggal filter (1 bulan terakhir)
|
||||
tanggalSelesai.value = DateTime.now();
|
||||
tanggalMulai.value = DateTime.now().subtract(const Duration(days: 30));
|
||||
loadLaporanData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadLaporanData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final laporanData = await _supabaseService.getLaporan(
|
||||
tanggalMulai.value,
|
||||
tanggalSelesai.value,
|
||||
);
|
||||
if (laporanData != null) {
|
||||
daftarLaporan.value =
|
||||
laporanData.map((data) => LaporanModel.fromJson(data)).toList();
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading laporan data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> generateLaporan(String jenis) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final laporan = LaporanModel(
|
||||
jenis: jenis,
|
||||
tanggalMulai: tanggalMulai.value,
|
||||
tanggalSelesai: tanggalSelesai.value,
|
||||
petugasId: user?.id,
|
||||
createdAt: DateTime.now(),
|
||||
);
|
||||
|
||||
final laporanId =
|
||||
await _supabaseService.generateLaporan(laporan.toJson());
|
||||
|
||||
if (laporanId != null) {
|
||||
await loadLaporanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Laporan berhasil dibuat',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error generating laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal membuat laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> downloadLaporan(String laporanId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final url = await _supabaseService.downloadLaporan(laporanId);
|
||||
if (url != null) {
|
||||
// Implementasi download file
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Laporan berhasil diunduh',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error downloading laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengunduh laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteLaporan(String laporanId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.deleteLaporan(laporanId);
|
||||
await loadLaporanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Laporan berhasil dihapus',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deleting laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menghapus laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void setTanggalMulai(DateTime tanggal) {
|
||||
tanggalMulai.value = tanggal;
|
||||
}
|
||||
|
||||
void setTanggalSelesai(DateTime tanggal) {
|
||||
tanggalSelesai.value = tanggal;
|
||||
}
|
||||
|
||||
Future<void> applyFilter() async {
|
||||
await loadLaporanData();
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadLaporanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void changeCategory(int index) {
|
||||
selectedCategoryIndex.value = index;
|
||||
}
|
||||
|
||||
List<LaporanModel> getFilteredLaporan() {
|
||||
switch (selectedCategoryIndex.value) {
|
||||
case 0:
|
||||
return daftarLaporan;
|
||||
case 1:
|
||||
return daftarLaporan
|
||||
.where((item) => item.jenis == 'PENYALURAN')
|
||||
.toList();
|
||||
case 2:
|
||||
return daftarLaporan
|
||||
.where((item) => item.jenis == 'STOK_BANTUAN')
|
||||
.toList();
|
||||
case 3:
|
||||
return daftarLaporan.where((item) => item.jenis == 'PENERIMA').toList();
|
||||
default:
|
||||
return daftarLaporan;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user