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:
60
lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart
Normal file
60
lib/app/data/models/pengajuan_kelayakan_bantuan_model.dart
Normal 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,
|
||||
};
|
||||
}
|
@ -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(),
|
||||
|
63
lib/app/data/models/skema_bantuan_model.dart
Normal file
63
lib/app/data/models/skema_bantuan_model.dart
Normal 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,
|
||||
};
|
||||
}
|
@ -200,7 +200,7 @@ class PermintaanPenjadwalanWidget extends StatelessWidget {
|
||||
return DropdownMenuItem<String>(
|
||||
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();
|
||||
|
||||
|
@ -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;
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<JadwalPenyaluranController> {
|
||||
const PenyaluranView({super.key});
|
||||
@ -13,7 +14,8 @@ class PenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Column(
|
||||
child: Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
TabBar(
|
||||
tabs: const [
|
||||
@ -37,6 +39,12 @@ class PenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => Get.to(() => const TambahPenyaluranView()),
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
child: const Icon(Icons.add, color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -430,7 +430,7 @@ class PermintaanPenjadwalanView extends GetView<JadwalPenyaluranController> {
|
||||
return DropdownMenuItem<String>(
|
||||
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();
|
||||
|
||||
|
355
lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart
Normal file
355
lib/app/modules/petugas_desa/views/tambah_penyaluran_view.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -1122,4 +1122,14 @@ class SupabaseService extends GetxService {
|
||||
// Tipe data lainnya
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user