Perbarui DonaturController dan tampilan terkait untuk mendukung stok bantuan
- Tambahkan metode fetchStokBantuan di DonaturController untuk mengambil data stok bantuan - Perbarui tampilan DaftarDonaturView untuk navigasi ke detail donatur - Tambahkan rute dan tampilan detail donatur - Ganti beberapa warna ikon dan teks untuk konsistensi tampilan - Gunakan dialog DetailPenitipanDialog untuk menampilkan detail penitipan
This commit is contained in:
@ -2,11 +2,14 @@ import 'package:get/get.dart';
|
|||||||
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
||||||
import 'package:penyaluran_app/app/data/models/donatur_model.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/penitipan_bantuan_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/stok_bantuan_model.dart';
|
||||||
|
|
||||||
class DonaturController extends GetxController {
|
class DonaturController extends GetxController {
|
||||||
final RxList<DonaturModel> daftarDonatur = <DonaturModel>[].obs;
|
final RxList<DonaturModel> daftarDonatur = <DonaturModel>[].obs;
|
||||||
final RxMap<String, List<PenitipanBantuanModel>> penitipanPerDonatur =
|
final RxMap<String, List<PenitipanBantuanModel>> penitipanPerDonatur =
|
||||||
<String, List<PenitipanBantuanModel>>{}.obs;
|
<String, List<PenitipanBantuanModel>>{}.obs;
|
||||||
|
final RxMap<String, StokBantuanModel> stokBantuanMap =
|
||||||
|
<String, StokBantuanModel>{}.obs;
|
||||||
final RxBool isLoading = false.obs;
|
final RxBool isLoading = false.obs;
|
||||||
final SupabaseService _supabaseService = SupabaseService.to;
|
final SupabaseService _supabaseService = SupabaseService.to;
|
||||||
|
|
||||||
@ -14,6 +17,7 @@ class DonaturController extends GetxController {
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
fetchDaftarDonatur();
|
fetchDaftarDonatur();
|
||||||
|
fetchStokBantuan();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -74,6 +78,25 @@ class DonaturController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fetchStokBantuan() async {
|
||||||
|
try {
|
||||||
|
final result = await _supabaseService.getStokBantuan();
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
stokBantuanMap.clear();
|
||||||
|
|
||||||
|
for (var data in result) {
|
||||||
|
final stokBantuan = StokBantuanModel.fromJson(data);
|
||||||
|
if (stokBantuan.id != null) {
|
||||||
|
stokBantuanMap[stokBantuan.id!] = stokBantuan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error saat mengambil data stok bantuan: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mendapatkan jumlah donasi untuk donatur tertentu
|
// Mendapatkan jumlah donasi untuk donatur tertentu
|
||||||
int getJumlahDonasi(String? donaturId) {
|
int getJumlahDonasi(String? donaturId) {
|
||||||
if (donaturId == null || !penitipanPerDonatur.containsKey(donaturId)) {
|
if (donaturId == null || !penitipanPerDonatur.containsKey(donaturId)) {
|
||||||
@ -231,4 +254,39 @@ class DonaturController extends GetxController {
|
|||||||
|
|
||||||
return penitipanList;
|
return penitipanList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getStokBantuanSatuan(String? stokBantuanId) {
|
||||||
|
if (stokBantuanId == null || !stokBantuanMap.containsKey(stokBantuanId)) {
|
||||||
|
return 'item';
|
||||||
|
}
|
||||||
|
return stokBantuanMap[stokBantuanId]?.satuan ?? 'item';
|
||||||
|
}
|
||||||
|
|
||||||
|
String getStokBantuanNama(String? stokBantuanId) {
|
||||||
|
if (stokBantuanId == null || !stokBantuanMap.containsKey(stokBantuanId)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return stokBantuanMap[stokBantuanId]?.nama ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mendapatkan nama donatur berdasarkan ID
|
||||||
|
String? getDonaturNama(String? donaturId) {
|
||||||
|
if (donaturId == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final donatur = daftarDonatur.firstWhere((d) => d.id == donaturId);
|
||||||
|
return donatur.nama;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mendapatkan nama petugas desa berdasarkan ID
|
||||||
|
String? getPetugasDesaNama(String? petugasDesaId) {
|
||||||
|
if (petugasDesaId == null) return null;
|
||||||
|
|
||||||
|
// Implementasi ini perlu disesuaikan dengan cara aplikasi menyimpan data petugas desa
|
||||||
|
// Contoh sederhana:
|
||||||
|
return 'Petugas Desa'; // Ganti dengan implementasi yang sesuai
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/donatur_controller.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/theme/app_theme.dart';
|
||||||
import 'package:penyaluran_app/app/data/models/donatur_model.dart';
|
import 'package:penyaluran_app/app/data/models/donatur_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
||||||
|
|
||||||
class DaftarDonaturView extends GetView<DonaturController> {
|
class DaftarDonaturView extends GetView<DonaturController> {
|
||||||
const DaftarDonaturView({super.key});
|
const DaftarDonaturView({super.key});
|
||||||
@ -199,8 +200,8 @@ class DaftarDonaturView extends GetView<DonaturController> {
|
|||||||
),
|
),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Navigasi ke halaman detail donatur (akan diimplementasikan nanti)
|
// Navigasi ke halaman detail donatur
|
||||||
// Get.toNamed('/daftar-donatur/detail', arguments: donatur.id);
|
Get.toNamed(Routes.detailDonatur, arguments: donatur.id);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -268,14 +269,14 @@ class DaftarDonaturView extends GetView<DonaturController> {
|
|||||||
const Icon(
|
const Icon(
|
||||||
Icons.attach_money,
|
Icons.attach_money,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Color.fromARGB(255, 210, 158, 4),
|
color: Colors.green,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'${jumlahDonasiUang}x Donasi Uang',
|
'${jumlahDonasiUang}x Donasi Uang',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Color.fromARGB(255, 210, 158, 4),
|
color: Colors.green,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -287,14 +288,14 @@ class DaftarDonaturView extends GetView<DonaturController> {
|
|||||||
const Icon(
|
const Icon(
|
||||||
Icons.inventory_2,
|
Icons.inventory_2,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Colors.purple,
|
color: Colors.orange,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'${jumlahDonasiBarang}x Donasi Barang',
|
'${jumlahDonasiBarang}x Donasi Barang',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.purple,
|
color: Colors.orange,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -319,8 +320,8 @@ class DaftarDonaturView extends GetView<DonaturController> {
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'AKTIF':
|
case 'AKTIF':
|
||||||
backgroundColor = Colors.green.withOpacity(0.1);
|
backgroundColor = Colors.blue.withOpacity(0.1);
|
||||||
textColor = Colors.green;
|
textColor = Colors.blue;
|
||||||
label = 'Aktif';
|
label = 'Aktif';
|
||||||
break;
|
break;
|
||||||
case 'NONAKTIF':
|
case 'NONAKTIF':
|
||||||
@ -446,7 +447,7 @@ class DonaturSearchDelegate extends SearchDelegate {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
close(context, null);
|
close(context, null);
|
||||||
// Get.toNamed('/daftar-donatur/detail', arguments: donatur.id);
|
Get.toNamed(Routes.detailDonatur, arguments: donatur.id);
|
||||||
},
|
},
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: AppTheme.primaryColor.withOpacity(0.1),
|
backgroundColor: AppTheme.primaryColor.withOpacity(0.1),
|
||||||
|
553
lib/app/modules/petugas_desa/views/detail_donatur_view.dart
Normal file
553
lib/app/modules/petugas_desa/views/detail_donatur_view.dart
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
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/detail_penitipan_dialog.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class DetailDonaturView extends GetView<DonaturController> {
|
||||||
|
const DetailDonaturView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final String donaturId = Get.arguments as String;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Detail Donatur'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi edit donatur (akan diimplementasikan nanti)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: FutureBuilder<DonaturModel?>(
|
||||||
|
future: controller.fetchDonaturById(donaturId),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(
|
||||||
|
child: Text('Error: ${snapshot.error}'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot.hasData || snapshot.data == null) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('Data donatur tidak ditemukan'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final donatur = snapshot.data!;
|
||||||
|
return _buildDetailContent(context, donatur);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDetailContent(BuildContext context, DonaturModel donatur) {
|
||||||
|
// Pilih ikon berdasarkan jenis donatur
|
||||||
|
IconData jenisIcon;
|
||||||
|
switch (donatur.jenis) {
|
||||||
|
case 'Perusahaan':
|
||||||
|
jenisIcon = Icons.business;
|
||||||
|
break;
|
||||||
|
case 'Organisasi':
|
||||||
|
jenisIcon = Icons.groups;
|
||||||
|
break;
|
||||||
|
case 'Individu':
|
||||||
|
jenisIcon = Icons.person;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
jenisIcon = Icons.help_outline;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hitung jumlah donasi dan total nilai donasi
|
||||||
|
final jumlahDonasi = controller.getJumlahDonasi(donatur.id);
|
||||||
|
final jumlahDonasiUang = controller.getJumlahDonasiUang(donatur.id);
|
||||||
|
final jumlahDonasiBarang = controller.getJumlahDonasiBarang(donatur.id);
|
||||||
|
final totalNilaiDonasiUang = controller.getTotalNilaiDonasiUang(donatur.id);
|
||||||
|
final totalNilaiDonasiUangFormatted =
|
||||||
|
controller.formatRupiah(totalNilaiDonasiUang);
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Header dengan informasi utama donatur
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Avatar dan nama donatur
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 40,
|
||||||
|
backgroundColor: AppTheme.primaryColor.withOpacity(0.1),
|
||||||
|
child: Icon(
|
||||||
|
jenisIcon,
|
||||||
|
size: 40,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
donatur.nama ?? 'Tanpa Nama',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: donatur.jenis == 'Perusahaan'
|
||||||
|
? Colors.blue.withOpacity(0.1)
|
||||||
|
: donatur.jenis == 'Organisasi'
|
||||||
|
? Colors.green.withOpacity(0.1)
|
||||||
|
: Colors.orange.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
donatur.jenis ?? 'Tidak Diketahui',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: donatur.jenis == 'Perusahaan'
|
||||||
|
? Colors.blue
|
||||||
|
: donatur.jenis == 'Organisasi'
|
||||||
|
? Colors.green
|
||||||
|
: Colors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: donatur.status == 'AKTIF'
|
||||||
|
? Colors.green.withOpacity(0.1)
|
||||||
|
: Colors.red.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
donatur.status == 'AKTIF'
|
||||||
|
? Icons.check_circle
|
||||||
|
: Icons.cancel,
|
||||||
|
size: 12,
|
||||||
|
color: donatur.status == 'AKTIF'
|
||||||
|
? Colors.green
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
donatur.status ?? 'TIDAK AKTIF',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: donatur.status == 'AKTIF'
|
||||||
|
? Colors.green
|
||||||
|
: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// Informasi kontak
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildInfoItem(Icons.location_on, 'Alamat',
|
||||||
|
donatur.alamat ?? 'Tidak ada alamat'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildInfoItem(Icons.phone, 'Telepon',
|
||||||
|
donatur.telepon ?? 'Tidak ada telepon'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildInfoItem(
|
||||||
|
Icons.email, 'Email', donatur.email ?? 'Tidak ada email'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildInfoItem(
|
||||||
|
Icons.calendar_today,
|
||||||
|
'Terdaftar Sejak',
|
||||||
|
donatur.createdAt != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(donatur.createdAt!)
|
||||||
|
: 'Tidak diketahui',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// Ringkasan donasi
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Ringkasan Donasi',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
'Total Donasi',
|
||||||
|
'$jumlahDonasi',
|
||||||
|
Icons.volunteer_activism,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
'Donasi Uang',
|
||||||
|
'$jumlahDonasiUang',
|
||||||
|
Icons.monetization_on,
|
||||||
|
Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
'Donasi Barang',
|
||||||
|
'$jumlahDonasiBarang',
|
||||||
|
Icons.inventory_2,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.monetization_on,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
'Total Nilai Donasi Uang:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
totalNilaiDonasiUangFormatted,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// Riwayat donasi
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Riwayat Donasi',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Navigasi ke halaman riwayat donasi lengkap
|
||||||
|
},
|
||||||
|
child: const Text('Lihat Semua'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildRiwayatDonasi(donatur.id),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoItem(IconData icon, String label, String value) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 20,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatItem(
|
||||||
|
String label, String value, IconData icon, Color color) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 25,
|
||||||
|
backgroundColor: color.withOpacity(0.1),
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRiwayatDonasi(String? donaturId) {
|
||||||
|
if (donaturId == null ||
|
||||||
|
!controller.penitipanPerDonatur.containsKey(donaturId)) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
child: Center(
|
||||||
|
child: Text('Belum ada riwayat donasi'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final penitipanList = controller.penitipanPerDonatur[donaturId]!;
|
||||||
|
|
||||||
|
// Tampilkan maksimal 3 donasi terbaru
|
||||||
|
final displayedPenitipan =
|
||||||
|
penitipanList.length > 3 ? penitipanList.sublist(0, 3) : penitipanList;
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: displayedPenitipan.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final penitipan = displayedPenitipan[index];
|
||||||
|
return _buildDonasiItem(penitipan);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDonasiItem(PenitipanBantuanModel penitipan) {
|
||||||
|
final isUang = penitipan.isUang == true;
|
||||||
|
final tanggal = penitipan.createdAt != null
|
||||||
|
? DateFormat('dd MMM yyyy', 'id_ID').format(penitipan.createdAt!)
|
||||||
|
: 'Tanggal tidak diketahui';
|
||||||
|
|
||||||
|
String nilaiDonasi = '';
|
||||||
|
if (isUang && penitipan.jumlah != null) {
|
||||||
|
nilaiDonasi = controller.formatRupiah(penitipan.jumlah!);
|
||||||
|
} else if (penitipan.jumlah != null) {
|
||||||
|
final satuan = controller.getStokBantuanSatuan(penitipan.stokBantuanId);
|
||||||
|
nilaiDonasi = '${penitipan.jumlah} $satuan';
|
||||||
|
} else {
|
||||||
|
nilaiDonasi = 'Jumlah tidak diketahui';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.grey[100],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// Tampilkan dialog detail penitipan
|
||||||
|
_showDetailPenitipan(penitipan);
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 20,
|
||||||
|
backgroundColor: isUang
|
||||||
|
? Colors.green.withOpacity(0.1)
|
||||||
|
: Colors.orange.withOpacity(0.1),
|
||||||
|
child: Icon(
|
||||||
|
isUang ? Icons.monetization_on : Icons.inventory_2,
|
||||||
|
color: isUang ? Colors.green : Colors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
isUang
|
||||||
|
? 'Donasi Uang'
|
||||||
|
: controller
|
||||||
|
.getStokBantuanNama(penitipan.stokBantuanId)
|
||||||
|
.isNotEmpty
|
||||||
|
? controller
|
||||||
|
.getStokBantuanNama(penitipan.stokBantuanId)
|
||||||
|
: penitipan.deskripsi ?? 'Donasi Barang',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
tanggal,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
nilaiDonasi,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: isUang ? Colors.green : Colors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metode untuk menampilkan dialog detail penitipan
|
||||||
|
void _showDetailPenitipan(PenitipanBantuanModel penitipan) {
|
||||||
|
// Dapatkan data yang diperlukan
|
||||||
|
final donaturNama = penitipan.donatur?.nama ??
|
||||||
|
controller.getDonaturNama(penitipan.donaturId) ??
|
||||||
|
'Donatur tidak ditemukan';
|
||||||
|
|
||||||
|
final kategoriNama = penitipan.kategoriBantuan?.nama ??
|
||||||
|
controller.getStokBantuanNama(penitipan.stokBantuanId);
|
||||||
|
|
||||||
|
final kategoriSatuan = penitipan.kategoriBantuan?.satuan ??
|
||||||
|
controller.getStokBantuanSatuan(penitipan.stokBantuanId);
|
||||||
|
|
||||||
|
// Tampilkan dialog
|
||||||
|
DetailPenitipanDialog.show(
|
||||||
|
context: Get.context!,
|
||||||
|
item: penitipan,
|
||||||
|
donaturNama: donaturNama,
|
||||||
|
kategoriNama: kategoriNama,
|
||||||
|
kategoriSatuan: kategoriSatuan,
|
||||||
|
getPetugasDesaNama: (String? id) =>
|
||||||
|
controller.getPetugasDesaNama(id) ?? 'Petugas tidak diketahui',
|
||||||
|
showFullScreenImage: (String imageUrl) {
|
||||||
|
DetailPenitipanDialog.showFullScreenImage(Get.context!, imageUrl);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
|
|||||||
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
import 'package:penyaluran_app/app/utils/date_formatter.dart';
|
import 'package:penyaluran_app/app/utils/date_formatter.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/detail_penitipan_dialog.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
class PenitipanView extends GetView<PenitipanBantuanController> {
|
class PenitipanView extends GetView<PenitipanBantuanController> {
|
||||||
@ -694,198 +695,18 @@ class PenitipanView extends GetView<PenitipanBantuanController> {
|
|||||||
final kategoriSatuan = item.kategoriBantuan?.satuan ??
|
final kategoriSatuan = item.kategoriBantuan?.satuan ??
|
||||||
controller.getKategoriSatuan(item.stokBantuanId);
|
controller.getKategoriSatuan(item.stokBantuanId);
|
||||||
|
|
||||||
// Cek apakah penitipan berbentuk uang
|
// Gunakan dialog yang sudah dibuat
|
||||||
final isUang = item.isUang ?? false;
|
DetailPenitipanDialog.show(
|
||||||
|
context: context,
|
||||||
Get.dialog(
|
item: item,
|
||||||
AlertDialog(
|
donaturNama: donaturNama,
|
||||||
title: const Text('Detail Penitipan'),
|
kategoriNama: kategoriNama,
|
||||||
content: SingleChildScrollView(
|
kategoriSatuan: kategoriSatuan,
|
||||||
child: Column(
|
getPetugasDesaNama: (String? id) =>
|
||||||
mainAxisSize: MainAxisSize.min,
|
controller.getPetugasDesaNama(id) ?? 'Tidak diketahui',
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
showFullScreenImage: (String imageUrl) {
|
||||||
children: [
|
DetailPenitipanDialog.showFullScreenImage(context, imageUrl);
|
||||||
_buildDetailItem('Donatur', donaturNama),
|
|
||||||
_buildDetailItem('Status', item.status ?? 'Tidak diketahui'),
|
|
||||||
_buildDetailItem('Kategori Bantuan', kategoriNama),
|
|
||||||
_buildDetailItem(
|
|
||||||
'Jumlah',
|
|
||||||
isUang
|
|
||||||
? 'Rp ${DateFormatter.formatNumber(item.jumlah)}'
|
|
||||||
: '${DateFormatter.formatNumber(item.jumlah)} $kategoriSatuan'),
|
|
||||||
if (isUang) _buildDetailItem('Jenis Bantuan', 'Uang (Rupiah)'),
|
|
||||||
_buildDetailItem(
|
|
||||||
'Deskripsi', item.deskripsi ?? 'Tidak ada deskripsi'),
|
|
||||||
_buildDetailItem(
|
|
||||||
'Tanggal Penitipan',
|
|
||||||
DateFormatter.formatDateTime(item.tanggalPenitipan,
|
|
||||||
defaultValue: 'Tidak ada tanggal'),
|
|
||||||
),
|
|
||||||
if (item.tanggalVerifikasi != null)
|
|
||||||
_buildDetailItem(
|
|
||||||
'Tanggal Verifikasi',
|
|
||||||
DateFormatter.formatDateTime(item.tanggalVerifikasi),
|
|
||||||
),
|
|
||||||
if (item.status == 'TERVERIFIKASI' && item.petugasDesaId != null)
|
|
||||||
_buildDetailItem(
|
|
||||||
'Diverifikasi Oleh',
|
|
||||||
controller.getPetugasDesaNama(item.petugasDesaId),
|
|
||||||
),
|
|
||||||
_buildDetailItem('Tanggal Dibuat',
|
|
||||||
DateFormatter.formatDateTime(item.createdAt)),
|
|
||||||
if (item.alasanPenolakan != null &&
|
|
||||||
item.alasanPenolakan!.isNotEmpty)
|
|
||||||
_buildDetailItem('Alasan Penolakan', item.alasanPenolakan!),
|
|
||||||
|
|
||||||
// Foto Bantuan
|
|
||||||
if (!isUang &&
|
|
||||||
item.fotoBantuan != null &&
|
|
||||||
item.fotoBantuan!.isNotEmpty)
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
'Foto Bantuan:',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
|
||||||
height: 100,
|
|
||||||
child: ListView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: item.fotoBantuan!.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
_showFullScreenImage(
|
|
||||||
context, item.fotoBantuan![index]);
|
|
||||||
},
|
},
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Image.network(
|
|
||||||
item.fotoBantuan![index],
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return Container(
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
color: Colors.grey.shade300,
|
|
||||||
child: const Icon(Icons.error),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Bukti Transfer (untuk bantuan uang)
|
|
||||||
if (isUang &&
|
|
||||||
item.fotoBantuan != null &&
|
|
||||||
item.fotoBantuan!.isNotEmpty)
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
'Bukti Transfer:',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
|
||||||
height: 100,
|
|
||||||
child: ListView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: item.fotoBantuan!.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
_showFullScreenImage(
|
|
||||||
context, item.fotoBantuan![index]);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Image.network(
|
|
||||||
item.fotoBantuan![index],
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return Container(
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
color: Colors.grey.shade300,
|
|
||||||
child: const Icon(Icons.error),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Bukti Serah Terima
|
|
||||||
if (item.fotoBuktiSerahTerima != null &&
|
|
||||||
item.fotoBuktiSerahTerima!.isNotEmpty)
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
'Bukti Serah Terima:',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
_showFullScreenImage(
|
|
||||||
context, item.fotoBuktiSerahTerima!);
|
|
||||||
},
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Image.network(
|
|
||||||
item.fotoBuktiSerahTerima!,
|
|
||||||
height: 200,
|
|
||||||
width: double.infinity,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return Container(
|
|
||||||
height: 200,
|
|
||||||
width: double.infinity,
|
|
||||||
color: Colors.grey.shade300,
|
|
||||||
child: const Icon(Icons.error),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Get.back(),
|
|
||||||
child: const Text('Tutup'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.volunteer_activism),
|
leading: const Icon(Icons.volunteer_activism_outlined),
|
||||||
title: const Text('Daftar Donatur'),
|
title: const Text('Daftar Donatur'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pop(context); // Tutup drawer terlebih dahulu
|
Navigator.pop(context); // Tutup drawer terlebih dahulu
|
||||||
|
@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
|
import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
|
||||||
import 'package:penyaluran_app/app/utils/date_formatter.dart';
|
import 'package:penyaluran_app/app/utils/date_formatter.dart';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
class RiwayatPenitipanView extends GetView<PenitipanBantuanController> {
|
class RiwayatPenitipanView extends GetView<PenitipanBantuanController> {
|
||||||
const RiwayatPenitipanView({super.key});
|
const RiwayatPenitipanView({super.key});
|
||||||
|
@ -10,6 +10,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/konfirmasi_penerim
|
|||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/pelaksanaan_penyaluran_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/pelaksanaan_penyaluran_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/riwayat_penitipan_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/riwayat_penitipan_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_donatur_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_donatur_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_donatur_view.dart';
|
||||||
|
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/penerima_binding.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/penerima_binding.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/donatur_binding.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/donatur_binding.dart';
|
||||||
@ -81,5 +82,10 @@ class AppPages {
|
|||||||
page: () => const DaftarDonaturView(),
|
page: () => const DaftarDonaturView(),
|
||||||
binding: DonaturBinding(),
|
binding: DonaturBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: _Paths.detailDonatur,
|
||||||
|
page: () => const DetailDonaturView(),
|
||||||
|
binding: DonaturBinding(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ abstract class Routes {
|
|||||||
static const profile = _Paths.profile;
|
static const profile = _Paths.profile;
|
||||||
static const riwayatPenitipan = _Paths.riwayatPenitipan;
|
static const riwayatPenitipan = _Paths.riwayatPenitipan;
|
||||||
static const daftarDonatur = _Paths.daftarDonatur;
|
static const daftarDonatur = _Paths.daftarDonatur;
|
||||||
|
static const detailDonatur = _Paths.detailDonatur;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Paths {
|
abstract class _Paths {
|
||||||
@ -38,4 +39,5 @@ abstract class _Paths {
|
|||||||
static const profile = '/profile';
|
static const profile = '/profile';
|
||||||
static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan';
|
static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan';
|
||||||
static const daftarDonatur = '/daftar-donatur';
|
static const daftarDonatur = '/daftar-donatur';
|
||||||
|
static const detailDonatur = '/daftar-donatur/detail';
|
||||||
}
|
}
|
||||||
|
306
lib/app/widgets/detail_penitipan_dialog.dart
Normal file
306
lib/app/widgets/detail_penitipan_dialog.dart
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
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/utils/date_formatter.dart';
|
||||||
|
|
||||||
|
/// Dialog untuk menampilkan detail penitipan bantuan
|
||||||
|
///
|
||||||
|
/// Contoh penggunaan:
|
||||||
|
/// ```dart
|
||||||
|
/// // Di halaman lain
|
||||||
|
/// void showDetailPenitipan(BuildContext context, PenitipanBantuanModel item) {
|
||||||
|
/// // Dapatkan data yang diperlukan
|
||||||
|
/// final donaturNama = item.donatur?.nama ?? 'Donatur tidak ditemukan';
|
||||||
|
/// final kategoriNama = item.kategoriBantuan?.nama ?? 'Kategori tidak ditemukan';
|
||||||
|
/// final kategoriSatuan = item.kategoriBantuan?.satuan ?? '';
|
||||||
|
///
|
||||||
|
/// // Tampilkan dialog
|
||||||
|
/// DetailPenitipanDialog.show(
|
||||||
|
/// context: context,
|
||||||
|
/// item: item,
|
||||||
|
/// donaturNama: donaturNama,
|
||||||
|
/// kategoriNama: kategoriNama,
|
||||||
|
/// kategoriSatuan: kategoriSatuan,
|
||||||
|
/// getPetugasDesaNama: (String? id) => 'Nama Petugas', // Sesuaikan dengan cara mendapatkan nama petugas
|
||||||
|
/// showFullScreenImage: (String imageUrl) {
|
||||||
|
/// DetailPenitipanDialog.showFullScreenImage(context, imageUrl);
|
||||||
|
/// },
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
class DetailPenitipanDialog {
|
||||||
|
static void show({
|
||||||
|
required BuildContext context,
|
||||||
|
required PenitipanBantuanModel item,
|
||||||
|
required String donaturNama,
|
||||||
|
required String kategoriNama,
|
||||||
|
required String kategoriSatuan,
|
||||||
|
required String Function(String?) getPetugasDesaNama,
|
||||||
|
required Function(String) showFullScreenImage,
|
||||||
|
}) {
|
||||||
|
// Cek apakah penitipan berbentuk uang
|
||||||
|
final isUang = item.isUang ?? false;
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('Detail Penitipan'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildDetailItem('Donatur', donaturNama),
|
||||||
|
_buildDetailItem('Status', item.status ?? 'Tidak diketahui'),
|
||||||
|
_buildDetailItem('Kategori Bantuan', kategoriNama),
|
||||||
|
_buildDetailItem(
|
||||||
|
'Jumlah',
|
||||||
|
isUang
|
||||||
|
? 'Rp ${DateFormatter.formatNumber(item.jumlah)}'
|
||||||
|
: '${DateFormatter.formatNumber(item.jumlah)} $kategoriSatuan'),
|
||||||
|
if (isUang) _buildDetailItem('Jenis Bantuan', 'Uang (Rupiah)'),
|
||||||
|
_buildDetailItem(
|
||||||
|
'Deskripsi', item.deskripsi ?? 'Tidak ada deskripsi'),
|
||||||
|
_buildDetailItem(
|
||||||
|
'Tanggal Penitipan',
|
||||||
|
DateFormatter.formatDateTime(item.tanggalPenitipan,
|
||||||
|
defaultValue: 'Tidak ada tanggal'),
|
||||||
|
),
|
||||||
|
if (item.tanggalVerifikasi != null)
|
||||||
|
_buildDetailItem(
|
||||||
|
'Tanggal Verifikasi',
|
||||||
|
DateFormatter.formatDateTime(item.tanggalVerifikasi),
|
||||||
|
),
|
||||||
|
if (item.status == 'TERVERIFIKASI' && item.petugasDesaId != null)
|
||||||
|
_buildDetailItem(
|
||||||
|
'Diverifikasi Oleh',
|
||||||
|
getPetugasDesaNama(item.petugasDesaId) ?? 'Tidak diketahui',
|
||||||
|
),
|
||||||
|
_buildDetailItem('Tanggal Dibuat',
|
||||||
|
DateFormatter.formatDateTime(item.createdAt)),
|
||||||
|
if (item.alasanPenolakan != null &&
|
||||||
|
item.alasanPenolakan!.isNotEmpty)
|
||||||
|
_buildDetailItem('Alasan Penolakan', item.alasanPenolakan!),
|
||||||
|
|
||||||
|
// Foto Bantuan
|
||||||
|
if (!isUang &&
|
||||||
|
item.fotoBantuan != null &&
|
||||||
|
item.fotoBantuan!.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text(
|
||||||
|
'Foto Bantuan:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: item.fotoBantuan!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showFullScreenImage(item.fotoBantuan![index]);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.network(
|
||||||
|
item.fotoBantuan![index],
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
child: const Icon(Icons.error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bukti Transfer (untuk bantuan uang)
|
||||||
|
if (isUang &&
|
||||||
|
item.fotoBantuan != null &&
|
||||||
|
item.fotoBantuan!.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text(
|
||||||
|
'Bukti Transfer:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: item.fotoBantuan!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showFullScreenImage(item.fotoBantuan![index]);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.network(
|
||||||
|
item.fotoBantuan![index],
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
child: const Icon(Icons.error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bukti Serah Terima
|
||||||
|
if (item.fotoBuktiSerahTerima != null &&
|
||||||
|
item.fotoBuktiSerahTerima!.isNotEmpty)
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text(
|
||||||
|
'Bukti Serah Terima:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showFullScreenImage(item.fotoBuktiSerahTerima!);
|
||||||
|
},
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.network(
|
||||||
|
item.fotoBuktiSerahTerima!,
|
||||||
|
height: 200,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
height: 200,
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
child: const Icon(Icons.error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('Tutup'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildDetailItem(String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showFullScreenImage(BuildContext context, String imageUrl) {
|
||||||
|
Get.dialog(
|
||||||
|
Dialog(
|
||||||
|
insetPadding: EdgeInsets.zero,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
InteractiveViewer(
|
||||||
|
panEnabled: true,
|
||||||
|
minScale: 0.5,
|
||||||
|
maxScale: 4,
|
||||||
|
child: Image.network(
|
||||||
|
imageUrl,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.error,
|
||||||
|
size: 50,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 20,
|
||||||
|
right: 20,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Get.back(),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withOpacity(0.5),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user