Tambahkan fitur penitipan dan perbarui tampilan modul Petugas Desa

- Tambahkan logika dan tampilan untuk manajemen penitipan bantuan
- Perbarui kontroler PetugasDesaController dengan metode loadPenitipanData()
- Tambahkan view PenitipanView untuk menampilkan daftar penitipan
- Integrasikan penitipan ke dalam drawer dan bottom navigation
- Tambahkan indikator jumlah penitipan yang menunggu verifikasi
This commit is contained in:
Khafidh Fuadi
2025-03-08 17:40:36 +07:00
parent 10ed95b3ac
commit 45ff26e7f8
6 changed files with 1007 additions and 190 deletions

View File

@ -43,6 +43,13 @@ class PetugasDesaController extends GetxController {
final RxDouble stokMasuk = 0.0.obs; final RxDouble stokMasuk = 0.0.obs;
final RxDouble stokKeluar = 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;
// Controller untuk pencarian // Controller untuk pencarian
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
@ -58,6 +65,7 @@ class PetugasDesaController extends GetxController {
loadJadwalData(); loadJadwalData();
loadNotifikasiData(); loadNotifikasiData();
loadInventarisData(); loadInventarisData();
loadPenitipanData();
} }
@override @override
@ -281,6 +289,65 @@ class PetugasDesaController extends GetxController {
} }
} }
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;
} catch (e) {
print('Error loading penitipan data: $e');
}
}
void tandaiNotifikasiDibaca(String id) { void tandaiNotifikasiDibaca(String id) {
// Implementasi untuk menandai notifikasi sebagai dibaca // Implementasi untuk menandai notifikasi sebagai dibaca
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status notifikasi // Di implementasi nyata, akan memanggil Supabase untuk memperbarui status notifikasi
@ -308,6 +375,25 @@ class PetugasDesaController extends GetxController {
loadInventarisData(); 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 logout() { void logout() {
_authController.logout(); _authController.logout();
} }

View File

@ -8,8 +8,6 @@ class InventarisView extends GetView<PetugasDesaController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return SingleChildScrollView( return SingleChildScrollView(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@ -39,7 +37,7 @@ class InventarisView extends GetView<PetugasDesaController> {
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppTheme.primaryColor.withOpacity(0.1), gradient: AppTheme.primaryGradient,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Column( child: Column(
@ -49,6 +47,7 @@ class InventarisView extends GetView<PetugasDesaController> {
'Ringkasan Inventaris', 'Ringkasan Inventaris',
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@ -60,7 +59,6 @@ class InventarisView extends GetView<PetugasDesaController> {
icon: Icons.inventory_2_outlined, icon: Icons.inventory_2_outlined,
title: 'Total Stok', title: 'Total Stok',
value: '1,250 kg', value: '1,250 kg',
color: Colors.blue,
), ),
), ),
Expanded( Expanded(
@ -69,7 +67,6 @@ class InventarisView extends GetView<PetugasDesaController> {
icon: Icons.input, icon: Icons.input,
title: 'Masuk Bulan Ini', title: 'Masuk Bulan Ini',
value: '500 kg', value: '500 kg',
color: Colors.green,
), ),
), ),
Expanded( Expanded(
@ -78,7 +75,6 @@ class InventarisView extends GetView<PetugasDesaController> {
icon: Icons.output, icon: Icons.output,
title: 'Keluar Bulan Ini', title: 'Keluar Bulan Ini',
value: '350 kg', value: '350 kg',
color: Colors.orange,
), ),
), ),
], ],
@ -93,19 +89,18 @@ class InventarisView extends GetView<PetugasDesaController> {
required IconData icon, required IconData icon,
required String title, required String title,
required String value, required String value,
required Color color,
}) { }) {
return Column( return Column(
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color.withOpacity(0.2), color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon( child: Icon(
icon, icon,
color: color, color: Colors.white,
size: 24, size: 24,
), ),
), ),
@ -114,12 +109,15 @@ class InventarisView extends GetView<PetugasDesaController> {
value, value,
style: Theme.of(context).textTheme.titleMedium?.copyWith( style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
title, title,
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],

View File

@ -16,6 +16,10 @@ class JadwalView extends GetView<PetugasDesaController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Ringkasan jadwal
_buildJadwalSummary(context),
const SizedBox(height: 20),
// Jadwal hari ini // Jadwal hari ini
_buildJadwalSection( _buildJadwalSection(
textTheme, textTheme,
@ -84,6 +88,102 @@ class JadwalView extends GetView<PetugasDesaController> {
); );
} }
Widget _buildJadwalSummary(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ringkasan Jadwal',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.pending_actions,
title: 'Terjadwal',
value: '5',
color: Colors.blue,
),
),
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.event_available,
title: 'Aktif',
value: '1',
color: Colors.green,
),
),
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.event_busy,
title: 'Selesai',
value: '12',
color: Colors.grey,
),
),
],
),
],
),
);
}
Widget _buildSummaryItem(
BuildContext context, {
required IconData icon,
required String title,
required String value,
required Color color,
}) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
icon,
color: Colors.white,
size: 24,
),
),
const SizedBox(height: 8),
Text(
value,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildJadwalSection( Widget _buildJadwalSection(
TextTheme textTheme, { TextTheme textTheme, {
required String title, required String title,

View File

@ -10,83 +10,116 @@ class NotifikasiView extends GetView<PetugasDesaController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
return SingleChildScrollView( return Scaffold(
child: Padding( appBar: AppBar(
padding: const EdgeInsets.all(16.0), title: const Text('Notifikasi'),
child: Column( leading: IconButton(
crossAxisAlignment: CrossAxisAlignment.start, icon: const Icon(Icons.arrow_back),
children: [ onPressed: () => Get.back(),
// Notifikasi hari ini
_buildNotifikasiSection(
textTheme,
title: 'Hari Ini',
notifikasiList: [
{
'judul': 'Jadwal Penyaluran Baru',
'pesan':
'Jadwal penyaluran beras telah ditambahkan untuk hari ini',
'waktu': '08:30',
'dibaca': false,
},
{
'judul': 'Pengajuan Bantuan Baru',
'pesan':
'Ada 3 pengajuan bantuan baru yang perlu diverifikasi',
'waktu': '10:15',
'dibaca': false,
},
],
),
const SizedBox(height: 20),
// Notifikasi kemarin
_buildNotifikasiSection(
textTheme,
title: 'Kemarin',
notifikasiList: [
{
'judul': 'Laporan Penyaluran',
'pesan':
'Laporan penyaluran bantuan tanggal 14 April 2023 telah selesai',
'waktu': '16:45',
'dibaca': true,
},
{
'judul': 'Pengaduan Warga',
'pesan':
'Ada pengaduan baru dari warga yang perlu ditindaklanjuti',
'waktu': '14:20',
'dibaca': true,
},
],
),
const SizedBox(height: 20),
// Notifikasi minggu ini
_buildNotifikasiSection(
textTheme,
title: 'Minggu Ini',
notifikasiList: [
{
'judul': 'Perubahan Jadwal',
'pesan':
'Jadwal penyaluran bantuan di Balai Desa A diubah menjadi tanggal 17 April 2023',
'waktu': 'Sen, 13:00',
'dibaca': true,
},
{
'judul': 'Donasi Baru',
'pesan':
'PT Sejahtera telah mengirimkan donasi baru berupa sembako',
'waktu': 'Sen, 09:30',
'dibaca': true,
},
],
),
],
), ),
actions: [
IconButton(
icon: const Icon(Icons.check_circle_outline),
tooltip: 'Tandai Semua Dibaca',
onPressed: () {
// Implementasi untuk menandai semua notifikasi sebagai dibaca
},
),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Notifikasi hari ini
_buildNotifikasiSection(
textTheme,
title: 'Hari Ini',
notifikasiList: [
{
'judul': 'Jadwal Penyaluran Baru',
'pesan':
'Jadwal penyaluran beras telah ditambahkan untuk hari ini',
'waktu': '08:30',
'dibaca': false,
},
{
'judul': 'Pengajuan Bantuan Baru',
'pesan':
'Ada 3 pengajuan bantuan baru yang perlu diverifikasi',
'waktu': '10:15',
'dibaca': false,
},
],
),
const SizedBox(height: 20),
// Notifikasi kemarin
_buildNotifikasiSection(
textTheme,
title: 'Kemarin',
notifikasiList: [
{
'judul': 'Laporan Penyaluran',
'pesan':
'Laporan penyaluran bantuan tanggal 14 April 2023 telah selesai',
'waktu': '16:45',
'dibaca': true,
},
{
'judul': 'Pengaduan Warga',
'pesan':
'Ada pengaduan baru dari warga yang perlu ditindaklanjuti',
'waktu': '14:20',
'dibaca': true,
},
],
),
const SizedBox(height: 20),
// Notifikasi minggu ini
_buildNotifikasiSection(
textTheme,
title: 'Minggu Ini',
notifikasiList: [
{
'judul': 'Perubahan Jadwal',
'pesan':
'Jadwal penyaluran bantuan di Balai Desa A diubah menjadi tanggal 17 April 2023',
'waktu': 'Sen, 13:00',
'dibaca': true,
},
{
'judul': 'Donasi Baru',
'pesan':
'PT Sejahtera telah mengirimkan donasi baru berupa sembako',
'waktu': 'Sen, 09:30',
'dibaca': true,
},
],
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Implementasi untuk menandai semua notifikasi sebagai dibaca
Get.snackbar(
'Notifikasi',
'Semua notifikasi telah ditandai sebagai dibaca',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppTheme.primaryColor,
colorText: Colors.white,
);
},
backgroundColor: AppTheme.primaryColor,
child: const Icon(Icons.done_all),
tooltip: 'Tandai Semua Dibaca',
), ),
); );
} }
@ -131,51 +164,66 @@ class NotifikasiView extends GetView<PetugasDesaController> {
), ),
], ],
), ),
child: Padding( child: InkWell(
padding: const EdgeInsets.all(16.0), onTap: () {
child: Row( // Implementasi untuk menandai notifikasi sebagai dibaca
crossAxisAlignment: CrossAxisAlignment.start, if (!dibaca) {
children: [ Get.snackbar(
if (!dibaca) 'Notifikasi',
Container( 'Notifikasi ditandai sebagai dibaca',
width: 10, snackPosition: SnackPosition.BOTTOM,
height: 10, backgroundColor: AppTheme.primaryColor,
margin: const EdgeInsets.only(top: 5, right: 10), colorText: Colors.white,
decoration: const BoxDecoration( );
color: AppTheme.primaryColor, }
shape: BoxShape.circle, },
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!dibaca)
Container(
width: 10,
height: 10,
margin: const EdgeInsets.only(top: 5, right: 10),
decoration: const BoxDecoration(
color: AppTheme.primaryColor,
shape: BoxShape.circle,
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
notifikasi['judul'] ?? '',
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
notifikasi['waktu'] ?? '',
style: textTheme.bodySmall?.copyWith(
color: Colors.grey,
),
),
],
),
const SizedBox(height: 8),
Text(
notifikasi['pesan'] ?? '',
style: textTheme.bodyMedium,
),
],
), ),
), ),
Expanded( ],
child: Column( ),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
notifikasi['judul'] ?? '',
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
notifikasi['waktu'] ?? '',
style: textTheme.bodySmall?.copyWith(
color: Colors.grey,
),
),
],
),
const SizedBox(height: 8),
Text(
notifikasi['pesan'] ?? '',
style: textTheme.bodyMedium,
),
],
),
),
],
), ),
), ),
); );

