ambil data stok bantuan
This commit is contained in:
@ -0,0 +1,187 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penyaluran_bantuan_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 JadwalPenyaluranController 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 jadwal
|
||||
final RxList<PenyaluranBantuanModel> jadwalHariIni =
|
||||
<PenyaluranBantuanModel>[].obs;
|
||||
final RxList<PenyaluranBantuanModel> jadwalMendatang =
|
||||
<PenyaluranBantuanModel>[].obs;
|
||||
final RxList<PenyaluranBantuanModel> jadwalSelesai =
|
||||
<PenyaluranBantuanModel>[].obs;
|
||||
|
||||
// Data untuk permintaan penjadwalan
|
||||
final RxList<PenyaluranBantuanModel> permintaanPenjadwalan =
|
||||
<PenyaluranBantuanModel>[].obs;
|
||||
final RxInt jumlahPermintaanPenjadwalan = 0.obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadJadwalData();
|
||||
loadPermintaanPenjadwalanData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadJadwalData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// Mengambil data jadwal hari ini
|
||||
final jadwalHariIniData = await _supabaseService.getJadwalHariIni();
|
||||
if (jadwalHariIniData != null) {
|
||||
jadwalHariIni.value = jadwalHariIniData
|
||||
.map((data) => PenyaluranBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Mengambil data jadwal mendatang
|
||||
final jadwalMendatangData = await _supabaseService.getJadwalMendatang();
|
||||
if (jadwalMendatangData != null) {
|
||||
jadwalMendatang.value = jadwalMendatangData
|
||||
.map((data) => PenyaluranBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Mengambil data jadwal selesai
|
||||
final jadwalSelesaiData = await _supabaseService.getJadwalSelesai();
|
||||
if (jadwalSelesaiData != null) {
|
||||
jadwalSelesai.value = jadwalSelesaiData
|
||||
.map((data) => PenyaluranBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading jadwal data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadPermintaanPenjadwalanData() async {
|
||||
try {
|
||||
final permintaanData = await _supabaseService.getPermintaanPenjadwalan();
|
||||
if (permintaanData != null) {
|
||||
permintaanPenjadwalan.value = permintaanData
|
||||
.map((data) => PenyaluranBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading permintaan penjadwalan data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> approveJadwal(String jadwalId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.approveJadwal(jadwalId);
|
||||
await loadPermintaanPenjadwalanData();
|
||||
await loadJadwalData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Jadwal berhasil disetujui',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error approving jadwal: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menyetujui jadwal: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> rejectJadwal(String jadwalId, String alasan) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.rejectJadwal(jadwalId, alasan);
|
||||
await loadPermintaanPenjadwalanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Jadwal berhasil ditolak',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error rejecting jadwal: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menolak jadwal: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> completeJadwal(String jadwalId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.completeJadwal(jadwalId);
|
||||
await loadJadwalData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Jadwal berhasil diselesaikan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error completing jadwal: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menyelesaikan jadwal: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadJadwalData();
|
||||
await loadPermintaanPenjadwalanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void changeCategory(int index) {
|
||||
selectedCategoryIndex.value = index;
|
||||
}
|
||||
}
|
197
lib/app/modules/petugas_desa/controllers/laporan_controller.dart
Normal file
197
lib/app/modules/petugas_desa/controllers/laporan_controller.dart
Normal file
@ -0,0 +1,197 @@
|
||||
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.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error generating laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal membuat laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
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.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error downloading laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengunduh laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
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.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deleting laporan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menghapus laporan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/warga_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 PenerimaBantuanController 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 penerima bantuan
|
||||
final RxList<WargaModel> daftarPenerima = <WargaModel>[].obs;
|
||||
final RxInt totalPenerima = 0.obs;
|
||||
final RxInt totalPenerimaAktif = 0.obs;
|
||||
final RxInt totalPenerimaNonaktif = 0.obs;
|
||||
|
||||
// Controller untuk pencarian dan form
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
final TextEditingController namaController = TextEditingController();
|
||||
final TextEditingController nikController = TextEditingController();
|
||||
final TextEditingController alamatController = TextEditingController();
|
||||
final TextEditingController teleponController = TextEditingController();
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
final TextEditingController catatanController = TextEditingController();
|
||||
|
||||
// Form key
|
||||
final GlobalKey<FormState> penerimaFormKey = GlobalKey<FormState>();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadPenerimaData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
namaController.dispose();
|
||||
nikController.dispose();
|
||||
alamatController.dispose();
|
||||
teleponController.dispose();
|
||||
emailController.dispose();
|
||||
catatanController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadPenerimaData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final penerimaData = await _supabaseService.getPenerimaBantuan();
|
||||
if (penerimaData != null) {
|
||||
daftarPenerima.value =
|
||||
penerimaData.map((data) => WargaModel.fromJson(data)).toList();
|
||||
|
||||
// Hitung total
|
||||
totalPenerima.value = daftarPenerima.length;
|
||||
totalPenerimaAktif.value =
|
||||
daftarPenerima.where((item) => item.status == 'AKTIF').length;
|
||||
totalPenerimaNonaktif.value =
|
||||
daftarPenerima.where((item) => item.status == 'NONAKTIF').length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading penerima data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tambahPenerima() async {
|
||||
if (!penerimaFormKey.currentState!.validate()) return;
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final penerima = WargaModel(
|
||||
nama: namaController.text,
|
||||
nik: nikController.text,
|
||||
alamat: alamatController.text,
|
||||
telepon: teleponController.text,
|
||||
email: emailController.text,
|
||||
catatan: catatanController.text,
|
||||
status: 'AKTIF',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
await _supabaseService.tambahPenerima(penerima.toJson());
|
||||
|
||||
// Clear form
|
||||
clearForm();
|
||||
|
||||
await loadPenerimaData();
|
||||
Get.back(); // Close dialog
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penerima bantuan berhasil ditambahkan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error adding penerima: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menambahkan penerima bantuan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updatePenerima(String penerimaId) async {
|
||||
if (!penerimaFormKey.currentState!.validate()) return;
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final penerima = WargaModel(
|
||||
id: penerimaId,
|
||||
nama: namaController.text,
|
||||
nik: nikController.text,
|
||||
alamat: alamatController.text,
|
||||
telepon: teleponController.text,
|
||||
email: emailController.text,
|
||||
catatan: catatanController.text,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
await _supabaseService.updatePenerima(penerimaId, penerima.toJson());
|
||||
|
||||
// Clear form
|
||||
clearForm();
|
||||
|
||||
await loadPenerimaData();
|
||||
Get.back(); // Close dialog
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penerima bantuan berhasil diperbarui',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error updating penerima: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memperbarui penerima bantuan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> nonaktifkanPenerima(String penerimaId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.updateStatusPenerima(penerimaId, 'NONAKTIF');
|
||||
await loadPenerimaData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penerima bantuan berhasil dinonaktifkan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deactivating penerima: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menonaktifkan penerima bantuan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> aktifkanPenerima(String penerimaId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.updateStatusPenerima(penerimaId, 'AKTIF');
|
||||
await loadPenerimaData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penerima bantuan berhasil diaktifkan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error activating penerima: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal mengaktifkan penerima bantuan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void setFormData(WargaModel penerima) {
|
||||
namaController.text = penerima.nama ?? '';
|
||||
nikController.text = penerima.nik ?? '';
|
||||
alamatController.text = penerima.alamat ?? '';
|
||||
teleponController.text = penerima.telepon ?? '';
|
||||
emailController.text = penerima.email ?? '';
|
||||
catatanController.text = penerima.catatan ?? '';
|
||||
}
|
||||
|
||||
void clearForm() {
|
||||
namaController.clear();
|
||||
nikController.clear();
|
||||
alamatController.clear();
|
||||
teleponController.clear();
|
||||
emailController.clear();
|
||||
catatanController.clear();
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadPenerimaData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void changeCategory(int index) {
|
||||
selectedCategoryIndex.value = index;
|
||||
}
|
||||
|
||||
List<WargaModel> getFilteredPenerima() {
|
||||
switch (selectedCategoryIndex.value) {
|
||||
case 0:
|
||||
return daftarPenerima;
|
||||
case 1:
|
||||
return daftarPenerima.where((item) => item.status == 'AKTIF').toList();
|
||||
case 2:
|
||||
return daftarPenerima
|
||||
.where((item) => item.status == 'NONAKTIF')
|
||||
.toList();
|
||||
default:
|
||||
return daftarPenerima;
|
||||
}
|
||||
}
|
||||
|
||||
// Validasi form
|
||||
String? validateNama(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateNIK(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'NIK tidak boleh kosong';
|
||||
}
|
||||
if (value.length != 16) {
|
||||
return 'NIK harus 16 digit';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateAlamat(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Alamat tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateTelepon(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nomor telepon tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateEmail(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return null; // Email boleh kosong
|
||||
}
|
||||
if (!GetUtils.isEmail(value)) {
|
||||
return 'Email tidak valid';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:penyaluran_app/app/utils/date_formatter.dart';
|
||||
|
||||
class PenerimaController extends GetxController {
|
||||
final RxList<Map<String, dynamic>> daftarPenerima =
|
||||
|
@ -0,0 +1,225 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/pengaduan_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/tindakan_pengaduan_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 PengaduanController 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 pengaduan
|
||||
final RxList<PengaduanModel> daftarPengaduan = <PengaduanModel>[].obs;
|
||||
final RxInt jumlahDiproses = 0.obs;
|
||||
final RxInt jumlahTindakan = 0.obs;
|
||||
final RxInt jumlahSelesai = 0.obs;
|
||||
|
||||
// Controller untuk pencarian dan form
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
final TextEditingController tindakanController = TextEditingController();
|
||||
final TextEditingController catatanController = TextEditingController();
|
||||
|
||||
// Form key
|
||||
final GlobalKey<FormState> tindakanFormKey = GlobalKey<FormState>();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadPengaduanData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
tindakanController.dispose();
|
||||
catatanController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadPengaduanData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final pengaduanData = await _supabaseService.getPengaduan();
|
||||
if (pengaduanData != null) {
|
||||
daftarPengaduan.value =
|
||||
pengaduanData.map((data) => PengaduanModel.fromJson(data)).toList();
|
||||
|
||||
// Hitung jumlah berdasarkan status
|
||||
jumlahDiproses.value =
|
||||
daftarPengaduan.where((item) => item.status == 'DIPROSES').length;
|
||||
jumlahTindakan.value =
|
||||
daftarPengaduan.where((item) => item.status == 'TINDAKAN').length;
|
||||
jumlahSelesai.value =
|
||||
daftarPengaduan.where((item) => item.status == 'SELESAI').length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading pengaduan data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> prosesPengaduan(String pengaduanId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.prosesPengaduan(pengaduanId);
|
||||
await loadPengaduanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Pengaduan berhasil diproses',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error processing pengaduan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memproses pengaduan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tambahTindakan(String pengaduanId) async {
|
||||
if (!tindakanFormKey.currentState!.validate()) return;
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final tindakan = TindakanPengaduanModel(
|
||||
pengaduanId: pengaduanId,
|
||||
tindakan: tindakanController.text,
|
||||
catatan: catatanController.text,
|
||||
tanggalTindakan: DateTime.now(),
|
||||
petugasId: user?.id,
|
||||
);
|
||||
|
||||
await _supabaseService.tambahTindakanPengaduan(tindakan.toJson());
|
||||
await _supabaseService.updateStatusPengaduan(pengaduanId, 'TINDAKAN');
|
||||
|
||||
// Clear form
|
||||
tindakanController.clear();
|
||||
catatanController.clear();
|
||||
|
||||
await loadPengaduanData();
|
||||
Get.back(); // Close dialog
|
||||
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Tindakan berhasil ditambahkan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error adding tindakan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menambahkan tindakan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> selesaikanPengaduan(String pengaduanId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.updateStatusPengaduan(pengaduanId, 'SELESAI');
|
||||
await loadPengaduanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Pengaduan berhasil diselesaikan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error completing pengaduan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menyelesaikan pengaduan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<TindakanPengaduanModel>> getTindakanPengaduan(
|
||||
String pengaduanId) async {
|
||||
try {
|
||||
final tindakanData =
|
||||
await _supabaseService.getTindakanPengaduan(pengaduanId);
|
||||
if (tindakanData != null) {
|
||||
return tindakanData
|
||||
.map((data) => TindakanPengaduanModel.fromJson(data))
|
||||
.toList();
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
print('Error getting tindakan pengaduan: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadPengaduanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void changeCategory(int index) {
|
||||
selectedCategoryIndex.value = index;
|
||||
}
|
||||
|
||||
List<PengaduanModel> getFilteredPengaduan() {
|
||||
switch (selectedCategoryIndex.value) {
|
||||
case 0:
|
||||
return daftarPengaduan;
|
||||
case 1:
|
||||
return daftarPengaduan
|
||||
.where((item) => item.status == 'DIPROSES')
|
||||
.toList();
|
||||
case 2:
|
||||
return daftarPengaduan
|
||||
.where((item) => item.status == 'TINDAKAN')
|
||||
.toList();
|
||||
case 3:
|
||||
return daftarPengaduan
|
||||
.where((item) => item.status == 'SELESAI')
|
||||
.toList();
|
||||
default:
|
||||
return daftarPengaduan;
|
||||
}
|
||||
}
|
||||
|
||||
// Validasi form
|
||||
String? validateTindakan(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Tindakan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/donatur_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 PenitipanBantuanController 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 penitipan
|
||||
final RxList<PenitipanBantuanModel> daftarPenitipan =
|
||||
<PenitipanBantuanModel>[].obs;
|
||||
final RxInt jumlahMenunggu = 0.obs;
|
||||
final RxInt jumlahTerverifikasi = 0.obs;
|
||||
final RxInt jumlahDitolak = 0.obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadPenitipanData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadPenitipanData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final penitipanData = await _supabaseService.getPenitipanBantuan();
|
||||
if (penitipanData != null) {
|
||||
daftarPenitipan.value = penitipanData
|
||||
.map((data) => PenitipanBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
|
||||
// Hitung jumlah berdasarkan status
|
||||
jumlahMenunggu.value =
|
||||
daftarPenitipan.where((item) => item.status == 'MENUNGGU').length;
|
||||
jumlahTerverifikasi.value = daftarPenitipan
|
||||
.where((item) => item.status == 'TERVERIFIKASI')
|
||||
.length;
|
||||
jumlahDitolak.value =
|
||||
daftarPenitipan.where((item) => item.status == 'DITOLAK').length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading penitipan data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> verifikasiPenitipan(String penitipanId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.verifikasiPenitipan(penitipanId);
|
||||
await loadPenitipanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penitipan berhasil diverifikasi',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error verifying penitipan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memverifikasi penitipan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tolakPenitipan(String penitipanId, String alasan) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.tolakPenitipan(penitipanId, alasan);
|
||||
await loadPenitipanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Penitipan berhasil ditolak',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error rejecting penitipan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menolak penitipan: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<DonaturModel?> getDonaturInfo(String donaturId) async {
|
||||
try {
|
||||
final donaturData = await _supabaseService.getDonaturById(donaturId);
|
||||
if (donaturData != null) {
|
||||
return DonaturModel.fromJson(donaturData);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
print('Error getting donatur info: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadPenitipanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void changeCategory(int index) {
|
||||
selectedCategoryIndex.value = index;
|
||||
}
|
||||
|
||||
List<PenitipanBantuanModel> getFilteredPenitipan() {
|
||||
switch (selectedCategoryIndex.value) {
|
||||
case 0:
|
||||
return daftarPenitipan;
|
||||
case 1:
|
||||
return daftarPenitipan
|
||||
.where((item) => item.status == 'MENUNGGU')
|
||||
.toList();
|
||||
case 2:
|
||||
return daftarPenitipan
|
||||
.where((item) => item.status == 'TERVERIFIKASI')
|
||||
.toList();
|
||||
case 3:
|
||||
return daftarPenitipan
|
||||
.where((item) => item.status == 'DITOLAK')
|
||||
.toList();
|
||||
default:
|
||||
return daftarPenitipan;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/desa_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';
|
||||
@ -8,81 +9,63 @@ class PetugasDesaController extends GetxController {
|
||||
final AuthController _authController = Get.find<AuthController>();
|
||||
final SupabaseService _supabaseService = SupabaseService.to;
|
||||
|
||||
final RxBool isLoading = false.obs;
|
||||
final Rx<Map<String, dynamic>?> roleData = Rx<Map<String, dynamic>?>(null);
|
||||
|
||||
// Indeks kategori yang dipilih untuk filter
|
||||
final RxInt selectedCategoryIndex = 0.obs;
|
||||
|
||||
// Indeks tab yang aktif di bottom navigation bar
|
||||
final RxInt activeTabIndex = 0.obs;
|
||||
|
||||
// Data untuk dashboard
|
||||
final RxInt totalPenerima = 0.obs;
|
||||
final RxInt totalBantuan = 0.obs;
|
||||
final RxInt totalPenyaluran = 0.obs;
|
||||
final RxDouble progressPenyaluran = 0.0.obs;
|
||||
|
||||
// Data untuk jadwal
|
||||
final RxList<Map<String, dynamic>> jadwalHariIni =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxList<Map<String, dynamic>> jadwalMendatang =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxList<Map<String, dynamic>> jadwalSelesai =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
|
||||
// Data untuk permintaan penjadwalan
|
||||
final RxList<Map<String, dynamic>> permintaanPenjadwalan =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxInt jumlahPermintaanPenjadwalan = 0.obs;
|
||||
|
||||
// Data untuk notifikasi
|
||||
final RxList<Map<String, dynamic>> notifikasiBelumDibaca =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxInt jumlahNotifikasiBelumDibaca = 0.obs;
|
||||
|
||||
// Data untuk inventaris
|
||||
final RxList<Map<String, dynamic>> daftarInventaris =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxDouble totalStok = 0.0.obs;
|
||||
final RxDouble stokMasuk = 0.0.obs;
|
||||
final RxDouble stokKeluar = 0.0.obs;
|
||||
|
||||
// Data untuk penitipan
|
||||
final RxList<Map<String, dynamic>> daftarPenitipan =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxInt jumlahMenunggu = 0.obs;
|
||||
final RxInt jumlahTerverifikasi = 0.obs;
|
||||
final RxInt jumlahDitolak = 0.obs;
|
||||
|
||||
// Data untuk pengaduan
|
||||
final RxList<Map<String, dynamic>> daftarPengaduan =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
final RxInt jumlahDiproses = 0.obs;
|
||||
final RxInt jumlahTindakan = 0.obs;
|
||||
final RxInt jumlahSelesai = 0.obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
// Data profil pengguna dari cache
|
||||
final RxMap<String, dynamic> userProfile = RxMap<String, dynamic>({});
|
||||
|
||||
// Model desa dari cache
|
||||
final Rx<DesaModel?> desaModel = Rx<DesaModel?>(null);
|
||||
|
||||
// Counter untuk notifikasi
|
||||
final RxInt jumlahNotifikasiBelumDibaca = 0.obs;
|
||||
|
||||
// Counter untuk permintaan menunggu
|
||||
final RxInt jumlahMenunggu = 0.obs;
|
||||
|
||||
// Counter untuk pengaduan yang diproses
|
||||
final RxInt jumlahDiproses = 0.obs;
|
||||
|
||||
// Data jadwal hari ini
|
||||
final RxList<dynamic> jadwalHariIni = <dynamic>[].obs;
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
String get role => user?.role ?? 'PETUGASDESA';
|
||||
String get nama => user?.name ?? 'Petugas Desa';
|
||||
|
||||
// Getter untuk nama lengkap dari profil pengguna
|
||||
String get namaLengkap => userProfile['name'] ?? user?.name ?? 'Petugas Desa';
|
||||
|
||||
// Getter untuk nama desa dari profil pengguna
|
||||
String get desa {
|
||||
// Prioritaskan model desa dari user
|
||||
if (user?.desa != null) {
|
||||
print('DEBUG: Menggunakan desa dari user model: ${user!.desa!.nama}');
|
||||
return user!.desa!.nama;
|
||||
}
|
||||
|
||||
// Kemudian coba dari userProfile
|
||||
if (userProfile['desa'] != null && userProfile['desa'] is Map) {
|
||||
final desaNama = userProfile['desa']['nama'] ?? 'Desa';
|
||||
print('DEBUG: Menggunakan desa dari userProfile: $desaNama');
|
||||
return desaNama;
|
||||
}
|
||||
|
||||
// Fallback ke nilai default
|
||||
print('DEBUG: Menggunakan nilai default untuk desa');
|
||||
return userProfile['desa_id'] != null ? 'Desa' : 'Desa';
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
|
||||
// Inisialisasi manual untuk pengaduan (untuk debugging)
|
||||
jumlahDiproses.value = 3;
|
||||
print('onInit - Jumlah pengaduan diproses: ${jumlahDiproses.value}');
|
||||
|
||||
loadRoleData();
|
||||
loadDashboardData();
|
||||
loadJadwalData();
|
||||
loadPermintaanPenjadwalanData();
|
||||
loadUserProfile();
|
||||
loadNotifikasiData();
|
||||
loadInventarisData();
|
||||
loadJadwalData();
|
||||
loadPenitipanData();
|
||||
loadPengaduanData();
|
||||
}
|
||||
@ -93,584 +76,106 @@ class PetugasDesaController extends GetxController {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadRoleData() async {
|
||||
isLoading.value = true;
|
||||
// Metode untuk memuat data profil pengguna dari cache
|
||||
Future<void> loadUserProfile() async {
|
||||
try {
|
||||
// Jika user sudah ada di AuthController, tidak perlu mengambil data lagi
|
||||
if (user != null) {
|
||||
final data = await _supabaseService.getRoleSpecificData(role);
|
||||
roleData.value = data;
|
||||
print('DEBUG: User ditemukan di AuthController: ${user!.email}');
|
||||
print('DEBUG: User desa: ${user!.desa?.nama}');
|
||||
|
||||
// Ambil data tambahan jika diperlukan, tapi gunakan cache
|
||||
final profileData = await _supabaseService.getUserProfile();
|
||||
if (profileData != null) {
|
||||
print('DEBUG: Profile data ditemukan: ${profileData['name']}');
|
||||
userProfile.value = profileData;
|
||||
|
||||
// Parse data desa jika ada
|
||||
if (profileData['desa'] != null &&
|
||||
profileData['desa'] is Map<String, dynamic>) {
|
||||
try {
|
||||
final desaData = profileData['desa'] as Map<String, dynamic>;
|
||||
print('DEBUG: Desa data ditemukan: $desaData');
|
||||
} catch (e) {
|
||||
print('Error parsing desa data: $e');
|
||||
}
|
||||
} else {
|
||||
print('DEBUG: Desa data tidak ditemukan atau bukan Map');
|
||||
}
|
||||
} else {
|
||||
print('DEBUG: Profile data tidak ditemukan');
|
||||
}
|
||||
} else {
|
||||
print('DEBUG: User tidak ditemukan di AuthController');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading role data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadDashboardData() async {
|
||||
try {
|
||||
// Simulasi data untuk dashboard
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
|
||||
totalPenerima.value = 120;
|
||||
totalBantuan.value = 5;
|
||||
totalPenyaluran.value = 8;
|
||||
progressPenyaluran.value = 0.75;
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getDashboardData();
|
||||
// totalPenerima.value = result['total_penerima'] ?? 0;
|
||||
// totalBantuan.value = result['total_bantuan'] ?? 0;
|
||||
// totalPenyaluran.value = result['total_penyaluran'] ?? 0;
|
||||
// progressPenyaluran.value = result['progress_penyaluran'] ?? 0.0;
|
||||
} catch (e) {
|
||||
print('Error loading dashboard data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadJadwalData() async {
|
||||
try {
|
||||
// Simulasi data untuk jadwal
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
|
||||
jadwalHariIni.value = [
|
||||
{
|
||||
'id': '1',
|
||||
'lokasi': 'Balai Desa Sukamaju',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'tanggal': '15 April 2023',
|
||||
'waktu': '09:00 - 12:00',
|
||||
'status': 'aktif',
|
||||
'jumlah_penerima': 45,
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'lokasi': 'Pos RW 03',
|
||||
'jenis_bantuan': 'Paket Sembako',
|
||||
'tanggal': '15 April 2023',
|
||||
'waktu': '13:00 - 15:00',
|
||||
'status': 'aktif',
|
||||
'jumlah_penerima': 30,
|
||||
},
|
||||
];
|
||||
|
||||
jadwalMendatang.value = [
|
||||
{
|
||||
'id': '3',
|
||||
'lokasi': 'Balai Desa Sukamaju',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'tanggal': '22 April 2023',
|
||||
'waktu': '09:00 - 12:00',
|
||||
'status': 'terjadwal',
|
||||
'jumlah_penerima': 50,
|
||||
},
|
||||
{
|
||||
'id': '4',
|
||||
'lokasi': 'Pos RW 05',
|
||||
'jenis_bantuan': 'Paket Sembako',
|
||||
'tanggal': '23 April 2023',
|
||||
'waktu': '13:00 - 15:00',
|
||||
'status': 'terjadwal',
|
||||
'jumlah_penerima': 35,
|
||||
},
|
||||
];
|
||||
|
||||
jadwalSelesai.value = [
|
||||
{
|
||||
'id': '5',
|
||||
'lokasi': 'Balai Desa Sukamaju',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'tanggal': '8 April 2023',
|
||||
'waktu': '09:00 - 12:00',
|
||||
'status': 'selesai',
|
||||
'jumlah_penerima': 48,
|
||||
},
|
||||
{
|
||||
'id': '6',
|
||||
'lokasi': 'Pos RW 02',
|
||||
'jenis_bantuan': 'Paket Sembako',
|
||||
'tanggal': '9 April 2023',
|
||||
'waktu': '13:00 - 15:00',
|
||||
'status': 'selesai',
|
||||
'jumlah_penerima': 32,
|
||||
},
|
||||
];
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getJadwalData();
|
||||
// jadwalHariIni.value = result['hari_ini'] ?? [];
|
||||
// jadwalMendatang.value = result['mendatang'] ?? [];
|
||||
// jadwalSelesai.value = result['selesai'] ?? [];
|
||||
} catch (e) {
|
||||
print('Error loading jadwal data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadPermintaanPenjadwalanData() async {
|
||||
try {
|
||||
// Simulasi data untuk permintaan penjadwalan
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
|
||||
permintaanPenjadwalan.value = [
|
||||
{
|
||||
'id': '1',
|
||||
'nama': 'Ahmad Sulaiman',
|
||||
'nik': '3201234567890001',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'tanggal_permintaan': '14 April 2023',
|
||||
'alamat': 'Dusun Sukamaju RT 02/03',
|
||||
'status': 'menunggu',
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'nama': 'Siti Aminah',
|
||||
'nik': '3201234567890002',
|
||||
'jenis_bantuan': 'Sembako',
|
||||
'tanggal_permintaan': '13 April 2023',
|
||||
'alamat': 'Dusun Sukamaju RT 01/03',
|
||||
'status': 'menunggu',
|
||||
},
|
||||
];
|
||||
|
||||
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getPermintaanPenjadwalanData();
|
||||
// permintaanPenjadwalan.value = result ?? [];
|
||||
// jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||
} catch (e) {
|
||||
print('Error loading permintaan penjadwalan data: $e');
|
||||
print('Error loading user profile: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk memuat data notifikasi
|
||||
Future<void> loadNotifikasiData() async {
|
||||
try {
|
||||
// Simulasi data untuk notifikasi
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// Hitung jumlah notifikasi yang belum dibaca
|
||||
final List<Map<String, dynamic>> notifikasi = [
|
||||
{
|
||||
'id': '1',
|
||||
'judul': 'Jadwal Penyaluran Baru',
|
||||
'pesan': 'Jadwal penyaluran beras telah ditambahkan untuk hari ini',
|
||||
'waktu': '08:30',
|
||||
'dibaca': false,
|
||||
'tanggal': 'hari_ini',
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'judul': 'Pengajuan Bantuan Baru',
|
||||
'pesan': 'Ada 3 pengajuan bantuan baru yang perlu diverifikasi',
|
||||
'waktu': '10:15',
|
||||
'dibaca': false,
|
||||
'tanggal': 'hari_ini',
|
||||
},
|
||||
{
|
||||
'id': '3',
|
||||
'judul': 'Laporan Penyaluran',
|
||||
'pesan':
|
||||
'Laporan penyaluran bantuan tanggal 14 April 2023 telah selesai',
|
||||
'waktu': '16:45',
|
||||
'dibaca': true,
|
||||
'tanggal': 'kemarin',
|
||||
},
|
||||
];
|
||||
|
||||
notifikasiBelumDibaca.value =
|
||||
notifikasi.where((n) => n['dibaca'] == false).toList();
|
||||
jumlahNotifikasiBelumDibaca.value = notifikasiBelumDibaca.length;
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getNotifikasiData();
|
||||
// notifikasiBelumDibaca.value = result.where((n) => n['dibaca'] == false).toList();
|
||||
// jumlahNotifikasiBelumDibaca.value = notifikasiBelumDibaca.length;
|
||||
if (user != null) {
|
||||
final notifikasiData =
|
||||
await _supabaseService.getNotifikasiBelumDibaca(user!.id);
|
||||
if (notifikasiData != null) {
|
||||
jumlahNotifikasiBelumDibaca.value = notifikasiData.length;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading notifikasi data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadInventarisData() async {
|
||||
// Metode untuk memuat data jadwal
|
||||
Future<void> loadJadwalData() async {
|
||||
try {
|
||||
// Simulasi data untuk inventaris
|
||||
await Future.delayed(const Duration(milliseconds: 700));
|
||||
|
||||
daftarInventaris.value = [
|
||||
{
|
||||
'id': '1',
|
||||
'nama': 'Beras',
|
||||
'jenis': 'Sembako',
|
||||
'stok': '750 kg',
|
||||
'stok_angka': 750.0,
|
||||
'lokasi': 'Gudang Utama',
|
||||
'tanggal_masuk': '10 April 2023',
|
||||
'kadaluarsa': '10 April 2024',
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'nama': 'Minyak Goreng',
|
||||
'jenis': 'Sembako',
|
||||
'stok': '250 liter',
|
||||
'stok_angka': 250.0,
|
||||
'lokasi': 'Gudang Utama',
|
||||
'tanggal_masuk': '12 April 2023',
|
||||
'kadaluarsa': '12 Oktober 2023',
|
||||
},
|
||||
{
|
||||
'id': '3',
|
||||
'nama': 'Paket Sembako',
|
||||
'jenis': 'Paket Bantuan',
|
||||
'stok': '100 paket',
|
||||
'stok_angka': 100.0,
|
||||
'lokasi': 'Gudang Cabang',
|
||||
'tanggal_masuk': '15 April 2023',
|
||||
'kadaluarsa': '15 Juli 2023',
|
||||
},
|
||||
];
|
||||
|
||||
// Hitung total stok, stok masuk, dan stok keluar
|
||||
totalStok.value = daftarInventaris.fold(
|
||||
0, (sum, item) => sum + (item['stok_angka'] as double));
|
||||
stokMasuk.value = 500.0; // Contoh data
|
||||
stokKeluar.value = 350.0; // Contoh data
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getInventarisData();
|
||||
// daftarInventaris.value = result['daftar'] ?? [];
|
||||
// totalStok.value = result['total_stok'] ?? 0.0;
|
||||
// stokMasuk.value = result['stok_masuk'] ?? 0.0;
|
||||
// stokKeluar.value = result['stok_keluar'] ?? 0.0;
|
||||
final jadwalHariIniData = await _supabaseService.getJadwalHariIni();
|
||||
if (jadwalHariIniData != null) {
|
||||
jadwalHariIni.value = jadwalHariIniData;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading inventaris data: $e');
|
||||
print('Error loading jadwal data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk memuat data penitipan
|
||||
Future<void> loadPenitipanData() async {
|
||||
try {
|
||||
// Simulasi data untuk penitipan
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
|
||||
daftarPenitipan.value = [
|
||||
{
|
||||
'id': '1',
|
||||
'donatur': 'PT Sejahtera Abadi',
|
||||
'jenis_bantuan': 'Sembako',
|
||||
'jumlah': '500 kg',
|
||||
'tanggal_pengajuan': '15 April 2023',
|
||||
'status': 'Menunggu',
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'donatur': 'Yayasan Peduli Sesama',
|
||||
'jenis_bantuan': 'Pakaian',
|
||||
'jumlah': '200 pcs',
|
||||
'tanggal_pengajuan': '14 April 2023',
|
||||
'status': 'Terverifikasi',
|
||||
},
|
||||
{
|
||||
'id': '3',
|
||||
'donatur': 'Bank BRI',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'jumlah': '300 kg',
|
||||
'tanggal_pengajuan': '13 April 2023',
|
||||
'status': 'Terverifikasi',
|
||||
},
|
||||
{
|
||||
'id': '4',
|
||||
'donatur': 'Komunitas Peduli',
|
||||
'jenis_bantuan': 'Alat Tulis',
|
||||
'jumlah': '100 set',
|
||||
'tanggal_pengajuan': '12 April 2023',
|
||||
'status': 'Ditolak',
|
||||
},
|
||||
];
|
||||
|
||||
// Hitung jumlah penitipan berdasarkan status
|
||||
jumlahMenunggu.value =
|
||||
daftarPenitipan.where((p) => p['status'] == 'Menunggu').length;
|
||||
jumlahTerverifikasi.value =
|
||||
daftarPenitipan.where((p) => p['status'] == 'Terverifikasi').length;
|
||||
jumlahDitolak.value =
|
||||
daftarPenitipan.where((p) => p['status'] == 'Ditolak').length;
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getPenitipanData();
|
||||
// daftarPenitipan.value = result ?? [];
|
||||
// jumlahMenunggu.value = daftarPenitipan.where((p) => p['status'] == 'Menunggu').length;
|
||||
// jumlahTerverifikasi.value = daftarPenitipan.where((p) => p['status'] == 'Terverifikasi').length;
|
||||
// jumlahDitolak.value = daftarPenitipan.where((p) => p['status'] == 'Ditolak').length;
|
||||
// Simulasi data untuk contoh
|
||||
jumlahMenunggu.value = 3;
|
||||
} catch (e) {
|
||||
print('Error loading penitipan data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk memuat data pengaduan
|
||||
Future<void> loadPengaduanData() async {
|
||||
try {
|
||||
// Simulasi data untuk pengaduan
|
||||
await Future.delayed(const Duration(milliseconds: 650));
|
||||
|
||||
// Pastikan data pengaduan tidak kosong
|
||||
daftarPengaduan.value = [
|
||||
{
|
||||
'id': '1',
|
||||
'nama': 'Budi Santoso',
|
||||
'nik': '3201020107030011',
|
||||
'jenis_pengaduan': 'Bantuan Tidak Diterima',
|
||||
'deskripsi':
|
||||
'Saya belum menerima bantuan beras yang dijadwalkan minggu lalu',
|
||||
'tanggal': '15 April 2023',
|
||||
'status': 'Diproses',
|
||||
},
|
||||
{
|
||||
'id': '2',
|
||||
'nama': 'Siti Rahayu',
|
||||
'nik': '3201020107030010',
|
||||
'jenis_pengaduan': 'Kualitas Bantuan',
|
||||
'deskripsi':
|
||||
'Beras yang diterima berkualitas buruk dan tidak layak konsumsi',
|
||||
'tanggal': '14 April 2023',
|
||||
'status': 'Tindakan',
|
||||
'tindakan':
|
||||
'Pengecekan kualitas beras di gudang dan pengambilan sampel',
|
||||
},
|
||||
{
|
||||
'id': '3',
|
||||
'nama': 'Ahmad Fauzi',
|
||||
'nik': '3201020107030013',
|
||||
'jenis_pengaduan': 'Jumlah Bantuan',
|
||||
'deskripsi':
|
||||
'Jumlah bantuan yang diterima tidak sesuai dengan yang dijanjikan',
|
||||
'tanggal': '13 April 2023',
|
||||
'status': 'Tindakan',
|
||||
'tindakan':
|
||||
'Verifikasi data penerima dan jumlah bantuan yang seharusnya diterima',
|
||||
},
|
||||
{
|
||||
'id': '4',
|
||||
'nama': 'Dewi Lestari',
|
||||
'nik': '3201020107030012',
|
||||
'jenis_pengaduan': 'Jadwal Penyaluran',
|
||||
'deskripsi':
|
||||
'Jadwal penyaluran bantuan sering berubah tanpa pemberitahuan',
|
||||
'tanggal': '10 April 2023',
|
||||
'status': 'Selesai',
|
||||
'tindakan':
|
||||
'Koordinasi dengan tim penyaluran untuk perbaikan sistem pemberitahuan',
|
||||
'hasil':
|
||||
'Implementasi sistem notifikasi perubahan jadwal melalui SMS dan pengumuman di balai desa',
|
||||
},
|
||||
// Tambahkan data pengaduan dengan status 'Diproses' untuk memastikan counter muncul
|
||||
{
|
||||
'id': '5',
|
||||
'nama': 'Joko Widodo',
|
||||
'nik': '3201020107030014',
|
||||
'jenis_pengaduan': 'Bantuan Tidak Sesuai',
|
||||
'deskripsi':
|
||||
'Bantuan yang diterima tidak sesuai dengan yang dijanjikan',
|
||||
'tanggal': '16 April 2023',
|
||||
'status': 'Diproses',
|
||||
},
|
||||
{
|
||||
'id': '6',
|
||||
'nama': 'Anita Sari',
|
||||
'nik': '3201020107030015',
|
||||
'jenis_pengaduan': 'Bantuan Tidak Tepat Sasaran',
|
||||
'deskripsi':
|
||||
'Bantuan diberikan kepada warga yang tidak berhak menerima',
|
||||
'tanggal': '17 April 2023',
|
||||
'status': 'Diproses',
|
||||
},
|
||||
];
|
||||
|
||||
// Hitung jumlah pengaduan berdasarkan status
|
||||
int jumlahDiprosesTemp =
|
||||
daftarPengaduan.where((p) => p['status'] == 'Diproses').length;
|
||||
int jumlahTindakanTemp =
|
||||
daftarPengaduan.where((p) => p['status'] == 'Tindakan').length;
|
||||
int jumlahSelesaiTemp =
|
||||
daftarPengaduan.where((p) => p['status'] == 'Selesai').length;
|
||||
|
||||
// Update nilai Rx
|
||||
jumlahDiproses.value = jumlahDiprosesTemp;
|
||||
jumlahTindakan.value = jumlahTindakanTemp;
|
||||
jumlahSelesai.value = jumlahSelesaiTemp;
|
||||
|
||||
// Print untuk debugging
|
||||
print('Data pengaduan dimuat:');
|
||||
print('Jumlah pengaduan diproses: ${jumlahDiproses.value}');
|
||||
print('Jumlah pengaduan tindakan: ${jumlahTindakan.value}');
|
||||
print('Jumlah pengaduan selesai: ${jumlahSelesai.value}');
|
||||
print('Total pengaduan: ${daftarPengaduan.length}');
|
||||
|
||||
// Perbarui UI secara manual
|
||||
update();
|
||||
|
||||
// Di implementasi nyata, data akan diambil dari Supabase
|
||||
// final result = await _supabaseService.getPengaduanData();
|
||||
// daftarPengaduan.value = result ?? [];
|
||||
// jumlahDiproses.value = daftarPengaduan.where((p) => p['status'] == 'Diproses').length;
|
||||
// jumlahTindakan.value = daftarPengaduan.where((p) => p['status'] == 'Tindakan').length;
|
||||
// jumlahSelesai.value = daftarPengaduan.where((p) => p['status'] == 'Selesai').length;
|
||||
// Simulasi data untuk contoh
|
||||
jumlahDiproses.value = 2;
|
||||
} catch (e) {
|
||||
print('Error loading pengaduan data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Method untuk memperbarui jumlah pengaduan secara manual (untuk debugging)
|
||||
void updatePengaduanCounter() {
|
||||
jumlahDiproses.value = 5; // Set nilai secara manual
|
||||
update(); // Perbarui UI
|
||||
print(
|
||||
'Counter pengaduan diperbarui secara manual: ${jumlahDiproses.value}');
|
||||
}
|
||||
|
||||
void tandaiNotifikasiDibaca(String id) {
|
||||
// Implementasi untuk menandai notifikasi sebagai dibaca
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status notifikasi
|
||||
// await _supabaseService.markNotificationAsRead(id);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadNotifikasiData();
|
||||
}
|
||||
|
||||
void tambahInventaris(Map<String, dynamic> data) {
|
||||
// Implementasi untuk menambah inventaris
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk menambah data inventaris
|
||||
// await _supabaseService.addInventory(data);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadInventarisData();
|
||||
}
|
||||
|
||||
void hapusInventaris(String id) {
|
||||
// Implementasi untuk menghapus inventaris
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk menghapus data inventaris
|
||||
// await _supabaseService.deleteInventory(id);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadInventarisData();
|
||||
}
|
||||
|
||||
void terimaPermohonanPenitipan(String id) {
|
||||
// Implementasi untuk menerima permohonan penitipan
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status penitipan
|
||||
// await _supabaseService.acceptDeposit(id);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadPenitipanData();
|
||||
loadInventarisData(); // Perbarui inventaris karena ada penambahan stok
|
||||
}
|
||||
|
||||
void tolakPermohonanPenitipan(String id) {
|
||||
// Implementasi untuk menolak permohonan penitipan
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status penitipan
|
||||
// await _supabaseService.rejectDeposit(id);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadPenitipanData();
|
||||
}
|
||||
|
||||
void prosesPengaduan(String id, String tindakan) {
|
||||
// Implementasi untuk memproses pengaduan
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status pengaduan
|
||||
// await _supabaseService.processPengaduan(id, tindakan);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadPengaduanData();
|
||||
}
|
||||
|
||||
void selesaikanPengaduan(String id, String hasil) {
|
||||
// Implementasi untuk menyelesaikan pengaduan
|
||||
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status pengaduan
|
||||
// await _supabaseService.completePengaduan(id, hasil);
|
||||
|
||||
// Perbarui data lokal
|
||||
loadPengaduanData();
|
||||
}
|
||||
|
||||
void logout() {
|
||||
_authController.logout();
|
||||
// Metode untuk memperbarui counter pengaduan
|
||||
Future<void> updatePengaduanCounter() async {
|
||||
try {
|
||||
await loadPengaduanData();
|
||||
} catch (e) {
|
||||
print('Error updating pengaduan counter: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk mengubah tab aktif
|
||||
void changeTab(int index) {
|
||||
activeTabIndex.value = index;
|
||||
}
|
||||
|
||||
// Metode untuk konfirmasi permintaan penjadwalan
|
||||
Future<void> konfirmasiPermintaanPenjadwalan(
|
||||
String id, String jadwalId) async {
|
||||
try {
|
||||
if (id.isEmpty || jadwalId.isEmpty) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'ID permintaan atau jadwal tidak valid',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
// Simulasi proses konfirmasi
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
|
||||
// Hapus permintaan dari daftar
|
||||
permintaanPenjadwalan.removeWhere((item) => item['id'] == id);
|
||||
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||
|
||||
// Di implementasi nyata, data akan diupdate ke Supabase
|
||||
// await _supabaseService.konfirmasiPermintaanPenjadwalan(id, jadwalId);
|
||||
// await loadPermintaanPenjadwalanData();
|
||||
// await loadJadwalData();
|
||||
} catch (e) {
|
||||
print('Error konfirmasi permintaan penjadwalan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Terjadi kesalahan saat mengkonfirmasi permintaan',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk menolak permintaan penjadwalan
|
||||
Future<void> tolakPermintaanPenjadwalan(String id, String alasan) async {
|
||||
try {
|
||||
if (id.isEmpty) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'ID permintaan tidak valid',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
// Simulasi proses penolakan
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
|
||||
// Hapus permintaan dari daftar
|
||||
permintaanPenjadwalan.removeWhere((item) => item['id'] == id);
|
||||
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||
|
||||
// Di implementasi nyata, data akan diupdate ke Supabase
|
||||
// await _supabaseService.tolakPermintaanPenjadwalan(id, alasan);
|
||||
// await loadPermintaanPenjadwalanData();
|
||||
} catch (e) {
|
||||
print('Error tolak permintaan penjadwalan: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Terjadi kesalahan saat menolak permintaan',
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
// Metode untuk logout
|
||||
Future<void> logout() async {
|
||||
await _authController.logout();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,126 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/notifikasi_model.dart';
|
||||
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
|
||||
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
||||
|
||||
class PetugasDesaDashboardController extends GetxController {
|
||||
final AuthController _authController = Get.find<AuthController>();
|
||||
final SupabaseService _supabaseService = SupabaseService.to;
|
||||
|
||||
final RxBool isLoading = false.obs;
|
||||
|
||||
// Data profil pengguna dari cache
|
||||
final RxMap<String, dynamic> userProfile = RxMap<String, dynamic>({});
|
||||
|
||||
// Data untuk dashboard
|
||||
final RxInt totalPenerima = 0.obs;
|
||||
final RxInt totalBantuan = 0.obs;
|
||||
final RxInt totalPenyaluran = 0.obs;
|
||||
final RxDouble progressPenyaluran = 0.0.obs;
|
||||
|
||||
// Data untuk notifikasi
|
||||
final RxList<NotifikasiModel> notifikasiBelumDibaca = <NotifikasiModel>[].obs;
|
||||
final RxInt jumlahNotifikasiBelumDibaca = 0.obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
String get role => user?.role ?? 'PETUGASDESA';
|
||||
String get nama => user?.name ?? 'Petugas Desa';
|
||||
|
||||
// Getter untuk nama lengkap dari profil pengguna
|
||||
String get namaLengkap => userProfile['name'] ?? user?.name ?? 'Petugas Desa';
|
||||
|
||||
// Getter untuk nama desa dari profil pengguna
|
||||
String get desa =>
|
||||
userProfile['desa']?['nama'] ??
|
||||
(userProfile['desa_id'] != null ? 'Desa' : 'Desa');
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadUserProfile();
|
||||
loadDashboardData();
|
||||
loadNotifikasiData();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// Metode untuk memuat data profil pengguna dari cache
|
||||
Future<void> loadUserProfile() async {
|
||||
try {
|
||||
// Jika user sudah ada di AuthController, tidak perlu mengambil data lagi
|
||||
if (user != null) {
|
||||
// Ambil data tambahan jika diperlukan, tapi gunakan cache
|
||||
final profileData = await _supabaseService.getUserProfile();
|
||||
if (profileData != null) {
|
||||
userProfile.value = profileData;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading user profile: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadDashboardData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
// Mengambil data total penerima
|
||||
final penerimaData = await _supabaseService.getTotalPenerima();
|
||||
totalPenerima.value = penerimaData ?? 0;
|
||||
|
||||
// Mengambil data total bantuan
|
||||
final bantuanData = await _supabaseService.getTotalBantuan();
|
||||
totalBantuan.value = bantuanData ?? 0;
|
||||
|
||||
// Mengambil data total penyaluran
|
||||
final penyaluranData = await _supabaseService.getTotalPenyaluran();
|
||||
totalPenyaluran.value = penyaluranData ?? 0;
|
||||
|
||||
// Menghitung progress penyaluran
|
||||
if (totalBantuan.value > 0) {
|
||||
progressPenyaluran.value =
|
||||
(totalPenyaluran.value / totalBantuan.value) * 100;
|
||||
} else {
|
||||
progressPenyaluran.value = 0.0;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading dashboard data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadNotifikasiData() async {
|
||||
try {
|
||||
final notifikasiData =
|
||||
await _supabaseService.getNotifikasiBelumDibaca(user?.id ?? '');
|
||||
|
||||
if (notifikasiData != null) {
|
||||
notifikasiBelumDibaca.value = notifikasiData
|
||||
.map((data) => NotifikasiModel.fromJson(data))
|
||||
.toList();
|
||||
jumlahNotifikasiBelumDibaca.value = notifikasiBelumDibaca.length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading notifikasi data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadDashboardData();
|
||||
await loadNotifikasiData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/data/models/stok_bantuan_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 StokBantuanController extends GetxController {
|
||||
final AuthController _authController = Get.find<AuthController>();
|
||||
final SupabaseService _supabaseService = SupabaseService.to;
|
||||
|
||||
final RxBool isLoading = false.obs;
|
||||
|
||||
// Data untuk stok bantuan
|
||||
final RxList<StokBantuanModel> daftarStokBantuan = <StokBantuanModel>[].obs;
|
||||
final RxDouble totalStok = 0.0.obs;
|
||||
final RxDouble stokMasuk = 0.0.obs;
|
||||
final RxDouble stokKeluar = 0.0.obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
final RxString searchQuery = ''.obs;
|
||||
|
||||
UserModel? get user => _authController.user;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadStokBantuanData();
|
||||
|
||||
// Listener untuk pencarian
|
||||
searchController.addListener(() {
|
||||
searchQuery.value = searchController.text;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> loadStokBantuanData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
final stokBantuanData = await _supabaseService.getStokBantuan();
|
||||
if (stokBantuanData != null) {
|
||||
daftarStokBantuan.value = stokBantuanData
|
||||
.map((data) => StokBantuanModel.fromJson(data))
|
||||
.toList();
|
||||
|
||||
// Hitung total stok
|
||||
totalStok.value = 0;
|
||||
for (var item in daftarStokBantuan) {
|
||||
totalStok.value += item.jumlah ?? 0;
|
||||
}
|
||||
|
||||
// Ambil data stok masuk dan keluar
|
||||
final stokData = await _supabaseService.getStokStatistics();
|
||||
if (stokData != null) {
|
||||
stokMasuk.value = stokData['masuk'] ?? 0;
|
||||
stokKeluar.value = stokData['keluar'] ?? 0;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading stok bantuan data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addStok(StokBantuanModel stok) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.addStok(stok.toJson());
|
||||
await loadStokBantuanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Stok berhasil ditambahkan',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error adding stok: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menambahkan stok: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateStok(StokBantuanModel stok) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.updateStok(stok.id ?? '', stok.toJson());
|
||||
await loadStokBantuanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Stok berhasil diperbarui',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error updating stok: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal memperbarui stok: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteStok(String stokId) async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await _supabaseService.deleteStok(stokId);
|
||||
await loadStokBantuanData();
|
||||
Get.snackbar(
|
||||
'Sukses',
|
||||
'Stok berhasil dihapus',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deleting stok: $e');
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
'Gagal menghapus stok: ${e.toString()}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshData() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadStokBantuanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<StokBantuanModel> getFilteredStokBantuan() {
|
||||
if (searchQuery.isEmpty) {
|
||||
return daftarStokBantuan;
|
||||
} else {
|
||||
return daftarStokBantuan
|
||||
.where((item) =>
|
||||
(item.nama
|
||||
?.toLowerCase()
|
||||
.contains(searchQuery.value.toLowerCase()) ??
|
||||
false) ||
|
||||
(item.satuan
|
||||
?.toLowerCase()
|
||||
.contains(searchQuery.value.toLowerCase()) ??
|
||||
false) ||
|
||||
(item.deskripsi
|
||||
?.toLowerCase()
|
||||
.contains(searchQuery.value.toLowerCase()) ??
|
||||
false))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user