Perbarui beberapa file konfigurasi fingerprint untuk arsitektur arm64-v8a, armeabi-v7a, x86, dan x86_64. Modifikasi model SkemaBantuan untuk menggunakan operator null-aware pada jumlah diterima per orang. Perbarui logika pengambilan data di AuthProvider untuk menyederhanakan pengecekan dan logging. Tambahkan fungsionalitas baru di DonaturDashboardController untuk mengunggah foto bantuan dan memperbarui data yang disimpan. Hapus tampilan yang tidak digunakan di DonaturRiwayatPenitipanView dan perbarui tampilan di beberapa view untuk meningkatkan pengalaman pengguna.

This commit is contained in:
Khafidh Fuadi
2025-03-26 09:57:13 +07:00
parent 88bef1c8e1
commit eede5ebd4d
33 changed files with 600 additions and 793 deletions

View File

@ -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  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>

}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2~
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
n
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
r
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2y
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2y
w
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2z
x
x
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 ~
|
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2w
u
u
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2|
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2|

View File

@ -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  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
~
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
p
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
t
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2{
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2{
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2|
z
z
xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>
~
~
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2y
w
w
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2~
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2~

View File

@ -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  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2x
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2x
v
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
{
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
h
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
l
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2s
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2s
q
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2t
r
r
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 x
v
v
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
~
|
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2q
o
o
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2v
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2v

View File

@ -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  ِ<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt  ِ<EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2{
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2{
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json  ِ<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
~
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json  ِ<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2m
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2m
k
iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja  ِ<EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2q
iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2q
o
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt  ِ<EFBFBD><EFBFBD><EFBFBD>2v
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2v
t
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt  ِ<EFBFBD><EFBFBD><EFBFBD>2
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2w
u
u
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 {
y
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>


}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2t
r
r
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2y
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2y

View File

@ -48,9 +48,7 @@ class SkemaBantuanModel {
: null,
stokBantuanId: json["stok_bantuan_id"],
kategoriBantuanId: json["kategori_bantuan_id"],
jumlahDiterimaPerOrang: json["jumlah_diterima_per_orang"] != null
? json["jumlah_diterima_per_orang"].toDouble()
: null,
jumlahDiterimaPerOrang: json["jumlah_diterima_per_orang"]?.toDouble(),
);
Map<String, dynamic> toJson() => {

View File

@ -89,19 +89,14 @@ class AuthProvider {
.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;
// 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 if (roleName == 'petugas_desa') {
profileResponse = await _supabaseService.client
.from('petugas_desa')
@ -109,17 +104,15 @@ class AuthProvider {
.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}');
// 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')
@ -252,19 +245,14 @@ class AuthProvider {
.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;
// 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 if (roleName.toLowerCase() == 'petugas_desa') {
profileResponse = await _supabaseService.client
.from('petugas_desa')
@ -273,15 +261,13 @@ class AuthProvider {
.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);
// 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')
@ -289,9 +275,7 @@ class AuthProvider {
.eq('id', userId)
.single();
if (profileResponse != null) {
roleData = DonaturModel.fromJson(profileResponse);
}
roleData = DonaturModel.fromJson(profileResponse);
}
if (roleData == null) {

View File

@ -401,9 +401,19 @@ class DonaturDashboardController extends GetxController {
throw Exception('Foto bantuan harus diunggah');
}
// Dapatkan informasi stok bantuan untuk mendapatkan nilai is_uang
final selectedStokBantuan = stokBantuan.firstWhere(
(stok) => stok.id == stokBantuanId,
orElse: () => StokBantuanModel(),
);
// Unggah foto bantuan ke storage menggunakan metode dari SupabaseService
final fotoBantuanUrls = await _supabaseService.uploadMultipleFiles(
fotoBantuanPaths, 'penitipan', 'foto_bantuan');
fotoBantuanPaths, 'bantuan', 'foto_bantuan');
if (fotoBantuanUrls == null || fotoBantuanUrls.isEmpty) {
throw 'Gagal mengupload foto bantuan';
}
// Data yang akan disimpan
final Map<String, dynamic> data = {
@ -414,6 +424,7 @@ class DonaturDashboardController extends GetxController {
'status': 'MENUNGGU',
'tanggal_penitipan': DateTime.now().toIso8601String(),
'foto_bantuan': fotoBantuanUrls,
'is_uang': selectedStokBantuan.isUang ?? false,
};
// Tambahkan skema bantuan jika ada
@ -438,9 +449,6 @@ class DonaturDashboardController extends GetxController {
colorText: Colors.white,
duration: const Duration(seconds: 3),
);
// Pindah ke tab riwayat penitipan
DefaultTabController.of(Get.context!)?.animateTo(0);
} catch (e) {
print('Error creating penitipan bantuan: $e');
Get.snackbar(

View File

@ -25,443 +25,9 @@ class DonaturPenitipanView extends GetView<DonaturDashboardController> {
}
}
class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
DonaturRiwayatPenitipanView({Key? key}) : super(key: key);
@override
DonaturDashboardController get controller {
return Get.find<DonaturDashboardController>(tag: 'donatur_dashboard');
}
final TextEditingController searchController = TextEditingController();
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('Riwayat Penitipan'),
bottom: const TabBar(
tabs: [
Tab(text: 'Menunggu'),
Tab(text: 'Diterima'),
Tab(text: 'Ditolak'),
],
),
),
body: TabBarView(
children: [
// Tab Menunggu
_buildPenitipanList(context, 'MENUNGGU'),
// Tab Diterima
_buildPenitipanList(context, 'DITERIMA'),
// Tab Ditolak
_buildPenitipanList(context, 'DITOLAK'),
],
),
),
);
}
Widget _buildPenitipanList(BuildContext context, String status) {
return Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
// Filter penitipan berdasarkan status
var filteredList = controller.penitipanBantuan
.where((item) => item.status == status)
.toList();
// Filter berdasarkan pencarian
final searchText = searchController.text.toLowerCase();
if (searchText.isNotEmpty) {
filteredList = filteredList.where((item) {
final kategoriNama = item.kategoriBantuan?.nama?.toLowerCase() ?? '';
final deskripsi = item.deskripsi?.toLowerCase() ?? '';
final tanggal = item.tanggalPenitipan != null
? DateFormat('dd MMMM yyyy', 'id_ID')
.format(item.tanggalPenitipan!)
.toLowerCase()
: '';
return kategoriNama.contains(searchText) ||
deskripsi.contains(searchText) ||
tanggal.contains(searchText);
}).toList();
}
return RefreshIndicator(
onRefresh: () async {
await controller.fetchPenitipanBantuan();
},
child: filteredList.isEmpty
? _buildEmptyState(status)
: _buildContentList(context, filteredList, status),
);
});
}
Widget _buildEmptyState(String status) {
String statusText = '';
switch (status) {
case 'MENUNGGU':
statusText = 'menunggu verifikasi';
break;
case 'DITERIMA':
statusText = 'diterima';
break;
case 'DITOLAK':
statusText = 'ditolak';
break;
}
return Center(
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inventory_2_outlined,
size: 80,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'Tidak ada penitipan $statusText',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Text(
'Anda belum memiliki riwayat penitipan yang $statusText',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
textAlign: TextAlign.center,
),
),
],
),
),
);
}
Widget _buildContentList(
BuildContext context, List<dynamic> filteredList, String status) {
Color statusColor;
switch (status) {
case 'DITERIMA':
statusColor = Colors.green;
break;
case 'DITOLAK':
statusColor = Colors.red;
break;
case 'MENUNGGU':
default:
statusColor = Colors.orange;
break;
}
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Search field
TextField(
controller: searchController,
decoration: InputDecoration(
hintText: 'Cari riwayat penitipan...',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.grey.shade100,
contentPadding: const EdgeInsets.symmetric(vertical: 0),
),
onChanged: (value) {
// Trigger update dengan GetX
controller.update();
},
),
const SizedBox(height: 16),
// Info jumlah item
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Daftar Penitipan ${status.toLowerCase()}',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
'${filteredList.length} item',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
),
],
),
const SizedBox(height: 16),
// Daftar penitipan
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: filteredList.length,
itemBuilder: (context, index) {
return _buildPenitipanCard(
context, filteredList[index], statusColor);
},
),
],
),
),
);
}
Widget _buildPenitipanCard(
BuildContext context, dynamic penitipan, Color statusColor) {
final formattedDate = penitipan.tanggalPenitipan != null
? DateFormat('dd MMMM yyyy', 'id_ID')
.format(penitipan.tanggalPenitipan!)
: 'Tanggal tidak tersedia';
IconData statusIcon;
switch (penitipan.status) {
case 'DITERIMA':
statusIcon = Icons.check_circle;
break;
case 'DITOLAK':
statusIcon = Icons.cancel;
break;
case 'MENUNGGU':
default:
statusIcon = Icons.hourglass_empty;
break;
}
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
statusIcon,
color: statusColor,
size: 24,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
penitipan.kategoriBantuan?.nama ?? 'Bantuan',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
penitipan.status ?? 'MENUNGGU',
style: TextStyle(
fontSize: 12,
color: statusColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.calendar_today,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
formattedDate,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.inventory_2_outlined,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
'Jumlah: ${penitipan.jumlah ?? 0}',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade800,
),
),
],
),
],
),
),
],
),
if (penitipan.deskripsi != null &&
penitipan.deskripsi!.isNotEmpty) ...[
const Divider(height: 24),
Text(
penitipan.deskripsi!,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
if (penitipan.status == 'DITOLAK' &&
penitipan.alasanPenolakan != null &&
penitipan.alasanPenolakan!.isNotEmpty) ...[
const Divider(height: 24),
Text(
'Alasan Penolakan:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.red.shade700,
),
),
const SizedBox(height: 4),
Text(
penitipan.alasanPenolakan!,
style: TextStyle(
fontSize: 14,
color: Colors.red.shade700,
),
),
],
],
),
),
),
);
}
Widget _buildContactInfo({
required IconData icon,
required String title,
required String content,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
color: Colors.blue,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
content,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
],
),
),
],
);
}
}
class FormPenitipanBantuan extends StatefulWidget {
const FormPenitipanBantuan({super.key});
@override
_FormPenitipanBantuanState createState() => _FormPenitipanBantuanState();
}

