From ecc1ccac59ce8ffb20b64a6dee374789172681f2 Mon Sep 17 00:00:00 2001 From: Khafidh Fuadi Date: Fri, 14 Mar 2025 21:29:32 +0700 Subject: [PATCH] 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 --- .../pengajuan_kelayakan_bantuan_model.dart | 60 +++ .../data/models/penyaluran_bantuan_model.dart | 6 - lib/app/data/models/skema_bantuan_model.dart | 63 ++++ .../permintaan_penjadwalan_widget.dart | 2 +- .../jadwal_penyaluran_controller.dart | 70 +++- .../petugas_desa/views/penyaluran_view.dart | 50 +-- .../views/permintaan_penjadwalan_view.dart | 2 +- .../views/tambah_penyaluran_view.dart | 355 ++++++++++++++++++ lib/app/routes/app_pages.dart | 6 + lib/app/routes/app_routes.dart | 2 + lib/app/services/supabase_service.dart | 10 + 11 files changed, 592 insertions(+), 34 deletions(-) create mode 100644 lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart create mode 100644 lib/app/data/models/skema_bantuan_model.dart create mode 100644 lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart diff --git a/lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart b/lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart new file mode 100644 index 0000000..ebabf49 --- /dev/null +++ b/lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart @@ -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 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 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, + }; +} diff --git a/lib/app/data/models/penyaluran_bantuan_model.dart b/lib/app/data/models/penyaluran_bantuan_model.dart index 857e0c2..dc1d07a 100644 --- a/lib/app/data/models/penyaluran_bantuan_model.dart +++ b/lib/app/data/models/penyaluran_bantuan_model.dart @@ -8,7 +8,6 @@ class PenyaluranBantuanModel { final String? petugasId; final String? status; final String? alasanPenolakan; - final DateTime? tanggalPenjadwalan; final DateTime? tanggalPenyaluran; final String? kategoriBantuanId; final DateTime? tanggalPermintaan; @@ -25,7 +24,6 @@ class PenyaluranBantuanModel { this.petugasId, this.status, this.alasanPenolakan, - this.tanggalPenjadwalan, this.tanggalPenyaluran, this.kategoriBantuanId, this.tanggalPermintaan, @@ -49,9 +47,6 @@ class PenyaluranBantuanModel { petugasId: json["petugas_id"], status: json["status"], alasanPenolakan: json["alasan_penolakan"], - tanggalPenjadwalan: json["tanggal_penjadwalan"] != null - ? DateTime.parse(json["tanggal_penjadwalan"]).toUtc() - : null, tanggalPenyaluran: json["tanggal_penyaluran"] != null ? DateTime.parse(json["tanggal_penyaluran"]).toUtc() : null, @@ -77,7 +72,6 @@ class PenyaluranBantuanModel { "petugas_id": petugasId, "status": status, "alasan_penolakan": alasanPenolakan, - "tanggal_penjadwalan": tanggalPenjadwalan?.toUtc().toIso8601String(), "tanggal_penyaluran": tanggalPenyaluran?.toUtc().toIso8601String(), "kategori_bantuan_id": kategoriBantuanId, "tanggal_permintaan": tanggalPermintaan?.toUtc().toIso8601String(), diff --git a/lib/app/data/models/skema_bantuan_model.dart b/lib/app/data/models/skema_bantuan_model.dart new file mode 100644 index 0000000..0377efc --- /dev/null +++ b/lib/app/data/models/skema_bantuan_model.dart @@ -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 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 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, + }; +} diff --git a/lib/app/modules/petugas_desa/components/permintaan_penjadwalan_widget.dart b/lib/app/modules/petugas_desa/components/permintaan_penjadwalan_widget.dart index a50a395..90e7e94 100644 --- a/lib/app/modules/petugas_desa/components/permintaan_penjadwalan_widget.dart +++ b/lib/app/modules/petugas_desa/components/permintaan_penjadwalan_widget.dart @@ -200,7 +200,7 @@ class PermintaanPenjadwalanWidget extends StatelessWidget { return DropdownMenuItem( value: jadwal.id, child: Text( - '${jadwal.tanggalPenjadwalan?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), + '${jadwal.createdAt?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), ); }).toList(); diff --git a/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart b/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart index 312d031..185028f 100644 --- a/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart @@ -260,16 +260,17 @@ class JadwalPenyaluranController extends GetxController { } } - Future rejectJadwal(String jadwalId, String alasan) async { + Future rejectJadwal(String jadwalId, String alasanPenolakan) async { isLoading.value = true; try { - await _supabaseService.rejectJadwal(jadwalId, alasan); + await _supabaseService.rejectJadwal(jadwalId, alasanPenolakan); await loadPermintaanPenjadwalanData(); + await loadJadwalData(); Get.snackbar( 'Sukses', 'Jadwal berhasil ditolak', snackPosition: SnackPosition.TOP, - backgroundColor: Colors.green, + backgroundColor: Colors.red, colorText: Colors.white, ); } catch (e) { @@ -317,8 +318,8 @@ class JadwalPenyaluranController extends GetxController { try { await loadJadwalData(); await loadPermintaanPenjadwalanData(); - await loadLokasiPenyaluranData(); - await loadKategoriBantuanData(); + } catch (e) { + print('Error refreshing data: $e'); } finally { isLoading.value = false; } @@ -327,4 +328,63 @@ class JadwalPenyaluranController extends GetxController { void changeCategory(int index) { selectedCategoryIndex.value = index; } + + // Fungsi untuk menambahkan penyaluran baru + Future 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; + } + } } diff --git a/lib/app/modules/petugas_desa/views/penyaluran_view.dart b/lib/app/modules/petugas_desa/views/penyaluran_view.dart index 01414d3..9658818 100644 --- a/lib/app/modules/petugas_desa/views/penyaluran_view.dart +++ b/lib/app/modules/petugas_desa/views/penyaluran_view.dart @@ -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/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/views/tambah_penyaluran_view.dart'; class PenyaluranView extends GetView { const PenyaluranView({super.key}); @@ -13,29 +14,36 @@ class PenyaluranView extends GetView { Widget build(BuildContext context) { return DefaultTabController( length: 2, - child: Column( - children: [ - TabBar( - tabs: const [ - 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(), + child: Scaffold( + body: Column( + children: [ + TabBar( + tabs: const [ + 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(), + ], + ), + ), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () => Get.to(() => const TambahPenyaluranView()), + backgroundColor: AppTheme.primaryColor, + child: const Icon(Icons.add, color: Colors.white), + ), ), ); } diff --git a/lib/app/modules/petugas_desa/views/permintaan_penjadwalan_view.dart b/lib/app/modules/petugas_desa/views/permintaan_penjadwalan_view.dart index cdeb7f9..9f4668c 100644 --- a/lib/app/modules/petugas_desa/views/permintaan_penjadwalan_view.dart +++ b/lib/app/modules/petugas_desa/views/permintaan_penjadwalan_view.dart @@ -430,7 +430,7 @@ class PermintaanPenjadwalanView extends GetView { return DropdownMenuItem( value: jadwal.id, child: Text( - '${jadwal.tanggalPenjadwalan?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), + '${jadwal.createdAt?.toString().substring(0, 10) ?? ''} - ${jadwal.lokasiPenyaluranId ?? ''} (${jadwal.nama ?? ''})'), ); }).toList(); diff --git a/lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart b/lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart new file mode 100644 index 0000000..0d58ebe --- /dev/null +++ b/lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart @@ -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 { + 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(); + 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 selectedKategoriBantuanId = Rx(null); + final Rx selectedLokasiPenyaluranId = Rx(null); + + // Tanggal dan waktu penyaluran + final Rx selectedDate = Rx(null); + final Rx selectedTime = Rx(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( + 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( + 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( + 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( + 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, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 026c911..f41eba7 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -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/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/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/donatur_binding.dart'; @@ -87,5 +88,10 @@ class AppPages { page: () => const DetailDonaturView(), binding: DonaturBinding(), ), + GetPage( + name: _Paths.tambahPenyaluran, + page: () => const TambahPenyaluranView(), + binding: PetugasDesaBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 5b45108..f0f6db5 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -19,6 +19,7 @@ abstract class Routes { static const riwayatPenitipan = _Paths.riwayatPenitipan; static const daftarDonatur = _Paths.daftarDonatur; static const detailDonatur = _Paths.detailDonatur; + static const tambahPenyaluran = _Paths.tambahPenyaluran; } abstract class _Paths { @@ -40,4 +41,5 @@ abstract class _Paths { static const riwayatPenitipan = '/petugas-desa/riwayat-penitipan'; static const daftarDonatur = '/daftar-donatur'; static const detailDonatur = '/daftar-donatur/detail'; + static const tambahPenyaluran = '/tambah-penyaluran'; } diff --git a/lib/app/services/supabase_service.dart b/lib/app/services/supabase_service.dart index e099009..4c0d98a 100644 --- a/lib/app/services/supabase_service.dart +++ b/lib/app/services/supabase_service.dart @@ -1122,4 +1122,14 @@ class SupabaseService extends GetxService { // Tipe data lainnya print('$prefix Data: $data (${data.runtimeType})'); } + + // Fungsi untuk menambahkan penyaluran baru + Future tambahPenyaluran(Map penyaluran) async { + try { + await client.from('penyaluran_bantuan').insert(penyaluran); + } catch (e) { + print('Error menambahkan penyaluran: $e'); + throw e.toString(); + } + } }