Perbarui model dan tampilan untuk mendukung penyaluran baru

- Hapus properti tanggalPenjadwalan dari model PenyaluranBantuanModel
- Ganti referensi tanggalPenjadwalan dengan createdAt di tampilan PermintaanPenjadwalanWidget dan PermintaanPenjadwalanView
- Tambahkan fungsi baru untuk menambahkan penyaluran di JadwalPenyaluranController
- Tambahkan rute dan tampilan untuk menambah penyaluran di aplikasi
- Perbarui SupabaseService untuk menyimpan data penyaluran baru ke database
This commit is contained in:
Khafidh Fuadi
2025-03-14 21:29:32 +07:00
parent 7c94b85434
commit ecc1ccac59
11 changed files with 592 additions and 34 deletions

View File

@ -0,0 +1,60 @@
import 'dart:convert';
enum StatusKelayakan { pending, disetujui, ditolak }
class PengajuanKelayakanBantuanModel {
final String? id;
final String? buktiKelayakan;
final String? alasanVerifikasi;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? skemaBantuanId;
final String? wargaId;
final StatusKelayakan? status;
PengajuanKelayakanBantuanModel({
this.id,
this.buktiKelayakan,
this.alasanVerifikasi,
this.createdAt,
this.updatedAt,
this.skemaBantuanId,
this.wargaId,
this.status,
});
factory PengajuanKelayakanBantuanModel.fromRawJson(String str) =>
PengajuanKelayakanBantuanModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory PengajuanKelayakanBantuanModel.fromJson(Map<String, dynamic> json) =>
PengajuanKelayakanBantuanModel(
id: json["id"],
buktiKelayakan: json["bukti_kelayakan"],
alasanVerifikasi: json["alasan_verifikasi"],
createdAt: json["created_at"] != null
? DateTime.parse(json["created_at"])
: null,
updatedAt: json["updated_at"] != null
? DateTime.parse(json["updated_at"])
: null,
skemaBantuanId: json["skema_bantuan_id"],
wargaId: json["warga_id"],
status: json["status"] != null
? StatusKelayakan.values.firstWhere(
(e) => e.toString() == 'StatusKelayakan.${json["status"]}')
: null,
);
Map<String, dynamic> toJson() => {
"id": id,
"bukti_kelayakan": buktiKelayakan,
"alasan_verifikasi": alasanVerifikasi,
"created_at": createdAt?.toIso8601String(),
"updated_at": updatedAt?.toIso8601String(),
"skema_bantuan_id": skemaBantuanId,
"warga_id": wargaId,
"status": status?.toString().split('.').last,
};
}

View File

