Perbarui beberapa file konfigurasi fingerprint untuk arsitektur arm64-v8a, armeabi-v7a, x86, dan x86_64. Modifikasi model SkemaBantuan untuk menggunakan operator null-aware pada jumlah diterima per orang. Perbarui logika pengambilan data di AuthProvider untuk menyederhanakan pengecekan dan logging. Tambahkan fungsionalitas baru di DonaturDashboardController untuk mengunggah foto bantuan dan memperbarui data yang disimpan. Hapus tampilan yang tidak digunakan di DonaturRiwayatPenitipanView dan perbarui tampilan di beberapa view untuk meningkatkan pengalaman pengguna.

This commit is contained in:
Khafidh Fuadi
2025-03-26 09:57:13 +07:00
parent 88bef1c8e1
commit eede5ebd4d
33 changed files with 600 additions and 793 deletions

View File

@ -39,35 +39,14 @@ class DetailPenyaluranController extends GetxController {
if (penyaluran.value?.id != null) {
loadPenyaluranDetails(penyaluran.value!.id!);
}
checkUserRole();
} else if (penyaluranId != null) {
// Jika hanya ID penyaluran yang diterima
loadPenyaluranData(penyaluranId);
checkUserRole();
} else {
isLoading.value = false;
}
}
Future<void> checkUserRole() async {
try {
final user = _supabaseService.client.auth.currentUser;
if (user != null) {
final userData = await _supabaseService.client
.from('user_profile')
.select('role')
.eq('id', user.id)
.single();
if (userData['role'] == 'PETUGASDESA') {
isPetugasDesa.value = true;
}
}
} catch (e) {
print('Error checking user role: $e');
}
}
Future<void> loadPenyaluranData(String penyaluranId) async {
try {
isLoading.value = true;
@ -522,36 +501,33 @@ class DetailPenyaluranController extends GetxController {
.eq('qr_code_hash', qrHash)
.single();
if (data != null) {
// Jika penerima ditemukan, konversi ke model
final Map<String, dynamic> sanitizedPenerimaData =
Map<String, dynamic>.from(data);
// Jika penerima ditemukan, konversi ke model
final Map<String, dynamic> sanitizedPenerimaData =
Map<String, dynamic>.from(data);
// Konversi jumlah_bantuan ke double jika bertipe String
if (sanitizedPenerimaData['jumlah_bantuan'] is String) {
sanitizedPenerimaData['jumlah_bantuan'] = double.tryParse(
sanitizedPenerimaData['jumlah_bantuan'] as String);
}
// Konversi data ke model
final penerima =
PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData);
// Set isProcessing ke false sebelum navigasi untuk menghindari masalah loading
isProcessing.value = false;
// Navigasi ke halaman konfirmasi dengan data terbaru
await Get.toNamed('/petugas-desa/konfirmasi-penerima/${penerima.id}',
arguments: {
'penerima': penerima,
'tanggal_penyaluran': penyaluran.value?.tanggalPenyaluran
});
// Refresh data
await refreshData();
return true;
// Konversi jumlah_bantuan ke double jika bertipe String
if (sanitizedPenerimaData['jumlah_bantuan'] is String) {
sanitizedPenerimaData['jumlah_bantuan'] =
double.tryParse(sanitizedPenerimaData['jumlah_bantuan'] as String);
}
// Konversi data ke model
final penerima = PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData);
// Set isProcessing ke false sebelum navigasi untuk menghindari masalah loading
isProcessing.value = false;
// Navigasi ke halaman konfirmasi dengan data terbaru
await Get.toNamed('/petugas-desa/konfirmasi-penerima/${penerima.id}',
arguments: {
'penerima': penerima,
'tanggal_penyaluran': penyaluran.value?.tanggalPenyaluran
});
// Refresh data
await refreshData();
return true;
return false;
} catch (e) {
print('Error verifikasi QR code: $e');

View File

@ -448,7 +448,7 @@ class JadwalPenyaluranController extends GetxController {
.eq('id', stokBantuanId)
.single();
if (stokData != null && stokData['total_stok'] != null) {
if (stokData['total_stok'] != null) {
final currentStok = stokData['total_stok'].toDouble();
final newStok = currentStok - totalStokDibutuhkan;

View File

@ -197,9 +197,63 @@ class PenitipanBantuanController extends GetxController {
}
Future<void> pickfotoBuktiSerahTerima() async {
try {
// Tampilkan bottom sheet untuk memilih sumber foto
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Pilih Sumber Foto',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 16),
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Kamera'),
onTap: () {
Get.back();
_pickfotoBuktiSerahTerimaFrom(ImageSource.camera);
},
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Galeri'),
onTap: () {
Get.back();
_pickfotoBuktiSerahTerimaFrom(ImageSource.gallery);
},
),
],
),
),
);
} catch (e) {
print('Error showing bottom sheet: $e');
Get.snackbar(
'Error',
'Terjadi kesalahan: ${e.toString()}',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
// Fungsi helper untuk mengambil foto dari sumber yang dipilih
Future<void> _pickfotoBuktiSerahTerimaFrom(ImageSource source) async {
try {
final pickedFile = await _imagePicker.pickImage(
source: ImageSource.camera,
source: source,
imageQuality: 70,
maxWidth: 1000,
);

View File

@ -74,9 +74,7 @@ class PetugasDesaController extends GetxController {
String get nama {
// 1. Coba ambil dari AuthController displayName yang paling lengkap
final authDisplayName = _authController.displayName;
if (authDisplayName != null &&
authDisplayName != 'Pengguna' &&
authDisplayName != user?.email) {
if (authDisplayName != 'Pengguna' && authDisplayName != user?.email) {
return authDisplayName;
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/routes/app_pages.dart';
import 'package:penyaluran_app/app/utils/date_time_helper.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/donatur_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.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/widgets/dialogs/detail_penitipan_dialog.dart';

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penerima_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/utils/date_time_helper.dart';
class DetailPenerimaView extends GetView<PenerimaController> {

View File

@ -743,10 +743,10 @@ class _KonfirmasiPenerimaPageState extends State<KonfirmasiPenerimaPage> {
// Hapus file sementara sebelum navigasi
try {
if (signatureFile != null && signatureFile.existsSync()) {
if (signatureFile.existsSync()) {
await signatureFile.delete();
}
if (tempDir != null && tempDir.existsSync()) {
if (tempDir.existsSync()) {
await tempDir.delete();
}
} catch (e) {

View File

@ -632,18 +632,26 @@ class PenitipanView extends GetView<PenitipanBantuanController> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.camera_alt,
Icons.add_photo_alternate,
size: 48,
color: Colors.grey.shade600,
),
const SizedBox(height: 8),
Text(
'Ambil Foto',
'Pilih Foto',
style: TextStyle(
color: Colors.grey.shade600,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'Kamera atau Galeri',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
],
),
),

View File

@ -426,7 +426,9 @@ class StokBantuanView extends GetView<StokBantuanController> {
context,
icon: Icons.access_time,
label: 'Terakhir Diperbarui',
value: DateTimeHelper.formatDateTime(item.updatedAt),
value: item.updatedAt != null
? '${item.updatedAt!.day}/${item.updatedAt!.month}/${item.updatedAt!.year} ${item.updatedAt!.hour}:${item.updatedAt!.minute}'
: 'Tidak ada data',
),
),
],

View File

@ -90,29 +90,20 @@ class TambahPenyaluranView extends GetView<JadwalPenyaluranController> {
print('stokData $stokData');
if (stokData != null) {
namaStokBantuan.value =
stokData['nama'] ?? 'Nama stok tidak tersedia';
satuanStokBantuan.value = stokData['satuan'] ?? 'Tidak ada satuan';
isUang.value = stokData['is_uang'] ?? false;
namaStokBantuan.value = stokData['nama'] ?? 'Nama stok tidak tersedia';
satuanStokBantuan.value = stokData['satuan'] ?? 'Tidak ada satuan';
isUang.value = stokData['is_uang'] ?? false;
// Ambil jumlah stok tersedia
if (stokData['total_stok'] != null) {
totalStokTersedia.value = stokData['total_stok'].toDouble();
} else {
totalStokTersedia.value = 0;
}
// Periksa kecukupan stok
isStokCukup.value =
totalStokTersedia.value >= totalStokDibutuhkan.value;
// Ambil jumlah stok tersedia
if (stokData['total_stok'] != null) {
totalStokTersedia.value = stokData['total_stok'].toDouble();
} else {
namaStokBantuan.value = 'Stok tidak ditemukan';
satuanStokBantuan.value = '';
totalStokTersedia.value = 0;
isStokCukup.value = false;
isUang.value = false;
}
// Periksa kecukupan stok
isStokCukup.value =
totalStokTersedia.value >= totalStokDibutuhkan.value;
} catch (e) {
print('Error loading stok bantuan: $e');
namaStokBantuan.value = 'Error memuat data stok';