View File

@ -4,7 +4,7 @@ import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/modules/donatur/controllers/donatur_dashboard_controller.dart';
class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
DonaturRiwayatPenitipanView({Key? key}) : super(key: key);
DonaturRiwayatPenitipanView({super.key});
@override
DonaturDashboardController get controller {
@ -23,7 +23,7 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
bottom: const TabBar(
tabs: [
Tab(text: 'Menunggu'),
Tab(text: 'Diterima'),
Tab(text: 'Terverifikasi'),
Tab(text: 'Ditolak'),
],
),
@ -33,7 +33,7 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
// Tab Menunggu
_buildPenitipanList(context, 'MENUNGGU'),
// Tab Diterima
_buildPenitipanList(context, 'DITERIMA'),
_buildPenitipanList(context, 'TERVERIFIKASI'),
// Tab Ditolak
_buildPenitipanList(context, 'DITOLAK'),
],
@ -88,8 +88,8 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
case 'MENUNGGU':
statusText = 'menunggu verifikasi';
break;
case 'DITERIMA':
statusText = 'diterima';
case 'TERVERIFIKASI':
statusText = 'terverifikasi';
break;
case 'DITOLAK':
statusText = 'ditolak';
@ -137,7 +137,7 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
BuildContext context, List<dynamic> filteredList, String status) {
Color statusColor;
switch (status) {
case 'DITERIMA':
case 'TERVERIFIKASI':
statusColor = Colors.green;
break;
case 'DITOLAK':
@ -221,7 +221,7 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
IconData statusIcon;
switch (penitipan.status) {
case 'DITERIMA':
case 'TERVERIFIKASI':
statusIcon = Icons.check_circle;
break;
case 'DITOLAK':
@ -233,160 +233,397 @@ class DonaturRiwayatPenitipanView extends GetView<DonaturDashboardController> {
break;
}
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 5),
return Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
print('Debug: Tapped on penitipan with ID: ${penitipan.id}');
_showDetailDialog(context, penitipan);
},
child: Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
statusIcon,
color: statusColor,
size: 24,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 50,
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
statusIcon,
color: statusColor,
size: 24,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
penitipan.kategoriBantuan?.nama ??
'Bantuan',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
penitipan.status ?? 'MENUNGGU',
style: TextStyle(
fontSize: 12,
color: statusColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.calendar_today,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
formattedDate,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.inventory_2_outlined,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
'Jumlah: ${penitipan.jumlah ?? 0}',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade800,
),
),
],
),
],
),
),
],
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
penitipan.kategoriBantuan?.nama ?? 'Bantuan',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (penitipan.deskripsi != null &&
penitipan.deskripsi!.isNotEmpty) ...[
const Divider(height: 24),
Text(
penitipan.deskripsi!,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
if (penitipan.status == 'DITOLAK' &&
penitipan.alasanPenolakan != null &&
penitipan.alasanPenolakan!.isNotEmpty) ...[
const Divider(height: 24),
Text(
'Alasan Penolakan:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.red.shade700,
),
),
const SizedBox(height: 4),
Text(
penitipan.alasanPenolakan!,
style: TextStyle(
fontSize: 14,
color: Colors.red.shade700,
),
),
],
// Tambahkan petunjuk visual
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.info_outline,
size: 14,
color: Colors.blue.shade700,
),
const SizedBox(width: 4),
Text(
'Tap untuk detail',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade700,
fontStyle: FontStyle.italic,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
penitipan.status ?? 'MENUNGGU',
style: TextStyle(
fontSize: 12,
color: statusColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.calendar_today,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
formattedDate,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.inventory_2_outlined,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
'Jumlah: ${penitipan.jumlah ?? 0}',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade800,
),
),
],
),
],
),
],
),
),
),
],
),
if (penitipan.deskripsi != null &&
penitipan.deskripsi!.isNotEmpty) ...[
const Divider(height: 24),
Text(
penitipan.deskripsi!,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
),
),
);
}
void _showDetailDialog(BuildContext context, dynamic penitipan) {
final isUang = penitipan.isUang ?? false;
final kategoriSatuan = penitipan.kategoriBantuan?.satuan ?? '';
String getPetugasDesaNama(String? id) {
return id != null ? 'Petugas Desa' : 'Tidak ada petugas';
}
void showFullScreenImage(String imageUrl) {
Get.dialog(
Dialog(
insetPadding: EdgeInsets.zero,
child: Container(
color: Colors.black,
child: Stack(
fit: StackFit.expand,
children: [
InteractiveViewer(
panEnabled: true,
minScale: 0.5,
maxScale: 4,
child: Image.network(
imageUrl,
fit: BoxFit.contain,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
),
),
Positioned(
top: 20,
right: 20,
child: GestureDetector(
onTap: () => Get.back(),
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
color: Colors.white,
size: 24,
),
),
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
if (penitipan.status == 'DITOLAK' &&
penitipan.alasanPenolakan != null &&
penitipan.alasanPenolakan!.isNotEmpty) ...[
const Divider(height: 24),
Text(
'Alasan Penolakan:',
),
),
),
);
}
Get.dialog(
AlertDialog(
title: const Text('Detail Penitipan'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoRow('ID', penitipan.id ?? '-'),
_buildInfoRow('Kategori', penitipan.kategoriBantuan?.nama ?? '-'),
_buildInfoRow(
'Jumlah',
isUang
? 'Rp ${penitipan.jumlah?.toStringAsFixed(0) ?? '0'}'
: '${penitipan.jumlah?.toString() ?? '0'} $kategoriSatuan',
),
_buildInfoRow(
'Tanggal Penitipan',
penitipan.tanggalPenitipan != null
? DateFormat('dd MMMM yyyy', 'id_ID')
.format(penitipan.tanggalPenitipan!)
: 'Tanggal tidak tersedia',
),
_buildInfoRow(
'Status',
penitipan.status ?? 'Belum diproses',
),
if (penitipan.tanggalVerifikasi != null)
_buildInfoRow(
'Tanggal Verifikasi',
DateFormat('dd MMMM yyyy HH:mm', 'id_ID')
.format(penitipan.tanggalVerifikasi!),
),
if (penitipan.deskripsi != null &&
penitipan.deskripsi!.isNotEmpty)
_buildInfoRow('Deskripsi', penitipan.deskripsi!),
if (penitipan.alasanPenolakan != null &&
penitipan.alasanPenolakan!.isNotEmpty)
_buildInfoRow('Alasan Penolakan', penitipan.alasanPenolakan!),
// Gambar bukti penitipan
if (penitipan.fotoBantuan != null &&
penitipan.fotoBantuan!.isNotEmpty) ...[
const SizedBox(height: 16),
const Text(
'Bukti Penitipan',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.red.shade700,
fontSize: 14,
),
),
const SizedBox(height: 4),
Text(
penitipan.alasanPenolakan!,
const SizedBox(height: 8),
GestureDetector(
onTap: () =>
showFullScreenImage(penitipan.fotoBantuan!.first),
child: Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: NetworkImage(penitipan.fotoBantuan!.first),
fit: BoxFit.cover,
),
),
),
),
],
// Bukti serah terima jika ada
if (penitipan.fotoBuktiSerahTerima != null &&
penitipan.fotoBuktiSerahTerima!.isNotEmpty) ...[
const SizedBox(height: 16),
const Text(
'Bukti Serah Terima',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.red.shade700,
),
),
const SizedBox(height: 8),
GestureDetector(
onTap: () =>
showFullScreenImage(penitipan.fotoBuktiSerahTerima!),
child: Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: NetworkImage(penitipan.fotoBuktiSerahTerima!),
fit: BoxFit.cover,
),
),
),
),
],
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text(
'Tutup',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(fontSize: 14),
),
],
),
);
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/modules/donatur/controllers/donatur_dashboard_controller.dart';
import 'package:penyaluran_app/app/widgets/section_header.dart';