@ -8,7 +8,6 @@ class PenyaluranBantuanModel {
final String? petugasId; final String? petugasId;
final String? status; final String? status;
final String? alasanPenolakan; final String? alasanPenolakan;
final DateTime? tanggalPenjadwalan;
final DateTime? tanggalPenyaluran; final DateTime? tanggalPenyaluran;
final String? kategoriBantuanId; final String? kategoriBantuanId;
final DateTime? tanggalPermintaan; final DateTime? tanggalPermintaan;
@ -25,7 +24,6 @@ class PenyaluranBantuanModel {
this.petugasId, this.petugasId,
this.status, this.status,
this.alasanPenolakan, this.alasanPenolakan,
this.tanggalPenjadwalan,
this.tanggalPenyaluran, this.tanggalPenyaluran,
this.kategoriBantuanId, this.kategoriBantuanId,
this.tanggalPermintaan, this.tanggalPermintaan,
@ -49,9 +47,6 @@ class PenyaluranBantuanModel {
petugasId: json["petugas_id"], petugasId: json["petugas_id"],
status: json["status"], status: json["status"],
alasanPenolakan: json["alasan_penolakan"], alasanPenolakan: json["alasan_penolakan"],
tanggalPenjadwalan: json["tanggal_penjadwalan"] != null
? DateTime.parse(json["tanggal_penjadwalan"]).toUtc()
: null,
tanggalPenyaluran: json["tanggal_penyaluran"] != null tanggalPenyaluran: json["tanggal_penyaluran"] != null
? DateTime.parse(json["tanggal_penyaluran"]).toUtc() ? DateTime.parse(json["tanggal_penyaluran"]).toUtc()
: null, : null,
@ -77,7 +72,6 @@ class PenyaluranBantuanModel {
"petugas_id": petugasId, "petugas_id": petugasId,
"status": status, "status": status,
"alasan_penolakan": alasanPenolakan, "alasan_penolakan": alasanPenolakan,
"tanggal_penjadwalan": tanggalPenjadwalan?.toUtc().toIso8601String(),
"tanggal_penyaluran": tanggalPenyaluran?.toUtc().toIso8601String(), "tanggal_penyaluran": tanggalPenyaluran?.toUtc().toIso8601String(),
"kategori_bantuan_id": kategoriBantuanId, "kategori_bantuan_id": kategoriBantuanId,
"tanggal_permintaan": tanggalPermintaan?.toUtc().toIso8601String(), "tanggal_permintaan": tanggalPermintaan?.toUtc().toIso8601String(),

View File

@ -0,0 +1,63 @@
import 'dart:convert';
class SkemaBantuanModel {
final String? id;
final int? petugasVerifikasiId;
final String? nama;
final String? deskripsi;
final int? kuota;
final String? kriteria;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? stokBantuanId;
final String? kategoriBantuanId;
SkemaBantuanModel({
this.id,
this.petugasVerifikasiId,
this.nama,
this.deskripsi,
this.kuota,
this.kriteria,
this.createdAt,
this.updatedAt,
this.stokBantuanId,
this.kategoriBantuanId,
});
factory SkemaBantuanModel.fromRawJson(String str) =>
SkemaBantuanModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SkemaBantuanModel.fromJson(Map<String, dynamic> json) =>
SkemaBantuanModel(
id: json["id"],
petugasVerifikasiId: json["petugas_verifikasi_id"],
nama: json["nama"],
deskripsi: json["deskripsi"],
kuota: json["kuota"],
kriteria: json["kriteria"],
createdAt: json["created_at"] != null
? DateTime.parse(json["created_at"])
: null,
updatedAt: json["updated_at"] != null
? DateTime.parse(json["updated_at"])
: null,
stokBantuanId: json["stok_bantuan_id"],
kategoriBantuanId: json["kategori_bantuan_id"],
);
Map<String, dynamic> toJson() => {
"id": id,
"petugas_verifikasi_id": petugasVerifikasiId,
"nama": nama,
"deskripsi": deskripsi,
"kuota": kuota,
"kriteria": kriteria,
"created_at": createdAt?.toIso8601String(),
"updated_at": updatedAt?.toIso8601String(),
"stok_bantuan_id": stokBantuanId,
"kategori_bantuan_id": kategoriBantuanId,
};
}

View File

@ -200,7 +200,7 @@ class PermintaanPenjadwalanWidget extends StatelessWidget {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: jadwal.id, value: jadwal.id,
child: Text( child: Text(
'${jadwal.tanggalPenjadwalan?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), '${jadwal.createdAt?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'),
); );
}).toList(); }).toList();

View File

@ -260,16 +260,17 @@ class JadwalPenyaluranController extends GetxController {
} }
} }
Future<void> rejectJadwal(String jadwalId, String alasan) async { Future<void> rejectJadwal(String jadwalId, String alasanPenolakan) async {
isLoading.value = true; isLoading.value = true;
try { try {
await _supabaseService.rejectJadwal(jadwalId, alasan); await _supabaseService.rejectJadwal(jadwalId, alasanPenolakan);
await loadPermintaanPenjadwalanData(); await loadPermintaanPenjadwalanData();
await loadJadwalData();
Get.snackbar( Get.snackbar(
'Sukses', 'Sukses',
'Jadwal berhasil ditolak', 'Jadwal berhasil ditolak',
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green, backgroundColor: Colors.red,
colorText: Colors.white, colorText: Colors.white,
); );
} catch (e) { } catch (e) {
@ -317,8 +318,8 @@ class JadwalPenyaluranController extends GetxController {
try { try {
await loadJadwalData(); await loadJadwalData();
await loadPermintaanPenjadwalanData(); await loadPermintaanPenjadwalanData();
await loadLokasiPenyaluranData(); } catch (e) {
await loadKategoriBantuanData(); print('Error refreshing data: $e');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
@ -327,4 +328,63 @@ class JadwalPenyaluranController extends GetxController {
void changeCategory(int index) { void changeCategory(int index) {
selectedCategoryIndex.value = index; selectedCategoryIndex.value = index;
} }
// Fungsi untuk menambahkan penyaluran baru
Future<void> tambahPenyaluran({
required String nama,
required String deskripsi,
required String kategoriBantuanId,
required String lokasiPenyaluranId,
required int jumlahPenerima,
required DateTime? tanggalPenyaluran,
}) async {
isLoading.value = true;
try {
// Pastikan user sudah login dan memiliki ID
if (user?.id == null) {
throw Exception('User tidak terautentikasi');
}
// Buat objek penyaluran
final penyaluran = {
'nama': nama,
'deskripsi': deskripsi,
'kategori_bantuan_id': kategoriBantuanId,
'lokasi_penyaluran_id': lokasiPenyaluranId,
'petugas_id': user!.id,
'jumlah_penerima': jumlahPenerima,
'tanggal_penyaluran': tanggalPenyaluran?.toUtc().toIso8601String(),
'status': 'DIJADWALKAN', // Status awal adalah terjadwal
};
// Simpan ke database
await _supabaseService.tambahPenyaluran(penyaluran);
// Refresh data
await loadJadwalData();
// Kembali ke halaman sebelumnya
Get.back();
// Tampilkan notifikasi sukses
Get.snackbar(
'Sukses',
'Penyaluran berhasil ditambahkan',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);
} catch (e) {
print('Error menambahkan penyaluran: $e');
Get.snackbar(
'Error',
'Gagal menambahkan penyaluran: ${e.toString()}',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
} finally {
isLoading.value = false;
}
}
} }

View File

@ -5,6 +5,7 @@ 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/jadwal_section_widget.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/components/permintaan_penjadwalan_summary_widget.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/components/permintaan_penjadwalan_summary_widget.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/components/calendar_view_widget.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/components/calendar_view_widget.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/tambah_penyaluran_view.dart';
class PenyaluranView extends GetView<JadwalPenyaluranController> { class PenyaluranView extends GetView<JadwalPenyaluranController> {
const PenyaluranView({super.key}); const PenyaluranView({super.key});
@ -13,29 +14,36 @@ class PenyaluranView extends GetView<JadwalPenyaluranController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultTabController( return DefaultTabController(
length: 2, length: 2,
child: Column( child: Scaffold(
children: [ body: Column(
TabBar( children: [
tabs: const [ TabBar(
Tab(text: 'Daftar Jadwal'), tabs: const [
Tab(text: 'Kalender'), Tab(text: 'Daftar Jadwal'),
], Tab(text: 'Kalender'),
labelColor: AppTheme.primaryColor,
indicatorColor: AppTheme.primaryColor,
unselectedLabelColor: Colors.grey,
),
Expanded(
child: TabBarView(
children: [
// Tab 1: Daftar Jadwal
_buildJadwalListView(),
// Tab 2: Kalender
_buildCalendarView(),
], ],
labelColor: AppTheme.primaryColor,
indicatorColor: AppTheme.primaryColor,
unselectedLabelColor: Colors.grey,
), ),
), Expanded(
], child: TabBarView(
children: [
// Tab 1: Daftar Jadwal
_buildJadwalListView(),
// Tab 2: Kalender
_buildCalendarView(),
],
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => Get.to(() => const TambahPenyaluranView()),
backgroundColor: AppTheme.primaryColor,
child: const Icon(Icons.add, color: Colors.white),
),
), ),
); );
} }

View File

@ -430,7 +430,7 @@ class PermintaanPenjadwalanView extends GetView<JadwalPenyaluranController> {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: jadwal.id, value: jadwal.id,
child: Text( child: Text(
'${jadwal.tanggalPenjadwalan?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), '${jadwal.createdAt?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'),
); );
}).toList(); }).toList();

View File

@ -0,0 +1,355 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class TambahPenyaluranView extends GetView<JadwalPenyaluranController> {
const TambahPenyaluranView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tambah Penyaluran Bantuan'),
backgroundColor: AppTheme.primaryColor,
foregroundColor: Colors.white,
),
body: _buildTambahPenyaluranForm(context),
);
}
Widget _buildTambahPenyaluranForm(BuildContext context) {
final formKey = GlobalKey<FormState>();
final TextEditingController namaController = TextEditingController();
final TextEditingController deskripsiController = TextEditingController();
final TextEditingController jumlahPenerimaController =
TextEditingController();
final TextEditingController tanggalPenyaluranController =
TextEditingController();
final TextEditingController waktuPenyaluranController =
TextEditingController();
// Variabel untuk menyimpan nilai yang dipilih
final Rx<String?> selectedKategoriBantuanId = Rx<String?>(null);
final Rx<String?> selectedLokasiPenyaluranId = Rx<String?>(null);
// Tanggal dan waktu penyaluran
final Rx<DateTime?> selectedDate = Rx<DateTime?>(null);
final Rx<TimeOfDay?> selectedTime = Rx<TimeOfDay?>(null);
return Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Judul Form
Text(
'Formulir Penyaluran Bantuan',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 24),
// Nama Penyaluran
Text(
'Nama Penyaluran',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
TextFormField(
controller: namaController,
decoration: InputDecoration(
hintText: 'Masukkan nama penyaluran',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Nama penyaluran tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: 16),
// Kategori Bantuan
Text(
'Kategori Bantuan',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
Obx(() => DropdownButtonFormField<String>(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
hint: const Text('Pilih kategori bantuan'),
value: selectedKategoriBantuanId.value,
items: controller.kategoriBantuanCache.entries
.map((entry) => DropdownMenuItem<String>(
value: entry.key,
child: Text(entry.value.nama ?? 'Tidak ada nama'),
))
.toList(),
onChanged: (value) {
selectedKategoriBantuanId.value = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Kategori bantuan harus dipilih';
}
return null;
},
)),
const SizedBox(height: 16),
// Lokasi Penyaluran
Text(
'Lokasi Penyaluran',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
Obx(() => DropdownButtonFormField<String>(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
hint: const Text('Pilih lokasi penyaluran'),
value: selectedLokasiPenyaluranId.value,
items: controller.lokasiPenyaluranCache.entries
.map((entry) => DropdownMenuItem<String>(
value: entry.key,
child: Text(entry.value.nama),
))
.toList(),
onChanged: (value) {
selectedLokasiPenyaluranId.value = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Lokasi penyaluran harus dipilih';
}
return null;
},
)),
const SizedBox(height: 16),
// Jumlah Penerima
Text(
'Jumlah Penerima',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
TextFormField(
controller: jumlahPenerimaController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'Masukkan jumlah penerima',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Jumlah penerima tidak boleh kosong';
}
if (int.tryParse(value) == null) {
return 'Jumlah penerima harus berupa angka';
}
if (int.parse(value) <= 0) {
return 'Jumlah penerima harus lebih dari 0';
}
return null;
},
),
const SizedBox(height: 16),
// Tanggal Penyaluran
Text(
'Tanggal Penyaluran',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
TextFormField(
controller: tanggalPenyaluranController,
readOnly: true,
decoration: InputDecoration(
hintText: 'Pilih tanggal penyaluran',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
suffixIcon: const Icon(Icons.calendar_today),
),
onTap: () async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
if (pickedDate != null) {
selectedDate.value = pickedDate;
tanggalPenyaluranController.text =
DateFormat('dd MMMM yyyy', 'id_ID').format(pickedDate);
}
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Tanggal penyaluran harus dipilih';
}
return null;
},
),
const SizedBox(height: 16),
// Waktu Penyaluran
Text(
'Waktu Penyaluran',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
TextFormField(
controller: waktuPenyaluranController,
readOnly: true,
decoration: InputDecoration(
hintText: 'Pilih waktu penyaluran',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
suffixIcon: const Icon(Icons.access_time),
),
onTap: () async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (pickedTime != null) {
selectedTime.value = pickedTime;
waktuPenyaluranController.text =
'${pickedTime.hour.toString().padLeft(2, '0')}:${pickedTime.minute.toString().padLeft(2, '0')}';
}
},
validator: (value) {
if (value == null || value.isEmpty) {
return 'Waktu penyaluran harus dipilih';
}
return null;
},
),
const SizedBox(height: 16),
// Deskripsi
Text(
'Deskripsi',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
TextFormField(
controller: deskripsiController,
maxLines: 4,
decoration: InputDecoration(
hintText: 'Masukkan deskripsi penyaluran',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Deskripsi tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: 24),
// Tombol Submit
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
if (formKey.currentState!.validate()) {
// Gabungkan tanggal dan waktu
DateTime? tanggalWaktuPenyaluran;
if (selectedDate.value != null &&
selectedTime.value != null) {
tanggalWaktuPenyaluran = DateTime(
selectedDate.value!.year,
selectedDate.value!.month,
selectedDate.value!.day,
selectedTime.value!.hour,
selectedTime.value!.minute,
).toLocal();
}
// Panggil fungsi untuk menambahkan penyaluran
controller.tambahPenyaluran(
nama: namaController.text,
deskripsi: deskripsiController.text,
kategoriBantuanId: selectedKategoriBantuanId.value!,
lokasiPenyaluranId: selectedLokasiPenyaluranId.value!,
jumlahPenerima:
int.parse(jumlahPenerimaController.text),
tanggalPenyaluran: tanggalWaktuPenyaluran,
);
}
},
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text(
'Simpan Penyaluran',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
),
);
}
}

View File

@ -11,6 +11,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/pelaksanaan_penyal
import 'package:penyaluran_app/app/modules/petugas_desa/views/riwayat_penitipan_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/riwayat_penitipan_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_donatur_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_donatur_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_donatur_view.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_donatur_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/views/tambah_penyaluran_view.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/penerima_binding.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/bindings/penerima_binding.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/donatur_binding.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/bindings/donatur_binding.dart';
@ -87,5 +88,10 @@ class AppPages {
page: () => const DetailDonaturView(), page: () => const DetailDonaturView(),
binding: DonaturBinding(), binding: DonaturBinding(),
), ),
GetPage(
name: _Paths.tambahPenyaluran,
page: () => const TambahPenyaluranView(),
binding: PetugasDesaBinding(),
),
]; ];
} }

