From 76b167c65c648642b31dcc0a87d3e34dff9fdb86 Mon Sep 17 00:00:00 2001 From: Khafidh Fuadi Date: Sun, 16 Mar 2025 19:37:37 +0700 Subject: [PATCH] Perbarui judul aplikasi dari 'Penyaluran App' menjadi 'Penerimaan App'. Tambahkan properti baru pada model PenerimaPenyaluranModel untuk mendukung informasi tambahan terkait penyaluran. Modifikasi tampilan di WargaDashboardView dan WargaPengaduanView untuk meningkatkan pengalaman pengguna. Hapus WargaPenyaluranView yang tidak digunakan dan perbarui rute aplikasi untuk mencerminkan perubahan ini. --- .../arm64-v8a/configure_fingerprint.bin | 24 +- .../armeabi-v7a/configure_fingerprint.bin | 24 +- .../626b5o2n/x86/configure_fingerprint.bin | 24 +- .../626b5o2n/x86_64/configure_fingerprint.bin | 24 +- .../models/penerima_penyaluran_model.dart | 36 + .../views/detail_penyaluran_page.dart | 2 +- .../warga_dashboard_controller.dart | 321 ++++++-- .../warga/views/warga_dashboard_view.dart | 698 +++++++----------- .../views/warga_detail_penerimaan_view.dart | 632 ++++++++++++++++ .../warga/views/warga_penerimaan_view.dart | 112 +++ .../warga/views/warga_pengaduan_view.dart | 37 +- .../warga/views/warga_penyaluran_view.dart | 187 ----- lib/app/modules/warga/views/warga_view.dart | 10 +- lib/app/routes/app_pages.dart | 14 +- lib/app/routes/app_routes.dart | 6 +- lib/app/widgets/bantuan_card.dart | 280 +++++++ lib/app/widgets/section_header.dart | 51 ++ lib/app/widgets/status_badge.dart | 79 ++ lib/main.dart | 2 +- 19 files changed, 1806 insertions(+), 757 deletions(-) create mode 100644 lib/app/modules/warga/views/warga_detail_penerimaan_view.dart create mode 100644 lib/app/modules/warga/views/warga_penerimaan_view.dart delete mode 100644 lib/app/modules/warga/views/warga_penyaluran_view.dart create mode 100644 lib/app/widgets/bantuan_card.dart create mode 100644 lib/app/widgets/section_header.dart create mode 100644 lib/app/widgets/status_badge.dart diff --git a/android/app/.cxx/Debug/626b5o2n/arm64-v8a/configure_fingerprint.bin b/android/app/.cxx/Debug/626b5o2n/arm64-v8a/configure_fingerprint.bin index 42a25c0..6f10174 100644 --- a/android/app/.cxx/Debug/626b5o2n/arm64-v8a/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/626b5o2n/arm64-v8a/configure_fingerprint.bin @@ -2,27 +2,27 @@ C/C++ Structured LogO M KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2 +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2  -}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt  2  2~ +}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt  2  2~ | -zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json  2 2 +zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json  2 2  -D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json  2 2p +D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json  2 2p n -lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja  2 2t +lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja  2 2t r -pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt  2y +pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt  2y w -uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt  2 K 2z +uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt  2 K 2z x -vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json  2 ~ +vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json  2 ~ | -zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin  2 +zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin  2   -D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt  2  2w +D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt  2  2w u -sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json  2  ( 2| +sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json  2  ( 2| z -xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\symbol_folder_index.txt  2  o 2 \ No newline at end of file +xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\symbol_folder_index.txt  2  o 2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/626b5o2n/armeabi-v7a/configure_fingerprint.bin b/android/app/.cxx/Debug/626b5o2n/armeabi-v7a/configure_fingerprint.bin index 4a8da5a..4919f33 100644 --- a/android/app/.cxx/Debug/626b5o2n/armeabi-v7a/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/626b5o2n/armeabi-v7a/configure_fingerprint.bin @@ -2,27 +2,27 @@ C/C++ Structured LogO M KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2 +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2  -D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt  2  2 +D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt  2  2 ~ -|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json  2 2 +|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json  2 2  -D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json  2 2r +D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json  2 2r p -nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja  2 2v +nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja  2 2v t -rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt  2{ +rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt  2{ y -wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  2 K 2| +wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  2 K 2| z -xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json  2  +xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json  2  ~ -|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json.bin  2 +|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json.bin  2   -D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\metadata_generation_command.txt  2  2y +D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\metadata_generation_command.txt  2  2y w -uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\prefab_config.json  2  ( 2~ +uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\prefab_config.json  2  ( 2~ | -zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\symbol_folder_index.txt  2  q 2 \ No newline at end of file +zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\symbol_folder_index.txt  2  q 2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/626b5o2n/x86/configure_fingerprint.bin b/android/app/.cxx/Debug/626b5o2n/x86/configure_fingerprint.bin index 224641d..272cd52 100644 --- a/android/app/.cxx/Debug/626b5o2n/x86/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/626b5o2n/x86/configure_fingerprint.bin @@ -2,27 +2,27 @@ C/C++ Structured LogO M KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2{ +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2{ y -wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt  2  2x +wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt  2  2x v -tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json  2 2} +tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json  2 2} { -yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json  2 2j +yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json  ů2 2j h -fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja  2 2n +fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja  ů2 2n l -jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt  2s +jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt  ů2s q -oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt  2 K 2t +oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt  ů2 K 2t r -pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json  2 x +pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json  ϯ2 x v -tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json.bin  2 +tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json.bin  ϯ2 ~ | -zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\metadata_generation_command.txt  2  2q +zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\metadata_generation_command.txt  ϯ2  2q o -mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\prefab_config.json  2  ( 2v +mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\prefab_config.json  ϯ2  ( 2v t -rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\symbol_folder_index.txt  2  i 2 \ No newline at end of file +rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\symbol_folder_index.txt  կ2  i 2 \ No newline at end of file diff --git a/android/app/.cxx/Debug/626b5o2n/x86_64/configure_fingerprint.bin b/android/app/.cxx/Debug/626b5o2n/x86_64/configure_fingerprint.bin index cdc51cb..a4de62a 100644 --- a/android/app/.cxx/Debug/626b5o2n/x86_64/configure_fingerprint.bin +++ b/android/app/.cxx/Debug/626b5o2n/x86_64/configure_fingerprint.bin @@ -2,27 +2,27 @@ C/C++ Structured LogO M KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC A -?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  2 2~ +?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  ̱2 2~ | -zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt  2  2{ +zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt  ̱2  2{ y -wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json  2 2 +wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json  ̱2 2 ~ -|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json  2 2m +|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json  ̱2 2m k -iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja  2 2q +iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja  ̱2 2q o -mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt  2v +mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt  ̱2v t -rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt  2 K 2w +rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt  ̱2 K 2w u -sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json  2 { +sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json  ̱2 { y -wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json.bin  2 +wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json.bin  ̱2   -}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\metadata_generation_command.txt  2  2t +}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\metadata_generation_command.txt  ̱2  2t r -pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\prefab_config.json  2  ( 2y +pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\prefab_config.json  ̱2  ( 2y w -uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\symbol_folder_index.txt  2  l 2 \ No newline at end of file +uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\symbol_folder_index.txt  ̱2  l 2 \ No newline at end of file diff --git a/lib/app/data/models/penerima_penyaluran_model.dart b/lib/app/data/models/penerima_penyaluran_model.dart index 03c97db..62bdfff 100644 --- a/lib/app/data/models/penerima_penyaluran_model.dart +++ b/lib/app/data/models/penerima_penyaluran_model.dart @@ -13,6 +13,15 @@ class PenerimaPenyaluranModel { final String? stokBantuanId; final Map? warga; // Data warga yang terkait final String? tandaTangan; + final bool? isUang; // Apakah bantuan berupa uang + final String? satuan; // Satuan bantuan + final Map? stokBantuan; // Data stok bantuan + final Map? penyaluranBantuan; // Data penyaluran bantuan + final String? kategoriNama; // Nama kategori bantuan + final String? namaPenyaluran; // Nama penyaluran + final String? deskripsiPenyaluran; // Deskripsi penyaluran + final String? lokasiPenyaluranNama; // Nama lokasi penyaluran + final String? lokasiPenyaluranAlamat; // Alamat lokasi penyaluran PenerimaPenyaluranModel({ this.id, @@ -27,6 +36,15 @@ class PenerimaPenyaluranModel { this.stokBantuanId, this.warga, this.tandaTangan, + this.isUang, + this.satuan, + this.stokBantuan, + this.penyaluranBantuan, + this.kategoriNama, + this.namaPenyaluran, + this.deskripsiPenyaluran, + this.lokasiPenyaluranNama, + this.lokasiPenyaluranAlamat, }); factory PenerimaPenyaluranModel.fromRawJson(String str) => @@ -52,6 +70,15 @@ class PenerimaPenyaluranModel { stokBantuanId: json["stok_bantuan_id"], warga: json["warga"], tandaTangan: json["tanda_tangan"], + isUang: json["is_uang"], + satuan: json["satuan"], + stokBantuan: json["stok_bantuan"], + penyaluranBantuan: json["penyaluran_bantuan"], + kategoriNama: json["kategori_nama"], + namaPenyaluran: json["nama_penyaluran"], + deskripsiPenyaluran: json["deskripsi_penyaluran"], + lokasiPenyaluranNama: json["lokasi_penyaluran_nama"], + lokasiPenyaluranAlamat: json["lokasi_penyaluran_alamat"], ); Map toJson() => { @@ -67,5 +94,14 @@ class PenerimaPenyaluranModel { "stok_bantuan_id": stokBantuanId, "warga": warga, "tanda_tangan": tandaTangan, + "is_uang": isUang, + "satuan": satuan, + "stok_bantuan": stokBantuan, + "penyaluran_bantuan": penyaluranBantuan, + "kategori_nama": kategoriNama, + "nama_penyaluran": namaPenyaluran, + "deskripsi_penyaluran": deskripsiPenyaluran, + "lokasi_penyaluran_nama": lokasiPenyaluranNama, + "lokasi_penyaluran_alamat": lokasiPenyaluranAlamat, }; } diff --git a/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart b/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart index 75db98f..4d7501b 100644 --- a/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart +++ b/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart @@ -35,7 +35,7 @@ class DetailPenyaluranPage extends StatelessWidget { title: const Text('Detail Penyaluran'), centerTitle: true, leading: IconButton( - icon: const Icon(Icons.arrow_back), + icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () => Get.back(), ), ), diff --git a/lib/app/modules/warga/controllers/warga_dashboard_controller.dart b/lib/app/modules/warga/controllers/warga_dashboard_controller.dart index c93991a..461cecb 100644 --- a/lib/app/modules/warga/controllers/warga_dashboard_controller.dart +++ b/lib/app/modules/warga/controllers/warga_dashboard_controller.dart @@ -4,9 +4,11 @@ import 'package:penyaluran_app/app/data/models/pengaduan_model.dart'; import 'package:penyaluran_app/app/data/models/pengajuan_kelayakan_bantuan_model.dart'; import 'package:penyaluran_app/app/data/models/user_model.dart'; import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart'; +import 'package:penyaluran_app/app/services/supabase_service.dart'; class WargaDashboardController extends GetxController { final AuthController _authController = Get.find(); + final SupabaseService _supabaseService = SupabaseService.to; final Rx currentUser = Rx(null); @@ -58,84 +60,22 @@ class WargaDashboardController extends GetxController { isLoading.value = true; try { - // TODO: Implementasi fetch data dari API - // Contoh data dummy untuk pengembangan UI - await Future.delayed(const Duration(seconds: 1)); + // Pastikan user sudah login dan memiliki ID + if (user?.id == null) { + throw Exception('User tidak terautentikasi'); + } - // Dummy data penerima penyaluran - penerimaPenyaluran.value = [ - PenerimaPenyaluranModel( - id: '1', - statusPenerimaan: 'DITERIMA', - tanggalPenerimaan: DateTime.now().subtract(const Duration(days: 5)), - jumlahBantuan: 50000, - keterangan: 'Bantuan Tunai', - ), - PenerimaPenyaluranModel( - id: '2', - statusPenerimaan: 'DITERIMA', - tanggalPenerimaan: DateTime.now().subtract(const Duration(days: 15)), - jumlahBantuan: 100000, - keterangan: 'Bantuan Sembako', - ), - ]; + // Ambil data penerima penyaluran dari server + await fetchPenerimaPenyaluran(); - totalPenyaluranDiterima.value = penerimaPenyaluran.length; + // Ambil data pengajuan kelayakan dari server + await fetchPengajuanKelayakan(); - // Dummy data pengajuan kelayakan - pengajuanKelayakan.value = [ - PengajuanKelayakanBantuanModel( - id: '1', - status: StatusKelayakan.MENUNGGU, - createdAt: DateTime.now().subtract(const Duration(days: 2)), - ), - PengajuanKelayakanBantuanModel( - id: '2', - status: StatusKelayakan.TERVERIFIKASI, - createdAt: DateTime.now().subtract(const Duration(days: 10)), - ), - PengajuanKelayakanBantuanModel( - id: '3', - status: StatusKelayakan.DITOLAK, - createdAt: DateTime.now().subtract(const Duration(days: 20)), - alasanVerifikasi: 'Dokumen tidak lengkap', - ), - ]; + // Ambil data pengaduan dari server + await fetchPengaduan(); - totalPengajuanMenunggu.value = pengajuanKelayakan - .where((p) => p.status == StatusKelayakan.MENUNGGU) - .length; - totalPengajuanTerverifikasi.value = pengajuanKelayakan - .where((p) => p.status == StatusKelayakan.TERVERIFIKASI) - .length; - totalPengajuanDitolak.value = pengajuanKelayakan - .where((p) => p.status == StatusKelayakan.DITOLAK) - .length; - - // Dummy data pengaduan - pengaduan.value = [ - PengaduanModel( - id: '1', - judul: 'Bantuan tidak sesuai', - status: 'PROSES', - tanggalPengaduan: DateTime.now().subtract(const Duration(days: 3)), - ), - PengaduanModel( - id: '2', - judul: 'Keterlambatan penyaluran', - status: 'SELESAI', - tanggalPengaduan: DateTime.now().subtract(const Duration(days: 25)), - ), - ]; - - totalPengaduan.value = pengaduan.length; - totalPengaduanProses.value = - pengaduan.where((p) => p.status == 'PROSES').length; - totalPengaduanSelesai.value = - pengaduan.where((p) => p.status == 'SELESAI').length; - - // Dummy data notifikasi - jumlahNotifikasiBelumDibaca.value = 3; + // Ambil data notifikasi + await fetchNotifikasi(); } catch (e) { print('Error fetching data: $e'); } finally { @@ -143,6 +83,239 @@ class WargaDashboardController extends GetxController { } } + // Fungsi untuk mengambil data penerima penyaluran + Future fetchPenerimaPenyaluran() async { + try { + print('Memulai fetchPenerimaPenyaluran()'); + print('User ID: ${user?.id}'); + + // Pertama, cari warga_id berdasarkan user_id + final wargaResponse = await _supabaseService.client + .from('warga') + .select('id') + .eq('user_id', user!.id) + .single(); + + print('Warga response: $wargaResponse'); + + if (wargaResponse == null) { + print('Tidak ditemukan data warga untuk user_id: ${user!.id}'); + return; + } + + final wargaId = wargaResponse['id']; + print('Warga ID: $wargaId'); + + // Ambil data penerima penyaluran dengan join ke warga, stok bantuan, dan penyaluran bantuan + final response = + await _supabaseService.client.from('penerima_penyaluran').select(''' + *, + warga:warga_id(*), + stok_bantuan:stok_bantuan_id( + *, + kategori_bantuan(*) + ), + penyaluran_bantuan:penyaluran_bantuan_id( + *, + lokasi_penyaluran(*) + ) + ''').eq('warga_id', wargaId).order('created_at', ascending: false); + + print('Response dari API: $response'); + + if (response != null) { + final List penerima = []; + for (var item in response) { + print('Memproses item: $item'); + + Map sanitizedPenerimaData = + Map.from(item); + + print('Data yang disanitasi: $sanitizedPenerimaData'); + + if (sanitizedPenerimaData['jumlah_bantuan'] is String) { + var jumlahBantuan = double.tryParse( + sanitizedPenerimaData['jumlah_bantuan'] as String); + print( + 'Konversi jumlah_bantuan dari String ke double: $jumlahBantuan'); + sanitizedPenerimaData['jumlah_bantuan'] = jumlahBantuan; + } + + // Tambahkan informasi apakah bantuan uang atau bukan dan satuan + if (sanitizedPenerimaData['stok_bantuan'] != null) { + print('Stok bantuan: ${sanitizedPenerimaData['stok_bantuan']}'); + + // Cek apakah bantuan uang + final isUang = + sanitizedPenerimaData['stok_bantuan']['is_uang'] ?? false; + sanitizedPenerimaData['is_uang'] = isUang; + + // Ambil satuan + final satuan = + sanitizedPenerimaData['stok_bantuan']['satuan'] ?? ''; + sanitizedPenerimaData['satuan'] = satuan; + + // Ambil nama kategori bantuan jika tersedia + if (sanitizedPenerimaData['stok_bantuan']['kategori_bantuan'] != + null) { + final kategoriNama = sanitizedPenerimaData['stok_bantuan'] + ['kategori_bantuan']['nama'] ?? + ''; + sanitizedPenerimaData['kategori_nama'] = kategoriNama; + } + + print('Is Uang: $isUang, Satuan: $satuan'); + } + + // Tambahkan informasi dari penyaluran bantuan + if (sanitizedPenerimaData['penyaluran_bantuan'] != null) { + print( + 'Penyaluran bantuan: ${sanitizedPenerimaData['penyaluran_bantuan']}'); + + // Ambil nama penyaluran + final namaPenyaluran = + sanitizedPenerimaData['penyaluran_bantuan']['nama'] ?? ''; + sanitizedPenerimaData['nama_penyaluran'] = namaPenyaluran; + + // Ambil deskripsi penyaluran + final deskripsiPenyaluran = + sanitizedPenerimaData['penyaluran_bantuan']['deskripsi'] ?? ''; + sanitizedPenerimaData['deskripsi_penyaluran'] = deskripsiPenyaluran; + + // Ambil lokasi penyaluran jika tersedia + if (sanitizedPenerimaData['penyaluran_bantuan'] + ['lokasi_penyaluran'] != + null) { + final lokasiNama = sanitizedPenerimaData['penyaluran_bantuan'] + ['lokasi_penyaluran']['nama'] ?? + ''; + sanitizedPenerimaData['lokasi_penyaluran_nama'] = lokasiNama; + + final lokasiAlamat = sanitizedPenerimaData['penyaluran_bantuan'] + ['lokasi_penyaluran']['alamat_lengkap'] ?? + ''; + sanitizedPenerimaData['lokasi_penyaluran_alamat'] = lokasiAlamat; + } + + print('Nama Penyaluran: $namaPenyaluran'); + } + + var model = PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData); + print('Model yang dibuat: $model'); + penerima.add(model); + } + + print('Total data yang diproses: ${penerima.length}'); + penerimaPenyaluran.assignAll(penerima); + + var diterima = + penerima.where((p) => p.statusPenerimaan == 'DITERIMA').length; + print('Total penyaluran diterima: $diterima'); + totalPenyaluranDiterima.value = diterima; + } + } catch (e, stackTrace) { + print('Error fetchPenerimaPenyaluran: $e'); + print('Stack trace: $stackTrace'); + } + } + + // Fungsi untuk mengambil data pengajuan kelayakan + Future fetchPengajuanKelayakan() async { + try { + // Pertama, cari warga_id berdasarkan user_id + final wargaResponse = await _supabaseService.client + .from('warga') + .select('id') + .eq('user_id', user!.id) + .single(); + + if (wargaResponse == null) { + print('Tidak ditemukan data warga untuk user_id: ${user!.id}'); + return; + } + + final wargaId = wargaResponse['id']; + print('Warga ID untuk pengajuan kelayakan: $wargaId'); + + final response = await _supabaseService.client + .from('xx02_pengajuan_kelayakan_bantuan') + .select('*') + .eq('warga_id', wargaId) + .order('created_at', ascending: false); + + if (response != null) { + final List pengajuan = []; + for (var item in response) { + // Konversi status ke enum + if (item['status'] != null) { + final statusStr = item['status'].toString(); + item['status'] = statusStr; // Pastikan status dalam format string + } + + pengajuan.add(PengajuanKelayakanBantuanModel.fromJson(item)); + } + pengajuanKelayakan.assignAll(pengajuan); + + // Hitung jumlah berdasarkan status + totalPengajuanMenunggu.value = + pengajuan.where((p) => p.status == StatusKelayakan.MENUNGGU).length; + totalPengajuanTerverifikasi.value = pengajuan + .where((p) => p.status == StatusKelayakan.TERVERIFIKASI) + .length; + totalPengajuanDitolak.value = + pengajuan.where((p) => p.status == StatusKelayakan.DITOLAK).length; + } + } catch (e) { + print('Error fetching pengajuan kelayakan: $e'); + } + } + + // Fungsi untuk mengambil data pengaduan + Future fetchPengaduan() async { + try { + final response = await _supabaseService.client + .from('pengaduan') + .select('*') + .eq('pelapor', user!.id) + .order('created_at', ascending: false); + + if (response != null) { + final List pengaduanList = []; + for (var item in response) { + pengaduanList.add(PengaduanModel.fromJson(item)); + } + pengaduan.assignAll(pengaduanList); + + // Hitung jumlah berdasarkan status + totalPengaduan.value = pengaduanList.length; + totalPengaduanProses.value = pengaduanList + .where((p) => p.status == 'PROSES' || p.status == 'DIPROSES') + .length; + totalPengaduanSelesai.value = + pengaduanList.where((p) => p.status == 'SELESAI').length; + } + } catch (e) { + print('Error fetching pengaduan: $e'); + } + } + + // Fungsi untuk mengambil data notifikasi + Future fetchNotifikasi() async { + try { + final response = await _supabaseService.client + .from('notifikasi') + .select('*') + .eq('user_id', user!.id) + .eq('dibaca', false) + .count(); + + jumlahNotifikasiBelumDibaca.value = response.count; + } catch (e) { + print('Error fetching notifikasi: $e'); + jumlahNotifikasiBelumDibaca.value = 0; + } + } + // Navigasi ke halaman detail void goToPenyaluranDetail() { changeTab(1); diff --git a/lib/app/modules/warga/views/warga_dashboard_view.dart b/lib/app/modules/warga/views/warga_dashboard_view.dart index 5d3e40a..cac0103 100644 --- a/lib/app/modules/warga/views/warga_dashboard_view.dart +++ b/lib/app/modules/warga/views/warga_dashboard_view.dart @@ -2,91 +2,147 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; -import 'package:penyaluran_app/app/data/models/pengajuan_kelayakan_bantuan_model.dart'; +import 'package:penyaluran_app/app/widgets/bantuan_card.dart'; +import 'package:penyaluran_app/app/widgets/section_header.dart'; class WargaDashboardView extends GetView { const WargaDashboardView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Obx(() { - if (controller.isLoading.value) { - return const Center(child: CircularProgressIndicator()); - } + return Scaffold( + body: Obx(() { + if (controller.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } - return RefreshIndicator( - onRefresh: () async { - controller.fetchData(); - }, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildWelcomeSection(), - const SizedBox(height: 24), - _buildPenyaluranSummary(), - const SizedBox(height: 24), - _buildPengajuanSection(), - const SizedBox(height: 24), - _buildPengaduanSummary(), - ], + return RefreshIndicator( + onRefresh: () async { + controller.fetchData(); + }, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildWelcomeSection(), + const SizedBox(height: 24), + _buildPenerimaanSummary(), + const SizedBox(height: 24), + _buildRecentPenerimaan(), + ], + ), ), - ), - ); - }); + ); + }), + ); } Widget _buildWelcomeSection() { return Card( elevation: 2, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular(16), ), child: Padding( - padding: const EdgeInsets.all(16.0), - child: Row( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - CircleAvatar( - radius: 30, - backgroundColor: Colors.blue.shade100, - child: const Icon( - Icons.person, - size: 40, - color: Colors.blue, - ), + Row( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.blue.shade100, + child: Icon( + Icons.person, + color: Colors.blue.shade700, + size: 28, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Selamat Datang,', + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + Text( + controller.nama, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ], ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Selamat Datang,', + const SizedBox(height: 16), + const Divider(), + const SizedBox(height: 16), + Row( + children: [ + Icon( + Icons.home, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Alamat tidak tersedia', style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, + color: Colors.grey.shade700, ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - const SizedBox(height: 4), - Text( - controller.nama, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.phone, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Text( + 'No. HP tidak tersedia', + style: TextStyle( + color: Colors.grey.shade700, ), - const SizedBox(height: 4), - Text( - controller.desa ?? 'Warga Desa', - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, - ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.location_city, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Text( + controller.desa ?? 'Desa tidak tersedia', + style: TextStyle( + color: Colors.grey.shade700, ), - ], - ), + ), + ], ), ], ), @@ -94,393 +150,179 @@ class WargaDashboardView extends GetView { ); } - Widget _buildPenyaluranSummary() { + Widget _buildPenerimaanSummary() { final currencyFormat = NumberFormat.currency( locale: 'id', symbol: 'Rp ', decimalDigits: 0, ); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Penyaluran Bantuan', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Total Bantuan Diterima', - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, - ), - ), - const SizedBox(height: 8), - Text( - '${controller.totalPenyaluranDiterima.value}', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.blue, - ), - ), - ], - ), - const Icon( - Icons.volunteer_activism, - size: 40, - color: Colors.blue, - ), - ], - ), - const Divider(height: 32), - if (controller.penerimaPenyaluran.isNotEmpty) - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: controller.penerimaPenyaluran.length > 2 - ? 2 - : controller.penerimaPenyaluran.length, - itemBuilder: (context, index) { - final item = controller.penerimaPenyaluran[index]; - return ListTile( - contentPadding: EdgeInsets.zero, - title: Text( - item.keterangan ?? 'Bantuan', - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - item.tanggalPenerimaan != null - ? DateFormat('dd MMMM yyyy', 'id_ID') - .format(item.tanggalPenerimaan!) - : '-', - ), - trailing: Text( - item.jumlahBantuan != null - ? currencyFormat.format(item.jumlahBantuan) - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.green, - ), - ), - ); - }, - ) - else - const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Text('Belum ada penyaluran bantuan'), - ), - ), - if (controller.penerimaPenyaluran.length > 2) - TextButton( - onPressed: () => controller.changeTab(1), - child: const Text('Lihat Semua'), - ), - ], - ), - ), - ), - ], - ); - } + // Hitung total bantuan uang dan non-uang + double totalUang = 0; + Map totalNonUang = {}; - Widget _buildPengajuanSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Pengajuan Kelayakan Bantuan', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildStatusCounter( - 'Menunggu', - controller.totalPengajuanMenunggu.value, - Colors.orange, - ), - _buildStatusCounter( - 'Terverifikasi', - controller.totalPengajuanTerverifikasi.value, - Colors.green, - ), - _buildStatusCounter( - 'Ditolak', - controller.totalPengajuanDitolak.value, - Colors.red, - ), - ], - ), - const Divider(height: 32), - if (controller.pengajuanKelayakan.isNotEmpty) - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: controller.pengajuanKelayakan.length > 3 - ? 3 - : controller.pengajuanKelayakan.length, - itemBuilder: (context, index) { - final item = controller.pengajuanKelayakan[index]; - return ListTile( - contentPadding: EdgeInsets.zero, - title: Text( - 'Pengajuan #${index + 1}', - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - item.createdAt != null - ? DateFormat('dd MMMM yyyy', 'id_ID') - .format(item.createdAt!) - : '-', - ), - trailing: _buildStatusBadge(item.status), - ); - }, - ) - else - const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Text('Belum ada pengajuan kelayakan'), - ), - ), - if (controller.pengajuanKelayakan.length > 3) - TextButton( - onPressed: controller.goToPengajuanDetail, - child: const Text('Lihat Semua'), - ), - ], - ), - ), - ), - ], - ); - } - - Widget _buildStatusCounter(String label, int count, Color color) { - return Column( - children: [ - Text( - '$count', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: color, - ), - ), - const SizedBox(height: 4), - Text( - label, - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, - ), - ), - ], - ); - } - - Widget _buildStatusBadge(StatusKelayakan? status) { - if (status == null) return const SizedBox(); - - Color color; - String text; - - switch (status) { - case StatusKelayakan.MENUNGGU: - color = Colors.orange; - text = 'Menunggu'; - break; - case StatusKelayakan.TERVERIFIKASI: - color = Colors.green; - text = 'Terverifikasi'; - break; - case StatusKelayakan.DITOLAK: - color = Colors.red; - text = 'Ditolak'; - break; + for (var item in controller.penerimaPenyaluran) { + if (item.statusPenerimaan == 'DITERIMA') { + if (item.isUang == true && item.jumlahBantuan != null) { + totalUang += item.jumlahBantuan!; + } else if (item.isUang == false && + item.jumlahBantuan != null && + item.satuan != null) { + if (totalNonUang.containsKey(item.satuan)) { + totalNonUang[item.satuan!] = + (totalNonUang[item.satuan] ?? 0) + item.jumlahBantuan!; + } else { + totalNonUang[item.satuan!] = item.jumlahBantuan!; + } + } + } } - return Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - border: Border.all(color: color), + return Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - child: Text( - text, - style: TextStyle( - color: color, - fontWeight: FontWeight.bold, - fontSize: 12, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SectionHeader( + title: 'Ringkasan Bantuan', + titleStyle: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + if (totalUang > 0) + _buildSummaryItem( + icon: Icons.attach_money, + color: Colors.green, + title: 'Total Bantuan Uang', + value: currencyFormat.format(totalUang), + ), + if (totalNonUang.isNotEmpty) ...[ + if (totalUang > 0) const SizedBox(height: 12), + ...totalNonUang.entries.map((entry) { + return _buildSummaryItem( + icon: Icons.inventory_2, + color: Colors.blue, + title: 'Total Bantuan ${entry.key}', + value: '${entry.value} ${entry.key}', + ); + }), + ], + if (totalUang == 0 && totalNonUang.isEmpty) + _buildSummaryItem( + icon: Icons.info_outline, + color: Colors.grey, + title: 'Belum Ada Bantuan', + value: 'Anda belum menerima bantuan', + ), + ], ), ), ); } - Widget _buildPengaduanSummary() { + Widget _buildSummaryItem({ + required IconData icon, + required Color color, + required String title, + required String value, + }) { + return Row( + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + icon, + color: color, + size: 24, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + Text( + value, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: color, + ), + ), + ], + ), + ), + ], + ); + } + + Widget _buildRecentPenerimaan() { + if (controller.penerimaPenyaluran.isEmpty) { + return const SizedBox.shrink(); + } + + final maxItems = controller.penerimaPenyaluran.length > 2 + ? 2 + : controller.penerimaPenyaluran.length; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - 'Pengaduan', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + SectionHeader( + title: 'Bantuan Terbaru', + viewAllText: 'Lihat Semua', + onViewAll: () { + Get.toNamed('/warga-penerimaan'); + }, ), - const SizedBox(height: 12), - Card( - elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _buildStatusCounter( - 'Total', - controller.totalPengaduan.value, - Colors.blue, - ), - _buildStatusCounter( - 'Proses', - controller.totalPengaduanProses.value, - Colors.orange, - ), - _buildStatusCounter( - 'Selesai', - controller.totalPengaduanSelesai.value, - Colors.green, - ), - ], - ), - const Divider(height: 32), - if (controller.pengaduan.isNotEmpty) - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: controller.pengaduan.length > 2 - ? 2 - : controller.pengaduan.length, - itemBuilder: (context, index) { - final item = controller.pengaduan[index]; - return ListTile( - contentPadding: EdgeInsets.zero, - title: Text( - item.judul ?? 'Pengaduan #${index + 1}', - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - item.tanggalPengaduan != null - ? DateFormat('dd MMMM yyyy', 'id_ID') - .format(item.tanggalPengaduan!) - : '-', - ), - trailing: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: item.status == 'PROSES' - ? Colors.orange.withOpacity(0.1) - : Colors.green.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: item.status == 'PROSES' - ? Colors.orange - : Colors.green, - ), - ), - child: Text( - item.status == 'PROSES' ? 'Proses' : 'Selesai', - style: TextStyle( - color: item.status == 'PROSES' - ? Colors.orange - : Colors.green, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ), - ); - }, - ) - else - const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), - child: Text('Belum ada pengaduan'), - ), - ), - if (controller.pengaduan.length > 2) - TextButton( - onPressed: () => controller.changeTab(2), - child: const Text('Lihat Semua'), - ), - const SizedBox(height: 8), - ElevatedButton.icon( - onPressed: () { - // TODO: Implementasi navigasi ke halaman buat pengaduan - }, - icon: const Icon(Icons.add), - label: const Text('Buat Pengaduan Baru'), - style: ElevatedButton.styleFrom( - minimumSize: const Size(double.infinity, 48), - ), - ), - ], + const SizedBox(height: 16), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: maxItems, + itemBuilder: (context, index) { + final item = controller.penerimaPenyaluran[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: BantuanCard( + item: item, + isCompact: true, + onTap: () { + Get.toNamed('/warga/detail-penerimaan', + arguments: {'id': item.id}); + }, + ), + ); + }, + ), + if (controller.penerimaPenyaluran.length > 2) + Center( + child: TextButton.icon( + onPressed: () { + Get.toNamed('/warga-penerimaan'); + }, + icon: const Icon(Icons.list), + label: const Text('Lihat Semua Bantuan'), ), ), - ), ], ); } diff --git a/lib/app/modules/warga/views/warga_detail_penerimaan_view.dart b/lib/app/modules/warga/views/warga_detail_penerimaan_view.dart new file mode 100644 index 0000000..e089f3f --- /dev/null +++ b/lib/app/modules/warga/views/warga_detail_penerimaan_view.dart @@ -0,0 +1,632 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart'; +import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; +import 'package:penyaluran_app/app/theme/app_theme.dart'; +import 'package:penyaluran_app/app/widgets/status_badge.dart'; + +class WargaDetailPenerimaanView extends GetView { + const WargaDetailPenerimaanView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final Map args = Get.arguments ?? {}; + final id = args['id']; + + if (id == null) { + return Scaffold( + appBar: AppBar( + title: const Text('Detail Penerimaan'), + ), + body: const Center( + child: Text('ID Penerimaan tidak ditemukan'), + ), + ); + } + + // Konversi id ke string untuk memastikan kompatibilitas dengan model + final String penyaluranId = id.toString(); + + // Cari data penerimaan berdasarkan ID + final PenerimaPenyaluranModel? penyaluran = controller.penerimaPenyaluran + .firstWhereOrNull((item) => item.id == penyaluranId); + + if (penyaluran == null) { + return Scaffold( + appBar: AppBar( + title: const Text('Detail Penerimaan'), + ), + body: const Center( + child: Text('Data penerimaan tidak ditemukan'), + ), + ); + } + + final bool isDiterima = penyaluran.statusPenerimaan == 'DITERIMA'; + + return Scaffold( + appBar: AppBar( + title: const Text('Detail Penerimaan'), + elevation: 0, + backgroundColor: Get.theme.primaryColor, + foregroundColor: Colors.white, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => Get.back(), + ), + ), + body: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Get.theme.primaryColor.withOpacity(0.05), + Colors.white, + ], + ), + ), + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeaderSection(penyaluran), + const SizedBox(height: 16), + _buildDetailSection(penyaluran), + const SizedBox(height: 16), + _buildLocationSection(penyaluran), + const SizedBox(height: 16), + if (isDiterima) _buildBuktiPenerimaanSection(penyaluran), + if (isDiterima) const SizedBox(height: 16), + _buildAdditionalInfoSection(penyaluran), + ], + ), + ), + ), + ); + } + + Widget _buildHeaderSection(PenerimaPenyaluranModel penyaluran) { + final currencyFormat = NumberFormat.currency( + locale: 'id', + symbol: 'Rp ', + decimalDigits: 0, + ); + + // Format jumlah bantuan berdasarkan tipe (uang atau bukan) + String formattedJumlah = ''; + if (penyaluran.jumlahBantuan != null) { + if (penyaluran.isUang == true) { + formattedJumlah = currencyFormat.format(penyaluran.jumlahBantuan); + } else { + formattedJumlah = + '${penyaluran.jumlahBantuan} ${penyaluran.satuan ?? ''}'; + } + } else { + formattedJumlah = '-'; + } + + return Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + gradient: AppTheme.primaryGradient, + ), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + penyaluran.namaPenyaluran ?? + penyaluran.keterangan ?? + 'Bantuan', + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + StatusBadge( + status: penyaluran.statusPenerimaan ?? 'MENUNGGU', + fontSize: 14, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + ), + ], + ), + const SizedBox(height: 16), + if (penyaluran.deskripsiPenyaluran != null && + penyaluran.deskripsiPenyaluran!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + penyaluran.deskripsiPenyaluran!, + style: TextStyle( + color: Colors.white, + fontSize: 16, + ), + ), + ), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.1), + spreadRadius: 1, + blurRadius: 3, + offset: const Offset(0, 1), + ), + ], + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: (penyaluran.isUang == true + ? Colors.green + : Colors.blue) + .withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + penyaluran.isUang == true + ? Icons.attach_money + : Icons.inventory_2, + color: penyaluran.isUang == true + ? Colors.green + : Colors.blue, + size: 28, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Jumlah Bantuan', + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + Text( + formattedJumlah, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: penyaluran.isUang == true + ? Colors.green.shade700 + : Colors.blue.shade700, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget _buildDetailSection(PenerimaPenyaluranModel penyaluran) { + return Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.info_outline, + color: Get.theme.primaryColor, + ), + const SizedBox(width: 8), + const Text( + 'Detail Bantuan', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 16), + _buildDetailItem( + icon: Icons.category, + title: 'Kategori', + value: penyaluran.kategoriNama ?? 'Tidak tersedia', + ), + const Divider(height: 24), + _buildDetailItem( + icon: Icons.calendar_today, + title: 'Tanggal Penerimaan', + value: penyaluran.tanggalPenerimaan != null + ? DateFormat('dd MMMM yyyy', 'id_ID') + .format(penyaluran.tanggalPenerimaan!) + : 'Belum diterima', + ), + const Divider(height: 24), + _buildDetailItem( + icon: Icons.access_time, + title: 'Waktu Penerimaan', + value: penyaluran.tanggalPenerimaan != null + ? DateFormat('HH:mm', 'id_ID') + .format(penyaluran.tanggalPenerimaan!) + : 'Belum diterima', + ), + if (penyaluran.keterangan != null && + penyaluran.keterangan!.isNotEmpty) ...[ + const Divider(height: 24), + _buildDetailItem( + icon: Icons.note, + title: 'Keterangan', + value: penyaluran.keterangan!, + ), + ], + ], + ), + ), + ); + } + + Widget _buildLocationSection(PenerimaPenyaluranModel penyaluran) { + return Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.location_on, + color: Get.theme.primaryColor, + ), + const SizedBox(width: 8), + const Text( + 'Lokasi Penerimaan', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 16), + _buildDetailItem( + icon: Icons.place, + title: 'Tempat Penerimaan', + value: penyaluran.lokasiPenyaluranNama ?? 'Tidak tersedia', + ), + if (penyaluran.lokasiPenyaluranAlamat != null && + penyaluran.lokasiPenyaluranAlamat!.isNotEmpty) ...[ + const Divider(height: 24), + _buildDetailItem( + icon: Icons.map, + title: 'Alamat', + value: penyaluran.lokasiPenyaluranAlamat!, + ), + ], + const SizedBox(height: 16), + // TODO: Implementasi peta lokasi jika koordinat tersedia + ], + ), + ), + ); + } + + Widget _buildBuktiPenerimaanSection(PenerimaPenyaluranModel penyaluran) { + return Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.verified, + color: Colors.green.shade700, + ), + const SizedBox(width: 8), + const Text( + 'Bukti Penerimaan', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 16), + + // Bukti Penerimaan (Foto) + if (penyaluran.buktiPenerimaan != null && + penyaluran.buktiPenerimaan!.isNotEmpty) ...[ + const Text( + 'Foto Bukti Penerimaan', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Container( + height: 200, + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.network( + penyaluran.buktiPenerimaan!, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.broken_image, + size: 48, + color: Colors.grey.shade400, + ), + const SizedBox(height: 8), + Text( + 'Gambar tidak dapat dimuat', + style: TextStyle(color: Colors.grey.shade600), + ), + ], + ), + ); + }, + ), + ), + ), + const SizedBox(height: 16), + ] else ...[ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + Icons.image_not_supported, + color: Colors.grey.shade600, + ), + const SizedBox(width: 12), + const Expanded( + child: Text( + 'Bukti penerimaan belum diunggah', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + ], + + // Tanda Tangan + const Divider(height: 24), + const Text( + 'Tanda Tangan Penerima', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + if (penyaluran.tandaTangan != null && + penyaluran.tandaTangan!.isNotEmpty) ...[ + Container( + height: 150, + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300), + color: Colors.white, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.network( + penyaluran.tandaTangan!, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.broken_image, + size: 48, + color: Colors.grey.shade400, + ), + const SizedBox(height: 8), + Text( + 'Tanda tangan tidak dapat dimuat', + style: TextStyle(color: Colors.grey.shade600), + ), + ], + ), + ); + }, + ), + ), + ), + ] else ...[ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + Icons.draw, + color: Colors.grey.shade600, + ), + const SizedBox(width: 12), + const Expanded( + child: Text( + 'Tanda tangan belum tersedia', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), + ), + ), + ], + ), + ), + ], + ], + ), + ), + ); + } + + Widget _buildAdditionalInfoSection(PenerimaPenyaluranModel penyaluran) { + return Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.more_horiz, + color: Get.theme.primaryColor, + ), + const SizedBox(width: 8), + const Text( + 'Informasi Tambahan', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 16), + _buildDetailItem( + icon: Icons.numbers, + title: 'ID Penerimaan', + value: '${penyaluran.id}', + ), + const Divider(height: 24), + _buildDetailItem( + icon: Icons.person, + title: 'Penerima', + value: controller.nama, + ), + const Divider(height: 24), + _buildDetailItem( + icon: Icons.update, + title: 'Terakhir Diperbarui', + value: penyaluran.tanggalPenerimaan != null + ? DateFormat('dd MMMM yyyy HH:mm', 'id_ID') + .format(penyaluran.tanggalPenerimaan!) + : 'Tidak tersedia', + ), + ], + ), + ), + ); + } + + Widget _buildDetailItem({ + required IconData icon, + required String title, + required String value, + }) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + icon, + size: 20, + color: Colors.grey.shade700, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 14, + color: Colors.grey.shade600, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/app/modules/warga/views/warga_penerimaan_view.dart b/lib/app/modules/warga/views/warga_penerimaan_view.dart new file mode 100644 index 0000000..e700f7d --- /dev/null +++ b/lib/app/modules/warga/views/warga_penerimaan_view.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; +import 'package:penyaluran_app/app/widgets/bantuan_card.dart'; + +class WargaPenerimaanView extends GetView { + const WargaPenerimaanView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Obx(() { + if (controller.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } + + return RefreshIndicator( + onRefresh: () async { + controller.fetchData(); + }, + child: controller.penerimaPenyaluran.isEmpty + ? _buildEmptyState() + : _buildPenerimaanList(), + ); + }), + floatingActionButton: FloatingActionButton( + onPressed: () { + // Navigasi ke halaman riwayat penerimaan + Get.toNamed('/riwayat-penyaluran'); + }, + backgroundColor: Colors.blue, + child: const Icon(Icons.history), + tooltip: 'Riwayat Penerimaan', + ), + ); + } + + Widget _buildEmptyState() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon( + Icons.volunteer_activism, + size: 80, + color: Colors.blue.shade400, + ), + ), + const SizedBox(height: 24), + Text( + 'Belum Ada Penerimaan', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey.shade800, + ), + ), + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + 'Anda belum menerima bantuan. Bantuan akan muncul di sini ketika Anda menerimanya.', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.grey.shade600, + ), + ), + ), + const SizedBox(height: 32), + ElevatedButton.icon( + onPressed: () { + controller.fetchData(); + }, + icon: const Icon(Icons.refresh), + label: const Text('Muat Ulang'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ], + ), + ); + } + + Widget _buildPenerimaanList() { + return ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: controller.penerimaPenyaluran.length, + itemBuilder: (context, index) { + final item = controller.penerimaPenyaluran[index]; + + return BantuanCard( + item: item, + onTap: () { + // Navigasi ke detail penerimaan + Get.toNamed('/warga/detail-penerimaan', arguments: {'id': item.id}); + }, + ); + }, + ); + } +} diff --git a/lib/app/modules/warga/views/warga_pengaduan_view.dart b/lib/app/modules/warga/views/warga_pengaduan_view.dart index bdd82d3..73fca8e 100644 --- a/lib/app/modules/warga/views/warga_pengaduan_view.dart +++ b/lib/app/modules/warga/views/warga_pengaduan_view.dart @@ -77,7 +77,32 @@ class WargaPengaduanView extends GetView { itemCount: controller.pengaduan.length, itemBuilder: (context, index) { final item = controller.pengaduan[index]; - final isProses = item.status == 'PROSES'; + + // Tentukan status dan warna berdasarkan status pengaduan + Color statusColor; + String statusText; + + switch (item.status?.toUpperCase()) { + case 'PROSES': + case 'DIPROSES': + case 'TINDAKAN': + statusColor = Colors.orange; + statusText = 'Proses'; + break; + case 'SELESAI': + statusColor = Colors.green; + statusText = 'Selesai'; + break; + case 'DITOLAK': + statusColor = Colors.red; + statusText = 'Ditolak'; + break; + default: + statusColor = Colors.grey; + statusText = item.status ?? 'Tidak Diketahui'; + } + + final isProses = statusText == 'Proses'; return Card( margin: const EdgeInsets.only(bottom: 16), @@ -115,18 +140,16 @@ class WargaPengaduanView extends GetView { vertical: 6, ), decoration: BoxDecoration( - color: isProses - ? Colors.orange.withOpacity(0.1) - : Colors.green.withOpacity(0.1), + color: statusColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all( - color: isProses ? Colors.orange : Colors.green, + color: statusColor, ), ), child: Text( - isProses ? 'Proses' : 'Selesai', + statusText, style: TextStyle( - color: isProses ? Colors.orange : Colors.green, + color: statusColor, fontWeight: FontWeight.bold, fontSize: 12, ), diff --git a/lib/app/modules/warga/views/warga_penyaluran_view.dart b/lib/app/modules/warga/views/warga_penyaluran_view.dart deleted file mode 100644 index f266523..0000000 --- a/lib/app/modules/warga/views/warga_penyaluran_view.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:intl/intl.dart'; -import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; - -class WargaPenyaluranView extends GetView { - const WargaPenyaluranView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Obx(() { - if (controller.isLoading.value) { - return const Center(child: CircularProgressIndicator()); - } - - return RefreshIndicator( - onRefresh: () async { - controller.fetchData(); - }, - child: controller.penerimaPenyaluran.isEmpty - ? _buildEmptyState() - : _buildPenyaluranList(), - ); - }); - } - - Widget _buildEmptyState() { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.volunteer_activism, - size: 80, - color: Colors.grey.shade400, - ), - const SizedBox(height: 16), - Text( - 'Belum Ada Penyaluran', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.grey.shade700, - ), - ), - const SizedBox(height: 8), - Text( - 'Anda belum menerima penyaluran bantuan', - style: TextStyle( - fontSize: 14, - color: Colors.grey.shade600, - ), - ), - ], - ), - ); - } - - Widget _buildPenyaluranList() { - final currencyFormat = NumberFormat.currency( - locale: 'id', - symbol: 'Rp ', - decimalDigits: 0, - ); - - return ListView.builder( - padding: const EdgeInsets.all(16), - itemCount: controller.penerimaPenyaluran.length, - itemBuilder: (context, index) { - final item = controller.penerimaPenyaluran[index]; - return Card( - margin: const EdgeInsets.only(bottom: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 2, - child: InkWell( - onTap: () { - // TODO: Navigasi ke detail penyaluran - }, - borderRadius: BorderRadius.circular(12), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - item.keterangan ?? 'Bantuan', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: Colors.green.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.green), - ), - child: const Text( - 'Diterima', - style: TextStyle( - color: Colors.green, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - Icon( - Icons.calendar_today, - size: 16, - color: Colors.grey.shade600, - ), - const SizedBox(width: 8), - Text( - item.tanggalPenerimaan != null - ? DateFormat('dd MMMM yyyy', 'id_ID') - .format(item.tanggalPenerimaan!) - : '-', - style: TextStyle( - color: Colors.grey.shade600, - ), - ), - ], - ), - const SizedBox(height: 8), - Row( - children: [ - Icon( - Icons.attach_money, - size: 16, - color: Colors.grey.shade600, - ), - const SizedBox(width: 8), - Text( - item.jumlahBantuan != null - ? currencyFormat.format(item.jumlahBantuan) - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.green, - ), - ), - ], - ), - const SizedBox(height: 16), - const Divider(height: 1), - const SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton.icon( - onPressed: () { - // TODO: Implementasi navigasi ke detail penyaluran - Get.toNamed('/detail-penyaluran', - arguments: {'id': item.id}); - }, - icon: const Icon(Icons.visibility), - label: const Text('Lihat Detail'), - ), - ], - ), - ], - ), - ), - ), - ); - }, - ); - } -} diff --git a/lib/app/modules/warga/views/warga_view.dart b/lib/app/modules/warga/views/warga_view.dart index 637f7a7..640f5cb 100644 --- a/lib/app/modules/warga/views/warga_view.dart +++ b/lib/app/modules/warga/views/warga_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; import 'package:penyaluran_app/app/modules/warga/views/warga_dashboard_view.dart'; -import 'package:penyaluran_app/app/modules/warga/views/warga_penyaluran_view.dart'; +import 'package:penyaluran_app/app/modules/warga/views/warga_penerimaan_view.dart'; import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_view.dart'; import 'package:penyaluran_app/app/widgets/app_drawer.dart'; import 'package:penyaluran_app/app/widgets/app_bottom_navigation_bar.dart'; @@ -23,7 +23,7 @@ class WargaView extends GetView { case 0: return const Text('Dashboard Warga'); case 1: - return const Text('Penyaluran Bantuan'); + return const Text('Penerimaan Bantuan'); case 2: return const Text('Pengaduan'); default: @@ -96,7 +96,7 @@ class WargaView extends GetView { ), DrawerMenuItem( icon: Icons.volunteer_activism_outlined, - title: 'Penyaluran', + title: 'Penerimaan', isSelected: controller.activeTabIndex.value == 1, onTap: () => controller.changeTab(1), ), @@ -125,7 +125,7 @@ class WargaView extends GetView { case 0: return const WargaDashboardView(); case 1: - return const WargaPenyaluranView(); + return const WargaPenerimaanView(); case 2: return const WargaPengaduanView(); default: @@ -144,7 +144,7 @@ class WargaView extends GetView { AppBottomNavigationBarItem( icon: Icons.volunteer_activism_outlined, activeIcon: Icons.volunteer_activism, - label: 'Penyaluran', + label: 'Penerimaan', badgeCount: controller.totalPenyaluranDiterima.value > 0 ? controller.totalPenyaluranDiterima.value : null, diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index bb7ac86..b0c3b4b 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -22,10 +22,11 @@ import 'package:penyaluran_app/app/modules/splash/bindings/splash_binding.dart'; import 'package:penyaluran_app/app/modules/splash/views/splash_view.dart'; import 'package:penyaluran_app/app/modules/warga/bindings/warga_binding.dart'; import 'package:penyaluran_app/app/modules/warga/views/warga_dashboard_view.dart'; -import 'package:penyaluran_app/app/modules/warga/views/warga_penyaluran_view.dart'; +import 'package:penyaluran_app/app/modules/warga/views/warga_penerimaan_view.dart'; import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_view.dart'; import 'package:penyaluran_app/app/modules/warga/views/warga_view.dart'; import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; +import 'package:penyaluran_app/app/modules/warga/views/warga_detail_penerimaan_view.dart'; part 'app_routes.dart'; @@ -46,12 +47,12 @@ class AppPages { binding: AuthBinding(), ), GetPage( - name: _Paths.wargaDashboard, + name: Routes.wargaDashboard, page: () => const WargaView(), binding: WargaBinding(), ), GetPage( - name: _Paths.wargaPenyaluran, + name: Routes.wargaPenerimaan, page: () { final controller = Get.find(); controller.activeTabIndex.value = 1; @@ -60,7 +61,7 @@ class AppPages { binding: WargaBinding(), ), GetPage( - name: _Paths.wargaPengaduan, + name: Routes.wargaPengaduan, page: () { final controller = Get.find(); controller.activeTabIndex.value = 2; @@ -123,5 +124,10 @@ class AppPages { page: () => DetailPenyaluranPage(), binding: PenyaluranBinding(), ), + GetPage( + name: Routes.wargaDetailPenerimaan, + page: () => const WargaDetailPenerimaanView(), + binding: WargaBinding(), + ), ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 418e98e..b521c92 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -6,7 +6,7 @@ abstract class Routes { static const login = _Paths.login; static const register = _Paths.register; static const wargaDashboard = _Paths.wargaDashboard; - static const wargaPenyaluran = _Paths.wargaPenyaluran; + static const wargaPenerimaan = _Paths.wargaPenerimaan; static const wargaPengaduan = _Paths.wargaPengaduan; static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard; static const petugasDesaDashboard = _Paths.petugasDesaDashboard; @@ -27,6 +27,7 @@ abstract class Routes { static const laporanPenyaluran = _Paths.laporanPenyaluran; static const detailPenyaluran = _Paths.detailPenyaluran; static const riwayatPenyaluran = _Paths.riwayatPenyaluran; + static const wargaDetailPenerimaan = _Paths.wargaDetailPenerimaan; } abstract class _Paths { @@ -35,7 +36,7 @@ abstract class _Paths { static const login = '/login'; static const register = '/register'; static const wargaDashboard = '/warga-dashboard'; - static const wargaPenyaluran = '/warga-penyaluran'; + static const wargaPenerimaan = '/warga-penerimaan'; static const wargaPengaduan = '/warga-pengaduan'; static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard'; static const petugasDesaDashboard = '/petugas-desa-dashboard'; @@ -56,4 +57,5 @@ abstract class _Paths { static const laporanPenyaluran = '/laporan-penyaluran'; static const detailPenyaluran = '/detail-penyaluran'; static const riwayatPenyaluran = '/petugas-desa/riwayat-penyaluran'; + static const wargaDetailPenerimaan = '/warga/detail-penerimaan'; } diff --git a/lib/app/widgets/bantuan_card.dart b/lib/app/widgets/bantuan_card.dart new file mode 100644 index 0000000..fbba55f --- /dev/null +++ b/lib/app/widgets/bantuan_card.dart @@ -0,0 +1,280 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart'; +import 'package:penyaluran_app/app/widgets/status_badge.dart'; + +class BantuanCard extends StatelessWidget { + final PenerimaPenyaluranModel item; + final VoidCallback? onTap; + final bool isCompact; + + const BantuanCard({ + Key? key, + required this.item, + this.onTap, + this.isCompact = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final currencyFormat = NumberFormat.currency( + locale: 'id', + symbol: 'Rp ', + decimalDigits: 0, + ); + + // Format jumlah bantuan berdasarkan tipe (uang atau bukan) + String formattedJumlah = ''; + if (item.jumlahBantuan != null) { + if (item.isUang == true) { + formattedJumlah = currencyFormat.format(item.jumlahBantuan); + } else { + formattedJumlah = '${item.jumlahBantuan} ${item.satuan ?? ''}'; + } + } else { + formattedJumlah = '-'; + } + + // Tampilan kompak untuk daftar ringkasan + if (isCompact) { + return Card( + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: (item.isUang == true ? Colors.green : Colors.blue) + .withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + item.isUang == true + ? Icons.attach_money + : Icons.inventory_2, + color: item.isUang == true ? Colors.green : Colors.blue, + size: 24, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.namaPenyaluran ?? item.keterangan ?? 'Bantuan', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + item.kategoriNama ?? 'Bantuan', + style: TextStyle( + color: Colors.grey.shade700, + fontSize: 14, + ), + ), + const SizedBox(height: 4), + Text( + item.tanggalPenerimaan != null + ? DateFormat('dd MMMM yyyy', 'id_ID') + .format(item.tanggalPenerimaan!) + : '-', + style: TextStyle( + color: Colors.grey.shade600, + fontSize: 12, + ), + ), + ], + ), + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + StatusBadge( + status: item.statusPenerimaan ?? 'MENUNGGU', + fontSize: 10, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + ), + const SizedBox(height: 8), + Text( + formattedJumlah, + style: TextStyle( + fontWeight: FontWeight.bold, + color: item.isUang == true ? Colors.green : Colors.blue, + fontSize: 14, + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + + // Tampilan detail untuk halaman daftar lengkap + return Card( + margin: const EdgeInsets.only(bottom: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 2, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + item.namaPenyaluran ?? item.keterangan ?? 'Bantuan', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + StatusBadge( + status: item.statusPenerimaan ?? 'MENUNGGU', + ), + ], + ), + if (item.deskripsiPenyaluran != null && + item.deskripsiPenyaluran!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + item.deskripsiPenyaluran!, + style: TextStyle( + color: Colors.grey.shade700, + fontSize: 14, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Icon( + Icons.category, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Text( + item.kategoriNama ?? 'Bantuan', + style: TextStyle( + color: Colors.grey.shade700, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Text( + item.tanggalPenerimaan != null + ? DateFormat('dd MMMM yyyy', 'id_ID') + .format(item.tanggalPenerimaan!) + : '-', + style: TextStyle( + color: Colors.grey.shade600, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.location_on, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + item.lokasiPenyaluranNama ?? 'Lokasi tidak tersedia', + style: TextStyle( + color: Colors.grey.shade600, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + item.isUang == true + ? Icons.attach_money + : Icons.inventory_2, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Text( + formattedJumlah, + style: TextStyle( + fontWeight: FontWeight.bold, + color: item.isUang == true ? Colors.green : Colors.blue, + ), + ), + ], + ), + const SizedBox(height: 16), + const Divider(height: 1), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton.icon( + onPressed: onTap, + icon: const Icon(Icons.visibility), + label: const Text('Lihat Detail'), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/app/widgets/section_header.dart b/lib/app/widgets/section_header.dart new file mode 100644 index 0000000..df89541 --- /dev/null +++ b/lib/app/widgets/section_header.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +class SectionHeader extends StatelessWidget { + final String title; + final VoidCallback? onViewAll; + final String? viewAllText; + final Widget? trailing; + final EdgeInsets padding; + final TextStyle? titleStyle; + + const SectionHeader({ + Key? key, + required this.title, + this.onViewAll, + this.viewAllText = 'Lihat Semua', + this.trailing, + this.padding = const EdgeInsets.only(bottom: 12), + this.titleStyle, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: padding, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: titleStyle ?? + const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + if (trailing != null) + trailing! + else if (onViewAll != null) + TextButton( + onPressed: onViewAll, + child: Text(viewAllText!), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 8), + minimumSize: const Size(0, 36), + ), + ), + ], + ), + ); + } +} diff --git a/lib/app/widgets/status_badge.dart b/lib/app/widgets/status_badge.dart new file mode 100644 index 0000000..aec1a84 --- /dev/null +++ b/lib/app/widgets/status_badge.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +class StatusBadge extends StatelessWidget { + final String status; + final Map? customColors; + final Map? customLabels; + final double fontSize; + final EdgeInsets padding; + + const StatusBadge({ + Key? key, + required this.status, + this.customColors, + this.customLabels, + this.fontSize = 12, + this.padding = const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final String statusUpper = status.toUpperCase(); + + // Default colors for common statuses + final Map defaultColors = { + 'DITERIMA': Colors.green, + 'MENUNGGU': Colors.orange, + 'DITOLAK': Colors.red, + 'PROSES': Colors.orange, + 'DIPROSES': Colors.orange, + 'TINDAKAN': Colors.orange, + 'SELESAI': Colors.green, + 'TERVERIFIKASI': Colors.green, + }; + + // Default labels for common statuses + final Map defaultLabels = { + 'DITERIMA': 'Diterima', + 'MENUNGGU': 'Menunggu', + 'DITOLAK': 'Ditolak', + 'PROSES': 'Proses', + 'DIPROSES': 'Proses', + 'TINDAKAN': 'Tindakan', + 'SELESAI': 'Selesai', + 'TERVERIFIKASI': 'Terverifikasi', + }; + + // Determine color and label based on status + final Color color = + (customColors != null && customColors!.containsKey(statusUpper)) + ? customColors![statusUpper]! + : defaultColors.containsKey(statusUpper) + ? defaultColors[statusUpper]! + : Colors.grey; + + final String label = + (customLabels != null && customLabels!.containsKey(statusUpper)) + ? customLabels![statusUpper]! + : defaultLabels.containsKey(statusUpper) + ? defaultLabels[statusUpper]! + : status; + + return Container( + padding: padding, + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all(color: color), + ), + child: Text( + label, + style: TextStyle( + color: color, + fontWeight: FontWeight.bold, + fontSize: fontSize, + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index fa811bb..ebac19b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,7 +40,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( - title: 'Penyaluran App', + title: 'Penerimaan App', theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: ThemeMode.light, // Default ke tema terang