Tambahkan fitur jenis bantuan pada modul stok bantuan

- Perbarui model StokBantuanModel untuk mendukung jenis bantuan
- Tambahkan metode loadJenisBantuanData() di StokBantuanController
- Integrasikan dropdown pemilihan jenis bantuan di form tambah/edit stok
- Perbarui SupabaseService untuk mengambil data jenis bantuan
- Tampilkan nama jenis bantuan di daftar stok bantuan
This commit is contained in:
Khafidh Fuadi
2025-03-11 18:55:06 +07:00
parent eec06ba79d
commit cdbd659d63
7 changed files with 95 additions and 36 deletions

View File

@ -5,6 +5,8 @@ class StokBantuanModel {
final String? nama; final String? nama;
final String? bentukBantuanId; final String? bentukBantuanId;
final String? sumberBantuanId; final String? sumberBantuanId;
final String? jenisBantuanId;
final Map<String, dynamic>? jenisBantuan;
final double? jumlah; final double? jumlah;
final String? satuan; final String? satuan;
final String? deskripsi; final String? deskripsi;
@ -19,6 +21,8 @@ class StokBantuanModel {
this.nama, this.nama,
this.bentukBantuanId, this.bentukBantuanId,
this.sumberBantuanId, this.sumberBantuanId,
this.jenisBantuanId,
this.jenisBantuan,
this.jumlah, this.jumlah,
this.satuan, this.satuan,
this.deskripsi, this.deskripsi,
@ -40,6 +44,8 @@ class StokBantuanModel {
nama: json["nama"], nama: json["nama"],
bentukBantuanId: json["bentuk_bantuan_id"], bentukBantuanId: json["bentuk_bantuan_id"],
sumberBantuanId: json["sumber_bantuan_id"], sumberBantuanId: json["sumber_bantuan_id"],
jenisBantuanId: json["jenis_bantuan_id"],
jenisBantuan: json["jenis_bantuan"],
jumlah: json["jumlah"] != null ? json["jumlah"].toDouble() : 0.0, jumlah: json["jumlah"] != null ? json["jumlah"].toDouble() : 0.0,
satuan: json["satuan"], satuan: json["satuan"],
deskripsi: json["deskripsi"], deskripsi: json["deskripsi"],
@ -63,6 +69,7 @@ class StokBantuanModel {
"nama": nama, "nama": nama,
"bentuk_bantuan_id": bentukBantuanId, "bentuk_bantuan_id": bentukBantuanId,
"sumber_bantuan_id": sumberBantuanId, "sumber_bantuan_id": sumberBantuanId,
"jenis_bantuan_id": jenisBantuanId,
"jumlah": jumlah, "jumlah": jumlah,
"satuan": satuan, "satuan": satuan,
"deskripsi": deskripsi, "deskripsi": deskripsi,

View File

@ -1,7 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/utils/date_formatter.dart';
class PenerimaController extends GetxController { class PenerimaController extends GetxController {
final RxList<Map<String, dynamic>> daftarPenerima = final RxList<Map<String, dynamic>> daftarPenerima =

View File

@ -17,6 +17,10 @@ class StokBantuanController extends GetxController {
final RxDouble stokMasuk = 0.0.obs; final RxDouble stokMasuk = 0.0.obs;
final RxDouble stokKeluar = 0.0.obs; final RxDouble stokKeluar = 0.0.obs;
// Data untuk jenis bantuan
final RxList<Map<String, dynamic>> daftarJenisBantuan =
<Map<String, dynamic>>[].obs;
// Controller untuk pencarian // Controller untuk pencarian
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
final RxString searchQuery = ''.obs; final RxString searchQuery = ''.obs;
@ -27,6 +31,7 @@ class StokBantuanController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
loadStokBantuanData(); loadStokBantuanData();
loadJenisBantuanData();
// Listener untuk pencarian // Listener untuk pencarian
searchController.addListener(() { searchController.addListener(() {
@ -69,6 +74,17 @@ class StokBantuanController extends GetxController {
} }
} }
Future<void> loadJenisBantuanData() async {
try {
final jenisBantuanData = await _supabaseService.getJenisBantuan();
if (jenisBantuanData != null) {
daftarJenisBantuan.value = jenisBantuanData;
}
} catch (e) {
print('Error loading jenis bantuan data: $e');
}
}
Future<void> addStok(StokBantuanModel stok) async { Future<void> addStok(StokBantuanModel stok) async {
isLoading.value = true; isLoading.value = true;
try { try {
@ -151,6 +167,7 @@ class StokBantuanController extends GetxController {
isLoading.value = true; isLoading.value = true;
try { try {
await loadStokBantuanData(); await loadStokBantuanData();
await loadJenisBantuanData();
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@ -124,34 +124,7 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
], ],
); );
} else if (activeTab == 4) { } else if (activeTab == 4) {
return Row( return notificationButton;
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;
} }

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/data/models/stok_bantuan_model.dart'; import 'package:penyaluran_app/app/data/models/stok_bantuan_model.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart'; import 'package:penyaluran_app/app/theme/app_theme.dart';
@ -277,7 +276,9 @@ class StokBantuanView extends GetView<StokBantuanController> {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Text( child: Text(
item.status ?? 'TERSEDIA', item.jenisBantuan != null
? (item.jenisBantuan!['nama'] ?? 'Tidak Ada Jenis')
: 'Tidak Ada Jenis',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: AppTheme.primaryColor, color: AppTheme.primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -417,6 +418,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
final jumlahController = TextEditingController(); final jumlahController = TextEditingController();
final satuanController = TextEditingController(); final satuanController = TextEditingController();
final deskripsiController = TextEditingController(); final deskripsiController = TextEditingController();
String? selectedJenisBantuanId;
DateTime? tanggalMasuk = DateTime.now(); DateTime? tanggalMasuk = DateTime.now();
DateTime? tanggalKadaluarsa; DateTime? tanggalKadaluarsa;
@ -444,6 +446,30 @@ class StokBantuanView extends GetView<StokBantuanController> {
}, },
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Jenis Bantuan',
border: OutlineInputBorder(),
),
value: selectedJenisBantuanId,
hint: const Text('Pilih Jenis Bantuan'),
items: controller.daftarJenisBantuan
.map((jenis) => DropdownMenuItem<String>(
value: jenis['id'],
child: Text(jenis['nama'] ?? ''),
))
.toList(),
onChanged: (value) {
selectedJenisBantuanId = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Jenis bantuan harus dipilih';
}
return null;
},
),
const SizedBox(height: 16),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -558,6 +584,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
jumlah: double.parse(jumlahController.text), jumlah: double.parse(jumlahController.text),
satuan: satuanController.text, satuan: satuanController.text,
deskripsi: deskripsiController.text, deskripsi: deskripsiController.text,
jenisBantuanId: selectedJenisBantuanId,
tanggalMasuk: tanggalMasuk, tanggalMasuk: tanggalMasuk,
tanggalKadaluarsa: tanggalKadaluarsa, tanggalKadaluarsa: tanggalKadaluarsa,
status: 'TERSEDIA', status: 'TERSEDIA',
@ -582,6 +609,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
TextEditingController(text: stok.jumlah?.toString()); TextEditingController(text: stok.jumlah?.toString());
final satuanController = TextEditingController(text: stok.satuan); final satuanController = TextEditingController(text: stok.satuan);
final deskripsiController = TextEditingController(text: stok.deskripsi); final deskripsiController = TextEditingController(text: stok.deskripsi);
String? selectedJenisBantuanId = stok.jenisBantuanId;
DateTime? tanggalMasuk = stok.tanggalMasuk; DateTime? tanggalMasuk = stok.tanggalMasuk;
DateTime? tanggalKadaluarsa = stok.tanggalKadaluarsa; DateTime? tanggalKadaluarsa = stok.tanggalKadaluarsa;
@ -609,6 +637,30 @@ class StokBantuanView extends GetView<StokBantuanController> {
}, },
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: 'Jenis Bantuan',
border: OutlineInputBorder(),
),
value: selectedJenisBantuanId,
hint: const Text('Pilih Jenis Bantuan'),
items: controller.daftarJenisBantuan
.map((jenis) => DropdownMenuItem<String>(
value: jenis['id'],
child: Text(jenis['nama'] ?? ''),
))
.toList(),
onChanged: (value) {
selectedJenisBantuanId = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Jenis bantuan harus dipilih';
}
return null;
},
),
const SizedBox(height: 16),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -724,6 +776,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
jumlah: double.parse(jumlahController.text), jumlah: double.parse(jumlahController.text),
satuan: satuanController.text, satuan: satuanController.text,
deskripsi: deskripsiController.text, deskripsi: deskripsiController.text,
jenisBantuanId: selectedJenisBantuanId,
tanggalMasuk: tanggalMasuk, tanggalMasuk: tanggalMasuk,
tanggalKadaluarsa: tanggalKadaluarsa, tanggalKadaluarsa: tanggalKadaluarsa,
status: stok.status, status: stok.status,

View File

@ -268,7 +268,9 @@ class SupabaseService extends GetxService {
// Stok bantuan methods // Stok bantuan methods
Future<List<Map<String, dynamic>>?> getStokBantuan() async { Future<List<Map<String, dynamic>>?> getStokBantuan() async {
try { try {
final response = await client.from('stok_bantuan').select('*'); final response = await client
.from('stok_bantuan')
.select('*, jenis_bantuan:jenis_bantuan_id(id, nama)');
return response; return response;
} catch (e) { } catch (e) {
@ -309,7 +311,6 @@ class SupabaseService extends GetxService {
Future<List<Map<String, dynamic>>?> getBentukBantuan() async { Future<List<Map<String, dynamic>>?> getBentukBantuan() async {
try { try {
final response = await client.from('bentuk_bantuan').select('*'); final response = await client.from('bentuk_bantuan').select('*');
return response; return response;
} catch (e) { } catch (e) {
print('Error getting bentuk bantuan: $e'); print('Error getting bentuk bantuan: $e');
@ -317,9 +318,19 @@ class SupabaseService extends GetxService {
} }
} }
Future<void> addStok(Map<String, dynamic> stok) async { Future<List<Map<String, dynamic>>?> getJenisBantuan() async {
try { try {
await client.from('stok_bantuan').insert(stok); final response = await client.from('jenis_bantuan').select('*');
return response;
} catch (e) {
print('Error getting jenis bantuan: $e');
return null;
}
}
Future<void> addStok(Map<String, dynamic> stokData) async {
try {
await client.from('stok_bantuan').insert(stokData);
} catch (e) { } catch (e) {
print('Error adding stok: $e'); print('Error adding stok: $e');
throw e.toString(); throw e.toString();

View File

@ -5,7 +5,6 @@ import 'package:penyaluran_app/app/services/auth_service.dart';
import 'package:penyaluran_app/app/services/supabase_service.dart'; import 'package:penyaluran_app/app/services/supabase_service.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart'; import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart'; import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
void main() async { void main() async {