466 lines
14 KiB
Dart
466 lines
14 KiB
Dart
import 'package:get/get.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:penyaluran_app/app/data/models/donatur_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/skema_bantuan_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/penyaluran_bantuan_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/laporan_penyaluran_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
|
import 'package:penyaluran_app/app/data/models/stok_bantuan_model.dart';
|
|
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
|
|
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
|
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'dart:io';
|
|
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
class DonaturDashboardController extends GetxController {
|
|
final AuthController _authController = Get.find<AuthController>();
|
|
final SupabaseService _supabaseService = SupabaseService.to;
|
|
|
|
final Rx<BaseUserModel?> currentUser = Rx<BaseUserModel?>(null);
|
|
|
|
// Variabel untuk foto profil
|
|
final RxString fotoProfil = ''.obs;
|
|
|
|
// Indeks tab yang aktif di bottom navigation bar
|
|
final RxInt activeTabIndex = 0.obs;
|
|
|
|
// Data untuk skema bantuan tersedia
|
|
final RxList<SkemaBantuanModel> skemaBantuan = <SkemaBantuanModel>[].obs;
|
|
|
|
// Data untuk jadwal penyaluran
|
|
final RxList<PenyaluranBantuanModel> jadwalPenyaluran =
|
|
<PenyaluranBantuanModel>[].obs;
|
|
|
|
// Data untuk riwayat penitipan bantuan
|
|
final RxList<PenitipanBantuanModel> penitipanBantuan =
|
|
<PenitipanBantuanModel>[].obs;
|
|
|
|
// Data untuk laporan penyaluran
|
|
final RxList<LaporanPenyaluranModel> laporanPenyaluran =
|
|
<LaporanPenyaluranModel>[].obs;
|
|
|
|
// Data untuk stok bantuan yang tersedia
|
|
final RxList<StokBantuanModel> stokBantuan = <StokBantuanModel>[].obs;
|
|
|
|
// Indikator loading
|
|
final RxBool isLoading = false.obs;
|
|
|
|
// Jumlah notifikasi belum dibaca
|
|
final RxInt jumlahNotifikasiBelumDibaca = 0.obs;
|
|
|
|
// Data untuk foto bantuan pada form penitipan
|
|
final RxList<String> fotoBantuanPaths = <String>[].obs;
|
|
final ImagePicker _imagePicker = ImagePicker();
|
|
|
|
// Getter untuk data user
|
|
BaseUserModel? get user => _authController.baseUser;
|
|
String get role => user?.role ?? 'DONATUR';
|
|
String get nama {
|
|
// Gunakan namaLengkap dari roleData jika tersedia
|
|
if (_authController.isDonatur && _authController.roleData != null) {
|
|
return _authController.roleData.namaLengkap ??
|
|
_authController.displayName;
|
|
}
|
|
// Gunakan displayName dari AuthController
|
|
return _authController.displayName;
|
|
}
|
|
|
|
String? get desa => user?.desa?.nama;
|
|
|
|
// Getter untuk alamat dan noHp
|
|
String? get alamat {
|
|
if (_authController.isDonatur && _authController.roleData != null) {
|
|
return (_authController.roleData as DonaturModel).alamat;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
String? get noHp {
|
|
if (_authController.isDonatur && _authController.roleData != null) {
|
|
return (_authController.roleData as DonaturModel).noHp;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Getter untuk jenis donatur
|
|
String? get jenis {
|
|
if (_authController.isDonatur && _authController.roleData != null) {
|
|
return (_authController.roleData as DonaturModel).jenis;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Getter untuk foto profil
|
|
String? get profilePhotoUrl {
|
|
// 1. Coba ambil dari fotoProfil yang sudah disimpan
|
|
if (fotoProfil.isNotEmpty) {
|
|
return fotoProfil.value;
|
|
}
|
|
|
|
// 2. Coba ambil dari roleData jika merupakan DonaturModel
|
|
if (_authController.isDonatur && _authController.roleData != null) {
|
|
final donaturData = _authController.roleData as DonaturModel;
|
|
if (donaturData.fotoProfil != null &&
|
|
donaturData.fotoProfil!.isNotEmpty) {
|
|
return donaturData.fotoProfil;
|
|
}
|
|
}
|
|
|
|
// 3. Coba ambil dari userData.roleData.fotoProfil
|
|
final userData = _authController.userData;
|
|
if (userData != null && userData.roleData is DonaturModel) {
|
|
final donaturData = userData.roleData as DonaturModel;
|
|
if (donaturData.fotoProfil != null &&
|
|
donaturData.fotoProfil!.isNotEmpty) {
|
|
return donaturData.fotoProfil;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
fetchData();
|
|
loadUserData();
|
|
}
|
|
|
|
@override
|
|
void onReady() {
|
|
super.onReady();
|
|
// Perbarui data user dan foto profil saat halaman siap
|
|
loadUserData();
|
|
}
|
|
|
|
void loadUserData() {
|
|
currentUser.value = _authController.baseUser;
|
|
|
|
if (_authController.userData != null) {
|
|
if (_authController.isDonatur) {
|
|
var donaturData = _authController.roleData;
|
|
|
|
// Ambil foto profil dari donaturData jika ada
|
|
if (donaturData != null &&
|
|
donaturData.fotoProfil != null &&
|
|
donaturData.fotoProfil!.isNotEmpty) {
|
|
fotoProfil.value = donaturData.fotoProfil!;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ambil foto profil dari database
|
|
_fetchProfilePhoto();
|
|
}
|
|
|
|
// Metode untuk mengambil foto profil
|
|
Future<void> _fetchProfilePhoto() async {
|
|
try {
|
|
if (user?.id == null) return;
|
|
|
|
final donaturData = await _supabaseService.client
|
|
.from('donatur')
|
|
.select('foto_profil')
|
|
.eq('id', user!.id)
|
|
.maybeSingle();
|
|
|
|
if (donaturData != null && donaturData['foto_profil'] != null) {
|
|
fotoProfil.value = donaturData['foto_profil'];
|
|
}
|
|
} catch (e) {
|
|
print('Error fetching profile photo: $e');
|
|
}
|
|
}
|
|
|
|
void fetchData() async {
|
|
isLoading.value = true;
|
|
|
|
try {
|
|
// Pastikan user sudah login dan memiliki ID
|
|
if (user?.id == null) {
|
|
throw Exception('User tidak terautentikasi');
|
|
}
|
|
|
|
// Ambil data skema bantuan
|
|
await fetchSkemaBantuan();
|
|
|
|
// Ambil data jadwal penyaluran
|
|
await fetchJadwalPenyaluran();
|
|
|
|
// Ambil data penitipan bantuan
|
|
await fetchPenitipanBantuan();
|
|
|
|
// Ambil data laporan penyaluran
|
|
await fetchLaporanPenyaluran();
|
|
|
|
// Ambil data stok bantuan
|
|
await fetchStokBantuan();
|
|
|
|
// Ambil data notifikasi
|
|
await fetchNotifikasi();
|
|
} catch (e) {
|
|
print('Error fetching data: $e');
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
// Ambil data skema bantuan
|
|
Future<void> fetchSkemaBantuan() async {
|
|
try {
|
|
final response = await _supabaseService.client
|
|
.from('xx02_skema_bantuan')
|
|
.select()
|
|
.order('created_at', ascending: false);
|
|
|
|
skemaBantuan.value = response
|
|
.map((data) => SkemaBantuanModel.fromJson(data))
|
|
.toList()
|
|
.cast<SkemaBantuanModel>();
|
|
} catch (e) {
|
|
print('Error fetching skema bantuan: $e');
|
|
}
|
|
}
|
|
|
|
// Ambil data jadwal penyaluran
|
|
Future<void> fetchJadwalPenyaluran() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final response = await _supabaseService.client
|
|
.from('penyaluran_bantuan')
|
|
.select()
|
|
.order('tanggal_penyaluran', ascending: true);
|
|
|
|
// Konversi ke model lalu filter di sisi client
|
|
final allJadwal = response
|
|
.map((data) => PenyaluranBantuanModel.fromJson(data))
|
|
.toList()
|
|
.cast<PenyaluranBantuanModel>();
|
|
|
|
// Filter jadwal yang tanggalnya lebih besar dari hari ini
|
|
jadwalPenyaluran.value = allJadwal
|
|
.where((jadwal) =>
|
|
jadwal.tanggalPenyaluran != null &&
|
|
jadwal.tanggalPenyaluran!.isAfter(now))
|
|
.toList();
|
|
} catch (e) {
|
|
print('Error fetching jadwal penyaluran: $e');
|
|
}
|
|
}
|
|
|
|
// Ambil data penitipan bantuan
|
|
Future<void> fetchPenitipanBantuan() async {
|
|
try {
|
|
if (user?.id == null) return;
|
|
|
|
final response = await _supabaseService.client
|
|
.from('penitipan_bantuan')
|
|
.select('*, donatur(*), stok_bantuan:stok_bantuan_id(*)')
|
|
.eq('donatur_id', user!.id)
|
|
.order('created_at', ascending: false);
|
|
|
|
penitipanBantuan.value = response
|
|
.map((data) => PenitipanBantuanModel.fromJson(data))
|
|
.toList()
|
|
.cast<PenitipanBantuanModel>();
|
|
} catch (e) {
|
|
print('Error fetching penitipan bantuan: $e');
|
|
}
|
|
}
|
|
|
|
// Ambil data laporan penyaluran
|
|
Future<void> fetchLaporanPenyaluran() async {
|
|
try {
|
|
final response = await _supabaseService.client
|
|
.from('laporan_penyaluran')
|
|
.select()
|
|
.order('created_at', ascending: false);
|
|
|
|
laporanPenyaluran.value = response
|
|
.map((data) => LaporanPenyaluranModel.fromJson(data))
|
|
.toList()
|
|
.cast<LaporanPenyaluranModel>();
|
|
} catch (e) {
|
|
print('Error fetching laporan penyaluran: $e');
|
|
}
|
|
}
|
|
|
|
// Ambil data stok bantuan
|
|
Future<void> fetchStokBantuan() async {
|
|
try {
|
|
final response = await _supabaseService.client
|
|
.from('stok_bantuan')
|
|
.select('*, kategori_bantuan:kategori_bantuan_id(*)')
|
|
.order('nama', ascending: true);
|
|
|
|
stokBantuan.value = response
|
|
.map((data) => StokBantuanModel.fromJson(data))
|
|
.toList()
|
|
.cast<StokBantuanModel>();
|
|
} catch (e) {
|
|
print('Error fetching stok bantuan: $e');
|
|
}
|
|
}
|
|
|
|
// Ambil data notifikasi
|
|
Future<void> fetchNotifikasi() async {
|
|
try {
|
|
if (user?.id == null) return;
|
|
|
|
final response = await _supabaseService.client
|
|
.from('notifikasi')
|
|
.select('*')
|
|
.eq('user_id', user!.id)
|
|
.eq('is_read', false)
|
|
.count();
|
|
|
|
jumlahNotifikasiBelumDibaca.value = response.count ?? 0;
|
|
} catch (e) {
|
|
print('Error fetching notifikasi: $e');
|
|
}
|
|
}
|
|
|
|
// Fungsi untuk logout
|
|
void logout() async {
|
|
try {
|
|
await _authController.logout();
|
|
Get.offAllNamed(Routes.login);
|
|
} catch (e) {
|
|
print('Error during logout: $e');
|
|
Get.snackbar(
|
|
'Error',
|
|
'Terjadi kesalahan saat logout: $e',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Mendapatkan daftar stok bantuan yang tersedia
|
|
List<StokBantuanModel> getAvailableStokBantuan() {
|
|
// Filter stok bantuan yang jumlahnya lebih dari 0
|
|
return stokBantuan.where((stok) => (stok.totalStok ?? 0) > 0).toList();
|
|
}
|
|
|
|
// Ambil gambar dari kamera atau galeri
|
|
Future<void> pickImage({required bool isCamera}) async {
|
|
try {
|
|
final source = isCamera ? ImageSource.camera : ImageSource.gallery;
|
|
final pickedFile = await _imagePicker.pickImage(
|
|
source: source,
|
|
imageQuality: 70, // Kurangi kualitas untuk menghemat ukuran
|
|
);
|
|
|
|
if (pickedFile != null) {
|
|
fotoBantuanPaths.add(pickedFile.path);
|
|
}
|
|
} catch (e) {
|
|
print('Error picking image: $e');
|
|
Get.snackbar(
|
|
'Gagal',
|
|
'Terjadi kesalahan saat mengambil gambar',
|
|
backgroundColor: Colors.red,
|
|
colorText: Colors.white,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Hapus foto bantuan dari daftar
|
|
void removeFotoBantuan(int index) {
|
|
if (index >= 0 && index < fotoBantuanPaths.length) {
|
|
fotoBantuanPaths.removeAt(index);
|
|
}
|
|
}
|
|
|
|
// Reset foto bantuan paths
|
|
void resetFotoBantuan() {
|
|
fotoBantuanPaths.clear();
|
|
}
|
|
|
|
// Membuat penitipan bantuan baru
|
|
Future<void> createPenitipanBantuan(
|
|
String? stokBantuanId,
|
|
double jumlah,
|
|
String deskripsi,
|
|
String? skemaBantuanId,
|
|
) async {
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
if (user?.id == null) {
|
|
throw Exception('User tidak terautentikasi');
|
|
}
|
|
|
|
if (stokBantuanId == null) {
|
|
throw Exception('Stok bantuan harus dipilih');
|
|
}
|
|
|
|
if (fotoBantuanPaths.isEmpty) {
|
|
throw Exception('Foto bantuan harus diunggah');
|
|
}
|
|
|
|
// Dapatkan informasi stok bantuan untuk mendapatkan nilai is_uang
|
|
final selectedStokBantuan = stokBantuan.firstWhere(
|
|
(stok) => stok.id == stokBantuanId,
|
|
orElse: () => StokBantuanModel(),
|
|
);
|
|
|
|
// Unggah foto bantuan ke storage menggunakan metode dari SupabaseService
|
|
final fotoBantuanUrls = await _supabaseService.uploadMultipleFiles(
|
|
fotoBantuanPaths, 'bantuan', 'foto_bantuan');
|
|
|
|
if (fotoBantuanUrls == null || fotoBantuanUrls.isEmpty) {
|
|
throw 'Gagal mengupload foto bantuan';
|
|
}
|
|
|
|
// Data yang akan disimpan
|
|
final Map<String, dynamic> data = {
|
|
'donatur_id': user!.id,
|
|
'stok_bantuan_id': stokBantuanId,
|
|
'jumlah': jumlah,
|
|
'deskripsi': deskripsi,
|
|
'status': 'MENUNGGU',
|
|
'tanggal_penitipan': DateTime.now().toIso8601String(),
|
|
'foto_bantuan': fotoBantuanUrls,
|
|
'is_uang': selectedStokBantuan.isUang ?? false,
|
|
};
|
|
|
|
// Tambahkan skema bantuan jika ada
|
|
if (skemaBantuanId != null && skemaBantuanId.isNotEmpty) {
|
|
data['skema_bantuan_id'] = skemaBantuanId;
|
|
}
|
|
|
|
// Simpan ke database
|
|
await _supabaseService.client.from('penitipan_bantuan').insert(data);
|
|
|
|
// Reset foto bantuan setelah berhasil disimpan
|
|
resetFotoBantuan();
|
|
|
|
// Ambil data penitipan bantuan yang baru
|
|
await fetchPenitipanBantuan();
|
|
|
|
// Tampilkan pesan sukses
|
|
Get.snackbar(
|
|
'Berhasil',
|
|
'Penitipan bantuan berhasil dikirim dan akan diproses oleh petugas desa',
|
|
backgroundColor: Colors.green,
|
|
colorText: Colors.white,
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
} catch (e) {
|
|
print('Error creating penitipan bantuan: $e');
|
|
Get.snackbar(
|
|
'Gagal',
|
|
'Terjadi kesalahan saat mengirim penitipan bantuan: $e',
|
|
backgroundColor: Colors.red,
|
|
colorText: Colors.white,
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
}
|