From 8e9553d1fce6a9a5d1eefb8c770ecd6860f19a5a Mon Sep 17 00:00:00 2001 From: Khafidh Fuadi Date: Tue, 25 Mar 2025 10:43:21 +0700 Subject: [PATCH] Perbarui model dan tampilan untuk mendukung perubahan struktur data pengguna. Ganti properti nama dan telepon dengan namaLengkap dan noHp di beberapa model, termasuk DonaturModel, PetugasDesaModel, dan WargaModel. Modifikasi tampilan dan controller untuk menggunakan properti baru ini. Tambahkan fungsionalitas baru untuk menampilkan nama lengkap dan nomor telepon dengan lebih baik di berbagai tampilan. Perbarui rute dan logika 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 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 6466 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 4078 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 9013 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 14295 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 19519 bytes assets/images/penyaluran-icon.png | Bin 0 -> 39627 bytes lib/app/data/models/donatur_model.dart | 32 +- lib/app/data/models/petugas_desa_model.dart | 77 ++- .../data/models/tindakan_pengaduan_model.dart | 12 +- lib/app/data/models/user_model.dart | 142 ++--- lib/app/data/models/warga_model.dart | 124 ++-- lib/app/data/providers/auth_provider.dart | 418 +++++++++--- .../auth/controllers/auth_controller.dart | 217 ++++--- .../laporan_penyaluran_controller.dart | 87 ++- .../views/laporan_penyaluran_detail_view.dart | 28 +- .../detail_penyaluran_controller.dart | 4 +- .../controllers/donatur_controller.dart | 2 +- .../jadwal_penyaluran_controller.dart | 2 +- .../penerima_bantuan_controller.dart | 53 +- .../controllers/pengaduan_controller.dart | 2 +- .../penitipan_bantuan_controller.dart | 61 +- .../controllers/petugas_desa_controller.dart | 140 +++- .../petugas_desa_dashboard_controller.dart | 2 +- .../riwayat_pengaduan_controller.dart | 2 +- .../controllers/stok_bantuan_controller.dart | 2 +- .../views/detail_donatur_view.dart | 2 +- .../views/detail_penyaluran_page.dart | 7 +- .../petugas_desa/views/penitipan_view.dart | 39 +- .../petugas_desa/views/petugas_desa_view.dart | 30 +- .../views/riwayat_penitipan_view.dart | 14 +- .../controllers/profile_controller.dart | 81 ++- .../modules/profile/views/profile_view.dart | 129 +++- .../warga_dashboard_controller.dart | 87 ++- lib/app/services/auth_service.dart | 31 +- lib/app/services/supabase_service.dart | 600 ++++++++++++++++-- 39 files changed, 1819 insertions(+), 704 deletions(-) create mode 100644 assets/images/penyaluran-icon.png 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 44c7df9..b8cb640 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 37946ec..093da6a 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 c805443..bc005df 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 abe166b..4d1087f 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/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..56d485a7429a4a0ab78b819489bf52903e728aab 100644 GIT binary patch literal 6466 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 EUsChokN^Mx literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..804e91b552ece660f0c6e60cf7e86c1249012e82 100644 GIT binary patch literal 4078 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 literal 442 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 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..a7bbc6e754be60305ead42660d6945bd9bd295b8 100644 GIT binary patch literal 14295 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372eebdb28e45604e46eeda8dd24651419bc0..7c50f36ed5e69e8dafc56e35d4f462d1058812c9 100644 GIT binary patch literal 19519 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 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/assets/images/penyaluran-icon.png b/assets/images/penyaluran-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0e1f9117b36a5f13fc5d2203ad59e9b3b126bb61 GIT binary patch literal 39627 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 Gu>S#96_n5b literal 0 HcmV?d00001 diff --git a/lib/app/data/models/donatur_model.dart b/lib/app/data/models/donatur_model.dart index 51ab672..939b5f0 100644 --- a/lib/app/data/models/donatur_model.dart +++ b/lib/app/data/models/donatur_model.dart @@ -1,24 +1,26 @@ import 'dart:convert'; class DonaturModel { - final String? id; - final String? nama; + final String id; // Primary key yang juga foreign key ke auth.users(id) + final String? namaLengkap; final String? alamat; - final String? telepon; + final String? noHp; final String? email; final String? jenis; + final String? deskripsi; final String? status; final DateTime? createdAt; final DateTime? updatedAt; DonaturModel({ - this.id, - this.nama, + required this.id, + this.namaLengkap, this.alamat, - this.telepon, + this.noHp, this.email, this.jenis, - this.status, + this.deskripsi, + this.status = 'AKTIF', this.createdAt, this.updatedAt, }); @@ -30,11 +32,12 @@ class DonaturModel { factory DonaturModel.fromJson(Map json) => DonaturModel( id: json["id"], - nama: json["nama"], + namaLengkap: json["nama_lengkap"], alamat: json["alamat"], - telepon: json["telepon"], + noHp: json["no_hp"], email: json["email"], jenis: json["jenis"], + deskripsi: json["deskripsi"], status: json["status"] ?? 'AKTIF', createdAt: json["created_at"] != null ? DateTime.parse(json["created_at"]) @@ -46,13 +49,20 @@ class DonaturModel { Map toJson() => { "id": id, - "nama": nama, + "nama_lengkap": namaLengkap, "alamat": alamat, - "telepon": telepon, + "no_hp": noHp, "email": email, "jenis": jenis, + "deskripsi": deskripsi, "status": status ?? 'AKTIF', "created_at": createdAt?.toIso8601String(), "updated_at": updatedAt?.toIso8601String(), }; + + // Helper method untuk mendapatkan nama yang ditampilkan + String get displayName => namaLengkap ?? 'Donatur'; + + // Getter untuk kompatibilitas dengan kode yang masih menggunakan nama + String? get nama => namaLengkap; } diff --git a/lib/app/data/models/petugas_desa_model.dart b/lib/app/data/models/petugas_desa_model.dart index 8d90b9e..9a8fd10 100644 --- a/lib/app/data/models/petugas_desa_model.dart +++ b/lib/app/data/models/petugas_desa_model.dart @@ -1,26 +1,31 @@ import 'dart:convert'; +import 'package:penyaluran_app/app/data/models/desa_model.dart'; class PetugasDesaModel { - final String? id; - final String? nama; - final String? alamatLengkap; - final String? noTelp; + final String id; // Primary key yang juga foreign key ke auth.users(id) + final String? desaId; + final String? namaLengkap; + final String? alamat; + final String? noHp; final String? email; final String? jabatan; - final String? userId; + final String? nip; final DateTime? createdAt; final DateTime? updatedAt; + final DesaModel? desa; PetugasDesaModel({ - this.id, - this.nama, - this.alamatLengkap, - this.noTelp, + required this.id, + this.desaId, + this.namaLengkap, + this.alamat, + this.noHp, this.email, this.jabatan, - this.userId, + this.nip, this.createdAt, this.updatedAt, + this.desa, }); factory PetugasDesaModel.fromRawJson(String str) => @@ -28,32 +33,44 @@ class PetugasDesaModel { String toRawJson() => json.encode(toJson()); - factory PetugasDesaModel.fromJson(Map json) => - PetugasDesaModel( - id: json["id"], - nama: json["nama"], - alamatLengkap: json["alamat_lengkap"], - noTelp: json["no_telp"], - email: json["email"], - jabatan: json["jabatan"], - userId: json["user_id"], - createdAt: json["created_at"] != null - ? DateTime.parse(json["created_at"]) - : null, - updatedAt: json["updated_at"] != null - ? DateTime.parse(json["updated_at"]) - : null, - ); + factory PetugasDesaModel.fromJson(Map json) { + DesaModel? desa; + if (json["desa"] != null && json["desa"] is Map) { + desa = DesaModel.fromJson(json["desa"]); + } + + return PetugasDesaModel( + id: json["id"], + desaId: json["desa_id"], + namaLengkap: json["nama_lengkap"], + alamat: json["alamat"], + noHp: json["no_hp"], + email: json["email"], + jabatan: json["jabatan"], + nip: json["nip"], + createdAt: json["created_at"] != null + ? DateTime.parse(json["created_at"]) + : null, + updatedAt: json["updated_at"] != null + ? DateTime.parse(json["updated_at"]) + : null, + desa: desa, + ); + } Map toJson() => { "id": id, - "nama": nama, - "alamat_lengkap": alamatLengkap, - "no_telp": noTelp, + "desa_id": desaId, + "nama_lengkap": namaLengkap, + "alamat": alamat, + "no_hp": noHp, "email": email, "jabatan": jabatan, - "user_id": userId, + "nip": nip, "created_at": createdAt?.toIso8601String(), "updated_at": updatedAt?.toIso8601String(), }; + + // Helper method untuk mendapatkan nama yang ditampilkan + String get displayName => namaLengkap ?? 'Petugas Desa'; } diff --git a/lib/app/data/models/tindakan_pengaduan_model.dart b/lib/app/data/models/tindakan_pengaduan_model.dart index c793516..2549b5a 100644 --- a/lib/app/data/models/tindakan_pengaduan_model.dart +++ b/lib/app/data/models/tindakan_pengaduan_model.dart @@ -101,20 +101,16 @@ class TindakanPengaduanModel { // Getter untuk mendapatkan nama petugas String get namaPetugas { - if (petugas != null && petugas!['nama'] != null) { - return petugas!['nama']; - } else if (petugas != null && petugas!['name'] != null) { - return petugas!['name']; + if (petugas != null && petugas!['nama_lengkap'] != null) { + return petugas!['nama_lengkap']; } return 'Petugas'; } // Getter untuk mendapatkan nama verifikator String get namaVerifikator { - if (verifikator != null && verifikator!['nama'] != null) { - return verifikator!['nama']; - } else if (verifikator != null && verifikator!['name'] != null) { - return verifikator!['name']; + if (verifikator != null && verifikator!['nama_lengkap'] != null) { + return verifikator!['nama_lengkap']; } return 'Belum diverifikasi'; } diff --git a/lib/app/data/models/user_model.dart b/lib/app/data/models/user_model.dart index 062e157..ac5926e 100644 --- a/lib/app/data/models/user_model.dart +++ b/lib/app/data/models/user_model.dart @@ -1,51 +1,58 @@ import 'package:penyaluran_app/app/data/models/desa_model.dart'; -class UserModel { +// Model dasar untuk pengguna dengan informasi autentikasi umum +class BaseUserModel { final String id; final String email; - final String? name; - final String? avatar; - final String role; + final int? roleId; + final String roleName; final bool isActive; - final DesaModel? desa; - final String? desaId; final DateTime? lastLogin; final DateTime? createdAt; final DateTime? updatedAt; + final String? name; + final String? role; + final DesaModel? desa; - UserModel({ + BaseUserModel({ required this.id, required this.email, - this.name, - this.avatar, - required this.role, + this.roleId, + required this.roleName, this.isActive = true, - this.desa, - this.desaId, this.lastLogin, this.createdAt, this.updatedAt, + this.name, + this.role, + this.desa, }); - factory UserModel.fromJson(Map json) { + factory BaseUserModel.fromJson(Map json) { + // Pastikan id dan email tidak null if (json['id'] == null || json['email'] == null) { - throw Exception('UserModel: id dan email tidak boleh null'); + throw Exception('BaseUserModel: id dan email tidak boleh null'); } - // Parse desa jika ada - DesaModel? desaModel; + // Dapatkan roleName, default ke 'warga' jika tidak tersedia + String roleName = 'warga'; + if (json['roles'] != null && json['roles']['role_name'] != null) { + roleName = json['roles']['role_name']; + } else if (json['role'] != null) { + roleName = json['role']; + } + + // Parse desa data jika ada + DesaModel? desa; if (json['desa'] != null && json['desa'] is Map) { - desaModel = DesaModel.fromJson(json['desa'] as Map); + desa = DesaModel.fromJson(json['desa']); } - return UserModel( + return BaseUserModel( id: json['id'], email: json['email'], - name: json['name'], - avatar: json['avatar'], - desa: desaModel, - desaId: json['desa_id'], - role: json['role'] ?? 'WARGA', + roleId: json['role_id'], + roleName: roleName, isActive: json['is_active'] ?? true, lastLogin: json['last_login'] != null ? DateTime.parse(json['last_login']) @@ -56,6 +63,9 @@ class UserModel { updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']) : null, + name: json['name'], + role: roleName, + desa: desa, ); } @@ -63,83 +73,25 @@ class UserModel { return { 'id': id, 'email': email, - 'name': name, - 'avatar': avatar, - 'desa_id': desaId, - 'desa': desa?.toJson(), - 'role': role, + 'role_id': roleId, + 'role': roleName, 'is_active': isActive, 'last_login': lastLogin?.toIso8601String(), 'created_at': createdAt?.toIso8601String(), 'updated_at': updatedAt?.toIso8601String(), - }; - } - - UserModel copyWith({ - String? id, - String? email, - String? name, - String? avatar, - DesaModel? desa, - String? desaId, - String? role, - bool? isActive, - DateTime? lastLogin, - DateTime? createdAt, - DateTime? updatedAt, - }) { - return UserModel( - id: id ?? this.id, - email: email ?? this.email, - name: name ?? this.name, - avatar: avatar ?? this.avatar, - desa: desa ?? this.desa, - desaId: desaId ?? this.desaId, - role: role ?? this.role, - isActive: isActive ?? this.isActive, - lastLogin: lastLogin ?? this.lastLogin, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ); - } -} - -class User { - final String? id; - final String? name; - final String? email; - final String? phone; - final String? role; - final String? token; - - User({ - this.id, - this.name, - this.email, - this.phone, - this.role, - this.token, - }); - - factory User.fromJson(Map json) { - return User( - id: json['id'], - name: json['name'], - email: json['email'], - phone: json['phone'], - role: json['role'], - token: json['token'], - ); - } - - Map toJson() { - return { - 'id': id, 'name': name, - 'email': email, - 'phone': phone, - 'role': role, - 'token': token, + 'desa': desa?.toJson(), }; } } + +// Class untuk menampung data user lengkap (BaseUserModel + data spesifik role) +class UserData { + final BaseUserModel baseUser; + final T roleData; + + UserData({ + required this.baseUser, + required this.roleData, + }); +} diff --git a/lib/app/data/models/warga_model.dart b/lib/app/data/models/warga_model.dart index 2c3393b..9a4b00a 100644 --- a/lib/app/data/models/warga_model.dart +++ b/lib/app/data/models/warga_model.dart @@ -1,41 +1,44 @@ import 'dart:convert'; +import 'package:penyaluran_app/app/data/models/desa_model.dart'; // warga == penerima bantuan class WargaModel { - final String? id; - final String? nama; - final String? nik; + final String id; // Primary key yang juga foreign key ke auth.users(id) + final String? desaId; + final String? namaLengkap; final String? alamat; - final String? desa; - final String? kecamatan; - final String? kabupaten; - final String? provinsi; - final String? telepon; + final String? noHp; final String? email; + final String? nik; + final String? tempatLahir; + final DateTime? tanggalLahir; + final String? jenisKelamin; + final String? agama; + final String? kategoriEkonomi; + final String? status; final String? catatan; - final String? kategori; // Contoh: 'lansia', 'disabilitas', 'miskin', dll - final String? status; // Contoh: 'AKTIF', 'NONAKTIF' - final String? lokasiPenyaluranId; // Referensi ke LokasiPenyaluran final DateTime? createdAt; final DateTime? updatedAt; + final DesaModel? desa; WargaModel({ - this.id, - this.nama, - this.nik, + required this.id, + this.desaId, + this.namaLengkap, this.alamat, - this.desa, - this.kecamatan, - this.kabupaten, - this.provinsi, - this.telepon, + this.noHp, this.email, + this.nik, + this.tempatLahir, + this.tanggalLahir, + this.jenisKelamin, + this.agama, + this.kategoriEkonomi, + this.status = 'AKTIF', this.catatan, - this.kategori, - this.status, - this.lokasiPenyaluranId, this.createdAt, this.updatedAt, + this.desa, }); factory WargaModel.fromRawJson(String str) => @@ -43,45 +46,58 @@ class WargaModel { String toRawJson() => json.encode(toJson()); - factory WargaModel.fromJson(Map json) => WargaModel( - id: json["id"], - nama: json["nama"], - nik: json["nik"], - alamat: json["alamat"], - desa: json["desa"], - kecamatan: json["kecamatan"], - kabupaten: json["kabupaten"], - provinsi: json["provinsi"], - telepon: json["telepon"] ?? json["no_telp"], - email: json["email"], - catatan: json["catatan"], - kategori: json["kategori"], - status: json["status"], - lokasiPenyaluranId: json["lokasi_penyaluran_id"], - createdAt: json["created_at"] != null - ? DateTime.parse(json["created_at"]) - : null, - updatedAt: json["updated_at"] != null - ? DateTime.parse(json["updated_at"]) - : null, - ); + factory WargaModel.fromJson(Map json) { + DesaModel? desa; + if (json["desa"] != null && json["desa"] is Map) { + desa = DesaModel.fromJson(json["desa"]); + } + + return WargaModel( + id: json["id"], + desaId: json["desa_id"], + namaLengkap: json["nama_lengkap"], + alamat: json["alamat"], + noHp: json["no_hp"], + email: json["email"], + nik: json["nik"], + tempatLahir: json["tempat_lahir"], + tanggalLahir: json["tanggal_lahir"] != null + ? DateTime.parse(json["tanggal_lahir"]) + : null, + jenisKelamin: json["jenis_kelamin"], + agama: json["agama"], + kategoriEkonomi: json["kategori_ekonomi"], + status: json["status"] ?? 'AKTIF', + catatan: json["catatan"], + createdAt: json["created_at"] != null + ? DateTime.parse(json["created_at"]) + : null, + updatedAt: json["updated_at"] != null + ? DateTime.parse(json["updated_at"]) + : null, + desa: desa, + ); + } Map toJson() => { "id": id, - "nama": nama, - "nik": nik, + "desa_id": desaId, + "nama_lengkap": namaLengkap, "alamat": alamat, - "desa": desa, - "kecamatan": kecamatan, - "kabupaten": kabupaten, - "provinsi": provinsi, - "telepon": telepon, + "no_hp": noHp, "email": email, - "catatan": catatan, - "kategori": kategori, + "nik": nik, + "tempat_lahir": tempatLahir, + "tanggal_lahir": tanggalLahir?.toIso8601String(), + "jenis_kelamin": jenisKelamin, + "agama": agama, + "kategori_ekonomi": kategoriEkonomi, "status": status, - "lokasi_penyaluran_id": lokasiPenyaluranId, + "catatan": catatan, "created_at": createdAt?.toIso8601String(), "updated_at": updatedAt?.toIso8601String(), }; + + // Helper method untuk mendapatkan nama yang ditampilkan + String get displayName => namaLengkap ?? 'Warga'; } diff --git a/lib/app/data/providers/auth_provider.dart b/lib/app/data/providers/auth_provider.dart index 921ca54..39fae41 100644 --- a/lib/app/data/providers/auth_provider.dart +++ b/lib/app/data/providers/auth_provider.dart @@ -1,44 +1,162 @@ import 'package:penyaluran_app/app/services/supabase_service.dart'; import 'package:penyaluran_app/app/data/models/user_model.dart'; +import 'package:penyaluran_app/app/data/models/warga_model.dart'; +import 'package:penyaluran_app/app/data/models/petugas_desa_model.dart'; +import 'package:penyaluran_app/app/data/models/donatur_model.dart'; +import 'package:penyaluran_app/app/data/models/desa_model.dart'; class AuthProvider { final SupabaseService _supabaseService = SupabaseService.to; - // Cache untuk menyimpan data profil pengguna - UserModel? _cachedUser; + // Cache untuk menyimpan data pengguna + UserData? _cachedUserData; // Metode untuk login - Future signIn(String email, String password) async { + Future signIn(String email, String password) async { try { - final response = await _supabaseService.signIn(email, password); + // Step 1: Login + final response = await _supabaseService.client.auth.signInWithPassword( + email: email, + password: password, + ); - if (response.user != null && response.user?.email != null) { - // Ambil profil pengguna dari database - final profileData = await _supabaseService.getUserProfile(); - print('DEBUG: Profile data dari signIn: $profileData'); - - if (profileData != null) { - // Buat UserModel dengan data yang ada - _cachedUser = UserModel.fromJson({ - ...profileData, - 'id': response.user!.id, - 'email': response.user!.email!, - }); - print( - 'DEBUG: User model dibuat: ${_cachedUser?.name}, desa: ${_cachedUser?.desa?.nama}'); - return _cachedUser; - } - - // Jika profil belum tersedia, gunakan data default - _cachedUser = UserModel( - id: response.user!.id, - email: response.user!.email!, - role: 'WARGA', // Default role - ); - print('DEBUG: User model default dibuat: ${_cachedUser?.email}'); - return _cachedUser; + final user = response.user; + if (user == null) { + print('Login gagal!'); + return null; } - return null; + + final userId = user.id; + + // Step 2: Ambil role dari view `users_with_roles` atau tabel roles + String roleName; + try { + final roleResponse = await _supabaseService.client + .from('users_with_roles') + .select('role_name') + .eq('id', userId) + .single(); + + roleName = roleResponse['role_name']; + } catch (e) { + print('Error mengambil role dari users_with_roles: $e'); + print('Mencoba ambil role dari tabel roles...'); + + // Fallback ke cara lama jika view users_with_roles tidak tersedia + try { + // Ambil role_id dari user metadata + final roleId = user.userMetadata?['role_id']; + if (roleId == null) { + print('Tidak ada role_id di user metadata'); + return null; + } + + final roleResponse = await _supabaseService.client + .from('roles') + .select('role_name') + .eq('id', roleId) + .single(); + + roleName = roleResponse['role_name']; + } catch (e) { + print('Error mengambil role dari tabel roles: $e'); + return null; + } + } + + print('Role: $roleName'); + + // Step 3: Ambil profil user berdasarkan role + Map? profileResponse; + dynamic roleData; + DesaModel? desa; + + // Buat BaseUserModel + final baseUser = BaseUserModel( + id: userId, + email: user.email ?? '', + roleId: user.userMetadata?['role_id'], + roleName: roleName, + createdAt: DateTime.parse(user.createdAt), + updatedAt: + user.updatedAt != null ? DateTime.parse(user.updatedAt!) : null, + ); + + if (roleName == 'warga') { + profileResponse = await _supabaseService.client + .from('warga') + .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .single(); + + if (profileResponse != null) { + // Ekstrak data desa jika ada + if (profileResponse['desa'] != null) { + desa = DesaModel.fromJson(profileResponse['desa']); + print('Data Desa: ${desa.nama}'); + } + + roleData = WargaModel.fromJson(profileResponse); + print('Data Warga: ${roleData.namaLengkap}'); + } else { + print('Tidak menemukan data warga untuk ID: $userId'); + return null; + } + } else if (roleName == 'petugas_desa') { + profileResponse = await _supabaseService.client + .from('petugas_desa') + .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .single(); + + if (profileResponse != null) { + // Ekstrak data desa jika ada + if (profileResponse['desa'] != null) { + desa = DesaModel.fromJson(profileResponse['desa']); + print('Data Desa: ${desa.nama}'); + } + + roleData = PetugasDesaModel.fromJson(profileResponse); + print( + 'Data Petugas Desa: ${roleData.namaLengkap}, Desa: ${roleData.desa?.nama}'); + } + } else if (roleName == 'donatur') { + profileResponse = await _supabaseService.client + .from('donatur') + .select('*') + .eq('id', userId) + .single(); + + roleData = DonaturModel.fromJson(profileResponse); + print('Data Donatur: ${roleData.namaLengkap}'); + } else { + return null; + } + + if (roleData == null) { + print('Tidak menemukan data profil untuk role: $roleName'); + return null; + } + + // Perbarui baseUser dengan data desa jika ada + final updatedBaseUser = BaseUserModel( + id: baseUser.id, + email: baseUser.email, + roleId: baseUser.roleId, + roleName: baseUser.roleName, + createdAt: baseUser.createdAt, + updatedAt: baseUser.updatedAt, + desa: desa, // Set desa dari data yang diambil + ); + + // Simpan cache user data + final userData = UserData( + baseUser: updatedBaseUser, + roleData: roleData, + ); + _cachedUserData = userData; + + return userData; } catch (e) { print('Error pada signIn: $e'); rethrow; @@ -49,57 +167,159 @@ class AuthProvider { Future signOut() async { try { await _supabaseService.signOut(); - _cachedUser = null; // Hapus cache saat logout + _cachedUserData = null; // Hapus cache saat logout } catch (e) { rethrow; } } // Metode untuk mendapatkan user saat ini - Future getCurrentUser() async { - // Jika ada cache dan user masih terautentikasi, gunakan cache - if (_cachedUser != null && _supabaseService.isAuthenticated) { - print( - 'DEBUG: Menggunakan data user dari cache: ${_cachedUser?.name}, desa: ${_cachedUser?.desa?.nama}'); - return _cachedUser; + Future getCurrentUser({bool skipCache = false}) async { + // Jika ada cache dan user masih terautentikasi, gunakan cache kecuali skipCache = true + if (!skipCache && + _cachedUserData != null && + _supabaseService.isAuthenticated) { + print('DEBUG: Menggunakan data user dari cache'); + return _cachedUserData; } - final user = _supabaseService.currentUser; - if (user != null) { + if (_supabaseService.currentUser != null) { try { - // Ambil profil pengguna dari database - final profileData = await _supabaseService.getUserProfile(); - print('DEBUG: Profile data dari getCurrentUser: $profileData'); + // Login berhasil, lakukan proses yang sama seperti di signIn + // tapi dengan user yang sudah ada + final user = _supabaseService.currentUser!; + final userId = user.id; - if (profileData != null) { - // Buat UserModel dengan data yang ada - _cachedUser = UserModel.fromJson({ - ...profileData, - 'id': user.id, - 'email': user.email!, - }); - print( - 'DEBUG: User model dibuat: ${_cachedUser?.name}, desa: ${_cachedUser?.desa?.nama}'); - return _cachedUser; + // Step 2: Ambil role dari view `users_with_roles` atau tabel roles + String roleName; + try { + final roleResponse = await _supabaseService.client + .from('users_with_roles') + .select('role_name') + .eq('id', userId) + .single(); + + roleName = roleResponse['role_name']; + } catch (e) { + print('Error mengambil role dari users_with_roles: $e'); + print('Mencoba ambil role dari tabel roles...'); + + // Fallback ke cara lama jika view users_with_roles tidak tersedia + try { + // Ambil role_id dari user metadata + final roleId = user.userMetadata?['role_id']; + if (roleId == null) { + print('Tidak ada role_id di user metadata'); + return null; + } + + final roleResponse = await _supabaseService.client + .from('roles') + .select('role_name') + .eq('id', roleId) + .single(); + + roleName = roleResponse['role_name']; + } catch (e) { + print('Error mengambil role dari tabel roles: $e'); + return null; + } } - // Jika profil belum tersedia, gunakan data default - _cachedUser = UserModel( - id: user.id, - email: user.email!, - role: 'WARGA', // Default role + print('Role: $roleName'); + + // Step 3: Ambil profil user berdasarkan role + Map? profileResponse; + dynamic roleData; + DesaModel? desa; + + // Buat BaseUserModel + final baseUser = BaseUserModel( + id: userId, + email: user.email ?? '', + roleId: user.userMetadata?['role_id'], + roleName: roleName ?? '', + createdAt: DateTime.parse(user.createdAt), + updatedAt: + user.updatedAt != null ? DateTime.parse(user.updatedAt!) : null, ); - print('DEBUG: User model default dibuat: ${_cachedUser?.email}'); - return _cachedUser; + + if ((roleName ?? '').toLowerCase() == 'warga') { + profileResponse = await _supabaseService.client + .from('warga') + .select( + '*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .single(); + + if (profileResponse != null) { + // Ekstrak data desa jika ada + if (profileResponse['desa'] != null) { + desa = DesaModel.fromJson(profileResponse['desa']); + print('Data Desa: ${desa.nama}'); + } + + roleData = WargaModel.fromJson(profileResponse); + print('Data Warga: ${roleData.namaLengkap}'); + } else { + print('Tidak menemukan data warga untuk ID: $userId'); + return null; + } + } else if (roleName.toLowerCase() == 'petugas_desa') { + profileResponse = await _supabaseService.client + .from('petugas_desa') + .select( + '*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .single(); + + if (profileResponse != null) { + // Ekstrak data desa jika ada + if (profileResponse['desa'] != null) { + desa = DesaModel.fromJson(profileResponse['desa']); + print('Data Desa: ${desa.nama}'); + } + + roleData = PetugasDesaModel.fromJson(profileResponse); + } + } else if (roleName.toLowerCase() == 'donatur') { + profileResponse = await _supabaseService.client + .from('donatur') + .select('*') + .eq('id', userId) + .single(); + + if (profileResponse != null) { + roleData = DonaturModel.fromJson(profileResponse); + } + } + + if (roleData == null) { + print('Tidak menemukan data profil untuk role: $roleName'); + return null; + } + + // Perbarui baseUser dengan data desa jika ada + final updatedBaseUser = BaseUserModel( + id: baseUser.id, + email: baseUser.email, + roleId: baseUser.roleId, + roleName: baseUser.roleName, + createdAt: baseUser.createdAt, + updatedAt: baseUser.updatedAt, + desa: desa, // Set desa dari data yang diambil + ); + + // Simpan cache user data + final userData = UserData( + baseUser: updatedBaseUser, + roleData: roleData, + ); + _cachedUserData = userData; + + return userData; } catch (e) { print('Error pada getCurrentUser: $e'); - // Jika terjadi error, kembalikan model dengan data minimal - _cachedUser = UserModel( - id: user.id, - email: user.email!, - role: 'WARGA', // Default role - ); - return _cachedUser; } } return null; @@ -115,31 +335,49 @@ class AuthProvider { return await _supabaseService.getWargaByUserId(); } - // Metode untuk membuat profil warga - Future createWargaProfile({ - required String nik, - required String namaLengkap, - required String jenisKelamin, - String? noHp, - String? alamat, - String? tempatLahir, - DateTime? tanggalLahir, - String? agama, - }) async { - await _supabaseService.createWargaProfile( - nik: nik, - namaLengkap: namaLengkap, - jenisKelamin: jenisKelamin, - noHp: noHp, - alamat: alamat, - tempatLahir: tempatLahir, - tanggalLahir: tanggalLahir, - agama: agama, - ); + // // Metode untuk membuat profil donatur + // Future createDonaturProfile({ + // required String namaLengkap, + // String? instansi, + // String? jabatan, + // String? noHp, + // String? alamat, + // String? desaId, + // }) async { + // await _supabaseService.createDonaturProfile( + // namaLengkap: namaLengkap, + // instansi: instansi, + // jabatan: jabatan, + // noHp: noHp, + // alamat: alamat, + // desaId: desaId, + // ); - // Invalidasi cache setelah membuat profil baru - _cachedUser = null; - } + // // Invalidasi cache setelah membuat profil baru + // _cachedUserData = null; + // } + + // // Metode untuk membuat profil petugas desa + // Future createPetugasDesaProfile({ + // required String namaLengkap, + // String? nip, + // String? jabatan, + // String? noHp, + // String? alamat, + // String? desaId, + // }) async { + // await _supabaseService.createPetugasDesaProfile( + // namaLengkap: namaLengkap, + // nip: nip, + // jabatan: jabatan, + // noHp: noHp, + // alamat: alamat, + // desaId: desaId, + // ); + + // // Invalidasi cache setelah membuat profil baru + // _cachedUserData = null; + // } // Metode untuk mendapatkan notifikasi pengguna Future>> getUserNotifications( diff --git a/lib/app/modules/auth/controllers/auth_controller.dart b/lib/app/modules/auth/controllers/auth_controller.dart index 92ecdb6..7a807b8 100644 --- a/lib/app/modules/auth/controllers/auth_controller.dart +++ b/lib/app/modules/auth/controllers/auth_controller.dart @@ -1,17 +1,51 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:penyaluran_app/app/data/models/donatur_model.dart'; +import 'package:penyaluran_app/app/data/models/petugas_desa_model.dart'; import 'package:penyaluran_app/app/data/models/user_model.dart'; +import 'package:penyaluran_app/app/data/models/warga_model.dart'; import 'package:penyaluran_app/app/data/providers/auth_provider.dart'; import 'package:penyaluran_app/app/routes/app_pages.dart'; -import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart'; class AuthController extends GetxController { static AuthController get to => Get.find(); final AuthProvider _authProvider = AuthProvider(); - final Rx _user = Rx(null); - UserModel? get user => _user.value; + final Rx _userData = Rx(null); + UserData? get userData => _userData.value; + + // Getter untuk BaseUserModel + BaseUserModel? get baseUser => _userData.value?.baseUser; + + // Getter untuk role + String get role => _userData.value?.baseUser.roleName.toLowerCase() ?? ''; + + // Getter dinamis untuk data role-specific + dynamic get roleData => _userData.value?.roleData; + + // Getter untuk memeriksa tipe roleData + bool get isWarga => + _userData.value?.roleData is WargaModel && role == 'warga'; + bool get isDonatur => + _userData.value?.roleData is DonaturModel && role == 'donatur'; + bool get isPetugasDesa => + _userData.value?.roleData is PetugasDesaModel && role == 'petugas_desa'; + + // Helper method untuk mendapatkan nama display + String get displayName { + if (roleData == null) return baseUser?.email ?? 'Pengguna'; + + if (isWarga) { + return (roleData as WargaModel).displayName; + } else if (isDonatur) { + return (roleData as DonaturModel).displayName; + } else if (isPetugasDesa) { + return (roleData as PetugasDesaModel).displayName; + } + + return baseUser?.email ?? 'Pengguna'; + } final RxBool isLoading = false.obs; final RxBool isWargaProfileComplete = false.obs; @@ -74,21 +108,21 @@ class AuthController extends GetxController { print('Memeriksa status autentikasi...'); // Jika user sudah ada di memori dan profil sudah diambil, gunakan data yang ada - if (_user.value != null && _hasLoadedProfile.value) { + if (_userData.value != null && _hasLoadedProfile.value) { print('Menggunakan data user yang sudah ada di memori'); - _handleAuthenticatedUser(_user.value!); + _handleAuthenticatedUser(_userData.value!); return; } // Jika belum ada data user, ambil dari provider - final currentUser = await _authProvider.getCurrentUser(); + final currentUserData = await _authProvider.getCurrentUser(); - if (currentUser != null) { + if (currentUserData != null) { print( - 'User terautentikasi: ${currentUser.email}, role: ${currentUser.role}'); - _user.value = currentUser; + 'User terautentikasi: ${currentUserData.baseUser.email}, role: ${currentUserData.baseUser.roleName}'); + _userData.value = currentUserData; _hasLoadedProfile.value = true; - _handleAuthenticatedUser(currentUser); + _handleAuthenticatedUser(currentUserData); } else { print('Tidak ada user yang terautentikasi'); _handleUnauthenticatedUser(); @@ -104,13 +138,13 @@ class AuthController extends GetxController { } // Metode untuk menangani user yang terautentikasi - void _handleAuthenticatedUser(UserModel user) { + void _handleAuthenticatedUser(UserData userData) { // Hindari navigasi jika sudah berada di halaman yang sesuai final currentRoute = Get.currentRoute; print('Rute saat ini: $currentRoute'); - // Pastikan role tidak null, gunakan default jika null - final role = user.role.isNotEmpty ? user.role : 'WARGA'; + // Dapatkan role dari BaseUserModel + final role = userData.baseUser.roleName.toLowerCase(); print('Role yang digunakan: $role'); // Untuk semua role, arahkan ke dashboard masing-masing @@ -146,9 +180,8 @@ class AuthController extends GetxController { // Memeriksa status profil warga Future checkWargaProfileStatus() async { try { - if (_user.value?.role == 'WARGA') { - final wargaData = await _authProvider.getWargaData(); - isWargaProfileComplete.value = wargaData != null; + if (role == 'warga') { + isWargaProfileComplete.value = roleData != null; } else { isWargaProfileComplete.value = true; } @@ -177,17 +210,14 @@ class AuthController extends GetxController { // Bersihkan dependensi form sebelum navigasi clearFormDependencies(); - switch (role) { - case 'WARGA': + switch (role.toLowerCase()) { + case 'warga': Get.offAllNamed(Routes.wargaDashboard); break; - case 'PETUGASVERIFIKASI': - Get.offAllNamed(Routes.petugasVerifikasiDashboard); - break; - case 'PETUGASDESA': + case 'petugas_desa': Get.offAllNamed(Routes.petugasDesaDashboard); break; - case 'DONATUR': + case 'donatur': Get.offAllNamed(Routes.donaturDashboard); break; default: @@ -229,88 +259,81 @@ class AuthController extends GetxController { isLoading.value = true; print('DEBUG: Memanggil _authProvider.signIn'); - final user = await _authProvider.signIn( + final userData = await _authProvider.signIn( email, password, ); - print('DEBUG: Hasil signIn: ${user != null ? 'Berhasil' : 'Gagal'}'); - if (user != null) { - print('DEBUG: User ditemukan, role: ${user.role}'); - _user.value = user; + print('DEBUG: Hasil signIn: ${userData != null ? 'Berhasil' : 'Gagal'}'); + if (userData != null) { + print('DEBUG: User ditemukan, role: ${userData.baseUser.roleName}'); + _userData.value = userData; _hasLoadedProfile.value = true; // Tandai bahwa profil sudah diambil clearControllers(); // Arahkan ke dashboard sesuai peran - print('DEBUG: Navigasi berdasarkan peran: ${user.role}'); - navigateBasedOnRole(user.role); + print( + 'DEBUG: Navigasi berdasarkan peran: ${userData.baseUser.roleName}'); + navigateBasedOnRole(userData.baseUser.roleName.toLowerCase()); } else { print('DEBUG: User null setelah login berhasil'); + handleLoginError(Exception('Data pengguna tidak ditemukan')); } } catch (e) { - print('DEBUG: Error detail pada login: $e'); - print('DEBUG: Stack trace: ${StackTrace.current}'); - Get.snackbar( - 'Error', - 'Login gagal: ${e.toString()}', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.red, - colorText: Colors.white, - ); + handleLoginError(e); } finally { print('DEBUG: Mengatur isLoading ke false'); isLoading.value = false; } } - // Metode untuk logout - Future logout() async { - try { - // Ambil semua controller yang mungkin perlu dibersihkan - try { - final wargaController = Get.find(); - wargaController.penerimaPenyaluran.clear(); - wargaController.pengajuanKelayakan.clear(); - wargaController.pengaduan.clear(); - } catch (e) { - // Jika controller tidak ditemukan, abaikan - print('Controller tidak ditemukan: $e'); - } + // Menangani error saat login + void handleLoginError(dynamic error) { + print('DEBUG: Error login: $error'); - // Logout dari Supabase - await _authProvider.signOut(); + String errorMessage = 'Terjadi kesalahan saat login. Silakan coba lagi.'; - // Reset semua state - _user.value = null; - _hasLoadedProfile.value = false; - isWargaProfileComplete.value = false; - - // Bersihkan dependensi form sebelum navigasi - clearFormDependencies(); - - // Navigasi ke halaman login - Get.offAllNamed(Routes.login); - } catch (e) { - Get.snackbar( - 'Error', - 'Logout gagal: ${e.toString()}', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.red, - colorText: Colors.white, - ); + if (error.toString().contains('Invalid login credentials')) { + errorMessage = 'Email atau password salah. Silakan coba lagi.'; + } else if (error.toString().contains('Too many requests')) { + errorMessage = 'Terlalu banyak percobaan login. Silakan coba lagi nanti.'; } + + Get.snackbar( + 'Error', + errorMessage, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red, + colorText: Colors.white, + ); } // Metode untuk membersihkan controller void clearControllers() { + emailController.clear(); + passwordController.clear(); + confirmPasswordController.clear(); + } + + // Metode untuk logout + Future logout() async { try { - if (emailController.text.isNotEmpty) emailController.clear(); - if (passwordController.text.isNotEmpty) passwordController.clear(); - if (confirmPasswordController.text.isNotEmpty) { - confirmPasswordController.clear(); - } + isLoading.value = true; + await _authProvider.signOut(); + _userData.value = null; + _hasLoadedProfile.value = false; + Get.offAllNamed(Routes.login); } catch (e) { - print('Error clearing controllers: $e'); + print('Error saat logout: $e'); + Get.snackbar( + 'Error', + 'Terjadi kesalahan saat logout. Silakan coba lagi.', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red, + colorText: Colors.white, + ); + } finally { + isLoading.value = false; } } @@ -355,16 +378,42 @@ class AuthController extends GetxController { } } + // Metode untuk refresh data user setelah update profil + Future refreshUserData() async { + try { + print('Memperbarui data pengguna...'); + isLoading.value = true; + + // Hapus cache profil yang sudah tidak valid + _hasLoadedProfile.value = false; + + // Ambil data user terbaru dari provider dengan menskip cache + final currentUserData = + await _authProvider.getCurrentUser(skipCache: true); + + if (currentUserData != null) { + print( + 'Data pengguna berhasil diperbarui: ${currentUserData.baseUser.name}'); + _userData.value = currentUserData; + _hasLoadedProfile.value = true; + } else { + print('Gagal memperbarui data pengguna'); + } + } catch (e) { + print('Error refreshing user data: $e'); + } finally { + isLoading.value = false; + } + } + // Mendapatkan rute target berdasarkan role String _getTargetRouteForRole(String role) { - switch (role) { - case 'WARGA': + switch (role.toLowerCase()) { + case 'warga': return Routes.wargaDashboard; - case 'PETUGASVERIFIKASI': - return Routes.petugasVerifikasiDashboard; - case 'PETUGASDESA': + case 'petugas_desa': return Routes.petugasDesaDashboard; - case 'DONATUR': + case 'donatur': return Routes.donaturDashboard; default: return Routes.home; diff --git a/lib/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart b/lib/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart index 2c378d4..5e59518 100644 --- a/lib/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart +++ b/lib/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart @@ -34,6 +34,7 @@ class LaporanPenyaluranController extends GetxController { final RxMap lokasiPenyaluran = RxMap(); final RxMap desaData = RxMap(); final RxMap kategoriBantuan = RxMap(); + final RxMap petugasData = RxMap(); // Form controllers final TextEditingController judulController = TextEditingController(); @@ -53,8 +54,8 @@ class LaporanPenyaluranController extends GetxController { final RxString filterStatus = 'SEMUA'.obs; // Getter untuk data user - get user => _authController.user; - String get role => user?.role ?? 'WARGA'; + dynamic get user => _authController.baseUser; + String get role => _authController.role; @override void onInit() { @@ -168,6 +169,8 @@ class LaporanPenyaluranController extends GetxController { .single(); selectedPenyaluran.value = PenyaluranBantuanModel.fromJson(response); + print( + 'PenyaluranDetail - petugasId: ${selectedPenyaluran.value?.petugasId}'); // Ambil data penerima terkait await fetchPenerimaPenyaluran(penyaluranId); @@ -178,6 +181,11 @@ class LaporanPenyaluranController extends GetxController { selectedPenyaluran.value!.lokasiPenyaluranId!); } + // Ambil data petugas jika ada + if (selectedPenyaluran.value?.petugasId != null) { + await fetchPetugasData(selectedPenyaluran.value!.petugasId!); + } + // Hitung penggunaan stok bantuan await calculateStokBantuanUsage(penyaluranId); } catch (e) { @@ -251,6 +259,22 @@ class LaporanPenyaluranController extends GetxController { } } + // Mendapatkan data petugas + Future fetchPetugasData(String petugasId) async { + try { + final response = await _supabaseService.client + .from('petugas_desa') + .select('*, desa(nama)') + .eq('id', petugasId) + .single(); + + petugasData.value = response; + print('Data petugas berhasil diambil: $petugasData'); + } catch (e) { + print('Error fetching petugas data: $e'); + } + } + // Menghitung penggunaan stok bantuan Future calculateStokBantuanUsage(String penyaluranId) async { try { @@ -563,7 +587,8 @@ class LaporanPenyaluranController extends GetxController { // Load logo - tidak perlu menampilkan error jika logo tidak ada pw.MemoryImage? logoImage; try { - final logoBytes = await rootBundle.load('assets/img/logo.png'); + final logoBytes = + await rootBundle.load('assets/images/penyaluran-icon.png'); logoImage = pw.MemoryImage(logoBytes.buffer.asUint8List()); } catch (e) { // Logo tidak ditemukan - tidak perlu print error @@ -600,7 +625,7 @@ class LaporanPenyaluranController extends GetxController { children: [ logoImage != null ? pw.Image(logoImage, width: 60, height: 60) - : pw.SizedBox(width: 60), + : pw.SizedBox(width: 10, height: 10), pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.end, children: [ @@ -924,10 +949,7 @@ class LaporanPenyaluranController extends GetxController { isHeader: true, align: pw.TextAlign.center, color: PdfColors.blue900), - _buildPdfTableCell('Satuan', ttfBold, - isHeader: true, - align: pw.TextAlign.center, - color: PdfColors.blue900), + _buildPdfTableCell('Status', ttfBold, isHeader: true, align: pw.TextAlign.center, @@ -947,13 +969,19 @@ class LaporanPenyaluranController extends GetxController { ? '${penerima.jumlahBantuan} ${penerima.satuan ?? ''}' : '-'; + final isUang = + penerima.satuan?.toLowerCase() == 'rupiah'; + final jumlahBantuan = penerima.jumlahBantuan ?? 0; + + final formattedJumlah = isUang + ? 'Rp ${NumberFormat.currency(locale: 'id', symbol: '', decimalDigits: 0).format(jumlahBantuan)}' + : '$jumlahBantuan ${penerima.satuan ?? ''}'; + return pw.TableRow( children: [ _buildPdfTableCell(nik, ttf), _buildPdfTableCell(wargaNama, ttf), - _buildPdfTableCell(jumlah, ttf, - align: pw.TextAlign.center), - _buildPdfTableCell(penerima.satuan ?? '-', ttf, + _buildPdfTableCell(formattedJumlah, ttf, align: pw.TextAlign.center), _buildPdfTableCell( penerima.statusPenerimaan ?? '-', ttf, @@ -1005,12 +1033,17 @@ class LaporanPenyaluranController extends GetxController { 'Dokumentasi dapat diakses melalui tautan berikut:', style: pw.TextStyle(font: ttf)), pw.SizedBox(height: 3), - pw.Text(laporan.dokumentasiUrl!, + pw.UrlLink( + destination: laporan.dokumentasiUrl!, + child: pw.Text( + laporan.dokumentasiUrl!, style: pw.TextStyle( font: ttf, color: PdfColors.blue, decoration: pw.TextDecoration.underline, - )), + ), + ), + ), if (laporan.beritaAcaraUrl != null) pw.SizedBox(height: 10), ], @@ -1024,12 +1057,17 @@ class LaporanPenyaluranController extends GetxController { 'Berita acara dapat diakses melalui tautan berikut:', style: pw.TextStyle(font: ttf)), pw.SizedBox(height: 3), - pw.Text(laporan.beritaAcaraUrl!, + pw.UrlLink( + destination: laporan.beritaAcaraUrl!, + child: pw.Text( + laporan.beritaAcaraUrl!, style: pw.TextStyle( font: ttf, color: PdfColors.blue, decoration: pw.TextDecoration.underline, - )), + ), + ), + ), ], ], ttfBold, @@ -1046,7 +1084,9 @@ class LaporanPenyaluranController extends GetxController { crossAxisAlignment: pw.CrossAxisAlignment.center, children: [ pw.Text( - 'Penanggung Jawab,', + petugasData.isNotEmpty + ? 'Petugas ${petugasData['desa'] != null ? "Desa " + (petugasData['desa']['nama'] ?? '') : ''}' + : 'Penanggung Jawab,', style: pw.TextStyle(font: ttf), ), pw.SizedBox(height: 50), // Ruang untuk tanda tangan @@ -1057,12 +1097,23 @@ class LaporanPenyaluranController extends GetxController { top: pw.BorderSide(color: PdfColors.black))), padding: const pw.EdgeInsets.only(top: 5), child: pw.Text( - // Sesuaikan dengan properti yang ada di model user - user?.email ?? 'Admin Sistem', + // Gunakan data petugas dari penyaluran, jika tidak ada gunakan data user + petugasData.isNotEmpty + ? petugasData['nama_lengkap'] ?? 'Petugas Desa' + : user?.nama ?? 'Admin Sistem', style: pw.TextStyle(font: ttfBold), textAlign: pw.TextAlign.center, ), ), + + // NIP atau nomor identitas petugas + pw.Text( + petugasData.isNotEmpty + ? petugasData['nip'] ?? '-' + : user?.nip ?? '-', + style: pw.TextStyle(font: ttf), + textAlign: pw.TextAlign.center, + ), ], ), ], diff --git a/lib/app/modules/laporan_penyaluran/views/laporan_penyaluran_detail_view.dart b/lib/app/modules/laporan_penyaluran/views/laporan_penyaluran_detail_view.dart index fdfbe7e..bd6b1b4 100644 --- a/lib/app/modules/laporan_penyaluran/views/laporan_penyaluran_detail_view.dart +++ b/lib/app/modules/laporan_penyaluran/views/laporan_penyaluran_detail_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:penyaluran_app/app/modules/laporan_penyaluran/controllers/laporan_penyaluran_controller.dart'; import 'package:penyaluran_app/app/theme/app_theme.dart'; +import 'package:penyaluran_app/app/utils/date_time_helper.dart'; import 'package:penyaluran_app/app/widgets/custom_app_bar.dart'; import 'package:penyaluran_app/app/widgets/section_header.dart'; import 'package:penyaluran_app/app/widgets/status_badge.dart'; @@ -185,15 +186,15 @@ class LaporanPenyaluranDetailView extends GetView { _buildInfoRow( 'Tanggal Penyaluran', penyaluran.tanggalPenyaluran != null - ? DateFormat('dd/MM/yyyy') - .format(penyaluran.tanggalPenyaluran!) + ? DateTimeHelper.formatDateTime( + penyaluran.tanggalPenyaluran!) : '-', ), _buildInfoRow( 'Tanggal Selesai', penyaluran.tanggalSelesai != null - ? DateFormat('dd/MM/yyyy') - .format(penyaluran.tanggalSelesai!) + ? DateTimeHelper.formatDateTime( + penyaluran.tanggalSelesai!) : '-', ), _buildInfoRow('Jumlah Penerima', @@ -525,7 +526,7 @@ class LaporanPenyaluranDetailView extends GetView { Expanded( flex: 3, child: Text( - 'Nama Penerima', + 'NIK', style: TextStyle( fontWeight: FontWeight.bold), ), @@ -533,7 +534,7 @@ class LaporanPenyaluranDetailView extends GetView { Expanded( flex: 3, child: Text( - 'Jenis Bantuan', + 'Nama Penerima', style: TextStyle( fontWeight: FontWeight.bold), ), @@ -568,12 +569,13 @@ class LaporanPenyaluranDetailView extends GetView { itemBuilder: (context, index) { final penerima = controller.daftarPenerima[index]; + final wargaNik = penerima.warga != null + ? penerima.warga!['nik'] ?? '-' + : '-'; final wargaNama = penerima.warga != null ? penerima.warga!['nama_lengkap'] ?? '-' : '-'; - final stokNama = penerima.stokBantuan != null - ? penerima.stokBantuan!['nama'] ?? '-' - : '-'; + final jumlah = penerima.jumlahBantuan != null ? '${penerima.jumlahBantuan} ${penerima.satuan ?? ''}' : '-'; @@ -591,11 +593,11 @@ class LaporanPenyaluranDetailView extends GetView { children: [ Expanded( flex: 3, - child: Text(wargaNama), + child: Text(wargaNik), ), Expanded( flex: 3, - child: Text(stokNama), + child: Text(wargaNama), ), Expanded( flex: 2, @@ -867,9 +869,7 @@ class LaporanPenyaluranDetailView extends GetView { ), const SizedBox(height: 6), Text( - tanggalLaporan != null - ? DateFormat('dd/MM/yyyy').format(tanggalLaporan) - : '-', + DateTimeHelper.formatDateTime(tanggalLaporan), style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, diff --git a/lib/app/modules/petugas_desa/controllers/detail_penyaluran_controller.dart b/lib/app/modules/petugas_desa/controllers/detail_penyaluran_controller.dart index de1136e..26e9b34 100644 --- a/lib/app/modules/petugas_desa/controllers/detail_penyaluran_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/detail_penyaluran_controller.dart @@ -54,12 +54,12 @@ class DetailPenyaluranController extends GetxController { final user = _supabaseService.client.auth.currentUser; if (user != null) { final userData = await _supabaseService.client - .from('users') + .from('user_profile') .select('role') .eq('id', user.id) .single(); - if (userData['role'] == 'petugas_desa') { + if (userData['role'] == 'PETUGASDESA') { isPetugasDesa.value = true; } } diff --git a/lib/app/modules/petugas_desa/controllers/donatur_controller.dart b/lib/app/modules/petugas_desa/controllers/donatur_controller.dart index 6fa1327..678a3db 100644 --- a/lib/app/modules/petugas_desa/controllers/donatur_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/donatur_controller.dart @@ -275,7 +275,7 @@ class DonaturController extends GetxController { try { final donatur = daftarDonatur.firstWhere((d) => d.id == donaturId); - return donatur.nama; + return donatur.namaLengkap; } catch (e) { return null; } diff --git a/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart b/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart index 5f11f5b..95a4d6d 100644 --- a/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart @@ -47,7 +47,7 @@ class JadwalPenyaluranController extends GetxController { // Controller untuk pencarian final TextEditingController searchController = TextEditingController(); - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; @override void onInit() { diff --git a/lib/app/modules/petugas_desa/controllers/penerima_bantuan_controller.dart b/lib/app/modules/petugas_desa/controllers/penerima_bantuan_controller.dart index d3feeb6..eae4b11 100644 --- a/lib/app/modules/petugas_desa/controllers/penerima_bantuan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/penerima_bantuan_controller.dart @@ -32,7 +32,7 @@ class PenerimaBantuanController extends GetxController { // Form key final GlobalKey penerimaFormKey = GlobalKey(); - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; @override void onInit() { @@ -79,19 +79,21 @@ class PenerimaBantuanController extends GetxController { isLoading.value = true; try { - final penerima = WargaModel( - nama: namaController.text, - nik: nikController.text, - alamat: alamatController.text, - telepon: teleponController.text, - email: emailController.text, - catatan: catatanController.text, - status: 'AKTIF', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - ); + // Biarkan Supabase yang akan menghasilkan ID saat insert + // Kita hanya perlu menyediakan data lainnya + final penerima = { + 'nama_lengkap': namaController.text, + 'nik': nikController.text, + 'alamat': alamatController.text, + 'no_hp': teleponController.text, + 'email': emailController.text, + 'catatan': catatanController.text, + 'status': 'AKTIF', + 'created_at': DateTime.now().toIso8601String(), + 'updated_at': DateTime.now().toIso8601String(), + }; - await _supabaseService.tambahPenerima(penerima.toJson()); + await _supabaseService.tambahPenerima(penerima); // Clear form clearForm(); @@ -125,18 +127,17 @@ class PenerimaBantuanController extends GetxController { isLoading.value = true; try { - final penerima = WargaModel( - id: penerimaId, - nama: namaController.text, - nik: nikController.text, - alamat: alamatController.text, - telepon: teleponController.text, - email: emailController.text, - catatan: catatanController.text, - updatedAt: DateTime.now(), - ); + final penerima = { + 'nama_lengkap': namaController.text, + 'nik': nikController.text, + 'alamat': alamatController.text, + 'no_hp': teleponController.text, + 'email': emailController.text, + 'catatan': catatanController.text, + 'updated_at': DateTime.now().toIso8601String(), + }; - await _supabaseService.updatePenerima(penerimaId, penerima.toJson()); + await _supabaseService.updatePenerima(penerimaId, penerima); // Clear form clearForm(); @@ -218,10 +219,10 @@ class PenerimaBantuanController extends GetxController { } void setFormData(WargaModel penerima) { - namaController.text = penerima.nama ?? ''; + namaController.text = penerima.namaLengkap ?? ''; nikController.text = penerima.nik ?? ''; alamatController.text = penerima.alamat ?? ''; - teleponController.text = penerima.telepon ?? ''; + teleponController.text = penerima.noHp ?? ''; emailController.text = penerima.email ?? ''; catatanController.text = penerima.catatan ?? ''; } diff --git a/lib/app/modules/petugas_desa/controllers/pengaduan_controller.dart b/lib/app/modules/petugas_desa/controllers/pengaduan_controller.dart index 4a94fc5..ca45d09 100644 --- a/lib/app/modules/petugas_desa/controllers/pengaduan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/pengaduan_controller.dart @@ -37,7 +37,7 @@ class PengaduanController extends GetxController { // Image picker final ImagePicker _imagePicker = ImagePicker(); - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; @override void onInit() { diff --git a/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart b/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart index 113a847..ebc21f7 100644 --- a/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/penitipan_bantuan_controller.dart @@ -53,7 +53,7 @@ class PenitipanBantuanController extends GetxController { // Tambahkan properti untuk waktu terakhir update final Rx lastUpdateTime = DateTime.now().obs; - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; // Getter untuk counter dari CounterService RxInt get jumlahMenunggu => _counterService.jumlahMenunggu; @@ -72,10 +72,9 @@ class PenitipanBantuanController extends GetxController { loadPenitipanData(); loadKategoriBantuanData(); - // Tambahkan delay untuk memastikan data petugas desa dimuat setelah penitipan - Future.delayed(const Duration(seconds: 1), () { - loadAllPetugasDesaData(); - }); + + // Hapus delay dan muat data petugas desa langsung + loadAllPetugasDesaData(); // Listener untuk pencarian donatur donaturSearchController.addListener(() { @@ -123,25 +122,24 @@ class PenitipanBantuanController extends GetxController { // Muat informasi petugas desa untuk item yang terverifikasi print( 'Memuat informasi petugas desa untuk ${daftarPenitipan.length} penitipan'); + + List petugasLoaders = []; + for (var item in daftarPenitipan) { if (item.status == 'TERVERIFIKASI' && item.petugasDesaId != null) { print( 'Memuat informasi petugas desa untuk penitipan ID: ${item.id}, petugasDesaId: ${item.petugasDesaId}'); - final petugasData = await getPetugasDesaInfo(item.petugasDesaId); - if (petugasData != null) { - print( - 'Berhasil memuat data petugas desa: ${petugasData['name']} untuk ID: ${item.petugasDesaId}'); - } else { - print( - 'Gagal memuat data petugas desa untuk ID: ${item.petugasDesaId}'); - } + petugasLoaders.add(getPetugasDesaInfo(item.petugasDesaId)); } } + // Tunggu semua data petugas desa selesai dimuat + await Future.wait(petugasLoaders); + // Debug: print semua data petugas desa yang sudah dimuat print('Data petugas desa yang sudah dimuat:'); petugasDesaCache.forEach((key, value) { - print('ID: $key, Nama: $value'); + print('ID: $key, Nama: ${value['nama_lengkap']}'); }); // Update waktu terakhir refresh @@ -490,7 +488,8 @@ class PenitipanBantuanController extends GetxController { final donaturId = item.donaturId; String donaturNama = ''; if (donaturId != null && donaturCache.containsKey(donaturId)) { - donaturNama = donaturCache[donaturId]?.nama?.toLowerCase() ?? ''; + donaturNama = + donaturCache[donaturId]?.namaLengkap?.toLowerCase() ?? ''; } final donaturMatch = donaturNama.contains(searchText); @@ -551,25 +550,37 @@ class PenitipanBantuanController extends GetxController { if (!petugasDesaCache.containsKey(petugasDesaId)) { print( 'Data petugas desa tidak ditemukan di cache untuk ID: $petugasDesaId'); - // Jadwalkan pengambilan data untuk nanti + // Muat data petugas dan perbarui UI loadPetugasDesaData(petugasDesaId); - return 'Memuat...'; + + // Coba cek lagi setelah pemuatan + if (petugasDesaCache.containsKey(petugasDesaId)) { + // Akses nama dari struktur data petugas_desa + final nama = petugasDesaCache[petugasDesaId]?['nama_lengkap']; + return nama ?? 'Tidak diketahui'; + } + + return 'Memuat data...'; } // Sekarang data seharusnya ada di cache - final nama = petugasDesaCache[petugasDesaId]?['name']; + // Akses nama dari struktur data petugas_desa + final nama = petugasDesaCache[petugasDesaId]?['nama_lengkap']; print('Nama petugas desa: $nama untuk ID: $petugasDesaId'); return nama ?? 'Tidak diketahui'; } // Fungsi untuk memuat data petugas desa dan memperbarui UI - void loadPetugasDesaData(String petugasDesaId) async { + Future loadPetugasDesaData(String petugasDesaId) async { try { + print('Memuat data petugas desa untuk ID: $petugasDesaId'); final petugasData = await getPetugasDesaInfo(petugasDesaId); if (petugasData != null) { // Data sudah dimasukkan ke cache oleh getPetugasDesaInfo - // Refresh UI - update(); + print('Berhasil memuat data petugas: ${petugasData['nama_lengkap']}'); + + // Refresh UI segera + update(['petugas_data']); } else { print( 'Gagal mengambil data petugas desa dari server untuk ID: $petugasDesaId'); @@ -597,7 +608,7 @@ class PenitipanBantuanController extends GetxController { // Debug: print semua data petugas desa yang sudah dimuat print('Data petugas desa yang sudah dimuat setelah reload:'); petugasDesaCache.forEach((key, value) { - print('ID: $key, Nama: $value'); + print('ID: $key, Nama: ${value['nama_lengkap']}'); }); } catch (e) { print('Error saat memuat ulang data petugas desa: $e'); @@ -643,15 +654,15 @@ class PenitipanBantuanController extends GetxController { Future tambahDonatur({ required String nama, - required String telepon, + required String noHp, String? alamat, String? email, String? jenis, }) async { try { final donaturData = { - 'nama': nama, - 'telepon': telepon, + 'nama_lengkap': nama, + 'no_hp': noHp, 'alamat': alamat, 'email': email, 'jenis': jenis, diff --git a/lib/app/modules/petugas_desa/controllers/petugas_desa_controller.dart b/lib/app/modules/petugas_desa/controllers/petugas_desa_controller.dart index aad316c..eb31efb 100644 --- a/lib/app/modules/petugas_desa/controllers/petugas_desa_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/petugas_desa_controller.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:penyaluran_app/app/data/models/desa_model.dart'; import 'package:penyaluran_app/app/data/models/user_model.dart'; +import 'package:penyaluran_app/app/data/models/petugas_desa_model.dart'; import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart'; import 'package:penyaluran_app/app/modules/petugas_desa/controllers/counter_service.dart'; import 'package:penyaluran_app/app/services/supabase_service.dart'; @@ -49,9 +50,57 @@ class PetugasDesaController extends GetxController { // Variabel untuk pencarian dan filter final searchQuery = ''.obs; - UserModel? get user => _authController.user; - String get role => user?.role ?? 'PETUGASDESA'; - String get nama => user?.name ?? 'Petugas Desa'; + BaseUserModel? get user => _authController.baseUser; + String get role => user?.roleName ?? 'PETUGAS_DESA'; + + // Helper method untuk format role agar lebih rapi + String get formattedRole { + final roleText = role.toLowerCase(); + if (roleText.contains('_')) { + return roleText + .split('_') + .map((word) => + word.isNotEmpty ? word[0].toUpperCase() + word.substring(1) : '') + .join(' '); + } + return roleText.isNotEmpty + ? roleText[0].toUpperCase() + roleText.substring(1) + : 'Petugas Desa'; + } + + String get nama { + // 1. Coba ambil dari AuthController displayName yang paling lengkap + final authDisplayName = _authController.displayName; + if (authDisplayName != null && + authDisplayName != 'Pengguna' && + authDisplayName != user?.email) { + return authDisplayName; + } + + // 2. Coba ambil dari roleData jika merupakan PetugasDesaModel + final userData = _authController.userData; + if (userData != null && userData.roleData is PetugasDesaModel) { + final petugasData = userData.roleData as PetugasDesaModel; + if (petugasData.namaLengkap != null && + petugasData.namaLengkap!.isNotEmpty) { + return petugasData.namaLengkap!; + } + } + + // 3. Coba ambil dari user.name + if (user?.name != null && user!.name!.isNotEmpty) { + return user!.name!; + } + + // 4. Fallback ke nama dari userProfile + if (userProfile['name'] != null && + userProfile['name'].toString().isNotEmpty) { + return userProfile['name']; + } + + // 5. Default fallback + return 'Petugas Desa'; + } // Getter untuk counter dari CounterService RxInt get jumlahNotifikasiBelumDibaca => @@ -64,6 +113,13 @@ class PetugasDesaController extends GetxController { // Getter untuk nama desa dari profil pengguna String get desa { + // Debug info + print('DEBUG: Memeriksa data desa...'); + if (user != null) { + print('DEBUG: User ID: ${user!.id}, User email: ${user!.email}'); + print('DEBUG: User desa: ${user!.desa}'); + } + // Prioritaskan model desa dari user if (user?.desa != null) { print('DEBUG: Menggunakan desa dari user model: ${user!.desa!.nama}'); @@ -77,9 +133,15 @@ class PetugasDesaController extends GetxController { return desaNama; } + // Jika masih tidak ada, coba dari desaModel + if (desaModel.value != null) { + print('DEBUG: Menggunakan desa dari desaModel: ${desaModel.value!.nama}'); + return desaModel.value!.nama; + } + // Fallback ke nilai default print('DEBUG: Menggunakan nilai default untuk desa'); - return userProfile['desa_id'] != null ? 'Desa' : 'Desa'; + return 'Desa'; } @override @@ -109,34 +171,76 @@ class PetugasDesaController extends GetxController { // Metode untuk memuat data profil pengguna dari cache Future loadUserProfile() async { try { - // Jika user sudah ada di AuthController, tidak perlu mengambil data lagi + // Jika user sudah ada di AuthController, gunakan data yang ada if (user != null) { print('DEBUG: User ditemukan di AuthController: ${user!.email}'); print('DEBUG: User desa: ${user!.desa?.nama}'); - // Ambil data tambahan jika diperlukan, tapi gunakan cache - final profileData = await _supabaseService.getUserProfile(); - if (profileData != null) { - print('DEBUG: Profile data ditemukan: ${profileData['name']}'); - userProfile.value = profileData; + // Tidak perlu mengambil data tambahan jika user.desa sudah ada + if (user!.desa != null) { + print( + 'DEBUG: Menggunakan desa dari AuthController: ${user!.desa!.nama}'); + desaModel.value = user!.desa; - // Parse data desa jika ada - if (profileData['desa'] != null && - profileData['desa'] is Map) { + // Perbarui userProfile untuk konsistensi + if (userProfile.isEmpty) { + userProfile.value = { + 'name': user!.name ?? _authController.displayName, + 'desa': user!.desa?.toJson(), + }; + } + + return; // Data sudah lengkap, tidak perlu fetch lagi + } + + print( + 'DEBUG: Data desa tidak ditemukan di AuthController, mencoba ambil dari cache'); + + // Jika tidak ada desa di AuthController, coba ambil dari userData roleData + final userData = _authController.userData; + if (userData != null) { + if (userData.roleData is PetugasDesaModel) { + final petugasData = userData.roleData as PetugasDesaModel; + if (petugasData.desa != null) { + print( + 'DEBUG: Menggunakan desa dari roleData: ${petugasData.desa!.nama}'); + desaModel.value = petugasData.desa; + + // Perbarui userProfile untuk konsistensi + userProfile.value = { + 'name': petugasData.displayName, + 'desa': petugasData.desa?.toJson(), + }; + + return; // Data sudah lengkap, tidak perlu fetch lagi + } + } + } + + // Jika tidak ada di cache, ambil dari API hanya jika benar-benar diperlukan + print('DEBUG: Data desa tidak ditemukan di cache, mengambil dari API'); + final baseProfile = await _supabaseService.getUserProfile(); + if (baseProfile != null) { + userProfile.value = baseProfile; + + if (baseProfile['desa'] != null && + baseProfile['desa'] is Map) { try { - final desaData = profileData['desa'] as Map; - print('DEBUG: Desa data ditemukan: $desaData'); + final desaData = baseProfile['desa'] as Map; + print('DEBUG: Desa data ditemukan dari API: $desaData'); + desaModel.value = DesaModel.fromJson(desaData); } catch (e) { print('Error parsing desa data: $e'); } } else { - print('DEBUG: Desa data tidak ditemukan atau bukan Map'); + print('DEBUG: Desa data tidak ditemukan di API'); } } else { - print('DEBUG: Profile data tidak ditemukan'); + print('DEBUG: Profile data tidak ditemukan dari API'); } } else { - print('DEBUG: User tidak ditemukan di AuthController'); + print( + 'DEBUG: User tidak ditemukan di AuthController, mungkin belum login'); } } catch (e) { print('Error loading user profile: $e'); diff --git a/lib/app/modules/petugas_desa/controllers/petugas_desa_dashboard_controller.dart b/lib/app/modules/petugas_desa/controllers/petugas_desa_dashboard_controller.dart index f261988..bc33795 100644 --- a/lib/app/modules/petugas_desa/controllers/petugas_desa_dashboard_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/petugas_desa_dashboard_controller.dart @@ -27,7 +27,7 @@ class PetugasDesaDashboardController extends GetxController { // Controller untuk pencarian final TextEditingController searchController = TextEditingController(); - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; String get role => user?.role ?? 'PETUGASDESA'; String get nama => user?.name ?? 'Petugas Desa'; diff --git a/lib/app/modules/petugas_desa/controllers/riwayat_pengaduan_controller.dart b/lib/app/modules/petugas_desa/controllers/riwayat_pengaduan_controller.dart index 34bf314..3179264 100644 --- a/lib/app/modules/petugas_desa/controllers/riwayat_pengaduan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/riwayat_pengaduan_controller.dart @@ -17,7 +17,7 @@ class RiwayatPengaduanController extends GetxController { // Controller untuk pencarian final TextEditingController searchController = TextEditingController(); - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; @override void onInit() { diff --git a/lib/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart b/lib/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart index 533b587..2fcaa30 100644 --- a/lib/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart +++ b/lib/app/modules/petugas_desa/controllers/stok_bantuan_controller.dart @@ -36,7 +36,7 @@ class StokBantuanController extends GetxController { // Tambahkan properti untuk waktu terakhir update Rx lastUpdateTime = DateTime.now().obs; - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; @override void onInit() { diff --git a/lib/app/modules/petugas_desa/views/detail_donatur_view.dart b/lib/app/modules/petugas_desa/views/detail_donatur_view.dart index b75ad9f..3c489fc 100644 --- a/lib/app/modules/petugas_desa/views/detail_donatur_view.dart +++ b/lib/app/modules/petugas_desa/views/detail_donatur_view.dart @@ -198,7 +198,7 @@ class DetailDonaturView extends GetView { donatur.alamat ?? 'Tidak ada alamat'), const SizedBox(height: 8), _buildInfoItem(Icons.phone, 'Telepon', - donatur.telepon ?? 'Tidak ada telepon'), + donatur.noHp ?? 'Tidak ada telepon'), const SizedBox(height: 8), _buildInfoItem( Icons.email, 'Email', donatur.email ?? 'Tidak ada email'), 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 6af3e60..eef94e4 100644 --- a/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart +++ b/lib/app/modules/petugas_desa/views/detail_penyaluran_page.dart @@ -1223,8 +1223,11 @@ class DetailPenyaluranPage extends StatelessWidget { const Divider(height: 24), if (warga != null) ...[ _buildInfoRow('NIK', warga['nik'] ?? '-'), - _buildInfoRow('Alamat Lengkap', - '${warga['alamat'] ?? '-'} Desa ${warga['desa'] ?? '-'} Kecamatan ${warga['kecamatan'] ?? '-'} Kabupaten ${warga['kabupaten'] ?? '-'} Provinsi ${warga['provinsi'] ?? '-'}'), + _buildInfoRow('Alamat', warga['alamat'] ?? '-'), + _buildInfoRow('Desa', warga['desa'] ?? '-'), + _buildInfoRow('Kecamatan', warga['kecamatan'] ?? '-'), + _buildInfoRow('Kabupaten', warga['kabupaten'] ?? '-'), + _buildInfoRow('Provinsi', warga['provinsi'] ?? '-'), _buildInfoRow( 'Jenis Kelamin', warga['jenis_kelamin'] ?? '-'), _buildInfoRow('No. Telepon', warga['no_hp'] ?? '-'), diff --git a/lib/app/modules/petugas_desa/views/penitipan_view.dart b/lib/app/modules/petugas_desa/views/penitipan_view.dart index 02be88d..4111ffc 100644 --- a/lib/app/modules/petugas_desa/views/penitipan_view.dart +++ b/lib/app/modules/petugas_desa/views/penitipan_view.dart @@ -918,9 +918,8 @@ class PenitipanView extends GetView { style: const TextStyle( fontWeight: FontWeight.bold), ), - if (selectedDonatur.value!.telepon != - null) - Text(selectedDonatur.value!.telepon!), + if (selectedDonatur.value!.noHp != null) + Text(selectedDonatur.value!.noHp!), ], ), ), @@ -984,9 +983,9 @@ class PenitipanView extends GetView { return ListTile( title: Text(donatur.nama ?? 'Tidak ada nama'), - subtitle: donatur.telepon != null - ? Text(donatur.telepon!) - : null, + subtitle: donatur.noHp != null + ? Text(donatur.noHp!) + : const Text('Tidak ada nomor telepon'), dense: true, onTap: () { selectedDonatur.value = donatur; @@ -1300,7 +1299,7 @@ class PenitipanView extends GetView { BuildContext context, Function(String) onDonaturAdded) { final formKey = GlobalKey(); final TextEditingController namaController = TextEditingController(); - final TextEditingController teleponController = TextEditingController(); + final TextEditingController noHpController = TextEditingController(); final TextEditingController alamatController = TextEditingController(); final TextEditingController emailController = TextEditingController(); final TextEditingController jenisController = TextEditingController(); @@ -1352,24 +1351,24 @@ class PenitipanView extends GetView { // Telepon Text( - 'Nomor Telepon', + 'Nomor HP', style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 8), TextFormField( - controller: teleponController, + controller: noHpController, keyboardType: TextInputType.phone, decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), - hintText: 'Masukkan nomor telepon', + hintText: 'Masukkan nomor HP', contentPadding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), ), validator: (value) { if (value == null || value.isEmpty) { - return 'Nomor telepon harus diisi'; + return 'Nomor HP harus diisi'; } return null; }, @@ -1396,24 +1395,16 @@ class PenitipanView extends GetView { : jenisController.text, items: const [ DropdownMenuItem( - value: 'Perorangan', - child: Text('Perorangan'), + value: 'Individu', + child: Text('Individu'), ), DropdownMenuItem( value: 'Perusahaan', child: Text('Perusahaan'), ), DropdownMenuItem( - value: 'Lembaga', - child: Text('Lembaga'), - ), - DropdownMenuItem( - value: 'Komunitas', - child: Text('Komunitas'), - ), - DropdownMenuItem( - value: 'Lainnya', - child: Text('Lainnya'), + value: 'Organisasi', + child: Text('Organisasi'), ), ], onChanged: (value) { @@ -1478,7 +1469,7 @@ class PenitipanView extends GetView { if (formKey.currentState!.validate()) { final donaturId = await controller.tambahDonatur( nama: namaController.text, - telepon: teleponController.text, + noHp: noHpController.text, alamat: alamatController.text.isEmpty ? null : alamatController.text, diff --git a/lib/app/modules/petugas_desa/views/petugas_desa_view.dart b/lib/app/modules/petugas_desa/views/petugas_desa_view.dart index f0c532b..19c30c2 100644 --- a/lib/app/modules/petugas_desa/views/petugas_desa_view.dart +++ b/lib/app/modules/petugas_desa/views/petugas_desa_view.dart @@ -151,7 +151,6 @@ class PetugasDesaView extends GetView { return const DashboardView(); case 1: return const PenyaluranView(); - case 2: return const PenitipanView(); case 3: @@ -182,22 +181,15 @@ class PetugasDesaView extends GetView { CircleAvatar( radius: 30, backgroundColor: Colors.white, - backgroundImage: controller.user?.avatar != null && - controller.user!.avatar!.isNotEmpty - ? NetworkImage(controller.user!.avatar!) - : null, - child: controller.user?.avatar == null || - controller.user!.avatar!.isEmpty - ? const Icon( - Icons.person, - size: 40, - color: AppTheme.primaryColor, - ) - : null, + child: const Icon( + Icons.person, + size: 40, + color: AppTheme.primaryColor, + ), ), const SizedBox(height: 10), Text( - controller.user?.name ?? 'Petugas Desa', + controller.nama, style: const TextStyle( color: Colors.white, fontSize: 18, @@ -206,8 +198,8 @@ class PetugasDesaView extends GetView { ), Text( controller.user?.desa?.nama != null - ? '${controller.user?.role} - ${controller.user!.desa!.nama}' - : controller.user?.role ?? 'PETUGAS_DESA', + ? '${controller.formattedRole} - ${controller.user!.desa!.nama}' + : controller.formattedRole, style: TextStyle( color: Colors.white.withAlpha(200), fontSize: 14, @@ -251,9 +243,9 @@ class PetugasDesaView extends GetView { ? Badge( label: Text(controller.jumlahDiproses.value.toString()), backgroundColor: Colors.red, - child: const Icon(Icons.support_outlined), + child: const Icon(Icons.warning_amber_outlined), ) - : const Icon(Icons.support_outlined), + : const Icon(Icons.warning_amber_outlined), title: const Text('Pengaduan'), selected: controller.activeTabIndex.value == 3, selectedColor: AppTheme.primaryColor, @@ -272,6 +264,7 @@ class PetugasDesaView extends GetView { controller.changeTab(4); }, ), + const Divider(), ListTile( leading: const Icon(Icons.person_add_outlined), title: const Text('Kelola Penerima'), @@ -296,7 +289,6 @@ class PetugasDesaView extends GetView { Get.toNamed('/laporan-penyaluran'); }, ), - const Divider(), ListTile( leading: const Icon(Icons.person_outline), title: const Text('Profil'), diff --git a/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart b/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart index b17e109..f6ecc18 100644 --- a/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart +++ b/lib/app/modules/petugas_desa/views/riwayat_penitipan_view.dart @@ -310,11 +310,15 @@ class RiwayatPenitipanView extends GetView { if (item.status == 'TERVERIFIKASI' && item.petugasDesaId != null) Expanded( - child: _buildItemDetail( - context, - icon: Icons.person, - label: 'Diverifikasi Oleh', - value: controller.getPetugasDesaNama(item.petugasDesaId), + child: GetBuilder( + id: 'petugas_data', + builder: (controller) => _buildItemDetail( + context, + icon: Icons.person, + label: 'Diverifikasi Oleh', + value: + controller.getPetugasDesaNama(item.petugasDesaId), + ), ), ), ], diff --git a/lib/app/modules/profile/controllers/profile_controller.dart b/lib/app/modules/profile/controllers/profile_controller.dart index 962055d..2d998c1 100644 --- a/lib/app/modules/profile/controllers/profile_controller.dart +++ b/lib/app/modules/profile/controllers/profile_controller.dart @@ -1,14 +1,17 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:penyaluran_app/app/data/models/user_model.dart'; -import 'package:penyaluran_app/app/services/auth_service.dart'; +import 'package:penyaluran_app/app/services/supabase_service.dart'; +import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart'; class ProfileController extends GetxController { - final AuthService _authService = Get.find(); + final SupabaseService _supabaseService = Get.find(); + final AuthController _authController = Get.find(); - final Rx user = Rx(null); + final Rx user = Rx(null); final RxBool isLoading = true.obs; final RxBool isEditing = false.obs; + final Rx?> roleData = Rx?>(null); // Form controllers late TextEditingController nameController; @@ -36,14 +39,23 @@ class ProfileController extends GetxController { isLoading.value = true; try { // Mendapatkan data user dari service - final userData = await _authService.getCurrentUser(); - user.value = userData; - - // Mengisi form controllers dengan data user + final userData = await _supabaseService.getUserProfile(); if (userData != null) { - nameController.text = userData.name ?? ''; - emailController.text = userData.email ?? ''; - phoneController.text = userData.phone ?? ''; + user.value = BaseUserModel.fromJson(userData); + + // Mengisi form controllers dengan data user + nameController.text = user.value?.name ?? ''; + emailController.text = user.value?.email ?? ''; + // Catatan: BaseUserModel tidak memiliki properti phone + // Jika ada data role_data, simpan untuk ditampilkan + if (userData['role_data'] != null) { + roleData.value = userData['role_data'] as Map?; + // Jika role adalah warga, ambil no telepon dari role data + if (user.value?.role?.toLowerCase() == 'warga' && + roleData.value?['no_hp'] != null) { + phoneController.text = roleData.value?['no_hp'] ?? ''; + } + } } } catch (e) { Get.snackbar( @@ -76,22 +88,45 @@ class ProfileController extends GetxController { isLoading.value = true; try { - // Update user data - final updatedUser = User( - id: user.value?.id, - name: nameController.text, - email: emailController.text, - phone: phoneController.text, - role: user.value?.role, - token: user.value?.token, - ); + final userData = user.value; + if (userData == null) throw 'Data user tidak ditemukan'; - // Panggil API untuk update profil - await _authService.updateProfile(updatedUser); + // Update data sesuai role + switch (userData.role?.toLowerCase() ?? 'unknown') { + case 'warga': + await _supabaseService.updateWargaProfile( + userId: userData.id, + namaLengkap: nameController.text, + noHp: phoneController.text, + email: emailController.text, + ); + break; + case 'donatur': + await _supabaseService.updateDonaturProfile( + userId: userData.id, + nama: nameController.text, + noHp: phoneController.text, + email: emailController.text, + ); + break; + case 'petugas_desa': + await _supabaseService.updatePetugasDesaProfile( + userId: userData.id, + nama: nameController.text, + noHp: phoneController.text, + email: emailController.text, + ); + break; + default: + throw 'Role tidak valid'; + } - // Refresh data + // Refresh data lokal await loadUserData(); + // Refresh data di AuthController untuk menyebarkan perubahan ke seluruh aplikasi + await _authController.refreshUserData(); + // Keluar dari mode edit isEditing.value = false; @@ -131,7 +166,7 @@ class ProfileController extends GetxController { isLoading.value = true; try { // Panggil API untuk ganti password - await _authService.changePassword(currentPassword, newPassword); + await _supabaseService.changePassword(currentPassword, newPassword); Get.back(); // Tutup dialog diff --git a/lib/app/modules/profile/views/profile_view.dart b/lib/app/modules/profile/views/profile_view.dart index 4fc149b..6028d49 100644 --- a/lib/app/modules/profile/views/profile_view.dart +++ b/lib/app/modules/profile/views/profile_view.dart @@ -87,6 +87,7 @@ class ProfileView extends GetView { Widget _buildProfileForm() { return Obx(() { final isEditing = controller.isEditing.value; + final user = controller.user.value; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -119,7 +120,7 @@ class ProfileView extends GetView { labelText: 'Email', border: OutlineInputBorder(), prefixIcon: Icon(Icons.email), - enabled: isEditing, + enabled: false, // Email tidak bisa diubah ), keyboardType: TextInputType.emailAddress, ), @@ -136,6 +137,113 @@ class ProfileView extends GetView { ), keyboardType: TextInputType.phone, ), + const SizedBox(height: 16), + + // Informasi tambahan sesuai role + if (user != null) ...[ + if (user.role?.toLowerCase() == 'warga') ...[ + const SizedBox(height: 24), + const Text( + 'Informasi Warga', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Obx(() { + final roleData = controller.roleData.value; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow( + Icons.perm_identity, 'NIK', roleData?['nik'] ?? '-'), + const SizedBox(height: 8), + _buildInfoRow(Icons.wc, 'Jenis Kelamin', + roleData?['jenis_kelamin'] ?? '-'), + const SizedBox(height: 8), + _buildInfoRow( + Icons.home, 'Alamat', roleData?['alamat'] ?? '-'), + if (user.desa != null) ...[ + const SizedBox(height: 8), + _buildInfoRow( + Icons.location_city, 'Desa', user.desa!.nama), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Kecamatan', + user.desa!.kecamatan ?? ''), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Kabupaten', + user.desa!.kabupaten ?? ''), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Provinsi', + user.desa!.provinsi ?? ''), + ], + ], + ); + }), + ], + if (user.role?.toLowerCase() == 'donatur') ...[ + const SizedBox(height: 24), + const Text( + 'Informasi Donatur', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Obx(() { + final roleData = controller.roleData.value; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow(Icons.business, 'Instansi', + roleData?['instansi'] ?? '-'), + const SizedBox(height: 8), + _buildInfoRow( + Icons.work, 'Jabatan', roleData?['jabatan'] ?? '-'), + ], + ); + }), + ], + if (user.role?.toLowerCase() == 'petugas_desa') ...[ + const SizedBox(height: 24), + const Text( + 'Informasi Petugas Desa', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Obx(() { + final roleData = controller.roleData.value; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoRow(Icons.badge, 'NIP', roleData?['nip'] ?? '-'), + const SizedBox(height: 8), + _buildInfoRow( + Icons.work, 'Jabatan', roleData?['jabatan'] ?? '-'), + if (user.desa != null) ...[ + const SizedBox(height: 8), + _buildInfoRow( + Icons.location_city, 'Desa', user.desa!.nama), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Kecamatan', + user.desa!.kecamatan ?? ''), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Kabupaten', + user.desa!.kabupaten ?? ''), + const SizedBox(height: 8), + _buildInfoRow(Icons.location_on, 'Provinsi', + user.desa!.provinsi ?? ''), + ], + ], + ); + }), + ], + ], ], ); }); @@ -230,4 +338,23 @@ class ProfileView extends GetView { ), ); } + + Widget _buildInfoRow(IconData icon, String label, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, size: 18, color: Colors.grey[700]), + const SizedBox(width: 8), + Expanded( + child: Text( + '$label: $value', + style: TextStyle( + fontSize: 14, + color: Colors.grey[700], + ), + ), + ), + ], + ); + } } diff --git a/lib/app/modules/warga/controllers/warga_dashboard_controller.dart b/lib/app/modules/warga/controllers/warga_dashboard_controller.dart index 91f899f..30cde09 100644 --- a/lib/app/modules/warga/controllers/warga_dashboard_controller.dart +++ b/lib/app/modules/warga/controllers/warga_dashboard_controller.dart @@ -11,7 +11,7 @@ class WargaDashboardController extends GetxController { final AuthController _authController = Get.find(); final SupabaseService _supabaseService = SupabaseService.to; - final Rx currentUser = Rx(null); + final Rx currentUser = Rx(null); // Indeks tab yang aktif di bottom navigation bar final RxInt activeTabIndex = 0.obs; @@ -41,9 +41,18 @@ class WargaDashboardController extends GetxController { final RxInt jumlahNotifikasiBelumDibaca = 0.obs; // Getter untuk data user - UserModel? get user => _authController.user; + BaseUserModel? get user => _authController.baseUser; String get role => user?.role ?? 'WARGA'; - String get nama => user?.name ?? 'Warga'; + String get nama { + // Gunakan namaLengkap dari roleData jika tersedia + if (_authController.isWarga && _authController.roleData != null) { + return _authController.roleData.namaLengkap ?? + _authController.displayName; + } + // Gunakan displayName dari AuthController + return _authController.displayName; + } + String? get desa => user?.desa?.nama; @override @@ -54,7 +63,30 @@ class WargaDashboardController extends GetxController { } void loadUserData() { - currentUser.value = _authController.user; + currentUser.value = _authController.baseUser; + + // Tambahkan log debugging + print('DEBUG WARGA: Memuat data user dari AuthController'); + print('DEBUG WARGA: baseUser: ${_authController.baseUser}'); + print('DEBUG WARGA: roleData: ${_authController.roleData}'); + print('DEBUG WARGA: nama yang akan ditampilkan: ${nama}'); + print( + 'DEBUG WARGA: displayName dari auth controller: ${_authController.displayName}'); + + if (_authController.userData != null) { + print( + 'DEBUG WARGA: userData ada, role: ${_authController.userData!.baseUser.roleName}'); + + if (_authController.isWarga) { + print('DEBUG WARGA: User adalah warga'); + var wargaData = _authController.roleData; + print('DEBUG WARGA: Data warga: ${wargaData?.namaLengkap}'); + } else { + print('DEBUG WARGA: User bukan warga'); + } + } else { + print('DEBUG WARGA: userData null'); + } } void fetchData() async { @@ -90,14 +122,8 @@ class WargaDashboardController extends GetxController { // Reset data terlebih dahulu untuk memastikan tidak ada data lama yang tersimpan penerimaPenyaluran.clear(); - // Pertama, cari warga_id berdasarkan user_id - final wargaResponse = await _supabaseService.client - .from('warga') - .select('id') - .eq('user_id', user!.id) - .single(); - - final wargaId = wargaResponse['id']; + // Gunakan langsung ID pengguna sebagai warga_id + final wargaId = user!.id; // Ambil data penerima penyaluran dengan join ke warga, stok bantuan, dan penyaluran bantuan final response = @@ -221,14 +247,8 @@ class WargaDashboardController extends GetxController { // 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(); - - final wargaId = wargaResponse['id']; + // Gunakan langsung ID pengguna sebagai warga_id + final wargaId = user!.id; final response = await _supabaseService.client .from('xx02_pengajuan_kelayakan_bantuan') @@ -257,19 +277,17 @@ class WargaDashboardController extends GetxController { totalPengajuanDitolak.value = pengajuan.where((p) => p.status == StatusKelayakan.DITOLAK).length; } catch (e) { - print('Error fetching pengajuan kelayakan: $e'); + print('Error fetchPengajuanKelayakan: $e'); } } // Fungsi untuk mengambil data pengaduan Future fetchPengaduan() async { try { - final wargaData = await _supabaseService.getWargaByUserId(); - if (wargaData == null) { - return; - } + // Gunakan langsung ID user dari AuthController, bukan getWargaByUserId() + String wargaId = user!.id; + print('DEBUG WARGA: Mengambil pengaduan untuk warga ID: $wargaId'); - final String wargaId = wargaData['id']; final response = await _supabaseService .getPengaduanWargaWithPenerimaPenyaluran(wargaId); @@ -287,15 +305,21 @@ class WargaDashboardController extends GetxController { .length; totalPengaduanSelesai.value = pengaduanList.where((p) => p.status == 'SELESAI').length; + + print( + 'DEBUG WARGA: Berhasil mendapatkan ${pengaduanList.length} pengaduan'); + } else { + print('DEBUG WARGA: Tidak ada pengaduan yang ditemukan'); } } catch (e) { - print('Error fetching pengaduan: $e'); + print('DEBUG WARGA: Error fetching pengaduan: $e'); } } // Fungsi untuk mengambil data notifikasi Future fetchNotifikasi() async { try { + // Notifikasi masih menggunakan user_id karena tabelnya terpisah final response = await _supabaseService.client .from('notifikasi') .select('*') @@ -429,13 +453,8 @@ class WargaDashboardController extends GetxController { try { isLoading.value = true; - // Cari warga_id berdasarkan user_id - final wargaData = await _supabaseService.getWargaByUserId(); - if (wargaData == null) { - throw Exception('Data warga tidak ditemukan'); - } - - final String wargaId = wargaData['id']; + // Gunakan langsung ID pengguna sebagai warga_id + final String wargaId = user!.id; // Upload foto pengaduan jika ada List fotoPengaduanUrls = []; diff --git a/lib/app/services/auth_service.dart b/lib/app/services/auth_service.dart index 5fe8c92..3d8e220 100644 --- a/lib/app/services/auth_service.dart +++ b/lib/app/services/auth_service.dart @@ -7,15 +7,17 @@ class AuthService extends GetxService { final Dio _dio = Dio(); final FlutterSecureStorage _storage = const FlutterSecureStorage(); - final Rx currentUser = Rx(null); + final Rx currentUser = Rx(null); + String? _token; // Mendapatkan data user saat ini - Future getCurrentUser() async { + Future getCurrentUser() async { try { // Implementasi untuk mendapatkan data user dari API atau local storage // Contoh implementasi sederhana: final token = await _storage.read(key: 'token'); if (token == null) return null; + _token = token; final response = await _dio.get( '/api/user/profile', @@ -27,7 +29,7 @@ class AuthService extends GetxService { ); if (response.statusCode == 200) { - final user = User.fromJson(response.data['data']); + final user = BaseUserModel.fromJson(response.data['data']); currentUser.value = user; return user; } @@ -40,7 +42,7 @@ class AuthService extends GetxService { } // Update profil user - Future updateProfile(User user) async { + Future updateProfile(BaseUserModel user) async { try { final token = await _storage.read(key: 'token'); if (token == null) return false; @@ -50,7 +52,7 @@ class AuthService extends GetxService { data: { 'name': user.name, 'email': user.email, - 'phone': user.phone, + // 'phone' tidak tersedia di BaseUserModel, jadi kita hilangkan atau gunakan parameter lain }, options: Options( headers: { @@ -100,7 +102,7 @@ class AuthService extends GetxService { } // Login - Future login(String email, String password) async { + Future login(String email, String password) async { try { final response = await _dio.post( '/api/auth/login', @@ -111,11 +113,13 @@ class AuthService extends GetxService { ); if (response.statusCode == 200) { - final user = User.fromJson(response.data['data']); + final user = BaseUserModel.fromJson(response.data['data']); - // Simpan token - if (user.token != null) { - await _storage.write(key: 'token', value: user.token); + // Simpan token yang datang dari respons + if (response.data['token'] != null) { + final token = response.data['token'].toString(); + await _storage.write(key: 'token', value: token); + _token = token; } currentUser.value = user; @@ -148,6 +152,7 @@ class AuthService extends GetxService { } finally { // Hapus token dan user data await _storage.delete(key: 'token'); + _token = null; currentUser.value = null; // Navigasi ke halaman login @@ -155,6 +160,12 @@ class AuthService extends GetxService { } } + // Mendapatkan token autentikasi + Future getToken() async { + if (_token != null) return _token; + return await _storage.read(key: 'token'); + } + // Inisialisasi service Future init() async { // Coba mendapatkan user saat ini diff --git a/lib/app/services/supabase_service.dart b/lib/app/services/supabase_service.dart index 5305c65..14ecb6c 100644 --- a/lib/app/services/supabase_service.dart +++ b/lib/app/services/supabase_service.dart @@ -1,4 +1,8 @@ import 'package:get/get.dart'; +import 'package:penyaluran_app/app/data/models/donatur_model.dart'; +import 'package:penyaluran_app/app/data/models/petugas_desa_model.dart'; +import 'package:penyaluran_app/app/data/models/user_model.dart'; +import 'package:penyaluran_app/app/data/models/warga_model.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'dart:io'; @@ -73,21 +77,6 @@ class SupabaseService extends GetxService { ); } - // Metode untuk login - Future signIn(String email, String password) async { - final response = await client.auth.signInWithPassword( - email: email, - password: password, - ); - - if (response.user != null) { - _isSessionInitialized = true; - print('DEBUG: Login berhasil, sesi diinisialisasi'); - } - - return response; - } - // Metode untuk logout Future signOut() async { _cachedUserProfile = null; // Hapus cache saat logout @@ -122,45 +111,301 @@ class SupabaseService extends GetxService { return false; } - // Metode untuk mendapatkan profil pengguna - Future?> getUserProfile() async { + // Metode untuk mendapatkan profil pengguna dasar + Future getBaseUserProfile() async { final user = currentUser; if (user == null) return null; try { - // Gunakan cache jika tersedia - if (_cachedUserProfile != null && _cachedUserProfile!['id'] == user.id) { - print('Menggunakan data profil dari cache'); + // Gunakan auth.getUser() daripada mengakses tabel auth.users + final userData = await client.auth.getUser(); + + print('userData: ${userData.user}'); + + if (userData.user == null) { + print('Tidak ada data user ditemukan'); + return null; + } + + // Dapatkan role dari tabel khusus roles jika diperlukan + String roleName = 'warga'; // default + if (userData.user!.userMetadata?['role_id'] != null) { + try { + final roleResponse = await client + .from('roles') + .select('role_name') + .eq('id', userData.user!.userMetadata!['role_id']) + .maybeSingle(); + + print('roleResponse: $roleResponse'); + + if (roleResponse != null) { + roleName = roleResponse['role_name']; + } + } catch (e) { + print('Error saat mengambil role: $e'); + // Lanjutkan dengan role default jika gagal + } + } + + // Gabungkan data user dan role + Map combinedData = { + 'id': userData.user!.id, + 'email': userData.user!.email, + 'created_at': userData.user!.createdAt, + 'updated_at': userData.user!.updatedAt, + 'role_id': userData.user!.userMetadata?['role_id'], + 'roles': {'role_name': roleName} + }; + + return BaseUserModel.fromJson(combinedData); + } catch (e) { + print('Error pada getBaseUserProfile: $e'); + return null; + } + } + + // Metode untuk mendapatkan profil pengguna dengan data lengkap + Future?> getUserProfile() async { + try { + // Jika cache profil tersedia, gunakan + if (_cachedUserProfile != null) { return _cachedUserProfile; } - final response = await client - .from('user_profile') - .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') - .eq('id', user.id) - .maybeSingle(); - print('response: $response'); - - // Simpan ke cache - _cachedUserProfile = response; - - // Log untuk debugging - if (response != null && response['desa'] != null) { - print('Desa data: ${response['desa']}'); - print('Desa type: ${response['desa'].runtimeType}'); + // Jika tidak ada cache, ambil dari database + final user = currentUser; + if (user == null) { + print('DEBUG: Tidak ada user yang login'); + return null; } - return response; + final userId = user.id; + + // Debug info + print('DEBUG: Mengambil data user profile untuk ID: $userId'); + + // Ambil data role dari database + final roleResponse = await client + .from('users_with_roles') + .select('role_name') + .eq('id', userId) + .maybeSingle(); + + if (roleResponse == null) { + print('DEBUG: Tidak menemukan role untuk user ID: $userId'); + return null; + } + + final roleName = roleResponse['role_name']; + print('DEBUG: Role name: $roleName'); + + // Ambil data khusus untuk role tersebut + Map? roleData; + switch (roleName.toLowerCase()) { + case 'warga': + final wargaResponse = await client + .from('warga') + .select( + '*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .maybeSingle(); + roleData = wargaResponse; + break; + case 'petugas_desa': + final petugasResponse = await client + .from('petugas_desa') + .select( + '*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) + .maybeSingle(); + roleData = petugasResponse; + break; + case 'donatur': + final donaturResponse = await client + .from('donatur') + .select('*') + .eq('id', userId) + .maybeSingle(); + roleData = donaturResponse; + break; + default: + print('DEBUG: Role tidak dikenali: $roleName'); + return null; + } + + if (roleData == null) { + print('DEBUG: Tidak menemukan data untuk role: $roleName'); + return null; + } + + // Siapkan data kombinasi dari Supabase Auth + data dari tabel role + final combinedData = { + 'id': userId, + 'email': user.email, + 'role': roleName, + 'created_at': user.createdAt, + 'updated_at': user.updatedAt, + 'role_data': roleData, + }; + + // Tambahkan nama dari data role jika ada berdasarkan role + switch (roleName.toLowerCase()) { + case 'warga': + if (roleData['nama_lengkap'] != null) { + combinedData['name'] = roleData['nama_lengkap']; + } + break; + case 'petugas_desa': + case 'donatur': + if (roleData['nama'] != null) { + combinedData['name'] = roleData['nama']; + } + break; + } + + // Tambahkan data role-specific + if (roleData != null) { + combinedData['role_data'] = roleData; + + // Tambahkan data desa jika ada + if (roleData['desa'] != null) { + combinedData['desa'] = roleData['desa']; + } + + // Tambahkan nama dari data role jika ada + if (roleData['nama_lengkap'] != null) { + combinedData['name'] = roleData['nama_lengkap']; + } + } + + // Cache profil untuk penggunaan berikutnya + _cachedUserProfile = combinedData; + print('combinedData: $combinedData'); + return combinedData; } catch (e) { print('Error pada getUserProfile: $e'); return null; } } - // Metode untuk mendapatkan role pengguna - Future getUserRole() async { - final profile = await getUserProfile(); - return profile?['role']; + // Metode eksplisit untuk membersihkan cache profil + void clearUserProfileCache() { + print('DEBUG: Membersihkan cache profil pengguna'); + _cachedUserProfile = null; + } + + // Metode untuk mendapatkan data warga + Future getWargaProfile(String userId) async { + try { + final response = await client + .from('warga') + .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) // id di tabel warga = userId + .maybeSingle(); + + if (response == null) { + print('Data warga tidak ditemukan'); + return null; + } + + return WargaModel.fromJson(response); + } catch (e) { + print('Error pada getWargaProfile: $e'); + return null; + } + } + + // Metode untuk mendapatkan data donatur + Future getDonaturProfile(String userId) async { + try { + final response = await client + .from('donatur') + .select('*') + .eq('id', userId) // id di tabel donatur = userId + .maybeSingle(); + + if (response == null) { + print('Data donatur tidak ditemukan'); + return null; + } + + return DonaturModel.fromJson(response); + } catch (e) { + print('Error pada getDonaturProfile: $e'); + return null; + } + } + + // Metode untuk mendapatkan data petugas desa + Future getPetugasDesaProfile(String userId) async { + try { + final response = await client + .from('petugas_desa') + .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') + .eq('id', userId) // id di tabel petugas_desa = userId + .maybeSingle(); + + if (response == null) { + print('Data petugas desa tidak ditemukan'); + return null; + } + + return PetugasDesaModel.fromJson(response); + } catch (e) { + print('Error pada getPetugasDesaProfile: $e'); + return null; + } + } + + // Metode untuk mendapatkan data user lengkap berdasarkan role + // @deprecated Gunakan AuthProvider.getCurrentUser() sebagai gantinya + // Metode ini dipertahankan untuk kompatibilitas mundur + Future getUserData() async { + print( + 'WARNING: Menggunakan metode getUserData() yang sudah deprecated. Gunakan AuthProvider.getCurrentUser() sebagai gantinya.'); + + final baseUser = await getBaseUserProfile(); + if (baseUser == null) return null; + + try { + switch (baseUser.roleName.toLowerCase()) { + case 'warga': + final wargaData = await getWargaProfile(baseUser.id); + if (wargaData != null) { + return UserData( + baseUser: baseUser, + roleData: wargaData, + ); + } + break; + case 'donatur': + final donaturData = await getDonaturProfile(baseUser.id); + if (donaturData != null) { + return UserData( + baseUser: baseUser, + roleData: donaturData, + ); + } + break; + case 'petugas_desa': + final petugasDesaData = await getPetugasDesaProfile(baseUser.id); + if (petugasDesaData != null) { + return UserData( + baseUser: baseUser, + roleData: petugasDesaData, + ); + } + break; + } + + // Jika data role-specific tidak ditemukan + print('Data spesifik tidak ditemukan untuk role: ${baseUser.roleName}'); + return null; + } catch (e) { + print('Error pada getUserData: $e'); + return null; + } } // ==================== PETUGAS DESA METHODS ==================== @@ -211,6 +456,7 @@ class SupabaseService extends GetxService { Future>?> getNotifikasiBelumDibaca( String userId) async { try { + // Notifikasi masih menggunakan user_id karena tabelnya terpisah final response = await client .from('notifikasi') .select('*') @@ -457,7 +703,7 @@ class SupabaseService extends GetxService { try { final response = await client .from('penitipan_bantuan') - .select('*, donatur:donatur_id(*), stok_bantuan:stok_bantuan_id(*)') + .select('*, donatur(*), stok_bantuan:stok_bantuan_id(*)') .order('tanggal_penitipan', ascending: false); return response; @@ -472,7 +718,7 @@ class SupabaseService extends GetxService { try { final response = await client .from('penitipan_bantuan') - .select('*, donatur:donatur_id(*), stok_bantuan:stok_bantuan_id(*)') + .select('*, donatur(*), stok_bantuan:stok_bantuan_id(*)') .eq('status', 'TERVERIFIKASI') .order('tanggal_penitipan', ascending: false); @@ -677,6 +923,12 @@ class SupabaseService extends GetxService { // Metode untuk menambahkan donatur baru Future tambahDonatur(Map donaturData) async { try { + // Pastikan field nama_lengkap ada di donaturData + if (donaturData.containsKey('nama')) { + donaturData['nama_lengkap'] = donaturData['nama']; + donaturData.remove('nama'); + } + final response = await client.from('donatur').insert(donaturData).select('id'); if (response.isNotEmpty) { @@ -798,8 +1050,8 @@ class SupabaseService extends GetxService { .from('tindakan_pengaduan') .select(''' *, - petugas:petugas_id(id, nama, email), - verifikator:verifikator_id(id, nama, email) + petugas:petugas_id(id, nama_lengkap, nip), + verifikator:verifikator_id(id, nama_lengkap, nip) ''') .eq('pengaduan_id', pengaduanId) .order('created_at', ascending: false); @@ -956,7 +1208,7 @@ class SupabaseService extends GetxService { final response = await client .from('warga') .select('*') - .eq('user_id', user.id) + .eq('id', user.id) .maybeSingle(); return response; @@ -981,8 +1233,27 @@ class SupabaseService extends GetxService { final user = currentUser; if (user == null) throw 'User tidak ditemukan'; + // Dapatkan role_id untuk warga + final roleResponse = await client + .from('roles') + .select('id') + .eq('role_name', 'warga') + .single(); + + if (roleResponse == null) { + throw 'Role warga tidak ditemukan'; + } + + final roleId = roleResponse['id']; + + // Update role_id di auth.users + await client + .from('auth.users') + .update({'role_id': roleId}).eq('id', user.id); + + // Buat profil warga await client.from('warga').insert({ - 'user_id': user.id, + 'id': user.id, // Gunakan id dari auth.users sebagai id di tabel warga 'nik': nik, 'nama_lengkap': namaLengkap, 'jenis_kelamin': jenisKelamin, @@ -995,15 +1266,111 @@ class SupabaseService extends GetxService { 'created_at': DateTime.now().toIso8601String(), 'updated_at': DateTime.now().toIso8601String(), }); + } catch (e) { + print('Error creating warga profile: $e'); + throw e.toString(); + } + } - // Update user profile role - await client.from('user_profile').upsert({ - 'id': user.id, - 'role': 'WARGA', + // Metode untuk membuat profil donatur + Future createDonaturProfile({ + required String nama_lengkap, + String? alamat, + String? noHp, + String? email, + String? jenis, + String? deskripsi, + }) async { + try { + final user = currentUser; + if (user == null) throw 'User tidak ditemukan'; + + // Dapatkan role_id untuk donatur + final roleResponse = await client + .from('roles') + .select('id') + .eq('role_name', 'donatur') + .single(); + + if (roleResponse == null) { + throw 'Role donatur tidak ditemukan'; + } + + final roleId = roleResponse['id']; + + // Update role_id di auth.users + await client + .from('auth.users') + .update({'role_id': roleId}).eq('id', user.id); + + // Buat profil donatur + await client.from('donatur').insert({ + 'id': user.id, // Gunakan id dari auth.users sebagai id di tabel donatur + 'nama_lengkap': nama_lengkap, + 'alamat': alamat, + 'no_hp': noHp, + 'email': email, + 'jenis': jenis, + 'deskripsi': deskripsi, + 'status': 'AKTIF', + 'created_at': DateTime.now().toIso8601String(), 'updated_at': DateTime.now().toIso8601String(), }); } catch (e) { - print('Error creating warga profile: $e'); + print('Error creating donatur profile: $e'); + throw e.toString(); + } + } + + // Metode untuk membuat profil petugas desa + Future createPetugasDesaProfile({ + required String nama_lengkap, + String? alamat, + String? noHp, + String? email, + String? jabatan, + String? nip, + required String desa_id, + }) async { + try { + final user = currentUser; + if (user == null) throw 'User tidak ditemukan'; + + // Dapatkan role_id untuk petugas desa + final roleResponse = await client + .from('roles') + .select('id') + .eq('role_name', 'petugas_desa') + .single(); + + if (roleResponse == null) { + throw 'Role petugas desa tidak ditemukan'; + } + + final roleId = roleResponse['id']; + + // Update role_id di auth.users + await client + .from('auth.users') + .update({'role_id': roleId}).eq('id', user.id); + + // Buat profil petugas desa + await client.from('petugas_desa').insert({ + 'id': user + .id, // Gunakan id dari auth.users sebagai id di tabel petugas_desa + 'nama_lengkap': nama_lengkap, + 'alamat': alamat, + 'no_hp': noHp, + 'email': email, + 'jabatan': jabatan, + 'nip': nip, + 'desa_id': desa_id, + 'status': 'AKTIF', + 'created_at': DateTime.now().toIso8601String(), + 'updated_at': DateTime.now().toIso8601String(), + }); + } catch (e) { + print('Error creating petugas desa profile: $e'); throw e.toString(); } } @@ -1015,6 +1382,7 @@ class SupabaseService extends GetxService { final user = currentUser; if (user == null) return []; + // Notifikasi masih menggunakan user_id karena tabelnya terpisah final query = unreadOnly ? client .from('notifikasi') @@ -1054,19 +1422,17 @@ class SupabaseService extends GetxService { try { print('Mengambil data petugas desa dengan ID: $petugasDesaId'); - // Coba ambil dari tabel user_profile dulu + // Gunakan tabel petugas_desa sebagai pengganti user_profile final response = await client - .from('user_profile') - .select('*') + .from('petugas_desa') + .select('*, desa:desa_id(id, nama, kecamatan, kabupaten, provinsi)') .eq('id', petugasDesaId) - .eq('role', 'PETUGASDESA') .maybeSingle(); print('Response: $response'); if (response != null) { - print( - 'Berhasil mendapatkan data petugas desa dari user_profile: $response'); + print('Berhasil mendapatkan data petugas desa: $response'); return response; } @@ -1245,4 +1611,126 @@ class SupabaseService extends GetxService { return null; } } + + // Metode untuk update profil warga + Future updateWargaProfile({ + required String userId, + required String namaLengkap, + String? noHp, + String? email, + String? alamat, + String? nik, + String? tempatLahir, + DateTime? tanggalLahir, + String? jenisKelamin, + String? agama, + String? kategoriEkonomi, + }) async { + try { + final data = { + 'nama_lengkap': namaLengkap, + 'updated_at': DateTime.now().toIso8601String(), + }; + + if (noHp != null) data['no_hp'] = noHp; + if (email != null) data['email'] = email; + if (alamat != null) data['alamat'] = alamat; + if (nik != null) data['nik'] = nik; + if (tempatLahir != null) data['tempat_lahir'] = tempatLahir; + if (tanggalLahir != null) + data['tanggal_lahir'] = tanggalLahir.toIso8601String(); + if (jenisKelamin != null) data['jenis_kelamin'] = jenisKelamin; + if (agama != null) data['agama'] = agama; + if (kategoriEkonomi != null) data['kategori_ekonomi'] = kategoriEkonomi; + + await client.from('warga').update(data).eq('id', userId); + + // Hapus cache setelah update + clearUserProfileCache(); + } catch (e) { + print('Error updating warga profile: $e'); + throw e.toString(); + } + } + + // Metode untuk update profil donatur + Future updateDonaturProfile({ + required String userId, + required String nama, + String? alamat, + String? noHp, + String? email, + String? jenis, + String? instansi, + String? jabatan, + }) async { + try { + final data = { + 'nama': nama, + 'updated_at': DateTime.now().toIso8601String(), + }; + + if (alamat != null) data['alamat'] = alamat; + if (noHp != null) data['no_hp'] = noHp; + if (email != null) data['email'] = email; + if (jenis != null) data['jenis'] = jenis; + if (instansi != null) data['instansi'] = instansi; + if (jabatan != null) data['jabatan'] = jabatan; + + await client.from('donatur').update(data).eq('id', userId); + + // Hapus cache setelah update + clearUserProfileCache(); + } catch (e) { + print('Error updating donatur profile: $e'); + throw e.toString(); + } + } + + // Metode untuk update profil petugas desa + Future updatePetugasDesaProfile({ + required String userId, + required String nama, + String? alamat, + String? noHp, + String? email, + String? nip, + String? jabatan, + }) async { + try { + final data = { + 'nama': nama, + 'updated_at': DateTime.now().toIso8601String(), + }; + + if (alamat != null) data['alamat'] = alamat; + if (noHp != null) data['no_hp'] = noHp; + if (email != null) data['email'] = email; + if (nip != null) data['nip'] = nip; + if (jabatan != null) data['jabatan'] = jabatan; + + await client.from('petugas_desa').update(data).eq('id', userId); + + // Hapus cache setelah update + clearUserProfileCache(); + } catch (e) { + print('Error updating petugas desa profile: $e'); + throw e.toString(); + } + } + + // Metode untuk ganti password + Future changePassword( + String currentPassword, String newPassword) async { + try { + await client.auth.updateUser( + UserAttributes( + password: newPassword, + ), + ); + } catch (e) { + print('Error changing password: $e'); + throw e.toString(); + } + } }