Refactor stok bantuan model dan kontroller untuk mendukung kategori bantuan
- Ubah model StokBantuanModel dari 'jenis bantuan' menjadi 'kategori bantuan' - Perbarui metode loadJenisBantuanData() menjadi loadKategoriBantuanData() - Tambahkan metode baru untuk menghitung stok hampir habis dan segera kadaluarsa - Update tampilan dan form untuk menggunakan kategori bantuan - Perbaiki logika navigasi dan binding pada berbagai modul terkait
This commit is contained in:
@ -116,9 +116,14 @@ class AuthController extends GetxController {
|
||||
final targetRoute = _getTargetRouteForRole(role);
|
||||
print('Target rute: $targetRoute');
|
||||
|
||||
if (currentRoute != targetRoute) {
|
||||
// Jika berada di splash atau login, navigasi ke dashboard
|
||||
if (currentRoute == Routes.splash || currentRoute == Routes.login) {
|
||||
print('Navigasi ke rute target berdasarkan role');
|
||||
navigateBasedOnRole(role);
|
||||
} else if (currentRoute != targetRoute) {
|
||||
// Jika berada di rute lain yang tidak sesuai dengan role, navigasi ke dashboard
|
||||
print('Berada di rute yang tidak sesuai, navigasi ke rute target');
|
||||
navigateBasedOnRole(role);
|
||||
} else {
|
||||
print('Sudah berada di rute yang sesuai, tidak perlu navigasi');
|
||||
}
|
||||
@ -334,7 +339,7 @@ class AuthController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// Mendapatkan rute target berdasarkan peran
|
||||
// Mendapatkan rute target berdasarkan role
|
||||
String _getTargetRouteForRole(String role) {
|
||||
switch (role) {
|
||||
case 'WARGA':
|
||||
|
@ -17,45 +17,54 @@ class PetugasDesaBinding extends Bindings {
|
||||
Get.put(AuthController(), permanent: true);
|
||||
}
|
||||
|
||||
// Main controller
|
||||
Get.lazyPut<PetugasDesaController>(
|
||||
() => PetugasDesaController(),
|
||||
fenix: true,
|
||||
);
|
||||
// Main controller - gunakan put dengan permanent untuk controller utama
|
||||
if (!Get.isRegistered<PetugasDesaController>()) {
|
||||
Get.put(PetugasDesaController(), permanent: true);
|
||||
} else {
|
||||
// Jika sudah terdaftar, gunakan find untuk mendapatkan instance yang ada
|
||||
Get.find<PetugasDesaController>();
|
||||
}
|
||||
|
||||
// Dashboard controller
|
||||
Get.lazyPut<PetugasDesaDashboardController>(
|
||||
() => PetugasDesaDashboardController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Jadwal penyaluran controller
|
||||
Get.lazyPut<JadwalPenyaluranController>(
|
||||
() => JadwalPenyaluranController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Stok bantuan controller
|
||||
Get.lazyPut<StokBantuanController>(
|
||||
() => StokBantuanController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Penitipan bantuan controller
|
||||
Get.lazyPut<PenitipanBantuanController>(
|
||||
() => PenitipanBantuanController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Pengaduan controller
|
||||
Get.lazyPut<PengaduanController>(
|
||||
() => PengaduanController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Penerima bantuan controller
|
||||
Get.lazyPut<PenerimaBantuanController>(
|
||||
() => PenerimaBantuanController(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// Laporan controller
|
||||
Get.lazyPut<LaporanController>(
|
||||
() => LaporanController(),
|
||||
fenix: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ class JadwalSectionWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Jenis Bantuan: ${jadwalData['jenis_bantuan'] ?? ''}',
|
||||
'Kategori Bantuan: ${jadwalData['kategori_bantuan'] ?? ''}',
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
|
@ -181,7 +181,7 @@ class PermintaanPenjadwalanSummaryWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Jenis: ${permintaanData['jenis_bantuan'] ?? ''}',
|
||||
'Kategori: ${permintaanData['kategori_bantuan'] ?? ''}',
|
||||
style: textTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
@ -17,8 +17,8 @@ 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 =
|
||||
// Data untuk kategori bantuan
|
||||
final RxList<Map<String, dynamic>> daftarKategoriBantuan =
|
||||
<Map<String, dynamic>>[].obs;
|
||||
|
||||
// Controller untuk pencarian
|
||||
@ -31,7 +31,7 @@ class StokBantuanController extends GetxController {
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadStokBantuanData();
|
||||
loadJenisBantuanData();
|
||||
loadKategoriBantuanData();
|
||||
|
||||
// Listener untuk pencarian
|
||||
searchController.addListener(() {
|
||||
@ -74,14 +74,14 @@ class StokBantuanController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadJenisBantuanData() async {
|
||||
Future<void> loadKategoriBantuanData() async {
|
||||
try {
|
||||
final jenisBantuanData = await _supabaseService.getJenisBantuan();
|
||||
if (jenisBantuanData != null) {
|
||||
daftarJenisBantuan.value = jenisBantuanData;
|
||||
final kategoriBantuanData = await _supabaseService.getKategoriBantuan();
|
||||
if (kategoriBantuanData != null) {
|
||||
daftarKategoriBantuan.value = kategoriBantuanData;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading jenis bantuan data: $e');
|
||||
print('Error loading kategori bantuan data: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ class StokBantuanController extends GetxController {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
await loadStokBantuanData();
|
||||
await loadJenisBantuanData();
|
||||
await loadKategoriBantuanData();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
@ -194,4 +194,18 @@ class StokBantuanController extends GetxController {
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk mendapatkan jumlah stok yang hampir habis (stok <= 10)
|
||||
int getStokHampirHabis() {
|
||||
return daftarStokBantuan.where((stok) => (stok.jumlah ?? 0) <= 10).length;
|
||||
}
|
||||
|
||||
// Metode untuk mendapatkan jumlah stok yang segera kadaluarsa (dalam 30 hari)
|
||||
int getStokSegeraKadaluarsa() {
|
||||
return daftarStokBantuan
|
||||
.where((stok) =>
|
||||
stok.tanggalKadaluarsa != null &&
|
||||
stok.tanggalKadaluarsa!.difference(DateTime.now()).inDays <= 30)
|
||||
.length;
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ class PelaksanaanPenyaluranView extends GetView<PetugasDesaController> {
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoItem(context,
|
||||
icon: Icons.category,
|
||||
label: 'Jenis Bantuan',
|
||||
value: jadwal['jenis_bantuan'] ?? '-'),
|
||||
label: 'Kategori Bantuan',
|
||||
value: jadwal['kategori_bantuan'] ?? '-'),
|
||||
const SizedBox(height: 8),
|
||||
_buildInfoItem(context,
|
||||
icon: Icons.calendar_today,
|
||||
|
@ -169,7 +169,7 @@ class PenitipanView extends GetView<PetugasDesaController> {
|
||||
{
|
||||
'id': '1',
|
||||
'donatur': 'PT Sejahtera Abadi',
|
||||
'jenis_bantuan': 'Sembako',
|
||||
'kategori_bantuan': 'Sembako',
|
||||
'jumlah': '500 kg',
|
||||
'tanggal_pengajuan': '15 April 2023',
|
||||
'status': 'Menunggu',
|
||||
@ -177,7 +177,7 @@ class PenitipanView extends GetView<PetugasDesaController> {
|
||||
{
|
||||
'id': '2',
|
||||
'donatur': 'Yayasan Peduli Sesama',
|
||||
'jenis_bantuan': 'Pakaian',
|
||||
'kategori_bantuan': 'Pakaian',
|
||||
'jumlah': '200 pcs',
|
||||
'tanggal_pengajuan': '14 April 2023',
|
||||
'status': 'Terverifikasi',
|
||||
@ -185,7 +185,7 @@ class PenitipanView extends GetView<PetugasDesaController> {
|
||||
{
|
||||
'id': '3',
|
||||
'donatur': 'Bank BRI',
|
||||
'jenis_bantuan': 'Beras',
|
||||
'kategori_bantuan': 'Beras',
|
||||
'jumlah': '300 kg',
|
||||
'tanggal_pengajuan': '13 April 2023',
|
||||
'status': 'Terverifikasi',
|
||||
@ -193,7 +193,7 @@ class PenitipanView extends GetView<PetugasDesaController> {
|
||||
{
|
||||
'id': '4',
|
||||
'donatur': 'Komunitas Peduli',
|
||||
'jenis_bantuan': 'Alat Tulis',
|
||||
'kategori_bantuan': 'Alat Tulis',
|
||||
'jumlah': '100 set',
|
||||
'tanggal_pengajuan': '12 April 2023',
|
||||
'status': 'Ditolak',
|
||||
@ -304,8 +304,8 @@ class PenitipanView extends GetView<PetugasDesaController> {
|
||||
child: _buildItemDetail(
|
||||
context,
|
||||
icon: Icons.category,
|
||||
label: 'Jenis Bantuan',
|
||||
value: item['jenis_bantuan'] ?? '',
|
||||
label: 'Kategori Bantuan',
|
||||
value: item['kategori_bantuan'] ?? '',
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
|
@ -23,7 +23,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
_showAddStokDialog(context);
|
||||
},
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
child: const Icon(Icons.add),
|
||||
child: const Icon(Icons.add, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -79,7 +79,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.inventory_2_outlined,
|
||||
title: 'Total Stok',
|
||||
title: 'Stok Tersedia',
|
||||
value: DateFormatter.formatNumber(controller.totalStok.value),
|
||||
),
|
||||
),
|
||||
@ -87,7 +87,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.input,
|
||||
title: 'Masuk',
|
||||
title: 'Total Masuk',
|
||||
value: DateFormatter.formatNumber(controller.stokMasuk.value),
|
||||
),
|
||||
),
|
||||
@ -95,13 +95,48 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.output,
|
||||
title: 'Keluar',
|
||||
title: 'Total Keluar',
|
||||
value:
|
||||
DateFormatter.formatNumber(controller.stokKeluar.value),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.warning_amber_rounded,
|
||||
title: 'Hampir Habis',
|
||||
value: '${controller.getStokHampirHabis()}',
|
||||
valueColor: controller.getStokHampirHabis() > 0
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.access_time,
|
||||
title: 'Segera Kadaluarsa',
|
||||
value: '${controller.getStokSegeraKadaluarsa()}',
|
||||
valueColor: controller.getStokSegeraKadaluarsa() > 0
|
||||
? Colors.amber
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildSummaryItem(
|
||||
context,
|
||||
icon: Icons.category_outlined,
|
||||
title: 'Kategori Bantuan',
|
||||
value: '${controller.daftarKategoriBantuan.length}',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -112,6 +147,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String value,
|
||||
Color? valueColor,
|
||||
}) {
|
||||
return Column(
|
||||
children: [
|
||||
@ -132,7 +168,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
value,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
color: valueColor ?? Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@ -276,9 +312,10 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
item.jenisBantuan != null
|
||||
? (item.jenisBantuan!['nama'] ?? 'Tidak Ada Jenis')
|
||||
: 'Tidak Ada Jenis',
|
||||
item.kategoriBantuan != null
|
||||
? (item.kategoriBantuan!['nama'] ??
|
||||
'Tidak Ada Kategori')
|
||||
: 'Tidak Ada Kategori',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: AppTheme.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -314,7 +351,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
context,
|
||||
icon: Icons.calendar_today,
|
||||
label: 'Tanggal Masuk',
|
||||
value: DateFormatter.formatDate(item.tanggalMasuk),
|
||||
value: DateFormatter.formatDateTime(item.tanggalMasuk),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -335,7 +372,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
context,
|
||||
icon: Icons.access_time,
|
||||
label: 'Terakhir Diperbarui',
|
||||
value: DateFormatter.formatDate(item.updatedAt),
|
||||
value: DateFormatter.formatDateTime(item.updatedAt),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -419,185 +456,192 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
||||
final satuanController = TextEditingController();
|
||||
final deskripsiController = TextEditingController();
|
||||
String? selectedJenisBantuanId;
|
||||
DateTime? tanggalMasuk = DateTime.now();
|
||||
|
||||
// Gunakan StatefulBuilder untuk memperbarui state dialog
|
||||
DateTime tanggalMasuk = DateTime.now();
|
||||
DateTime? tanggalKadaluarsa;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Tambah Stok Bantuan'),
|
||||
content: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: namaController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Nama Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama bantuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
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(
|
||||
flex: 2,
|
||||
child: TextFormField(
|
||||
controller: jumlahController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Jumlah',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Jumlah tidak boleh kosong';
|
||||
}
|
||||
if (double.tryParse(value) == null) {
|
||||
return 'Jumlah harus berupa angka';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextFormField(
|
||||
controller: satuanController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Satuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Satuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: deskripsiController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Deskripsi',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalMasuk ?? DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
tanggalMasuk = picked;
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setState) => AlertDialog(
|
||||
title: const Text('Tambah Stok Bantuan'),
|
||||
content: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: namaController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Masuk',
|
||||
labelText: 'Nama Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalMasuk),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama bantuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalKadaluarsa ??
|
||||
DateTime.now().add(const Duration(days: 365)),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
tanggalKadaluarsa = picked;
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
const SizedBox(height: 16),
|
||||
DropdownButtonFormField<String>(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Kadaluarsa',
|
||||
labelText: 'Kategori Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalKadaluarsa),
|
||||
value: selectedJenisBantuanId,
|
||||
hint: const Text('Pilih Kategori Bantuan'),
|
||||
items: controller.daftarKategoriBantuan
|
||||
.map((kategori) => DropdownMenuItem<String>(
|
||||
value: kategori['id'],
|
||||
child: Text(kategori['nama'] ?? ''),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
selectedJenisBantuanId = value;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Kategori bantuan harus dipilih';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: TextFormField(
|
||||
controller: jumlahController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Jumlah',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Jumlah tidak boleh kosong';
|
||||
}
|
||||
if (double.tryParse(value) == null) {
|
||||
return 'Jumlah harus berupa angka';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextFormField(
|
||||
controller: satuanController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Satuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Satuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: deskripsiController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Deskripsi',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalMasuk,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
tanggalMasuk = picked;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Masuk',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDateTime(tanggalMasuk),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalKadaluarsa ??
|
||||
DateTime.now().add(const Duration(days: 365)),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
tanggalKadaluarsa = picked;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Kadaluarsa',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalKadaluarsa),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Batal'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (formKey.currentState!.validate()) {
|
||||
final stok = StokBantuanModel(
|
||||
nama: namaController.text,
|
||||
jumlah: double.parse(jumlahController.text),
|
||||
satuan: satuanController.text,
|
||||
deskripsi: deskripsiController.text,
|
||||
kategoriBantuanId: selectedJenisBantuanId,
|
||||
tanggalMasuk: tanggalMasuk,
|
||||
tanggalKadaluarsa: tanggalKadaluarsa,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
controller.addStok(stok);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text('Simpan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Batal'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (formKey.currentState!.validate()) {
|
||||
final stok = StokBantuanModel(
|
||||
nama: namaController.text,
|
||||
jumlah: double.parse(jumlahController.text),
|
||||
satuan: satuanController.text,
|
||||
deskripsi: deskripsiController.text,
|
||||
jenisBantuanId: selectedJenisBantuanId,
|
||||
tanggalMasuk: tanggalMasuk,
|
||||
tanggalKadaluarsa: tanggalKadaluarsa,
|
||||
status: 'TERSEDIA',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
controller.addStok(stok);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text('Simpan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -609,187 +653,199 @@ 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;
|
||||
String? selectedJenisBantuanId = stok.kategoriBantuanId;
|
||||
|
||||
// Gunakan StatefulBuilder untuk memperbarui state dialog
|
||||
DateTime? tanggalMasuk = stok.tanggalMasuk;
|
||||
DateTime? tanggalKadaluarsa = stok.tanggalKadaluarsa;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Edit Stok Bantuan'),
|
||||
content: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: namaController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Nama Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama bantuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
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(
|
||||
flex: 2,
|
||||
child: TextFormField(
|
||||
controller: jumlahController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Jumlah',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Jumlah tidak boleh kosong';
|
||||
}
|
||||
if (double.tryParse(value) == null) {
|
||||
return 'Jumlah harus berupa angka';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextFormField(
|
||||
controller: satuanController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Satuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Satuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: deskripsiController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Deskripsi',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalMasuk ?? DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
tanggalMasuk = picked;
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setState) => AlertDialog(
|
||||
title: const Text('Edit Stok Bantuan'),
|
||||
content: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: namaController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Masuk',
|
||||
labelText: 'Nama Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalMasuk),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Nama bantuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalKadaluarsa ??
|
||||
DateTime.now().add(const Duration(days: 365)),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
tanggalKadaluarsa = picked;
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
const SizedBox(height: 16),
|
||||
DropdownButtonFormField<String>(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Kadaluarsa',
|
||||
labelText: 'Kategori Bantuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalKadaluarsa),
|
||||
value: selectedJenisBantuanId,
|
||||
hint: const Text('Pilih Kategori Bantuan'),
|
||||
isExpanded: true,
|
||||
items: controller.daftarKategoriBantuan
|
||||
.map((kategori) => DropdownMenuItem<String>(
|
||||
value: kategori['id'],
|
||||
child: Text(
|
||||
kategori['nama'] ?? '',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
selectedJenisBantuanId = value;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Kategori bantuan harus dipilih';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: TextFormField(
|
||||
controller: jumlahController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Jumlah',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Jumlah tidak boleh kosong';
|
||||
}
|
||||
if (double.tryParse(value) == null) {
|
||||
return 'Jumlah harus berupa angka';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextFormField(
|
||||
controller: satuanController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Satuan',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Satuan tidak boleh kosong';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: deskripsiController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Deskripsi',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalMasuk ?? DateTime.now(),
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
tanggalMasuk = picked;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Masuk',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDateTime(tanggalMasuk),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: tanggalKadaluarsa ??
|
||||
DateTime.now().add(const Duration(days: 365)),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
tanggalKadaluarsa = picked;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Tanggal Kadaluarsa',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
DateFormatter.formatDate(tanggalKadaluarsa),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Batal'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (formKey.currentState!.validate()) {
|
||||
final updatedStok = StokBantuanModel(
|
||||
id: stok.id,
|
||||
nama: namaController.text,
|
||||
jumlah: double.parse(jumlahController.text),
|
||||
satuan: satuanController.text,
|
||||
deskripsi: deskripsiController.text,
|
||||
kategoriBantuanId: selectedJenisBantuanId,
|
||||
tanggalMasuk: tanggalMasuk,
|
||||
tanggalKadaluarsa: tanggalKadaluarsa,
|
||||
createdAt: stok.createdAt,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
controller.updateStok(updatedStok);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text('Simpan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Batal'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (formKey.currentState!.validate()) {
|
||||
final updatedStok = StokBantuanModel(
|
||||
id: stok.id,
|
||||
nama: namaController.text,
|
||||
jumlah: double.parse(jumlahController.text),
|
||||
satuan: satuanController.text,
|
||||
deskripsi: deskripsiController.text,
|
||||
jenisBantuanId: selectedJenisBantuanId,
|
||||
tanggalMasuk: tanggalMasuk,
|
||||
tanggalKadaluarsa: tanggalKadaluarsa,
|
||||
status: stok.status,
|
||||
createdAt: stok.createdAt,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
controller.updateStok(updatedStok);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text('Simpan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
|
||||
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||
|
||||
@ -14,12 +15,19 @@ class _SplashViewState extends State<SplashView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_navigateToLogin();
|
||||
_checkAuthAndNavigate();
|
||||
}
|
||||
|
||||
_navigateToLogin() async {
|
||||
_checkAuthAndNavigate() async {
|
||||
// Tunggu 2 detik untuk menampilkan splash screen
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
Get.offAllNamed(Routes.login);
|
||||
|
||||
// Dapatkan AuthController dan periksa status autentikasi
|
||||
final AuthController authController = Get.find<AuthController>();
|
||||
await authController.checkAuthStatus();
|
||||
|
||||
// Navigasi akan ditangani oleh AuthController
|
||||
// Tidak perlu navigasi manual di sini
|
||||
}
|
||||
|
||||
@override
|
||||
|
Reference in New Issue
Block a user