View File

@ -6,6 +6,7 @@ import 'package:penyaluran_app/app/modules/donatur/views/donatur_skema_view.dart
import 'package:penyaluran_app/app/modules/donatur/views/donatur_jadwal_view.dart';
import 'package:penyaluran_app/app/modules/donatur/views/donatur_laporan_view.dart';
import 'package:penyaluran_app/app/modules/donatur/views/donatur_penitipan_view.dart';
import 'package:penyaluran_app/app/modules/donatur/views/donatur_riwayat_penitipan_view.dart';
import 'package:penyaluran_app/app/widgets/app_bottom_navigation_bar.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';

View File

@ -891,13 +891,14 @@ class LaporanPenyaluranController extends GetxController {
final stokBantuan = daftarPenerima
.firstWhere((p) => p.stokBantuanId == stokId,
orElse: () => PenerimaPenyaluranModel())
?.stokBantuan;
.stokBantuan;
if (stokBantuan == null)
if (stokBantuan == null) {
return pw.TableRow(children: [
_buildPdfTableCell('-', ttf),
_buildPdfTableCell('-', ttf),
]);
}
final isUang = stokBantuan['is_uang'] == true;
final formattedJumlah = isUang
@ -912,7 +913,7 @@ class LaporanPenyaluranController extends GetxController {
align: pw.TextAlign.center),
],
);
}).toList(),
}),
],
),
],
@ -988,7 +989,7 @@ class LaporanPenyaluranController extends GetxController {
align: pw.TextAlign.center),
],
);
}).toList(),
}),
],
),
],

