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? bentukBantuanId;
final String? sumberBantuanId;
final String? jenisBantuanId;
final Map<String, dynamic>? jenisBantuan;
final double? jumlah;
final String? satuan;
final String? deskripsi;
@ -19,6 +21,8 @@ class StokBantuanModel {
this.nama,
this.bentukBantuanId,
this.sumberBantuanId,
this.jenisBantuanId,
this.jenisBantuan,
this.jumlah,
this.satuan,
this.deskripsi,
@ -40,6 +44,8 @@ class StokBantuanModel {
nama: json["nama"],
bentukBantuanId: json["bentuk_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,
satuan: json["satuan"],
deskripsi: json["deskripsi"],
@ -63,6 +69,7 @@ class StokBantuanModel {
"nama": nama,
"bentuk_bantuan_id": bentukBantuanId,
"sumber_bantuan_id": sumberBantuanId,
"jenis_bantuan_id": jenisBantuanId,
"jumlah": jumlah,
"satuan": satuan,
"deskripsi": deskripsi,

View File

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

View File

@ -17,6 +17,10 @@ class StokBantuanController extends GetxController {
final RxDouble stokMasuk = 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
final TextEditingController searchController = TextEditingController();
final RxString searchQuery = ''.obs;
@ -27,6 +31,7 @@ class StokBantuanController extends GetxController {
void onInit() {
super.onInit();
loadStokBantuanData();
loadJenisBantuanData();
// Listener untuk pencarian
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 {
isLoading.value = true;
try {
@ -151,6 +167,7 @@ class StokBantuanController extends GetxController {
isLoading.value = true;
try {
await loadStokBantuanData();
await loadJenisBantuanData();
} finally {
isLoading.value = false;
}

View File

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

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.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/modules/petugas_desa/controllers/stok_bantuan_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
@ -277,7 +276,9 @@ class StokBantuanView extends GetView<StokBantuanController> {
borderRadius: BorderRadius.circular(8),
),
child: Text(
item.status ?? 'TERSEDIA',
item.jenisBantuan != null
? (item.jenisBantuan!['nama'] ?? 'Tidak Ada Jenis')
: 'Tidak Ada Jenis',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: AppTheme.primaryColor,
fontWeight: FontWeight.bold,
@ -417,6 +418,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
final jumlahController = TextEditingController();
final satuanController = TextEditingController();
final deskripsiController = TextEditingController();
String? selectedJenisBantuanId;
DateTime? tanggalMasuk = DateTime.now();
DateTime? tanggalKadaluarsa;
@ -444,6 +446,30 @@ class StokBantuanView extends GetView<StokBantuanController> {
},
),
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(
children: [
Expanded(
@ -558,6 +584,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
jumlah: double.parse(jumlahController.text),
satuan: satuanController.text,
deskripsi: deskripsiController.text,
jenisBantuanId: selectedJenisBantuanId,
tanggalMasuk: tanggalMasuk,
tanggalKadaluarsa: tanggalKadaluarsa,
status: 'TERSEDIA',
@ -582,6 +609,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
TextEditingController(text: stok.jumlah?.toString());
final satuanController = TextEditingController(text: stok.satuan);
final deskripsiController = TextEditingController(text: stok.deskripsi);
String? selectedJenisBantuanId = stok.jenisBantuanId;
DateTime? tanggalMasuk = stok.tanggalMasuk;
DateTime? tanggalKadaluarsa = stok.tanggalKadaluarsa;
@ -609,6 +637,30 @@ class StokBantuanView extends GetView<StokBantuanController> {
},
),
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(
children: [
Expanded(
@ -724,6 +776,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
jumlah: double.parse(jumlahController.text),
satuan: satuanController.text,
deskripsi: deskripsiController.text,
jenisBantuanId: selectedJenisBantuanId,
tanggalMasuk: tanggalMasuk,
tanggalKadaluarsa: tanggalKadaluarsa,
status: stok.status,

View File

@ -268,7 +268,9 @@ class SupabaseService extends GetxService {
// Stok bantuan methods
Future<List<Map<String, dynamic>>?> getStokBantuan() async {
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;
} catch (e) {
@ -309,7 +311,6 @@ class SupabaseService extends GetxService {
Future<List<Map<String, dynamic>>?> getBentukBantuan() async {
try {
final response = await client.from('bentuk_bantuan').select('*');
return response;
} catch (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 {
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) {
print('Error adding stok: $e');
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/theme/app_theme.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';
void main() async {