View File

@ -0,0 +1,428 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/petugas_desa_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class PenitipanView extends GetView<PetugasDesaController> {
const PenitipanView({super.key});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Ringkasan penitipan
_buildPenitipanSummary(context),
const SizedBox(height: 24),
// Filter dan pencarian
_buildFilterSearch(context),
const SizedBox(height: 20),
// Daftar penitipan
_buildPenitipanList(context),
],
),
),
);
}
Widget _buildPenitipanSummary(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ringkasan Penitipan',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.pending_actions,
title: 'Menunggu',
value: '5',
color: Colors.orange,
),
),
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.check_circle,
title: 'Terverifikasi',
value: '12',
color: Colors.green,
),
),
Expanded(
child: _buildSummaryItem(
context,
icon: Icons.cancel,
title: 'Ditolak',
value: '2',
color: Colors.red,
),
),
],
),
],
),
);
}
Widget _buildSummaryItem(
BuildContext context, {
required IconData icon,
required String title,
required String value,
required Color color,
}) {
return Column(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
icon,
color: Colors.white,
size: 24,
),
),
const SizedBox(height: 8),
Text(
value,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.white,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildFilterSearch(BuildContext context) {
return Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Cari 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),
),
),
),
const SizedBox(width: 12),
Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: IconButton(
onPressed: () {
// Tampilkan dialog filter
},
icon: const Icon(Icons.filter_list),
tooltip: 'Filter',
),
),
],
);
}
Widget _buildPenitipanList(BuildContext context) {
final List<Map<String, dynamic>> penitipanList = [
{
'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',
},
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Daftar Penitipan',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
...penitipanList.map((item) => _buildPenitipanItem(context, item)),
],
);
}
Widget _buildPenitipanItem(BuildContext context, Map<String, dynamic> item) {
Color statusColor;
IconData statusIcon;
switch (item['status']) {
case 'Menunggu':
statusColor = Colors.orange;
statusIcon = Icons.pending_actions;
break;
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;
}
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(
item['donatur'] ?? '',
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'] ?? '',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: statusColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildItemDetail(
context,
icon: Icons.category,
label: 'Jenis Bantuan',
value: item['jenis_bantuan'] ?? '',
),
),
Expanded(
child: _buildItemDetail(
context,
icon: Icons.inventory,
label: 'Jumlah',
value: item['jumlah'] ?? '',
),
),
],
),
const SizedBox(height: 8),
_buildItemDetail(
context,
icon: Icons.calendar_today,
label: 'Tanggal Pengajuan',
value: item['tanggal_pengajuan'] ?? '',
),
const SizedBox(height: 12),
if (item['status'] == 'Menunggu')
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton.icon(
onPressed: () {
// Implementasi untuk menerima penitipan
},
icon: const Icon(Icons.check, size: 18),
label: const Text('Terima'),
style: TextButton.styleFrom(
foregroundColor: Colors.green,
padding: const EdgeInsets.symmetric(horizontal: 8),
),
),
TextButton.icon(
onPressed: () {
// Implementasi untuk menolak penitipan
},
icon: const Icon(Icons.close, size: 18),
label: const Text('Tolak'),
style: TextButton.styleFrom(
foregroundColor: Colors.red,
padding: const EdgeInsets.symmetric(horizontal: 8),
),
),
TextButton.icon(
onPressed: () {
// Implementasi untuk melihat detail penitipan
},
icon: const Icon(Icons.info_outline, size: 18),
label: const Text('Detail'),
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(horizontal: 8),
),
),
],
)
else
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton.icon(
onPressed: () {
// Implementasi untuk melihat detail penitipan
},
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,
),
],
),
),
],
);
}
}