View File

@ -10,7 +10,7 @@ import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
class LaporanPenyaluranCreateView extends GetView<LaporanPenyaluranController> {
const LaporanPenyaluranCreateView({Key? key}) : super(key: key);
const LaporanPenyaluranCreateView({super.key});
@override
Widget build(BuildContext context) {

View File

@ -11,7 +11,7 @@ import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
import 'package:url_launcher/url_launcher.dart';
class LaporanPenyaluranDetailView extends GetView<LaporanPenyaluranController> {
const LaporanPenyaluranDetailView({Key? key}) : super(key: key);
const LaporanPenyaluranDetailView({super.key});
@override
Widget build(BuildContext context) {
@ -385,8 +385,9 @@ class LaporanPenyaluranDetailView extends GetView<LaporanPenyaluranController> {
orElse: () => PenerimaPenyaluranModel())
.stokBantuan;
if (stokBantuan == null)
if (stokBantuan == null) {
return const SizedBox.shrink();
}
final kategori =
stokBantuan['kategori_bantuan'] != null
@ -939,9 +940,9 @@ class LaporanPenyaluranDetailView extends GetView<LaporanPenyaluranController> {
return InkWell(
onTap: () async {
final Uri _url = Uri.parse(url);
if (await canLaunchUrl(_url)) {
await launchUrl(_url, mode: LaunchMode.externalApplication);
final Uri url0 = Uri.parse(url);
if (await canLaunchUrl(url0)) {
await launchUrl(url0, mode: LaunchMode.externalApplication);
} else {
Get.snackbar(
'Error',

View File

@ -8,7 +8,7 @@ import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
class LaporanPenyaluranEditView extends GetView<LaporanPenyaluranController> {
const LaporanPenyaluranEditView({Key? key}) : super(key: key);
const LaporanPenyaluranEditView({super.key});
@override
Widget build(BuildContext context) {

View File

@ -10,7 +10,7 @@ import 'package:penyaluran_app/app/widgets/status_badge.dart';
import 'package:intl/intl.dart';
class LaporanPenyaluranView extends GetView<LaporanPenyaluranController> {
const LaporanPenyaluranView({Key? key}) : super(key: key);
const LaporanPenyaluranView({super.key});
@override
Widget build(BuildContext context) {

View File

@ -39,35 +39,14 @@ class DetailPenyaluranController extends GetxController {
if (penyaluran.value?.id != null) {
loadPenyaluranDetails(penyaluran.value!.id!);
}
checkUserRole();
} else if (penyaluranId != null) {
// Jika hanya ID penyaluran yang diterima
loadPenyaluranData(penyaluranId);
checkUserRole();
} else {
isLoading.value = false;
}
}
Future<void> checkUserRole() async {
try {
final user = _supabaseService.client.auth.currentUser;
if (user != null) {
final userData = await _supabaseService.client
.from('user_profile')
.select('role')
.eq('id', user.id)
.single();
if (userData['role'] == 'PETUGASDESA') {
isPetugasDesa.value = true;
}
}
} catch (e) {
print('Error checking user role: $e');
}
}
Future<void> loadPenyaluranData(String penyaluranId) async {
try {
isLoading.value = true;
@ -522,36 +501,33 @@ class DetailPenyaluranController extends GetxController {
.eq('qr_code_hash', qrHash)
.single();
if (data != null) {
// Jika penerima ditemukan, konversi ke model
final Map<String, dynamic> sanitizedPenerimaData =
Map<String, dynamic>.from(data);
// Jika penerima ditemukan, konversi ke model
final Map<String, dynamic> sanitizedPenerimaData =
Map<String, dynamic>.from(data);
// Konversi jumlah_bantuan ke double jika bertipe String
if (sanitizedPenerimaData['jumlah_bantuan'] is String) {
sanitizedPenerimaData['jumlah_bantuan'] = double.tryParse(
sanitizedPenerimaData['jumlah_bantuan'] as String);
}
// Konversi data ke model
final penerima =
PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData);
// Set isProcessing ke false sebelum navigasi untuk menghindari masalah loading
isProcessing.value = false;
// Navigasi ke halaman konfirmasi dengan data terbaru
await Get.toNamed('/petugas-desa/konfirmasi-penerima/${penerima.id}',
arguments: {
'penerima': penerima,
'tanggal_penyaluran': penyaluran.value?.tanggalPenyaluran
});
// Refresh data
await refreshData();
return true;
// Konversi jumlah_bantuan ke double jika bertipe String
if (sanitizedPenerimaData['jumlah_bantuan'] is String) {
sanitizedPenerimaData['jumlah_bantuan'] =
double.tryParse(sanitizedPenerimaData['jumlah_bantuan'] as String);
}
// Konversi data ke model
final penerima = PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData);
// Set isProcessing ke false sebelum navigasi untuk menghindari masalah loading
isProcessing.value = false;
// Navigasi ke halaman konfirmasi dengan data terbaru
await Get.toNamed('/petugas-desa/konfirmasi-penerima/${penerima.id}',
arguments: {
'penerima': penerima,
'tanggal_penyaluran': penyaluran.value?.tanggalPenyaluran
});
// Refresh data
await refreshData();
return true;
return false;
} catch (e) {
print('Error verifikasi QR code: $e');

View File

@ -448,7 +448,7 @@ class JadwalPenyaluranController extends GetxController {
.eq('id', stokBantuanId)
.single();
if (stokData != null && stokData['total_stok'] != null) {
if (stokData['total_stok'] != null) {
final currentStok = stokData['total_stok'].toDouble();
final newStok = currentStok - totalStokDibutuhkan;

View File

@ -197,9 +197,63 @@ class PenitipanBantuanController extends GetxController {
}
Future<void> pickfotoBuktiSerahTerima() async {
try {
// Tampilkan bottom sheet untuk memilih sumber foto
Get.bottomSheet(
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Pilih Sumber Foto',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 16),
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Kamera'),
onTap: () {
Get.back();
_pickfotoBuktiSerahTerimaFrom(ImageSource.camera);
},
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Galeri'),
onTap: () {
Get.back();
_pickfotoBuktiSerahTerimaFrom(ImageSource.gallery);
},
),
],
),
),
);
} catch (e) {
print('Error showing bottom sheet: $e');
Get.snackbar(
'Error',
'Terjadi kesalahan: ${e.toString()}',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
// Fungsi helper untuk mengambil foto dari sumber yang dipilih
Future<void> _pickfotoBuktiSerahTerimaFrom(ImageSource source) async {
try {
final pickedFile = await _imagePicker.pickImage(
source: ImageSource.camera,
source: source,
imageQuality: 70,
maxWidth: 1000,
);

View File

@ -74,9 +74,7 @@ class PetugasDesaController extends GetxController {
String get nama {
// 1. Coba ambil dari AuthController displayName yang paling lengkap
final authDisplayName = _authController.displayName;
if (authDisplayName != null &&
authDisplayName != 'Pengguna' &&
authDisplayName != user?.email) {
if (authDisplayName != 'Pengguna' && authDisplayName != user?.email) {
return authDisplayName;
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/routes/app_pages.dart';
import 'package:penyaluran_app/app/utils/date_time_helper.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/donatur_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/data/models/donatur_model.dart';
import 'package:penyaluran_app/app/data/models/penitipan_bantuan_model.dart';
import 'package:penyaluran_app/app/widgets/dialogs/detail_penitipan_dialog.dart';

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/penerima_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/utils/date_time_helper.dart';
class DetailPenerimaView extends GetView<PenerimaController> {

View File

@ -743,10 +743,10 @@ class _KonfirmasiPenerimaPageState extends State<KonfirmasiPenerimaPage> {
// Hapus file sementara sebelum navigasi
try {
if (signatureFile != null && signatureFile.existsSync()) {
if (signatureFile.existsSync()) {
await signatureFile.delete();
}
if (tempDir != null && tempDir.existsSync()) {
if (tempDir.existsSync()) {
await tempDir.delete();
}
} catch (e) {

View File

@ -632,18 +632,26 @@ class PenitipanView extends GetView<PenitipanBantuanController> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.camera_alt,
Icons.add_photo_alternate,
size: 48,
color: Colors.grey.shade600,
),
const SizedBox(height: 8),
Text(
'Ambil Foto',
'Pilih Foto',
style: TextStyle(
color: Colors.grey.shade600,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'Kamera atau Galeri',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
],
),
),

View File

@ -426,7 +426,9 @@ class StokBantuanView extends GetView<StokBantuanController> {
context,
icon: Icons.access_time,
label: 'Terakhir Diperbarui',
value: DateTimeHelper.formatDateTime(item.updatedAt),
value: item.updatedAt != null
? '${item.updatedAt!.day}/${item.updatedAt!.month}/${item.updatedAt!.year} ${item.updatedAt!.hour}:${item.updatedAt!.minute}'
: 'Tidak ada data',
),
),
],

View File

@ -90,29 +90,20 @@ class TambahPenyaluranView extends GetView<JadwalPenyaluranController> {
print('stokData $stokData');
if (stokData != null) {
namaStokBantuan.value =
stokData['nama'] ?? 'Nama stok tidak tersedia';
satuanStokBantuan.value = stokData['satuan'] ?? 'Tidak ada satuan';
isUang.value = stokData['is_uang'] ?? false;
namaStokBantuan.value = stokData['nama'] ?? 'Nama stok tidak tersedia';
satuanStokBantuan.value = stokData['satuan'] ?? 'Tidak ada satuan';
isUang.value = stokData['is_uang'] ?? false;
// Ambil jumlah stok tersedia
if (stokData['total_stok'] != null) {
totalStokTersedia.value = stokData['total_stok'].toDouble();
} else {
totalStokTersedia.value = 0;
}
// Periksa kecukupan stok
isStokCukup.value =
totalStokTersedia.value >= totalStokDibutuhkan.value;
// Ambil jumlah stok tersedia
if (stokData['total_stok'] != null) {
totalStokTersedia.value = stokData['total_stok'].toDouble();
} else {
namaStokBantuan.value = 'Stok tidak ditemukan';
satuanStokBantuan.value = '';
totalStokTersedia.value = 0;
isStokCukup.value = false;
isUang.value = false;
}
// Periksa kecukupan stok
isStokCukup.value =
totalStokTersedia.value >= totalStokDibutuhkan.value;
} catch (e) {
print('Error loading stok bantuan: $e');
namaStokBantuan.value = 'Error memuat data stok';

View File

@ -4,7 +4,6 @@ import 'package:penyaluran_app/app/data/models/user_model.dart';
import 'package:penyaluran_app/app/services/supabase_service.dart';
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
class ProfileController extends GetxController {
final SupabaseService _supabaseService = Get.find<SupabaseService>();

View File

@ -743,10 +743,10 @@ class ProfileView extends GetView<ProfileController> {
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('Batal'),
style: TextButton.styleFrom(
foregroundColor: Colors.grey[700],
),
child: const Text('Batal'),
),
ElevatedButton(
onPressed: () {

View File

@ -125,7 +125,7 @@ class WargaDashboardController extends GetxController {
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: nama yang akan ditampilkan: $nama');
print(
'DEBUG WARGA: displayName dari auth controller: ${_authController.displayName}');

View File

@ -539,7 +539,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
),
],
);
}).toList(),
}),
],
if (totalUang == 0 && totalNonUang.isEmpty)
_buildSummaryItem(

View File

@ -4,7 +4,6 @@ import 'package:intl/intl.dart';
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
import 'package:penyaluran_app/app/data/models/pengaduan_model.dart';
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/widgets/status_badge.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:image_picker/image_picker.dart';

View File

@ -265,18 +265,16 @@ class SupabaseService extends GetxService {
}
// Tambahkan data role-specific
if (roleData != null) {
combinedData['role_data'] = roleData;
combinedData['role_data'] = roleData;
// Tambahkan data desa jika ada
if (roleData['desa'] != null) {
combinedData['desa'] = roleData['desa'];
}
// 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'];
}
// Tambahkan nama dari data role jika ada
if (roleData['nama_lengkap'] != null) {
combinedData['name'] = roleData['nama_lengkap'];
}
// Cache profil untuk penggunaan berikutnya
@ -783,6 +781,8 @@ class SupabaseService extends GetxService {
Future<String?> uploadFile(
String filePath, String bucket, String folder) async {
try {
print(
'Uploading file from path: $filePath to bucket: $bucket in folder: $folder');
final fileName = filePath.split('/').last;
final fileExt = fileName.split('.').last;
final fileKey =
@ -1327,10 +1327,6 @@ class SupabaseService extends GetxService {
.eq('role_name', 'warga')
.single();
if (roleResponse == null) {
throw 'Role warga tidak ditemukan';
}
final roleId = roleResponse['id'];
// Update role_id di auth.users
@ -1379,10 +1375,6 @@ class SupabaseService extends GetxService {
.eq('role_name', 'donatur')
.single();
if (roleResponse == null) {
throw 'Role donatur tidak ditemukan';
}
final roleId = roleResponse['id'];
// Update role_id di auth.users
@ -1477,10 +1469,6 @@ class SupabaseService extends GetxService {
.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