From d8bf361d2178354747be5a57e393dac5cd50fa3c Mon Sep 17 00:00:00 2001 From: Khafidh Fuadi Date: Wed, 12 Mar 2025 21:48:36 +0700 Subject: [PATCH] Perbarui binding dan controller untuk manajemen penitipan bantuan - Perbarui PetugasDesaBinding untuk menggunakan Get.lazyPut pada controller - Tambahkan listener pencarian donatur di PenitipanBantuanController - Perbaiki logika pencarian dan tampilan daftar penitipan di PenitipanView - Tambahkan rute dan tampilan untuk riwayat penitipan di aplikasi --- .../bindings/petugas_desa_binding.dart | 37 +- .../penitipan_bantuan_controller.dart | 20 +- .../petugas_desa/views/penitipan_view.dart | 31 +- .../petugas_desa/views/petugas_desa_view.dart | 15 + .../views/riwayat_penitipan_view.dart | 670 ++++++++++++++++++ lib/app/routes/app_pages.dart | 6 + lib/app/routes/app_routes.dart | 2 + 7 files changed, 751 insertions(+), 30 deletions(-) create mode 100644 lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart diff --git a/lib/app/modules/petugas_desa/bindings/petugas_desa_binding.dart b/lib/app/modules/petugas_desa/bindings/petugas_desa_binding.dart index 6b0f927..6b3c913 100644 --- a/lib/app/modules/petugas_desa/bindings/petugas_desa_binding.dart +++ b/lib/app/modules/petugas_desa/bindings/petugas_desa_binding.dart @@ -13,64 +13,55 @@ import 'package:penyaluran_app/app/modules/petugas_desa/controllers/counter_serv class PetugasDesaBinding extends Bindings { @override void dependencies() { - // Pastikan AuthController tersedia + // Pastikan AuthController sudah terdaftar if (!Get.isRegistered()) { Get.put(AuthController(), permanent: true); } - // Daftarkan CounterService terlebih dahulu + // Daftarkan CounterService jika belum terdaftar if (!Get.isRegistered()) { Get.put(CounterService(), permanent: true); } - // Main controller - gunakan put dengan permanent untuk controller utama - if (!Get.isRegistered()) { - Get.put(PetugasDesaController(), permanent: true); - } else { - // Jika sudah terdaftar, gunakan find untuk mendapatkan instance yang ada - Get.find(); - } + // Daftarkan controller utama + Get.lazyPut( + () => PetugasDesaController(), + ); - // Dashboard controller + // Daftarkan controller dashboard Get.lazyPut( () => PetugasDesaDashboardController(), - fenix: true, ); - // Jadwal penyaluran controller + // Daftarkan controller jadwal penyaluran Get.lazyPut( () => JadwalPenyaluranController(), - fenix: true, ); - // Stok bantuan controller + // Daftarkan controller stok bantuan Get.lazyPut( () => StokBantuanController(), - fenix: true, ); - // Penitipan bantuan controller + // Daftarkan controller penitipan bantuan Get.lazyPut( () => PenitipanBantuanController(), - fenix: true, + fenix: true, // Agar controller tetap hidup saat berpindah halaman ); - // Pengaduan controller + // Daftarkan controller pengaduan Get.lazyPut( () => PengaduanController(), - fenix: true, ); - // Penerima bantuan controller + // Daftarkan controller penerima bantuan Get.lazyPut( () => PenerimaBantuanController(), - fenix: true, ); - // Laporan controller + // Daftarkan controller laporan Get.lazyPut( () => LaporanController(), - fenix: true, ); } } diff --git a/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart b/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart index 015d43a..ea2cc25 100644 --- a/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart @@ -51,7 +51,7 @@ class PenitipanBantuanController extends GetxController { final TextEditingController searchController = TextEditingController(); // Tambahkan properti untuk waktu terakhir update - Rx lastUpdateTime = DateTime.now().obs; + final Rx lastUpdateTime = DateTime.now().obs; UserModel? get user => _authController.user; @@ -76,6 +76,15 @@ class PenitipanBantuanController extends GetxController { Future.delayed(const Duration(seconds: 1), () { loadAllPetugasDesaData(); }); + + // Listener untuk pencarian donatur + donaturSearchController.addListener(() { + if (donaturSearchController.text.length >= 3) { + searchDonatur(donaturSearchController.text); + } else { + hasilPencarianDonatur.clear(); + } + }); } @override @@ -132,7 +141,7 @@ class PenitipanBantuanController extends GetxController { // Debug: print semua data petugas desa yang sudah dimuat print('Data petugas desa yang sudah dimuat:'); petugasDesaCache.forEach((key, value) { - print('ID: $key, Nama: ${value['name']}'); + print('ID: $key, Nama: $value'); }); // Update waktu terakhir refresh @@ -588,7 +597,7 @@ class PenitipanBantuanController extends GetxController { // Debug: print semua data petugas desa yang sudah dimuat print('Data petugas desa yang sudah dimuat setelah reload:'); petugasDesaCache.forEach((key, value) { - print('ID: $key, Nama: ${value['name']}'); + print('ID: $key, Nama: $value'); }); } catch (e) { print('Error saat memuat ulang data petugas desa: $e'); @@ -689,6 +698,11 @@ class PenitipanBantuanController extends GetxController { ditolak: ditolak, ); + // Update counter lokal + jumlahMenunggu.value = menunggu; + jumlahTerverifikasi.value = terverifikasi; + jumlahDitolak.value = ditolak; + // Debug counter values print( 'Counter updated - Menunggu: $menunggu, Terverifikasi: $terverifikasi, Ditolak: $ditolak'); diff --git a/lib/app/modules/petugas_desa/views/penitipan_view.dart b/lib/app/modules/petugas_desa/views/penitipan_view.dart index 72366c4..5d8bd57 100644 --- a/lib/app/modules/petugas_desa/views/penitipan_view.dart +++ b/lib/app/modules/petugas_desa/views/penitipan_view.dart @@ -209,7 +209,7 @@ class PenitipanView extends GetView { } Widget _buildPenitipanList(BuildContext context) { - final filteredList = controller.getFilteredPenitipan(); + final filteredList = getFilteredPenitipan(); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -218,7 +218,7 @@ class PenitipanView extends GetView { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Daftar Penitipan', + 'Perlu Diverifikasi', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), @@ -385,7 +385,7 @@ class PenitipanView extends GetView { label: 'Jumlah', value: isUang ? 'Rp ${DateFormatter.formatNumber(item.jumlah)}' - : '${DateFormatter.formatNumber(item.jumlah)} ${kategoriSatuan}', + : '${DateFormatter.formatNumber(item.jumlah)} $kategoriSatuan', ), ), ], @@ -712,7 +712,7 @@ class PenitipanView extends GetView { 'Jumlah', isUang ? 'Rp ${DateFormatter.formatNumber(item.jumlah)}' - : '${DateFormatter.formatNumber(item.jumlah)} ${kategoriSatuan}'), + : '${DateFormatter.formatNumber(item.jumlah)} $kategoriSatuan'), if (isUang) _buildDetailItem('Jenis Bantuan', 'Uang (Rupiah)'), _buildDetailItem( 'Deskripsi', item.deskripsi ?? 'Tidak ada deskripsi'), @@ -1799,4 +1799,27 @@ class PenitipanView extends GetView { ); }); } + + List getFilteredPenitipan() { + final searchText = controller.searchController.text.toLowerCase(); + // Hanya tampilkan penitipan dengan status MENUNGGU + var filteredList = controller.daftarPenitipan + .where((item) => item.status == 'MENUNGGU') + .toList(); + + // Filter berdasarkan pencarian jika ada teks pencarian + if (searchText.isNotEmpty) { + filteredList = filteredList.where((item) { + final donaturNama = item.donatur?.nama?.toLowerCase() ?? ''; + final kategoriNama = item.kategoriBantuan?.nama?.toLowerCase() ?? ''; + final deskripsi = item.deskripsi?.toLowerCase() ?? ''; + + return donaturNama.contains(searchText) || + kategoriNama.contains(searchText) || + deskripsi.contains(searchText); + }).toList(); + } + + return filteredList; + } } diff --git a/lib/app/modules/petugas_desa/views/petugas_desa_view.dart b/lib/app/modules/petugas_desa/views/petugas_desa_view.dart index c27f710..716ddb5 100644 --- a/lib/app/modules/petugas_desa/views/petugas_desa_view.dart +++ b/lib/app/modules/petugas_desa/views/petugas_desa_view.dart @@ -93,6 +93,21 @@ class PetugasDesaView extends GetView { ), ], ); + // Tampilkan tombol riwayat hanya jika tab Penitipan aktif + if (activeTab == 2) { + return Row( + children: [ + IconButton( + onPressed: () { + Get.toNamed('/petugas-desa/riwayat-penitipan'); + }, + icon: const Icon(Icons.history), + tooltip: 'Riwayat Penitipan', + ), + notificationButton, + ], + ); + } return notificationButton; }), diff --git a/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart b/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart new file mode 100644 index 0000000..3cf9b40 --- /dev/null +++ b/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart @@ -0,0 +1,670 @@ +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/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 'dart:io'; + +class RiwayatPenitipanView extends GetView { + const RiwayatPenitipanView({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + appBar: AppBar( + title: const Text('Riwayat Penitipan'), + bottom: const TabBar( + tabs: [ + Tab(text: 'Terverifikasi'), + Tab(text: 'Ditolak'), + ], + ), + ), + body: Obx(() => TabBarView( + children: [ + // Tab Terverifikasi + _buildPenitipanList(context, 'TERVERIFIKASI'), + // Tab Ditolak + _buildPenitipanList(context, 'DITOLAK'), + ], + )), + ), + ); + } + + Widget _buildPenitipanList(BuildContext context, String status) { + var filteredList = controller.daftarPenitipan + .where((item) => item.status == status) + .toList(); + + // Filter berdasarkan pencarian jika ada teks pencarian + final searchText = controller.searchController.text.toLowerCase(); + if (searchText.isNotEmpty) { + filteredList = filteredList.where((item) { + final donaturNama = item.donatur?.nama?.toLowerCase() ?? ''; + final kategoriNama = item.kategoriBantuan?.nama?.toLowerCase() ?? ''; + final deskripsi = item.deskripsi?.toLowerCase() ?? ''; + final tanggal = + DateFormatter.formatDateTime(item.tanggalPenitipan).toLowerCase(); + + return donaturNama.contains(searchText) || + kategoriNama.contains(searchText) || + deskripsi.contains(searchText) || + tanggal.contains(searchText); + }).toList(); + } + + return RefreshIndicator( + onRefresh: controller.refreshData, + child: controller.isLoading.value + ? const Center(child: CircularProgressIndicator()) + : SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Search field + TextField( + controller: controller.searchController, + decoration: InputDecoration( + hintText: 'Cari riwayat penitipan...', + prefixIcon: const Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + filled: true, + fillColor: Colors.grey.shade100, + contentPadding: const EdgeInsets.symmetric(vertical: 0), + ), + onChanged: (value) { + // Trigger rebuild dengan Obx + controller.update(); + }, + ), + const SizedBox(height: 16), + // Info jumlah item + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Daftar Penitipan ${status.toLowerCase()}', + style: + Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + Text( + '${DateFormatter.formatNumber(filteredList.length)} item', + style: + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Colors.grey, + ), + ), + ], + ), + const SizedBox(height: 12), + // Informasi jumlah item + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total: ${DateFormatter.formatNumber(filteredList.length)} item', + style: + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Colors.grey, + ), + ), + // Informasi terakhir update + Row( + children: [ + Icon(Icons.update, + size: 16, color: Colors.grey[600]), + const SizedBox(width: 4), + Text( + 'Update: ${DateFormatter.formatDateTimeWithHour(controller.lastUpdateTime.value)}', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + fontStyle: FontStyle.italic, + ), + ), + ], + ), + ], + ), + const SizedBox(height: 16), + + // Daftar penitipan + filteredList.isEmpty + ? Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + Icon( + Icons.inbox_outlined, + size: 80, + color: Colors.grey.shade400, + ), + const SizedBox(height: 16), + Text( + 'Tidak ada data penitipan ${status.toLowerCase()}', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith( + color: Colors.grey.shade600, + ), + ), + ], + ), + ), + ) + : ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: filteredList.length, + itemBuilder: (context, index) { + return _buildPenitipanItem( + context, filteredList[index]); + }, + ), + ], + ), + ), + ), + ); + } + + Widget _buildPenitipanItem(BuildContext context, PenitipanBantuanModel item) { + Color statusColor; + IconData statusIcon; + + switch (item.status) { + case 'TERVERIFIKASI': + statusColor = Colors.green; + statusIcon = Icons.check_circle; + break; + case 'DITOLAK': + statusColor = Colors.red; + statusIcon = Icons.cancel; + break; + default: + statusColor = Colors.grey; + statusIcon = Icons.help_outline; + } + + final donaturNama = item.donatur?.nama ?? 'Donatur tidak ditemukan'; + final kategoriNama = item.kategoriBantuan?.nama ?? + controller.getKategoriNama(item.stokBantuanId); + final kategoriSatuan = item.kategoriBantuan?.satuan ?? + controller.getKategoriSatuan(item.stokBantuanId); + final isUang = item.isUang ?? false; + + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withAlpha(26), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 1), + ), + ], + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + donaturNama, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + ), + Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + statusIcon, + size: 16, + color: statusColor, + ), + const SizedBox(width: 4), + Text( + item.status ?? 'Tidak diketahui', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: statusColor, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: _buildItemDetail( + context, + icon: isUang ? Icons.monetization_on : Icons.category, + label: 'Kategori Bantuan', + value: kategoriNama, + ), + ), + Expanded( + child: _buildItemDetail( + context, + icon: + isUang ? Icons.account_balance_wallet : Icons.inventory, + label: 'Jumlah', + value: isUang + ? 'Rp ${DateFormatter.formatNumber(item.jumlah)}' + : '${DateFormatter.formatNumber(item.jumlah)} $kategoriSatuan', + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: _buildItemDetail( + context, + icon: Icons.calendar_today, + label: item.status == 'TERVERIFIKASI' + ? 'Tanggal Verifikasi' + : 'Tanggal Penolakan', + value: DateFormatter.formatDateTime( + item.status == 'TERVERIFIKASI' + ? item.tanggalVerifikasi + : item.updatedAt, + defaultValue: 'Tidak ada tanggal'), + ), + ), + if (item.status == 'TERVERIFIKASI' && + item.petugasDesaId != null) + Expanded( + child: _buildItemDetail( + context, + icon: Icons.person, + label: 'Diverifikasi Oleh', + value: controller.getPetugasDesaNama(item.petugasDesaId), + ), + ), + ], + ), + if (item.alasanPenolakan != null && + item.alasanPenolakan!.isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + _buildItemDetail( + context, + icon: Icons.info_outline, + label: 'Alasan Penolakan', + value: item.alasanPenolakan!, + ), + ], + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton.icon( + onPressed: () { + _showDetailDialog(context, item, donaturNama); + }, + icon: const Icon(Icons.info_outline, size: 18), + label: const Text('Detail'), + style: TextButton.styleFrom( + foregroundColor: Colors.blue, + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildItemDetail( + BuildContext context, { + required IconData icon, + required String label, + required String value, + }) { + return Row( + children: [ + Icon( + icon, + size: 16, + color: Colors.grey, + ), + const SizedBox(width: 4), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.grey, + ), + ), + Text( + value, + style: Theme.of(context).textTheme.bodyMedium, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ], + ); + } + + void _showDetailDialog( + BuildContext context, PenitipanBantuanModel item, String donaturNama) { + final kategoriNama = item.kategoriBantuan?.nama ?? + controller.getKategoriNama(item.stokBantuanId); + final kategoriSatuan = item.kategoriBantuan?.satuan ?? + controller.getKategoriSatuan(item.stokBantuanId); + 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', + 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'), + ), + ], + ), + ); + } + + 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, + ), + ), + ), + ), + ], + ), + ), + ); + } + + 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(), + ], + ), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 1f33893..242adee 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -8,6 +8,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_penerima_vi import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_penerima_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/konfirmasi_penerima_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/bindings/penerima_binding.dart'; import 'package:penyaluran_app/app/modules/profile/bindings/profile_binding.dart'; @@ -68,5 +69,10 @@ class AppPages { page: () => const ProfileView(), binding: ProfileBinding(), ), + GetPage( + name: _Paths.riwayatPenitipan, + page: () => const RiwayatPenitipanView(), + binding: PetugasDesaBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 35e2f72..bff559d 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -16,6 +16,7 @@ abstract class Routes { static const konfirmasiPenerima = _Paths.konfirmasiPenerima; static const pelaksanaanPenyaluran = _Paths.pelaksanaanPenyaluran; static const profile = _Paths.profile; + static const riwayatPenitipan = _Paths.riwayatPenitipan; } abstract class _Paths { @@ -34,4 +35,5 @@ abstract class _Paths { static const konfirmasiPenerima = '/daftar-penerima/konfirmasi'; static const pelaksanaanPenyaluran = '/pelaksanaan-penyaluran'; static const profile = '/profile'; + static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan'; }