View File

@ -5,6 +5,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/dashboard_view.dar
import 'package:penyaluran_app/app/modules/petugas_desa/views/jadwal_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/jadwal_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/notifikasi_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/notifikasi_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/inventaris_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/inventaris_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/penitipan_view.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart'; import 'package:penyaluran_app/app/theme/app_theme.dart';
class PetugasDesaView extends GetView<PetugasDesaController> { class PetugasDesaView extends GetView<PetugasDesaController> {
@ -24,9 +25,9 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
case 1: case 1:
return const Text('Jadwal Penyaluran'); return const Text('Jadwal Penyaluran');
case 2: case 2:
return const Text('Notifikasi');
case 3:
return const Text('Inventaris'); return const Text('Inventaris');
case 3:
return const Text('Penitipan');
default: default:
return const Text('Petugas Desa'); return const Text('Petugas Desa');
} }
@ -49,7 +50,13 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
IconButton( IconButton(
icon: const Icon(Icons.notifications_outlined), icon: const Icon(Icons.notifications_outlined),
onPressed: () { onPressed: () {
controller.changeTab(2); // Pindah ke tab notifikasi // Navigasi ke halaman notifikasi
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NotifikasiView(),
),
);
}, },
), ),
if (controller.jumlahNotifikasiBelumDibaca.value > 0) if (controller.jumlahNotifikasiBelumDibaca.value > 0)
@ -95,14 +102,6 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
], ],
); );
} else if (activeTab == 2) { } else if (activeTab == 2) {
return IconButton(
icon: const Icon(Icons.check_circle_outline),
tooltip: 'Tandai Semua Dibaca',
onPressed: () {
// Implementasi untuk menandai semua notifikasi sebagai dibaca
},
);
} else if (activeTab == 3) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -130,9 +129,9 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
case 1: case 1:
return const JadwalView(); return const JadwalView();
case 2: case 2:
return const NotifikasiView();
case 3:
return const InventarisView(); return const InventarisView();
case 3:
return const PenitipanView();
default: default:
return const DashboardView(); return const DashboardView();
} }
@ -182,26 +181,76 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
], ],
), ),
), ),
ListTile( Obx(() => ListTile(
leading: const Icon(Icons.dashboard_outlined), leading: const Icon(Icons.dashboard_outlined),
title: const Text('Dashboard'), title: const Text('Dashboard'),
selected: controller.activeTabIndex.value == 0, selected: controller.activeTabIndex.value == 0,
selectedColor: AppTheme.primaryColor, selectedColor: AppTheme.primaryColor,
onTap: () { onTap: () {
controller.changeTab(0); controller.changeTab(0);
Get.back(); Navigator.pop(context);
}, },
), )),
ListTile( Obx(() => ListTile(
leading: const Icon(Icons.calendar_today_outlined), leading: const Icon(Icons.calendar_today_outlined),
title: const Text('Jadwal Penyaluran'), title: const Text('Jadwal Penyaluran'),
selected: controller.activeTabIndex.value == 1, selected: controller.activeTabIndex.value == 1,
selectedColor: AppTheme.primaryColor, selectedColor: AppTheme.primaryColor,
onTap: () { onTap: () {
controller.changeTab(1); controller.changeTab(1);
Get.back(); Navigator.pop(context);
}, },
), )),
Obx(() => ListTile(
leading: const Icon(Icons.inventory_2_outlined),
title: const Text('Inventaris'),
selected: controller.activeTabIndex.value == 2,
selectedColor: AppTheme.primaryColor,
onTap: () {
controller.changeTab(2);
Navigator.pop(context);
},
)),
Obx(() => ListTile(
leading: Stack(
alignment: Alignment.center,
children: [
const Icon(Icons.handshake_outlined),
if (controller.jumlahMenunggu.value > 0)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
controller.jumlahMenunggu.value.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
),
],
),
title: const Text('Penitipan'),
selected: controller.activeTabIndex.value == 3,
selectedColor: AppTheme.primaryColor,
onTap: () {
controller.changeTab(3);
Navigator.pop(context);
},
)),
const Divider(),
ListTile( ListTile(
leading: Stack( leading: Stack(
alignment: Alignment.center, alignment: Alignment.center,
@ -234,30 +283,22 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
], ],
), ),
title: const Text('Notifikasi'), title: const Text('Notifikasi'),
selected: controller.activeTabIndex.value == 2,
selectedColor: AppTheme.primaryColor,
onTap: () { onTap: () {
controller.changeTab(2); Navigator.pop(context); // Tutup drawer terlebih dahulu
Get.back(); Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NotifikasiView(),
),
);
}, },
), ),
ListTile(
leading: const Icon(Icons.inventory_2_outlined),
title: const Text('Inventaris'),
selected: controller.activeTabIndex.value == 3,
selectedColor: AppTheme.primaryColor,
onTap: () {
controller.changeTab(3);
Get.back();
},
),
const Divider(),
ListTile( ListTile(
leading: const Icon(Icons.person_outline), leading: const Icon(Icons.person_outline),
title: const Text('Profil'), title: const Text('Profil'),
onTap: () { onTap: () {
// Navigasi ke halaman profil // Navigasi ke halaman profil
Get.back(); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
@ -265,14 +306,14 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
title: const Text('Pengaturan'), title: const Text('Pengaturan'),
onTap: () { onTap: () {
// Navigasi ke halaman pengaturan // Navigasi ke halaman pengaturan
Get.back(); Navigator.pop(context);
}, },
), ),
ListTile( ListTile(
leading: const Icon(Icons.logout), leading: const Icon(Icons.logout),
title: const Text('Keluar'), title: const Text('Keluar'),
onTap: () { onTap: () {
Get.back(); Navigator.pop(context);
controller.logout(); controller.logout();
}, },
), ),
@ -288,27 +329,143 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
selectedItemColor: AppTheme.primaryColor, selectedItemColor: AppTheme.primaryColor,
unselectedItemColor: Colors.grey, unselectedItemColor: Colors.grey,
items: const [ items: [
BottomNavigationBarItem( const BottomNavigationBarItem(
icon: Icon(Icons.dashboard_outlined), icon: Icon(Icons.dashboard_outlined),
activeIcon: Icon(Icons.dashboard), activeIcon: Icon(Icons.dashboard),
label: 'Dashboard', label: 'Dashboard',
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.calendar_today_outlined), icon: Stack(
activeIcon: Icon(Icons.calendar_today), alignment: Alignment.center,
children: [
const Icon(Icons.calendar_today_outlined),
if (controller.jadwalHariIni.isNotEmpty)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
controller.jadwalHariIni.length.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
),
],
),
activeIcon: Stack(
alignment: Alignment.center,
children: [
const Icon(Icons.calendar_today),
if (controller.jadwalHariIni.isNotEmpty)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
controller.jadwalHariIni.length.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
),
],
),
label: 'Jadwal', label: 'Jadwal',
), ),
BottomNavigationBarItem( const BottomNavigationBarItem(
icon: Icon(Icons.notifications_outlined),
activeIcon: Icon(Icons.notifications),
label: 'Notifikasi',
),
BottomNavigationBarItem(
icon: Icon(Icons.inventory_2_outlined), icon: Icon(Icons.inventory_2_outlined),
activeIcon: Icon(Icons.inventory_2), activeIcon: Icon(Icons.inventory_2),
label: 'Inventaris', label: 'Inventaris',
), ),
BottomNavigationBarItem(
icon: Stack(
alignment: Alignment.center,
children: [
const Icon(Icons.handshake_outlined),
if (controller.jumlahMenunggu.value > 0)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
controller.jumlahMenunggu.value.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
),
],
),
activeIcon: Stack(
alignment: Alignment.center,
children: [
const Icon(Icons.handshake),
if (controller.jumlahMenunggu.value > 0)
Positioned(
top: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
controller.jumlahMenunggu.value.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
),
],
),
label: 'Penitipan',
),
], ],
)); ));
} }