Tambahkan fitur pengaduan dan perbarui navigasi Petugas Desa
- Tambahkan kontroler untuk manajemen data pengaduan - Buat tampilan PengaduanView untuk menampilkan daftar pengaduan - Perbarui navigasi dengan menambahkan tab dan item baru untuk pengaduan - Tambahkan logika untuk menghitung dan menampilkan jumlah pengaduan yang diproses - Integrasikan fitur pengaduan ke dalam drawer dan bottom navigation bar
This commit is contained in:
@ -6,6 +6,7 @@ class PetugasDesaBinding extends Bindings {
|
|||||||
void dependencies() {
|
void dependencies() {
|
||||||
Get.lazyPut<PetugasDesaController>(
|
Get.lazyPut<PetugasDesaController>(
|
||||||
() => PetugasDesaController(),
|
() => PetugasDesaController(),
|
||||||
|
fenix: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/petugas_desa_controller.dart';
|
||||||
|
|
||||||
|
class JadwalSectionWidget extends StatelessWidget {
|
||||||
|
final PetugasDesaController controller;
|
||||||
|
final String title;
|
||||||
|
final List<Map<String, dynamic>> jadwalList;
|
||||||
|
final String status;
|
||||||
|
|
||||||
|
const JadwalSectionWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
required this.title,
|
||||||
|
required this.jadwalList,
|
||||||
|
required this.status,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Obx(() {
|
||||||
|
final currentJadwalList = _getCurrentJadwalList();
|
||||||
|
|
||||||
|
if (currentJadwalList.isEmpty) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.withAlpha(20),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Tidak ada jadwal $title',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: currentJadwalList
|
||||||
|
.map((jadwal) => _buildJadwalItem(textTheme, jadwal))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> _getCurrentJadwalList() {
|
||||||
|
switch (title) {
|
||||||
|
case 'Hari Ini':
|
||||||
|
return controller.jadwalHariIni;
|
||||||
|
case 'Mendatang':
|
||||||
|
return controller.jadwalMendatang;
|
||||||
|
case 'Selesai':
|
||||||
|
return controller.jadwalSelesai;
|
||||||
|
default:
|
||||||
|
return jadwalList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildJadwalItem(TextTheme textTheme, Map<String, dynamic> jadwal) {
|
||||||
|
Color statusColor;
|
||||||
|
switch (status) {
|
||||||
|
case 'Aktif':
|
||||||
|
statusColor = Colors.green;
|
||||||
|
break;
|
||||||
|
case 'Terjadwal':
|
||||||
|
statusColor = Colors.blue;
|
||||||
|
break;
|
||||||
|
case 'Selesai':
|
||||||
|
statusColor = Colors.grey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusColor = Colors.orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
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: [
|
||||||
|
Text(
|
||||||
|
jadwal['lokasi'] ?? '',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: statusColor.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
status,
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: statusColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Jenis Bantuan: ${jadwal['jenis_bantuan'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Tanggal: ${jadwal['tanggal'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Waktu: ${jadwal['waktu'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
if (jadwal['jumlah_penerima'] != null) ...[
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Jumlah Penerima: ${jadwal['jumlah_penerima']}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
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/routes/app_pages.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class PermintaanPenjadwalanSummaryWidget extends StatelessWidget {
|
||||||
|
final PetugasDesaController controller;
|
||||||
|
|
||||||
|
const PermintaanPenjadwalanSummaryWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
return Obx(() {
|
||||||
|
final jumlahPermintaan = controller.jumlahPermintaanPenjadwalan.value;
|
||||||
|
final permintaanList = controller.permintaanPenjadwalan;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withAlpha(30),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
border: Border.all(
|
||||||
|
color: jumlahPermintaan > 0
|
||||||
|
? Colors.orange.withAlpha(50)
|
||||||
|
: Colors.grey.withAlpha(30),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Permintaan Penjadwalan',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: jumlahPermintaan > 0
|
||||||
|
? Colors.red.withAlpha(26)
|
||||||
|
: Colors.grey.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'$jumlahPermintaan',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: jumlahPermintaan > 0 ? Colors.red : Colors.grey,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
if (jumlahPermintaan == 0)
|
||||||
|
Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.event_note,
|
||||||
|
size: 48,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Tidak ada permintaan penjadwalan',
|
||||||
|
style: textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
...permintaanList.take(1).map((permintaan) =>
|
||||||
|
_buildPermintaanPreview(textTheme, permintaan)),
|
||||||
|
if (jumlahPermintaan > 1)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
'+ ${jumlahPermintaan - 1} permintaan lainnya',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: () => Get.toNamed(Routes.permintaanPenjadwalan),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.visibility),
|
||||||
|
label: const Text('Lihat Semua Permintaan'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPermintaanPreview(
|
||||||
|
TextTheme textTheme, Map<String, dynamic> permintaan) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.withAlpha(15),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
permintaan['nama'] ?? '',
|
||||||
|
style: textTheme.titleSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.orange.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Menunggu',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.orange,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Jenis: ${permintaan['jenis_bantuan'] ?? ''}',
|
||||||
|
style: textTheme.bodySmall,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Tanggal: ${permintaan['tanggal_permintaan'] ?? ''}',
|
||||||
|
style: textTheme.bodySmall,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,348 @@
|
|||||||
|
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 PermintaanPenjadwalanWidget extends StatelessWidget {
|
||||||
|
final PetugasDesaController controller;
|
||||||
|
|
||||||
|
const PermintaanPenjadwalanWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Permintaan Penjadwalan',
|
||||||
|
style: textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(() => Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'${controller.jumlahPermintaanPenjadwalan.value}',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.red,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Obx(() {
|
||||||
|
final permintaanList = controller.permintaanPenjadwalan;
|
||||||
|
|
||||||
|
// Jika tidak ada permintaan, tampilkan pesan kosong
|
||||||
|
if (permintaanList.isEmpty) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.withAlpha(20),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.event_note,
|
||||||
|
size: 48,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Tidak ada permintaan penjadwalan',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: permintaanList
|
||||||
|
.map(
|
||||||
|
(permintaan) => _buildPermintaanItem(textTheme, permintaan))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk menampilkan item permintaan penjadwalan
|
||||||
|
Widget _buildPermintaanItem(
|
||||||
|
TextTheme textTheme, Map<String, dynamic> permintaan) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.orange.withAlpha(50),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
permintaan['nama'] ?? '',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.orange.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Menunggu',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.orange,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'NIK: ${permintaan['nik'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Jenis Bantuan: ${permintaan['jenis_bantuan'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Tanggal Permintaan: ${permintaan['tanggal_permintaan'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Alamat: ${permintaan['alamat'] ?? ''}',
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: () => _showTolakDialog(permintaan),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.red,
|
||||||
|
side: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
child: const Text('Tolak'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => _showKonfirmasiDialog(permintaan),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
child: const Text('Konfirmasi'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog untuk konfirmasi permintaan
|
||||||
|
void _showKonfirmasiDialog(Map<String, dynamic> permintaan) {
|
||||||
|
String? selectedJadwalId;
|
||||||
|
|
||||||
|
// Data jadwal yang tersedia dari controller
|
||||||
|
final jadwalOptions = controller.jadwalMendatang.map((jadwal) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: jadwal['id'],
|
||||||
|
child: Text(
|
||||||
|
'${jadwal['tanggal']} - ${jadwal['lokasi']} (${jadwal['jenis_bantuan']})'),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
// Tambahkan opsi jadwal lain jika diperlukan
|
||||||
|
jadwalOptions.add(
|
||||||
|
const DropdownMenuItem<String>(
|
||||||
|
value: '3',
|
||||||
|
child: Text('25 April 2023 - Kantor Kepala Desa (Beras)'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('Konfirmasi Permintaan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Anda akan mengkonfirmasi permintaan penjadwalan dari ${permintaan['nama']}.'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text('Pilih jadwal penyaluran:'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
DropdownButtonFormField<String>(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
items: jadwalOptions,
|
||||||
|
onChanged: (value) {
|
||||||
|
selectedJadwalId = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (selectedJadwalId != null) {
|
||||||
|
// Panggil metode konfirmasi di controller
|
||||||
|
controller.konfirmasiPermintaanPenjadwalan(
|
||||||
|
permintaan['id'],
|
||||||
|
selectedJadwalId!,
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.back();
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Permintaan penjadwalan berhasil dikonfirmasi',
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Peringatan',
|
||||||
|
'Silakan pilih jadwal penyaluran terlebih dahulu',
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
child: const Text('Konfirmasi'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog untuk menolak permintaan
|
||||||
|
void _showTolakDialog(Map<String, dynamic> permintaan) {
|
||||||
|
final TextEditingController alasanController = TextEditingController();
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('Tolak Permintaan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Anda akan menolak permintaan penjadwalan dari ${permintaan['nama']}.'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text('Alasan penolakan:'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: alasanController,
|
||||||
|
maxLines: 3,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
hintText: 'Masukkan alasan penolakan',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (alasanController.text.trim().isNotEmpty) {
|
||||||
|
// Panggil metode tolak di controller
|
||||||
|
controller.tolakPermintaanPenjadwalan(
|
||||||
|
permintaan['id'],
|
||||||
|
alasanController.text.trim(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.back();
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Permintaan penjadwalan berhasil ditolak',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Peringatan',
|
||||||
|
'Silakan masukkan alasan penolakan',
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
child: const Text('Tolak'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,11 @@ class PetugasDesaController extends GetxController {
|
|||||||
final RxList<Map<String, dynamic>> jadwalSelesai =
|
final RxList<Map<String, dynamic>> jadwalSelesai =
|
||||||
<Map<String, dynamic>>[].obs;
|
<Map<String, dynamic>>[].obs;
|
||||||
|
|
||||||
|
// Data untuk permintaan penjadwalan
|
||||||
|
final RxList<Map<String, dynamic>> permintaanPenjadwalan =
|
||||||
|
<Map<String, dynamic>>[].obs;
|
||||||
|
final RxInt jumlahPermintaanPenjadwalan = 0.obs;
|
||||||
|
|
||||||
// Data untuk notifikasi
|
// Data untuk notifikasi
|
||||||
final RxList<Map<String, dynamic>> notifikasiBelumDibaca =
|
final RxList<Map<String, dynamic>> notifikasiBelumDibaca =
|
||||||
<Map<String, dynamic>>[].obs;
|
<Map<String, dynamic>>[].obs;
|
||||||
@ -50,6 +55,13 @@ class PetugasDesaController extends GetxController {
|
|||||||
final RxInt jumlahTerverifikasi = 0.obs;
|
final RxInt jumlahTerverifikasi = 0.obs;
|
||||||
final RxInt jumlahDitolak = 0.obs;
|
final RxInt jumlahDitolak = 0.obs;
|
||||||
|
|
||||||
|
// Data untuk pengaduan
|
||||||
|
final RxList<Map<String, dynamic>> daftarPengaduan =
|
||||||
|
<Map<String, dynamic>>[].obs;
|
||||||
|
final RxInt jumlahDiproses = 0.obs;
|
||||||
|
final RxInt jumlahTindakan = 0.obs;
|
||||||
|
final RxInt jumlahSelesai = 0.obs;
|
||||||
|
|
||||||
// Controller untuk pencarian
|
// Controller untuk pencarian
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
|
|
||||||
@ -60,12 +72,19 @@ class PetugasDesaController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|
||||||
|
// Inisialisasi manual untuk pengaduan (untuk debugging)
|
||||||
|
jumlahDiproses.value = 3;
|
||||||
|
print('onInit - Jumlah pengaduan diproses: ${jumlahDiproses.value}');
|
||||||
|
|
||||||
loadRoleData();
|
loadRoleData();
|
||||||
loadDashboardData();
|
loadDashboardData();
|
||||||
loadJadwalData();
|
loadJadwalData();
|
||||||
|
loadPermintaanPenjadwalanData();
|
||||||
loadNotifikasiData();
|
loadNotifikasiData();
|
||||||
loadInventarisData();
|
loadInventarisData();
|
||||||
loadPenitipanData();
|
loadPenitipanData();
|
||||||
|
loadPengaduanData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -187,6 +206,43 @@ class PetugasDesaController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadPermintaanPenjadwalanData() async {
|
||||||
|
try {
|
||||||
|
// Simulasi data untuk permintaan penjadwalan
|
||||||
|
await Future.delayed(const Duration(milliseconds: 600));
|
||||||
|
|
||||||
|
permintaanPenjadwalan.value = [
|
||||||
|
{
|
||||||
|
'id': '1',
|
||||||
|
'nama': 'Ahmad Sulaiman',
|
||||||
|
'nik': '3201234567890001',
|
||||||
|
'jenis_bantuan': 'Beras',
|
||||||
|
'tanggal_permintaan': '14 April 2023',
|
||||||
|
'alamat': 'Dusun Sukamaju RT 02/03',
|
||||||
|
'status': 'menunggu',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '2',
|
||||||
|
'nama': 'Siti Aminah',
|
||||||
|
'nik': '3201234567890002',
|
||||||
|
'jenis_bantuan': 'Sembako',
|
||||||
|
'tanggal_permintaan': '13 April 2023',
|
||||||
|
'alamat': 'Dusun Sukamaju RT 01/03',
|
||||||
|
'status': 'menunggu',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||||
|
|
||||||
|
// Di implementasi nyata, data akan diambil dari Supabase
|
||||||
|
// final result = await _supabaseService.getPermintaanPenjadwalanData();
|
||||||
|
// permintaanPenjadwalan.value = result ?? [];
|
||||||
|
// jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||||
|
} catch (e) {
|
||||||
|
print('Error loading permintaan penjadwalan data: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadNotifikasiData() async {
|
Future<void> loadNotifikasiData() async {
|
||||||
try {
|
try {
|
||||||
// Simulasi data untuk notifikasi
|
// Simulasi data untuk notifikasi
|
||||||
@ -348,6 +404,126 @@ class PetugasDesaController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadPengaduanData() async {
|
||||||
|
try {
|
||||||
|
// Simulasi data untuk pengaduan
|
||||||
|
await Future.delayed(const Duration(milliseconds: 650));
|
||||||
|
|
||||||
|
// Pastikan data pengaduan tidak kosong
|
||||||
|
daftarPengaduan.value = [
|
||||||
|
{
|
||||||
|
'id': '1',
|
||||||
|
'nama': 'Budi Santoso',
|
||||||
|
'nik': '3201020107030011',
|
||||||
|
'jenis_pengaduan': 'Bantuan Tidak Diterima',
|
||||||
|
'deskripsi':
|
||||||
|
'Saya belum menerima bantuan beras yang dijadwalkan minggu lalu',
|
||||||
|
'tanggal': '15 April 2023',
|
||||||
|
'status': 'Diproses',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '2',
|
||||||
|
'nama': 'Siti Rahayu',
|
||||||
|
'nik': '3201020107030010',
|
||||||
|
'jenis_pengaduan': 'Kualitas Bantuan',
|
||||||
|
'deskripsi':
|
||||||
|
'Beras yang diterima berkualitas buruk dan tidak layak konsumsi',
|
||||||
|
'tanggal': '14 April 2023',
|
||||||
|
'status': 'Tindakan',
|
||||||
|
'tindakan':
|
||||||
|
'Pengecekan kualitas beras di gudang dan pengambilan sampel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '3',
|
||||||
|
'nama': 'Ahmad Fauzi',
|
||||||
|
'nik': '3201020107030013',
|
||||||
|
'jenis_pengaduan': 'Jumlah Bantuan',
|
||||||
|
'deskripsi':
|
||||||
|
'Jumlah bantuan yang diterima tidak sesuai dengan yang dijanjikan',
|
||||||
|
'tanggal': '13 April 2023',
|
||||||
|
'status': 'Tindakan',
|
||||||
|
'tindakan':
|
||||||
|
'Verifikasi data penerima dan jumlah bantuan yang seharusnya diterima',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '4',
|
||||||
|
'nama': 'Dewi Lestari',
|
||||||
|
'nik': '3201020107030012',
|
||||||
|
'jenis_pengaduan': 'Jadwal Penyaluran',
|
||||||
|
'deskripsi':
|
||||||
|
'Jadwal penyaluran bantuan sering berubah tanpa pemberitahuan',
|
||||||
|
'tanggal': '10 April 2023',
|
||||||
|
'status': 'Selesai',
|
||||||
|
'tindakan':
|
||||||
|
'Koordinasi dengan tim penyaluran untuk perbaikan sistem pemberitahuan',
|
||||||
|
'hasil':
|
||||||
|
'Implementasi sistem notifikasi perubahan jadwal melalui SMS dan pengumuman di balai desa',
|
||||||
|
},
|
||||||
|
// Tambahkan data pengaduan dengan status 'Diproses' untuk memastikan counter muncul
|
||||||
|
{
|
||||||
|
'id': '5',
|
||||||
|
'nama': 'Joko Widodo',
|
||||||
|
'nik': '3201020107030014',
|
||||||
|
'jenis_pengaduan': 'Bantuan Tidak Sesuai',
|
||||||
|
'deskripsi':
|
||||||
|
'Bantuan yang diterima tidak sesuai dengan yang dijanjikan',
|
||||||
|
'tanggal': '16 April 2023',
|
||||||
|
'status': 'Diproses',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '6',
|
||||||
|
'nama': 'Anita Sari',
|
||||||
|
'nik': '3201020107030015',
|
||||||
|
'jenis_pengaduan': 'Bantuan Tidak Tepat Sasaran',
|
||||||
|
'deskripsi':
|
||||||
|
'Bantuan diberikan kepada warga yang tidak berhak menerima',
|
||||||
|
'tanggal': '17 April 2023',
|
||||||
|
'status': 'Diproses',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Hitung jumlah pengaduan berdasarkan status
|
||||||
|
int jumlahDiprosesTemp =
|
||||||
|
daftarPengaduan.where((p) => p['status'] == 'Diproses').length;
|
||||||
|
int jumlahTindakanTemp =
|
||||||
|
daftarPengaduan.where((p) => p['status'] == 'Tindakan').length;
|
||||||
|
int jumlahSelesaiTemp =
|
||||||
|
daftarPengaduan.where((p) => p['status'] == 'Selesai').length;
|
||||||
|
|
||||||
|
// Update nilai Rx
|
||||||
|
jumlahDiproses.value = jumlahDiprosesTemp;
|
||||||
|
jumlahTindakan.value = jumlahTindakanTemp;
|
||||||
|
jumlahSelesai.value = jumlahSelesaiTemp;
|
||||||
|
|
||||||
|
// Print untuk debugging
|
||||||
|
print('Data pengaduan dimuat:');
|
||||||
|
print('Jumlah pengaduan diproses: ${jumlahDiproses.value}');
|
||||||
|
print('Jumlah pengaduan tindakan: ${jumlahTindakan.value}');
|
||||||
|
print('Jumlah pengaduan selesai: ${jumlahSelesai.value}');
|
||||||
|
print('Total pengaduan: ${daftarPengaduan.length}');
|
||||||
|
|
||||||
|
// Perbarui UI secara manual
|
||||||
|
update();
|
||||||
|
|
||||||
|
// Di implementasi nyata, data akan diambil dari Supabase
|
||||||
|
// final result = await _supabaseService.getPengaduanData();
|
||||||
|
// daftarPengaduan.value = result ?? [];
|
||||||
|
// jumlahDiproses.value = daftarPengaduan.where((p) => p['status'] == 'Diproses').length;
|
||||||
|
// jumlahTindakan.value = daftarPengaduan.where((p) => p['status'] == 'Tindakan').length;
|
||||||
|
// jumlahSelesai.value = daftarPengaduan.where((p) => p['status'] == 'Selesai').length;
|
||||||
|
} catch (e) {
|
||||||
|
print('Error loading pengaduan data: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method untuk memperbarui jumlah pengaduan secara manual (untuk debugging)
|
||||||
|
void updatePengaduanCounter() {
|
||||||
|
jumlahDiproses.value = 5; // Set nilai secara manual
|
||||||
|
update(); // Perbarui UI
|
||||||
|
print(
|
||||||
|
'Counter pengaduan diperbarui secara manual: ${jumlahDiproses.value}');
|
||||||
|
}
|
||||||
|
|
||||||
void tandaiNotifikasiDibaca(String id) {
|
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
|
||||||
@ -394,6 +570,24 @@ class PetugasDesaController extends GetxController {
|
|||||||
loadPenitipanData();
|
loadPenitipanData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void prosesPengaduan(String id, String tindakan) {
|
||||||
|
// Implementasi untuk memproses pengaduan
|
||||||
|
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status pengaduan
|
||||||
|
// await _supabaseService.processPengaduan(id, tindakan);
|
||||||
|
|
||||||
|
// Perbarui data lokal
|
||||||
|
loadPengaduanData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selesaikanPengaduan(String id, String hasil) {
|
||||||
|
// Implementasi untuk menyelesaikan pengaduan
|
||||||
|
// Di implementasi nyata, akan memanggil Supabase untuk memperbarui status pengaduan
|
||||||
|
// await _supabaseService.completePengaduan(id, hasil);
|
||||||
|
|
||||||
|
// Perbarui data lokal
|
||||||
|
loadPengaduanData();
|
||||||
|
}
|
||||||
|
|
||||||
void logout() {
|
void logout() {
|
||||||
_authController.logout();
|
_authController.logout();
|
||||||
}
|
}
|
||||||
@ -401,4 +595,82 @@ class PetugasDesaController extends GetxController {
|
|||||||
void changeTab(int index) {
|
void changeTab(int index) {
|
||||||
activeTabIndex.value = index;
|
activeTabIndex.value = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Metode untuk konfirmasi permintaan penjadwalan
|
||||||
|
Future<void> konfirmasiPermintaanPenjadwalan(
|
||||||
|
String id, String jadwalId) async {
|
||||||
|
try {
|
||||||
|
if (id.isEmpty || jadwalId.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'ID permintaan atau jadwal tidak valid',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
// Simulasi proses konfirmasi
|
||||||
|
await Future.delayed(const Duration(milliseconds: 800));
|
||||||
|
|
||||||
|
// Hapus permintaan dari daftar
|
||||||
|
permintaanPenjadwalan.removeWhere((item) => item['id'] == id);
|
||||||
|
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||||
|
|
||||||
|
// Di implementasi nyata, data akan diupdate ke Supabase
|
||||||
|
// await _supabaseService.konfirmasiPermintaanPenjadwalan(id, jadwalId);
|
||||||
|
// await loadPermintaanPenjadwalanData();
|
||||||
|
// await loadJadwalData();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error konfirmasi permintaan penjadwalan: $e');
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'Terjadi kesalahan saat mengkonfirmasi permintaan',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metode untuk menolak permintaan penjadwalan
|
||||||
|
Future<void> tolakPermintaanPenjadwalan(String id, String alasan) async {
|
||||||
|
try {
|
||||||
|
if (id.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'ID permintaan tidak valid',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
// Simulasi proses penolakan
|
||||||
|
await Future.delayed(const Duration(milliseconds: 800));
|
||||||
|
|
||||||
|
// Hapus permintaan dari daftar
|
||||||
|
permintaanPenjadwalan.removeWhere((item) => item['id'] == id);
|
||||||
|
jumlahPermintaanPenjadwalan.value = permintaanPenjadwalan.length;
|
||||||
|
|
||||||
|
// Di implementasi nyata, data akan diupdate ke Supabase
|
||||||
|
// await _supabaseService.tolakPermintaanPenjadwalan(id, alasan);
|
||||||
|
// await loadPermintaanPenjadwalanData();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error tolak permintaan penjadwalan: $e');
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'Terjadi kesalahan saat menolak permintaan',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,289 +0,0 @@
|
|||||||
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 JadwalView extends GetView<PetugasDesaController> {
|
|
||||||
const JadwalView({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 jadwal
|
|
||||||
_buildJadwalSummary(context),
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
// Jadwal hari ini
|
|
||||||
_buildJadwalSection(
|
|
||||||
textTheme,
|
|
||||||
title: 'Hari Ini',
|
|
||||||
jadwalList: [
|
|
||||||
{
|
|
||||||
'lokasi': 'Kantor Kepala Desa',
|
|
||||||
'jenisBantuan': 'Beras',
|
|
||||||
'tanggal': '15 April 2023',
|
|
||||||
'waktu': '13:00 - 14:00',
|
|
||||||
'status': 'Aktif',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Jadwal mendatang
|
|
||||||
_buildJadwalSection(
|
|
||||||
textTheme,
|
|
||||||
title: 'Mendatang',
|
|
||||||
jadwalList: [
|
|
||||||
{
|
|
||||||
'lokasi': 'Balai Desa A',
|
|
||||||
'jenisBantuan': 'Sembako',
|
|
||||||
'tanggal': '17 April 2023',
|
|
||||||
'waktu': '13:00 - 14:00',
|
|
||||||
'status': 'Terjadwal',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'lokasi': 'Balai Desa B',
|
|
||||||
'jenisBantuan': 'Uang Tunai',
|
|
||||||
'tanggal': '20 April 2023',
|
|
||||||
'waktu': '10:00 - 12:00',
|
|
||||||
'status': 'Terjadwal',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Jadwal selesai
|
|
||||||
_buildJadwalSection(
|
|
||||||
textTheme,
|
|
||||||
title: 'Selesai',
|
|
||||||
jadwalList: [
|
|
||||||
{
|
|
||||||
'lokasi': 'Kantor Kepala Desa',
|
|
||||||
'jenisBantuan': 'Beras',
|
|
||||||
'tanggal': '10 April 2023',
|
|
||||||
'waktu': '13:00 - 14:00',
|
|
||||||
'status': 'Selesai',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'lokasi': 'Balai Desa C',
|
|
||||||
'jenisBantuan': 'Sembako',
|
|
||||||
'tanggal': '5 April 2023',
|
|
||||||
'waktu': '09:00 - 11:00',
|
|
||||||
'status': 'Selesai',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
TextTheme textTheme, {
|
|
||||||
required String title,
|
|
||||||
required List<Map<String, String>> jadwalList,
|
|
||||||
}) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: textTheme.titleLarge?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
...jadwalList.map((jadwal) => _buildJadwalItem(textTheme, jadwal)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildJadwalItem(TextTheme textTheme, Map<String, String> jadwal) {
|
|
||||||
Color statusColor;
|
|
||||||
switch (jadwal['status']) {
|
|
||||||
case 'Aktif':
|
|
||||||
statusColor = Colors.green;
|
|
||||||
break;
|
|
||||||
case 'Terjadwal':
|
|
||||||
statusColor = Colors.blue;
|
|
||||||
break;
|
|
||||||
case 'Selesai':
|
|
||||||
statusColor = Colors.grey;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
statusColor = Colors.orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(bottom: 10),
|
|
||||||
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: [
|
|
||||||
Text(
|
|
||||||
jadwal['lokasi'] ?? '',
|
|
||||||
style: textTheme.titleMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: statusColor.withAlpha(26),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
jadwal['status'] ?? '',
|
|
||||||
style: textTheme.bodySmall?.copyWith(
|
|
||||||
color: statusColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'Jenis Bantuan: ${jadwal['jenisBantuan'] ?? ''}',
|
|
||||||
style: textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Tanggal: ${jadwal['tanggal'] ?? ''}',
|
|
||||||
style: textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Waktu: ${jadwal['waktu'] ?? ''}',
|
|
||||||
style: textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
649
lib/app/modules/petugas_desa/views/pengaduan_view.dart
Normal file
649
lib/app/modules/petugas_desa/views/pengaduan_view.dart
Normal file
@ -0,0 +1,649 @@
|
|||||||
|
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 PengaduanView extends GetView<PetugasDesaController> {
|
||||||
|
const PengaduanView({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 pengaduan
|
||||||
|
_buildPengaduanSummary(context),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Filter dan pencarian
|
||||||
|
_buildFilterSearch(context),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Daftar pengaduan
|
||||||
|
_buildPengaduanList(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengaduanSummary(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 Pengaduan',
|
||||||
|
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: 'Diproses',
|
||||||
|
value: '3',
|
||||||
|
color: Colors.orange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildSummaryItem(
|
||||||
|
context,
|
||||||
|
icon: Icons.engineering,
|
||||||
|
title: 'Tindakan',
|
||||||
|
value: '2',
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildSummaryItem(
|
||||||
|
context,
|
||||||
|
icon: Icons.check_circle,
|
||||||
|
title: 'Selesai',
|
||||||
|
value: '8',
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 pengaduan...',
|
||||||
|
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
|
||||||
|
_showFilterDialog(context);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.filter_list),
|
||||||
|
tooltip: 'Filter',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showFilterDialog(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Filter Pengaduan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CheckboxListTile(
|
||||||
|
title: const Text('Diproses'),
|
||||||
|
value: true,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
title: const Text('Tindakan'),
|
||||||
|
value: true,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
CheckboxListTile(
|
||||||
|
title: const Text('Selesai'),
|
||||||
|
value: true,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Terapkan'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengaduanList(BuildContext context) {
|
||||||
|
final List<Map<String, dynamic>> pengaduanList = [
|
||||||
|
{
|
||||||
|
'id': '1',
|
||||||
|
'nama': 'Budi Santoso',
|
||||||
|
'nik': '3201020107030011',
|
||||||
|
'jenis_pengaduan': 'Bantuan Tidak Diterima',
|
||||||
|
'deskripsi':
|
||||||
|
'Saya belum menerima bantuan beras yang dijadwalkan minggu lalu',
|
||||||
|
'tanggal': '15 April 2023',
|
||||||
|
'status': 'Diproses',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '2',
|
||||||
|
'nama': 'Siti Rahayu',
|
||||||
|
'nik': '3201020107030010',
|
||||||
|
'jenis_pengaduan': 'Kualitas Bantuan',
|
||||||
|
'deskripsi':
|
||||||
|
'Beras yang diterima berkualitas buruk dan tidak layak konsumsi',
|
||||||
|
'tanggal': '14 April 2023',
|
||||||
|
'status': 'Tindakan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '3',
|
||||||
|
'nama': 'Ahmad Fauzi',
|
||||||
|
'nik': '3201020107030013',
|
||||||
|
'jenis_pengaduan': 'Jumlah Bantuan',
|
||||||
|
'deskripsi':
|
||||||
|
'Jumlah bantuan yang diterima tidak sesuai dengan yang dijanjikan',
|
||||||
|
'tanggal': '13 April 2023',
|
||||||
|
'status': 'Tindakan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': '4',
|
||||||
|
'nama': 'Dewi Lestari',
|
||||||
|
'nik': '3201020107030012',
|
||||||
|
'jenis_pengaduan': 'Jadwal Penyaluran',
|
||||||
|
'deskripsi':
|
||||||
|
'Jadwal penyaluran bantuan sering berubah tanpa pemberitahuan',
|
||||||
|
'tanggal': '10 April 2023',
|
||||||
|
'status': 'Selesai',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Daftar Pengaduan',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
...pengaduanList.map((item) => _buildPengaduanItem(context, item)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengaduanItem(BuildContext context, Map<String, dynamic> item) {
|
||||||
|
Color statusColor;
|
||||||
|
IconData statusIcon;
|
||||||
|
|
||||||
|
switch (item['status']) {
|
||||||
|
case 'Diproses':
|
||||||
|
statusColor = Colors.orange;
|
||||||
|
statusIcon = Icons.pending_actions;
|
||||||
|
break;
|
||||||
|
case 'Tindakan':
|
||||||
|
statusColor = Colors.blue;
|
||||||
|
statusIcon = Icons.engineering;
|
||||||
|
break;
|
||||||
|
case 'Selesai':
|
||||||
|
statusColor = Colors.green;
|
||||||
|
statusIcon = Icons.check_circle;
|
||||||
|
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['nama'] ?? '',
|
||||||
|
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: 4),
|
||||||
|
Text(
|
||||||
|
'NIK: ${item['nik'] ?? ''}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
item['jenis_pengaduan'] ?? '',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
item['deskripsi'] ?? '',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_today,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
item['tanggal'] ?? '',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: _buildActionButtons(context, item),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildActionButtons(
|
||||||
|
BuildContext context, Map<String, dynamic> item) {
|
||||||
|
final status = item['status'];
|
||||||
|
|
||||||
|
if (status == 'Diproses') {
|
||||||
|
return [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk memproses pengaduan
|
||||||
|
_showTindakanDialog(context, item);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.engineering, size: 18),
|
||||||
|
label: const Text('Tindakan'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: Colors.blue,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk melihat detail pengaduan
|
||||||
|
_showDetailDialog(context, item);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.info_outline, size: 18),
|
||||||
|
label: const Text('Detail'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: Colors.grey,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} else if (status == 'Tindakan') {
|
||||||
|
return [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk menyelesaikan pengaduan
|
||||||
|
_showSelesaikanDialog(context, item);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.check_circle, size: 18),
|
||||||
|
label: const Text('Selesaikan'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: Colors.green,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk melihat detail pengaduan
|
||||||
|
_showDetailDialog(context, item);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.info_outline, size: 18),
|
||||||
|
label: const Text('Detail'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: Colors.grey,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk melihat detail pengaduan
|
||||||
|
_showDetailDialog(context, item);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.info_outline, size: 18),
|
||||||
|
label: const Text('Detail'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: Colors.grey,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showDetailDialog(BuildContext context, Map<String, dynamic> item) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text('Detail Pengaduan: ${item['id']}'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
_buildDetailItem('Nama', item['nama'] ?? ''),
|
||||||
|
_buildDetailItem('NIK', item['nik'] ?? ''),
|
||||||
|
_buildDetailItem(
|
||||||
|
'Jenis Pengaduan', item['jenis_pengaduan'] ?? ''),
|
||||||
|
_buildDetailItem('Tanggal', item['tanggal'] ?? ''),
|
||||||
|
_buildDetailItem('Status', item['status'] ?? ''),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Deskripsi:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(item['deskripsi'] ?? ''),
|
||||||
|
if (item['status'] == 'Tindakan' ||
|
||||||
|
item['status'] == 'Selesai') ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Tindakan:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(item['tindakan'] ??
|
||||||
|
'Pengecekan ke lokasi dan verifikasi data penerima'),
|
||||||
|
],
|
||||||
|
if (item['status'] == 'Selesai') ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Hasil:',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(item['hasil'] ??
|
||||||
|
'Pengaduan telah diselesaikan dengan penyaluran ulang bantuan'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Tutup'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDetailItem(String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: Text(
|
||||||
|
'$label:',
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: Text(value)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showTindakanDialog(BuildContext context, Map<String, dynamic> item) {
|
||||||
|
final TextEditingController tindakanController = TextEditingController();
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Tindakan Pengaduan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('Pengaduan dari: ${item['nama']}'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: tindakanController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Tindakan yang dilakukan',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk menyimpan tindakan
|
||||||
|
Navigator.pop(context);
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Status pengaduan berhasil diubah menjadi Tindakan',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Simpan'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSelesaikanDialog(BuildContext context, Map<String, dynamic> item) {
|
||||||
|
final TextEditingController hasilController = TextEditingController();
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Selesaikan Pengaduan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('Pengaduan dari: ${item['nama']}'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: hasilController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Hasil penyelesaian',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk menyimpan hasil
|
||||||
|
Navigator.pop(context);
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Status pengaduan berhasil diubah menjadi Selesai',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Selesaikan'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
159
lib/app/modules/petugas_desa/views/penyaluran_view.dart
Normal file
159
lib/app/modules/petugas_desa/views/penyaluran_view.dart
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
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';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/components/jadwal_section_widget.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/components/permintaan_penjadwalan_summary_widget.dart';
|
||||||
|
|
||||||
|
class PenyaluranView extends GetView<PetugasDesaController> {
|
||||||
|
const PenyaluranView({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 jadwal
|
||||||
|
_buildJadwalSummary(context),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Ringkasan Permintaan Penjadwalan
|
||||||
|
PermintaanPenjadwalanSummaryWidget(controller: controller),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Jadwal hari ini
|
||||||
|
JadwalSectionWidget(
|
||||||
|
controller: controller,
|
||||||
|
title: 'Hari Ini',
|
||||||
|
jadwalList: controller.jadwalHariIni,
|
||||||
|
status: 'Aktif',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Jadwal mendatang
|
||||||
|
JadwalSectionWidget(
|
||||||
|
controller: controller,
|
||||||
|
title: 'Mendatang',
|
||||||
|
jadwalList: controller.jadwalMendatang,
|
||||||
|
status: 'Terjadwal',
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Jadwal selesai
|
||||||
|
JadwalSectionWidget(
|
||||||
|
controller: controller,
|
||||||
|
title: 'Selesai',
|
||||||
|
jadwalList: controller.jadwalSelesai,
|
||||||
|
status: 'Selesai',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: Obx(() => _buildSummaryItem(
|
||||||
|
context,
|
||||||
|
icon: Icons.pending_actions,
|
||||||
|
title: 'Terjadwal',
|
||||||
|
value: '${controller.jadwalMendatang.length}',
|
||||||
|
color: Colors.blue,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildSummaryItem(
|
||||||
|
context,
|
||||||
|
icon: Icons.event_available,
|
||||||
|
title: 'Aktif',
|
||||||
|
value: '${controller.jadwalHariIni.length}',
|
||||||
|
color: Colors.green,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() => _buildSummaryItem(
|
||||||
|
context,
|
||||||
|
icon: Icons.event_busy,
|
||||||
|
title: 'Selesai',
|
||||||
|
value: '${controller.jadwalSelesai.length}',
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,341 @@
|
|||||||
|
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 PermintaanPenjadwalanView extends GetView<PetugasDesaController> {
|
||||||
|
const PermintaanPenjadwalanView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// Pastikan controller sudah diinisialisasi
|
||||||
|
if (!Get.isRegistered<PetugasDesaController>()) {
|
||||||
|
Get.put(PetugasDesaController());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Permintaan Penjadwalan'),
|
||||||
|
elevation: 0,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Obx(() {
|
||||||
|
final permintaanList = controller.permintaanPenjadwalan;
|
||||||
|
|
||||||
|
if (permintaanList.isEmpty) {
|
||||||
|
return _buildEmptyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
itemCount: permintaanList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final permintaan = permintaanList[index];
|
||||||
|
return _buildPermintaanItem(context, permintaan);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyState() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.event_note,
|
||||||
|
size: 80,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Tidak ada permintaan penjadwalan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Semua permintaan penjadwalan akan muncul di sini',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPermintaanItem(
|
||||||
|
BuildContext context, Map<String, dynamic> permintaan) {
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
side: BorderSide(
|
||||||
|
color: Colors.orange.withAlpha(50),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
permintaan['nama'] ?? '',
|
||||||
|
style: textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.orange.withAlpha(26),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Menunggu',
|
||||||
|
style: textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.orange,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_buildInfoRow(Icons.person, 'NIK: ${permintaan['nik'] ?? ''}'),
|
||||||
|
_buildInfoRow(Icons.category,
|
||||||
|
'Jenis Bantuan: ${permintaan['jenis_bantuan'] ?? ''}'),
|
||||||
|
_buildInfoRow(Icons.calendar_today,
|
||||||
|
'Tanggal Permintaan: ${permintaan['tanggal_permintaan'] ?? ''}'),
|
||||||
|
_buildInfoRow(
|
||||||
|
Icons.location_on, 'Alamat: ${permintaan['alamat'] ?? ''}'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
OutlinedButton.icon(
|
||||||
|
onPressed: () => _showTolakDialog(permintaan),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.red,
|
||||||
|
side: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
label: const Text('Tolak'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () => _showKonfirmasiDialog(permintaan),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
label: const Text('Konfirmasi'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoRow(IconData icon, String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog untuk konfirmasi permintaan
|
||||||
|
void _showKonfirmasiDialog(Map<String, dynamic> permintaan) {
|
||||||
|
String? selectedJadwalId;
|
||||||
|
|
||||||
|
// Data jadwal yang tersedia dari controller
|
||||||
|
final jadwalOptions = controller.jadwalMendatang.map((jadwal) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: jadwal['id'],
|
||||||
|
child: Text(
|
||||||
|
'${jadwal['tanggal'] ?? ''} - ${jadwal['lokasi'] ?? ''} (${jadwal['jenis_bantuan'] ?? ''})'),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
// Tambahkan opsi jadwal lain jika diperlukan
|
||||||
|
jadwalOptions.add(
|
||||||
|
const DropdownMenuItem<String>(
|
||||||
|
value: '3',
|
||||||
|
child: Text('25 April 2023 - Kantor Kepala Desa (Beras)'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('Konfirmasi Permintaan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Anda akan mengkonfirmasi permintaan penjadwalan dari ${permintaan['nama'] ?? 'Penerima'}.'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text('Pilih jadwal penyaluran:'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
DropdownButtonFormField<String>(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
items: jadwalOptions,
|
||||||
|
onChanged: (value) {
|
||||||
|
selectedJadwalId = value;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (selectedJadwalId != null) {
|
||||||
|
// Panggil metode konfirmasi di controller
|
||||||
|
controller.konfirmasiPermintaanPenjadwalan(
|
||||||
|
permintaan['id'] ?? '',
|
||||||
|
selectedJadwalId ?? '',
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.back();
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Permintaan penjadwalan berhasil dikonfirmasi',
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Peringatan',
|
||||||
|
'Silakan pilih jadwal penyaluran terlebih dahulu',
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
child: const Text('Konfirmasi'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog untuk menolak permintaan
|
||||||
|
void _showTolakDialog(Map<String, dynamic> permintaan) {
|
||||||
|
final TextEditingController alasanController = TextEditingController();
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: const Text('Tolak Permintaan'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Anda akan menolak permintaan penjadwalan dari ${permintaan['nama'] ?? 'Penerima'}.'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text('Alasan penolakan:'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: alasanController,
|
||||||
|
maxLines: 3,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
hintText: 'Masukkan alasan penolakan',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
child: const Text('Batal'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (alasanController.text.trim().isNotEmpty) {
|
||||||
|
// Panggil metode tolak di controller
|
||||||
|
controller.tolakPermintaanPenjadwalan(
|
||||||
|
permintaan['id'] ?? '',
|
||||||
|
alasanController.text.trim(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Get.back();
|
||||||
|
Get.snackbar(
|
||||||
|
'Berhasil',
|
||||||
|
'Permintaan penjadwalan berhasil ditolak',
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar(
|
||||||
|
'Peringatan',
|
||||||
|
'Silakan masukkan alasan penolakan',
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
child: const Text('Tolak'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/petugas_desa_controller.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/petugas_desa_controller.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/dashboard_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/dashboard_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/jadwal_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/penyaluran_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/modules/petugas_desa/views/penitipan_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/pengaduan_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> {
|
||||||
@ -15,6 +16,12 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
// Perbarui counter pengaduan secara manual saat aplikasi dimulai
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
controller.updatePengaduanCounter();
|
||||||
|
print('Counter pengaduan diperbarui saat aplikasi dimulai');
|
||||||
|
});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: scaffoldKey,
|
key: scaffoldKey,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@ -23,11 +30,13 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
case 0:
|
case 0:
|
||||||
return const Text('Dashboard');
|
return const Text('Dashboard');
|
||||||
case 1:
|
case 1:
|
||||||
return const Text('Jadwal Penyaluran');
|
return const Text('Penyaluran');
|
||||||
case 2:
|
case 2:
|
||||||
return const Text('Inventaris');
|
|
||||||
case 3:
|
|
||||||
return const Text('Penitipan');
|
return const Text('Penitipan');
|
||||||
|
case 3:
|
||||||
|
return const Text('Pengaduan');
|
||||||
|
case 4:
|
||||||
|
return const Text('Inventaris');
|
||||||
default:
|
default:
|
||||||
return const Text('Petugas Desa');
|
return const Text('Petugas Desa');
|
||||||
}
|
}
|
||||||
@ -115,6 +124,35 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
notificationButton,
|
notificationButton,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
} else if (activeTab == 4) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
tooltip: 'Tambah Pengaduan',
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk menambah pengaduan baru
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.filter_list),
|
||||||
|
tooltip: 'Filter Pengaduan',
|
||||||
|
onPressed: () {
|
||||||
|
// Implementasi untuk filter pengaduan
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
tooltip: 'Perbarui Counter',
|
||||||
|
onPressed: () {
|
||||||
|
// Perbarui counter pengaduan secara manual
|
||||||
|
controller.updatePengaduanCounter();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
notificationButton,
|
||||||
|
],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return notificationButton;
|
return notificationButton;
|
||||||
}
|
}
|
||||||
@ -127,11 +165,14 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
case 0:
|
case 0:
|
||||||
return const DashboardView();
|
return const DashboardView();
|
||||||
case 1:
|
case 1:
|
||||||
return const JadwalView();
|
return const PenyaluranView();
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return const InventarisView();
|
|
||||||
case 3:
|
|
||||||
return const PenitipanView();
|
return const PenitipanView();
|
||||||
|
case 3:
|
||||||
|
return const PengaduanView();
|
||||||
|
case 4:
|
||||||
|
return const InventarisView();
|
||||||
default:
|
default:
|
||||||
return const DashboardView();
|
return const DashboardView();
|
||||||
}
|
}
|
||||||
@ -193,7 +234,7 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
)),
|
)),
|
||||||
Obx(() => 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('Penyaluran'),
|
||||||
selected: controller.activeTabIndex.value == 1,
|
selected: controller.activeTabIndex.value == 1,
|
||||||
selectedColor: AppTheme.primaryColor,
|
selectedColor: AppTheme.primaryColor,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -201,16 +242,6 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
Navigator.pop(context);
|
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(
|
Obx(() => ListTile(
|
||||||
leading: Stack(
|
leading: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@ -243,12 +274,67 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
title: const Text('Penitipan'),
|
title: const Text('Penitipan'),
|
||||||
|
selected: controller.activeTabIndex.value == 2,
|
||||||
|
selectedColor: AppTheme.primaryColor,
|
||||||
|
onTap: () {
|
||||||
|
controller.changeTab(2);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
Obx(() {
|
||||||
|
final int jumlahPengaduanDiproses = controller.jumlahDiproses.value;
|
||||||
|
print(
|
||||||
|
'Drawer - Jumlah pengaduan diproses: $jumlahPengaduanDiproses');
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
leading: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.report_problem_outlined),
|
||||||
|
// Selalu tampilkan badge untuk debugging
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
jumlahPengaduanDiproses.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: const Text('Pengaduan'),
|
||||||
selected: controller.activeTabIndex.value == 3,
|
selected: controller.activeTabIndex.value == 3,
|
||||||
selectedColor: AppTheme.primaryColor,
|
selectedColor: AppTheme.primaryColor,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
controller.changeTab(3);
|
controller.changeTab(3);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Obx(() => ListTile(
|
||||||
|
leading: const Icon(Icons.inventory_2_outlined),
|
||||||
|
title: const Text('Inventaris'),
|
||||||
|
selected: controller.activeTabIndex.value == 4,
|
||||||
|
selectedColor: AppTheme.primaryColor,
|
||||||
|
onTap: () {
|
||||||
|
controller.changeTab(4);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
)),
|
)),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
@ -323,7 +409,15 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBottomNavigationBar() {
|
Widget _buildBottomNavigationBar() {
|
||||||
return Obx(() => BottomNavigationBar(
|
// Tambahkan print statement untuk debugging
|
||||||
|
print('Jumlah pengaduan diproses: ${controller.jumlahDiproses.value}');
|
||||||
|
print('Jumlah jadwal hari ini: ${controller.jadwalHariIni.length}');
|
||||||
|
|
||||||
|
return Obx(() {
|
||||||
|
// Hitung jumlah pengaduan yang diproses
|
||||||
|
final int jumlahPengaduanDiproses = controller.jumlahDiproses.value;
|
||||||
|
|
||||||
|
return BottomNavigationBar(
|
||||||
currentIndex: controller.activeTabIndex.value,
|
currentIndex: controller.activeTabIndex.value,
|
||||||
onTap: controller.changeTab,
|
onTap: controller.changeTab,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
@ -396,12 +490,7 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
label: 'Jadwal',
|
label: 'Penyaluran',
|
||||||
),
|
|
||||||
const BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.inventory_2_outlined),
|
|
||||||
activeIcon: Icon(Icons.inventory_2),
|
|
||||||
label: 'Inventaris',
|
|
||||||
),
|
),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: Stack(
|
icon: Stack(
|
||||||
@ -466,7 +555,76 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
),
|
),
|
||||||
label: 'Penitipan',
|
label: 'Penitipan',
|
||||||
),
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.report_problem_outlined),
|
||||||
|
// Selalu tampilkan badge untuk debugging
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
controller.jumlahDiproses.value.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
));
|
),
|
||||||
|
activeIcon: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.report_problem),
|
||||||
|
// Selalu tampilkan badge untuk debugging
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
controller.jumlahDiproses.value.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
label: 'Pengaduan',
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.inventory_2_outlined),
|
||||||
|
activeIcon: Icon(Icons.inventory_2),
|
||||||
|
label: 'Inventaris',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
lib/app/modules/splash/bindings/splash_binding.dart
Normal file
8
lib/app/modules/splash/bindings/splash_binding.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class SplashBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
// Tidak perlu menambahkan controller khusus untuk splash screen
|
||||||
|
}
|
||||||
|
}
|
83
lib/app/modules/splash/views/splash_view.dart
Normal file
83
lib/app/modules/splash/views/splash_view.dart
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class SplashView extends StatefulWidget {
|
||||||
|
const SplashView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SplashView> createState() => _SplashViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashViewState extends State<SplashView> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_navigateToLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
_navigateToLogin() async {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
Get.offAllNamed(Routes.login);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: AppTheme.primaryGradient,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/logo.png',
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.people,
|
||||||
|
size: 60,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
const Text(
|
||||||
|
'Aplikasi Penyaluran',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Bantuan Sosial',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
const CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import 'package:penyaluran_app/app/modules/home/bindings/home_binding.dart';
|
|||||||
import 'package:penyaluran_app/app/modules/dashboard/bindings/dashboard_binding.dart';
|
import 'package:penyaluran_app/app/modules/dashboard/bindings/dashboard_binding.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/petugas_desa_view.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/petugas_desa_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/petugas_desa_binding.dart';
|
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/petugas_desa_binding.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/petugas_desa/views/permintaan_penjadwalan_view.dart';
|
||||||
|
|
||||||
part 'app_routes.dart';
|
part 'app_routes.dart';
|
||||||
|
|
||||||
@ -48,5 +49,10 @@ class AppPages {
|
|||||||
page: () => const DonaturDashboardView(),
|
page: () => const DonaturDashboardView(),
|
||||||
binding: DashboardBinding(),
|
binding: DashboardBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: _Paths.permintaanPenjadwalan,
|
||||||
|
page: () => const PermintaanPenjadwalanView(),
|
||||||
|
binding: PetugasDesaBinding(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ abstract class Routes {
|
|||||||
static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard;
|
static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard;
|
||||||
static const petugasDesaDashboard = _Paths.petugasDesaDashboard;
|
static const petugasDesaDashboard = _Paths.petugasDesaDashboard;
|
||||||
static const donaturDashboard = _Paths.donaturDashboard;
|
static const donaturDashboard = _Paths.donaturDashboard;
|
||||||
|
static const splash = _Paths.splash;
|
||||||
|
static const permintaanPenjadwalan = _Paths.permintaanPenjadwalan;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _Paths {
|
abstract class _Paths {
|
||||||
@ -20,4 +22,6 @@ abstract class _Paths {
|
|||||||
static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard';
|
static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard';
|
||||||
static const petugasDesaDashboard = '/petugas-desa-dashboard';
|
static const petugasDesaDashboard = '/petugas-desa-dashboard';
|
||||||
static const donaturDashboard = '/donatur-dashboard';
|
static const donaturDashboard = '/donatur-dashboard';
|
||||||
|
static const splash = '/splash';
|
||||||
|
static const permintaanPenjadwalan = '/permintaan-penjadwalan';
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user