View File

@ -19,6 +19,7 @@ abstract class Routes {
static const riwayatPenitipan = _Paths.riwayatPenitipan; static const riwayatPenitipan = _Paths.riwayatPenitipan;
static const daftarDonatur = _Paths.daftarDonatur; static const daftarDonatur = _Paths.daftarDonatur;
static const detailDonatur = _Paths.detailDonatur; static const detailDonatur = _Paths.detailDonatur;
static const tambahPenyaluran = _Paths.tambahPenyaluran;
} }
abstract class _Paths { abstract class _Paths {
@ -40,4 +41,5 @@ abstract class _Paths {
static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan'; static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan';
static const daftarDonatur = '/daftar-donatur'; static const daftarDonatur = '/daftar-donatur';
static const detailDonatur = '/daftar-donatur/detail'; static const detailDonatur = '/daftar-donatur/detail';
static const tambahPenyaluran = '/tambah-penyaluran';
} }

View File

@ -1122,4 +1122,14 @@ class SupabaseService extends GetxService {
// Tipe data lainnya // Tipe data lainnya
print('$prefix Data: $data (${data.runtimeType})'); print('$prefix Data: $data (${data.runtimeType})');
} }
// Fungsi untuk menambahkan penyaluran baru
Future<void> tambahPenyaluran(Map<String, dynamic> penyaluran) async {
try {
await client.from('penyaluran_bantuan').insert(penyaluran);
} catch (e) {
print('Error menambahkan penyaluran: $e');
throw e.toString();
}
}
} }