Perbarui model Pengaduan dengan menambahkan getter isUang untuk memeriksa jenis bantuan. Modifikasi tampilan dan controller di modul donatur dan petugas desa untuk meningkatkan pengalaman pengguna, termasuk penggantian ikon dan penyesuaian format tampilan jumlah bantuan. Hapus kode yang tidak diperlukan untuk menjaga kebersihan kode.

This commit is contained in:
Khafidh Fuadi
2025-04-10 14:25:41 +07:00
parent 3f78514175
commit ca6c28f3d6
40 changed files with 3103 additions and 2270 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>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>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>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  ԥ<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>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  ե<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>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja  ե<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>2y
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt  ե<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>2
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt  ե<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  ե<><D5A5><EFBFBD>2 ~
|
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin  ե<><D5A5><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  ե<><D5A5><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  ե<><D5A5><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  ΁Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint  <EFBFBD><EFBFBD><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  ΁Ƹ<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  <EFBFBD><EFBFBD><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  ΁Ƹ<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  <EFBFBD><EFBFBD><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  ΁Ƹ<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  <EFBFBD><EFBFBD><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  ΁Ƹ<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  <EFBFBD><EFBFBD><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  ΁Ƹ<EFBFBD>2{
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt  <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2{
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  ΁Ƹ<EFBFBD>2
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt  <EFBFBD><EFBFBD><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>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>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>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>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>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>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>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>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>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>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>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>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>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>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

@ -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>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\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\RelWithDebInfo\1o2r2b1v\arm64-v8a\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\RelWithDebInfo\1o2r2b1v\arm64-v8a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2y
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2y
w
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2}
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2}
{
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2<07>
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2<07>
<EFBFBD>
~D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
~D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\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\RelWithDebInfo\1o2r2b1v\arm64-v8a\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
~
~
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\arm64-v8a\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>

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>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  Т<><D0A2><EFBFBD>2<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  Т<><D0A2><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> ڢ<><DAA2><EFBFBD>2<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\android_gradle_build.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> ڢ<><DAA2><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
y
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2
}
{D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2<07>
{D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2<07>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\armeabi-v7a\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\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\RelWithDebInfo\1o2r2b1v\armeabi-v7a\metadata_generation_command.txt  <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\RelWithDebInfo\1o2r2b1v\armeabi-v7a\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>

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>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\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\RelWithDebInfo\1o2r2b1v\x86\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\RelWithDebInfo\1o2r2b1v\x86\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\RelWithDebInfo\1o2r2b1v\x86\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2s
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2s
q
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2w
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2w
u
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2|
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2|
z
xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2}
{
{
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>


}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2z
x
x
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86\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>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\additional_project_files.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2  <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\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\RelWithDebInfo\1o2r2b1v\x86_64\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\RelWithDebInfo\1o2r2b1v\x86_64\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2v
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\android_gradle_build_mini.json  <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2v
t
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2z
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build.ninja  <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2z
x
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build.ninja.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
}
{D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
{D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\build_file_index.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2
K <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
~
~
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\compile_commands.json  <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\compile_commands.json.bin  <08><><EFBFBD><EFBFBD><EFBFBD>2
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\metadata_generation_command.txt  <08><><EFBFBD><EFBFBD><EFBFBD>2 <18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
{
{
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\RelWithDebInfo\1o2r2b1v\x86_64\prefab_config.json  <08><><EFBFBD><EFBFBD><EFBFBD>2
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
 ( <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>

View File

@ -111,4 +111,9 @@ class PengaduanModel {
if (jumlah == null) return 'Tidak diketahui';
return '$jumlah $satuan';
}
// Getter untuk memeriksa apakah bantuan berupa uang
bool get isUang {
return stokBantuan?['is_uang'] == true;
}
}

View File

@ -177,14 +177,13 @@ class AuthProvider {
String? jenis,
}) async {
try {
// Step 1: Daftarkan user dengan role_id 3 (donatur)
print('DEBUG: Memulai proses registrasi donatur dengan email: $email');
// Step 1: Daftarkan user tanpa metadata dulu
// Karena mungkin ada masalah dengan kolom role_id custom
final response = await _supabaseService.client.auth.signUp(
email: email,
password: password,
data: {
'role_id': 3, // Otomatis set sebagai donatur
},
// Tidak menggunakan email redirect karena kita akan auto-confirm
);
final user = response.user;
@ -192,47 +191,51 @@ class AuthProvider {
throw Exception('Gagal membuat akun donatur');
}
print('User berhasil terdaftar dengan ID: ${user.id}');
print('DEBUG: User berhasil terdaftar dengan ID: ${user.id}');
try {
// Step 2: Buat data donatur di tabel donatur
await _supabaseService.client.from('donatur').insert({
print('DEBUG: Mencoba menyimpan data donatur ke tabel donatur');
final currentTime = DateTime.now().toIso8601String();
// Data untuk tabel donatur
final donaturData = {
'id': user.id,
'nama_lengkap': namaLengkap,
'alamat': alamat,
'no_hp': noHp,
'email': email,
'jenis': jenis ?? 'Individu',
'created_at': DateTime.now().toIso8601String(),
});
'created_at': currentTime,
'updated_at': currentTime,
};
// Step 3: Pastikan di tabel profiles_backup juga ada data ini (jika digunakan)
try {
await _supabaseService.client.from('profiles_backup').insert({
'id': user.id,
'updated_at': DateTime.now().toIso8601String(),
'role_id': 3,
});
// Insert ke tabel donatur
print('DEBUG: Inserting data to donatur table');
await _supabaseService.client.from('donatur').insert(donaturData);
print('DEBUG: Berhasil menyimpan data donatur');
// Step 3: Coba update role_id user menggunakan service yang lebih aman
print('DEBUG: Mencoba mengupdate role_id user');
await _supabaseService.updateUserRole(user.id, 3);
} catch (e) {
print('Error menyimpan di profiles_backup: $e');
// Lanjutkan proses meskipun ada error di step ini
print('ERROR: Gagal menyimpan data donatur: $e');
throw Exception('Gagal menyimpan data donatur. Silakan coba lagi.');
}
print('Berhasil mendaftarkan donatur: $namaLengkap');
// Pesan untuk pengembang
print(
'CATATAN: User akan perlu login manual. Jika email konfirmasi masih diperlukan,');
print(
'nonaktifkan verifikasi email di dashboard Supabase: Authentication > Email Templates > Disable Email Confirmation');
'DEBUG: Berhasil mendaftarkan donatur: $namaLengkap dengan ID: ${user.id}');
} catch (e) {
print('Error pada signUpDonatur: $e');
print('ERROR: Error pada signUpDonatur: $e');
if (e.toString().contains('User already registered')) {
throw Exception(
'Email sudah terdaftar. Silakan gunakan email lain atau login dengan email tersebut.');
} else {
// Untuk error konfirmasi email, berikan instruksi yang jelas
if (e.toString().contains('Error sending confirmation mail')) {
} else if (e.toString().contains('Database error saving new user')) {
throw Exception(
'Terjadi kesalahan saat menyimpan data pengguna. Silakan coba lagi atau hubungi administrator.');
} else if (e.toString().contains('Error sending confirmation mail')) {
print('===== PERHATIAN PENGEMBANG =====');
print(
'Error ini terjadi karena Supabase gagal mengirim email konfirmasi.');
@ -244,7 +247,7 @@ class AuthProvider {
print('==============================');
throw Exception(
'Gagal mengirim email konfirmasi. Mohon hubungi administrator.');
}
} else {
rethrow;
}
}

View File

@ -537,17 +537,41 @@ class AuthController extends GetxController {
}
isLoading.value = true;
// Kita akan mencoba maksimal 2 kali jika terjadi error
int maxAttempts = 2;
int attempt = 0;
bool success = false;
Exception? lastError;
while (attempt < maxAttempts && !success) {
attempt++;
print(
'DEBUG: Mencoba pendaftaran donatur (percobaan ke-$attempt dari $maxAttempts)');
try {
// Proses registrasi donatur dengan role_id 3
print('DEBUG: Validasi form berhasil, melanjutkan proses registrasi');
print('DEBUG: Email: ${emailController.text}');
print('DEBUG: Nama: ${namaController.text}');
print('DEBUG: Alamat: ${alamatController.text}');
print('DEBUG: No HP: ${noHpController.text}');
print(
'DEBUG: Jenis: ${jenisController.text.isEmpty ? 'Individu' : jenisController.text}');
// Proses registrasi donatur
await _authProvider.signUpDonatur(
email: emailController.text,
password: passwordController.text,
namaLengkap: namaController.text,
alamat: alamatController.text,
noHp: noHpController.text,
jenis: jenisController.text.isEmpty ? 'Individu' : jenisController.text,
jenis:
jenisController.text.isEmpty ? 'Individu' : jenisController.text,
);
print('DEBUG: Registrasi donatur berhasil');
success = true;
Get.snackbar(
'Sukses',
'Registrasi donatur berhasil! Silakan login dengan akun Anda.',
@ -563,8 +587,24 @@ class AuthController extends GetxController {
// Arahkan ke halaman login
Get.offAllNamed(Routes.login);
} catch (e) {
print('Error registrasi donatur: $e');
lastError = e is Exception ? e : Exception(e.toString());
print('ERROR: Error registrasi donatur (percobaan ke-$attempt): $e');
print('ERROR Detail: ${e.runtimeType} - ${e.toString()}');
// Cek apakah bisa dicoba lagi atau tidak
bool canRetry = false;
if (e.toString().contains('Database error saving new user')) {
canRetry = true; // Error database bisa dicoba lagi
print('DEBUG: Error database, akan mencoba lagi');
// Delay sedikit sebelum mencoba lagi
await Future.delayed(const Duration(seconds: 1));
} else if (e.toString().contains('User already registered')) {
// Jangan coba lagi jika email sudah terdaftar
canRetry = false;
}
if (!canRetry || attempt >= maxAttempts) {
// Jika tidak bisa retry atau sudah maksimal, tampilkan error
String errorMessage = 'Gagal melakukan registrasi';
// Tangani error sesuai jenisnya
@ -579,6 +619,9 @@ class AuthController extends GetxController {
'Password terlalu lemah. Gunakan kombinasi huruf, angka, dan simbol.';
} else if (e.toString().contains('invalid-email')) {
errorMessage = 'Format email tidak valid.';
} else if (e.toString().contains('Database error')) {
errorMessage =
'Terjadi kesalahan pada database. Silakan coba lagi nanti atau hubungi administrator.';
} else {
errorMessage = 'Gagal melakukan registrasi: ${e.toString()}';
}
@ -589,10 +632,19 @@ class AuthController extends GetxController {
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
duration: const Duration(seconds: 5),
duration: const Duration(seconds: 7),
);
} finally {
break; // Keluar dari loop
}
}
}
isLoading.value = false;
// Jika gagal setelah mencoba beberapa kali
if (!success && lastError != null) {
print('ERROR: Pendaftaran gagal setelah $attempt percobaan');
// Error sudah ditampilkan di dalam loop
}
}

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
import 'package:penyaluran_app/app/routes/app_pages.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class RegisterDonaturView extends GetView<AuthController> {
const RegisterDonaturView({super.key});
@ -13,7 +14,7 @@ class RegisterDonaturView extends GetView<AuthController> {
appBar: AppBar(
title: const Text('Daftar Donatur'),
centerTitle: true,
backgroundColor: Colors.blue,
backgroundColor: AppTheme.primaryColor,
foregroundColor: Colors.white,
elevation: 0,
shape: const RoundedRectangleBorder(
@ -22,7 +23,15 @@ class RegisterDonaturView extends GetView<AuthController> {
),
),
),
body: SafeArea(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFE3F2FD), Colors.white],
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: SingleChildScrollView(
@ -36,8 +45,16 @@ class RegisterDonaturView extends GetView<AuthController> {
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.blue.shade50,
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: AppTheme.primaryColor.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
@ -46,21 +63,21 @@ class RegisterDonaturView extends GetView<AuthController> {
width: 120,
height: 120,
),
const Text(
Text(
'Daftar Sebagai Donatur',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
color: AppTheme.primaryColor,
),
),
const SizedBox(height: 4),
const Text(
Text(
'Bergabunglah dengan kami untuk membantu mereka yang membutuhkan',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.blueGrey,
color: const Color(0xFF546E7A),
),
),
],
@ -68,16 +85,16 @@ class RegisterDonaturView extends GetView<AuthController> {
),
const SizedBox(height: 20),
// Step indicator
const Row(
Row(
children: [
Icon(Icons.person_add, color: Colors.blue),
SizedBox(width: 10),
Icon(Icons.person_add, color: AppTheme.primaryColor),
const SizedBox(width: 10),
Text(
'Informasi Akun',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blue,
color: AppTheme.primaryColor,
),
),
],
@ -93,17 +110,18 @@ class RegisterDonaturView extends GetView<AuthController> {
decoration: InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Masukkan nama lengkap Anda',
prefixIcon: const Icon(Icons.person, color: Colors.blue),
prefixIcon:
Icon(Icons.person, color: AppTheme.primaryColor),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -121,17 +139,18 @@ class RegisterDonaturView extends GetView<AuthController> {
decoration: InputDecoration(
labelText: 'Email',
hintText: 'contoh@email.com',
prefixIcon: const Icon(Icons.email, color: Colors.blue),
prefixIcon:
Icon(Icons.email, color: AppTheme.primaryColor),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -150,27 +169,28 @@ class RegisterDonaturView extends GetView<AuthController> {
labelText: 'Password',
hintText: 'Minimal 8 karakter',
prefixIcon:
const Icon(Icons.lock, color: Colors.blue),
Icon(Icons.lock, color: AppTheme.primaryColor),
suffixIcon: IconButton(
icon: Icon(
controller.isPasswordHidden.value
? Icons.visibility_off
: Icons.visibility,
color: Colors.blue,
color: AppTheme.primaryColor,
),
onPressed: () =>
controller.togglePasswordVisibility(),
),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
borderSide:
BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -188,28 +208,29 @@ class RegisterDonaturView extends GetView<AuthController> {
decoration: InputDecoration(
labelText: 'Konfirmasi Password',
hintText: 'Masukkan password yang sama',
prefixIcon: const Icon(Icons.lock_outline,
color: Colors.blue),
prefixIcon: Icon(Icons.lock_outline,
color: AppTheme.primaryColor),
suffixIcon: IconButton(
icon: Icon(
controller.isConfirmPasswordHidden.value
? Icons.visibility_off
: Icons.visibility,
color: Colors.blue,
color: AppTheme.primaryColor,
),
onPressed: () =>
controller.toggleConfirmPasswordVisibility(),
),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
borderSide:
BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -221,16 +242,17 @@ class RegisterDonaturView extends GetView<AuthController> {
const SizedBox(height: 15),
// Section heading
const Row(
Row(
children: [
Icon(Icons.person_pin_circle, color: Colors.blue),
SizedBox(width: 10),
Icon(Icons.person_pin_circle,
color: AppTheme.primaryColor),
const SizedBox(width: 10),
Text(
'Informasi Profil',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blue,
color: AppTheme.primaryColor,
),
),
],
@ -246,17 +268,18 @@ class RegisterDonaturView extends GetView<AuthController> {
decoration: InputDecoration(
labelText: 'Nomor HP',
hintText: 'Masukkan nomor HP aktif',
prefixIcon: const Icon(Icons.phone, color: Colors.blue),
prefixIcon:
Icon(Icons.phone, color: AppTheme.primaryColor),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -275,17 +298,18 @@ class RegisterDonaturView extends GetView<AuthController> {
decoration: InputDecoration(
labelText: 'Alamat Lengkap',
hintText: 'Masukkan alamat lengkap Anda',
prefixIcon: const Icon(Icons.home, color: Colors.blue),
prefixIcon:
Icon(Icons.home, color: AppTheme.primaryColor),
filled: true,
fillColor: Colors.grey.shade100,
fillColor: Colors.white,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: Colors.blue, width: 2),
borderSide: BorderSide(
color: AppTheme.primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
@ -299,7 +323,7 @@ class RegisterDonaturView extends GetView<AuthController> {
// Jenis Donatur (Dropdown)
Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade300),
),
@ -309,8 +333,9 @@ class RegisterDonaturView extends GetView<AuthController> {
: controller.jenisController.text,
decoration: InputDecoration(
labelText: 'Jenis Donatur',
prefixIcon:
const Icon(Icons.category, color: Colors.blue),
labelStyle: TextStyle(color: AppTheme.primaryColor),
prefixIcon: Icon(Icons.category,
color: AppTheme.primaryColor),
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 10),
@ -328,6 +353,9 @@ class RegisterDonaturView extends GetView<AuthController> {
onChanged: (value) {
controller.jenisController.text = value ?? 'Individu';
},
dropdownColor: Colors.white,
icon: Icon(Icons.arrow_drop_down,
color: AppTheme.primaryColor),
),
),
@ -337,13 +365,14 @@ class RegisterDonaturView extends GetView<AuthController> {
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.blue.shade200),
color: const Color(0xFFF1F8E9),
borderRadius: BorderRadius.circular(15),
border: Border.all(color: const Color(0xFFAED581)),
),
child: Row(
children: [
const Icon(Icons.info_outline, color: Colors.blue),
const Icon(Icons.info_outline,
color: Color(0xFF558B2F)),
const SizedBox(width: 10),
Expanded(
child: Column(
@ -353,7 +382,7 @@ class RegisterDonaturView extends GetView<AuthController> {
'Informasi',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
color: Color(0xFF558B2F),
),
),
SizedBox(height: 5),
@ -361,7 +390,7 @@ class RegisterDonaturView extends GetView<AuthController> {
'Data Anda akan terverifikasi dan terlindungi. Kami menjaga privasi dan keamanan data Anda.',
style: TextStyle(
fontSize: 14,
color: Colors.blueGrey,
color: Color(0xFF33691E),
),
),
],
@ -379,7 +408,7 @@ class RegisterDonaturView extends GetView<AuthController> {
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.3),
color: AppTheme.primaryColor.withOpacity(0.3),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 2),
@ -392,7 +421,7 @@ class RegisterDonaturView extends GetView<AuthController> {
: controller.registerDonatur,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15),
backgroundColor: Colors.blue,
backgroundColor: AppTheme.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
@ -406,7 +435,8 @@ class RegisterDonaturView extends GetView<AuthController> {
: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.how_to_reg, color: Colors.white),
Icon(Icons.how_to_reg,
color: Colors.white),
SizedBox(width: 10),
Text(
'DAFTAR SEKARANG',
@ -414,6 +444,7 @@ class RegisterDonaturView extends GetView<AuthController> {
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1,
),
),
],
@ -426,7 +457,7 @@ class RegisterDonaturView extends GetView<AuthController> {
Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.grey.shade50,
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade200),
),
@ -435,15 +466,17 @@ class RegisterDonaturView extends GetView<AuthController> {
children: [
const Text(
'Sudah punya akun?',
style: TextStyle(color: Colors.grey),
style: TextStyle(color: Color(0xFF546E7A)),
),
TextButton(
onPressed: () => Get.offAllNamed(Routes.login),
style: TextButton.styleFrom(
foregroundColor: AppTheme.primaryColor,
),
child: const Text(
'Masuk',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
),
@ -457,6 +490,7 @@ class RegisterDonaturView extends GetView<AuthController> {
),
),
),
),
);
}
}

View File

@ -36,54 +36,6 @@ class DonaturDashboardView extends GetView<DonaturDashboardController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header DisalurKita dengan logo dan slogan
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Image.asset(
'assets/images/logo-disalurkita.png',
width: 50,
height: 50,
),
const SizedBox(width: 15),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DisalurKita',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1565C0),
),
),
SizedBox(height: 5),
Text(
'Salurkan dengan Pasti, Pantau dengan Bukti',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
_buildWelcomeSection(),
const SizedBox(height: 24),
_buildStatisticSection(),
@ -242,19 +194,7 @@ class DonaturDashboardView extends GetView<DonaturDashboardController> {
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildActionButton(
icon: Icons.edit_rounded,
label: 'Edit Profil',
color: Colors.blue.shade700,
onTap: () => Get.toNamed(Routes.profile),
),
),
const SizedBox(width: 10),
Expanded(
child: _buildActionButton(
_buildActionButton(
icon: Icons.add_box_rounded,
label: 'Titip Bantuan',
color: Colors.green.shade700,
@ -263,9 +203,6 @@ class DonaturDashboardView extends GetView<DonaturDashboardController> {
controller.activeTabIndex.value = 3;
},
),
),
],
),
],
),
),

View File

@ -661,7 +661,8 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
children: [
Expanded(
child: _buildInfoItem(
icon: isUang ? Icons.attach_money : Icons.category,
icon:
isUang ? Icons.payment_rounded : Icons.category,
label: 'Jenis Bantuan',
value: stokNama,
iconColor: isUang
@ -944,7 +945,8 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
children: [
Expanded(
child: _buildInfoItem(
icon: isUang ? Icons.attach_money : Icons.category,
icon:
isUang ? Icons.payment_rounded : Icons.category,
label: 'Jenis Bantuan',
value: stokNama,
iconColor: isUang
@ -1042,7 +1044,7 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
children: [
Icon(
// Tampilkan ikon uang jika stok bantuan berbentuk uang
isUang ? Icons.attach_money : Icons.numbers,
isUang ? Icons.payment_rounded : Icons.numbers,
color: Colors.grey.shade700),
const SizedBox(width: 8),
Text(
@ -1660,7 +1662,7 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
const Divider(),
_buildDetailRow(
icon: isUangBantuan
? Icons.attach_money
? Icons.payment_rounded
: Icons.shopping_bag,
label: 'Jumlah',
value: jumlahBantuan + (isUangBantuan ? '' : ' item'),

View File

@ -8,6 +8,7 @@ import 'package:penyaluran_app/app/modules/donatur/views/donatur_laporan_view.da
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/widgets/app_drawer.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class DonaturView extends GetView<DonaturDashboardController> {
@ -166,301 +167,93 @@ class DonaturView extends GetView<DonaturDashboardController> {
}
Widget _buildDrawer(BuildContext context) {
return Drawer(
child: Column(
children: [
Container(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
),
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 16,
bottom: 24,
left: 16,
right: 16),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white70,
backgroundImage: controller.profilePhotoUrl != null &&
controller.profilePhotoUrl!.isNotEmpty
? NetworkImage(controller.profilePhotoUrl!)
: null,
child: (controller.profilePhotoUrl == null ||
controller.profilePhotoUrl!.isEmpty)
? Text(
controller.nama.isNotEmpty
? controller.nama
.toString()
.substring(0, 1)
.toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
fontSize: 24,
),
)
: null,
),
),
const SizedBox(height: 16),
const Text(
'Halo,',
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
Text(
controller.nama,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
const SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Donatur',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.location_on,
color: Colors.white,
size: 14,
),
const SizedBox(width: 4),
Text(
controller.desa ?? 'Tidak ada desa',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
],
),
),
],
),
],
),
),
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
_buildMenuCategory('Menu Utama'),
Obx(() => _buildMenuItem(
return Obx(() {
Map<String, List<DrawerMenuItem>> menuCategories = {
'Menu Utama': [
DrawerMenuItem(
icon: Icons.dashboard_outlined,
activeIcon: Icons.dashboard,
title: 'Dashboard',
isSelected: controller.activeTabIndex.value == 0,
onTap: () {
Navigator.pop(context);
controller.activeTabIndex.value = 0;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.description_outlined,
activeIcon: Icons.description,
title: 'Skema Bantuan',
isSelected: controller.activeTabIndex.value == 1,
onTap: () {
Navigator.pop(context);
controller.activeTabIndex.value = 1;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.calendar_today_outlined,
activeIcon: Icons.calendar_today,
title: 'Jadwal Penyaluran',
isSelected: controller.activeTabIndex.value == 2,
onTap: () {
Navigator.pop(context);
controller.activeTabIndex.value = 2;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.add_box_outlined,
activeIcon: Icons.add_box,
title: 'Penitipan Bantuan',
isSelected: controller.activeTabIndex.value == 3,
onTap: () {
Navigator.pop(context);
controller.activeTabIndex.value = 3;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.assignment_outlined,
activeIcon: Icons.assignment,
title: 'Laporan Penyaluran',
isSelected: controller.activeTabIndex.value == 4,
onTap: () {
Navigator.pop(context);
controller.activeTabIndex.value = 4;
},
)),
_buildMenuCategory('Pengaturan'),
_buildMenuItem(
),
],
'Pengaturan': [
DrawerMenuItem(
icon: Icons.person_outline,
activeIcon: Icons.person,
title: 'Profil',
onTap: () {
Navigator.pop(context);
Get.toNamed('/profile');
},
),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.info_outline,
activeIcon: Icons.info,
title: 'Tentang Kami',
onTap: () {
Navigator.pop(context);
Get.toNamed('/about');
},
),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.logout,
title: 'Keluar',
isLogout: true,
onTap: () {
Navigator.pop(context);
controller.logout();
},
isLogout: true,
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'© ${DateTime.now().year} DisalurKita',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
),
],
),
);
}
};
Widget _buildMenuCategory(String title) {
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
);
}
Widget _buildMenuItem({
required IconData icon,
IconData? activeIcon,
required String title,
bool isSelected = false,
String? badge,
required Function() onTap,
bool isLogout = false,
}) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: isSelected
? AppTheme.primaryColor.withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: ListTile(
leading: Icon(
isSelected ? (activeIcon ?? icon) : icon,
color: isSelected
? AppTheme.primaryColor
: (isLogout ? Colors.red : null),
),
title: Text(
title,
style: TextStyle(
color: isSelected
? AppTheme.primaryColor
: (isLogout ? Colors.red : null),
fontWeight: isSelected ? FontWeight.bold : null,
),
),
trailing: badge != null
? Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 20,
minHeight: 20,
),
child: Text(
badge,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
textAlign: TextAlign.center,
),
)
: null,
onTap: onTap,
),
return AppDrawer(
nama: controller.nama,
role: 'Donatur',
desa: controller.desa,
avatar: controller.profilePhotoUrl,
menuItems: const [], // Tidak digunakan karena menggunakan menuCategories
menuCategories: menuCategories,
onLogout: controller.logout,
footerText: '© ${DateTime.now().year} DisalurKita',
);
});
}
}

View File

@ -10,6 +10,12 @@ class CalendarViewWidget extends StatelessWidget {
final JadwalPenyaluranController controller;
final CalendarController _calendarController = CalendarController();
// Tambahkan variabel untuk status filter
final RxBool _showAllSchedules = true.obs;
// Tambahkan variabel untuk mode tampilan kalender
final Rx<CalendarView> _calendarView = CalendarView.month.obs;
CalendarViewWidget({
super.key,
required this.controller,
@ -47,12 +53,16 @@ class CalendarViewWidget extends StatelessWidget {
topRight: Radius.circular(16),
),
),
child: Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title pada baris pertama
Row(
children: [
const Icon(
Icons.calendar_month_rounded,
color: Colors.white,
size: 28,
size: 24,
),
const SizedBox(width: 12),
Text(
@ -64,6 +74,117 @@ class CalendarViewWidget extends StatelessWidget {
),
],
),
const SizedBox(height: 12),
// Tombol filter pada baris kedua
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// Tombol mode tampilan
Obx(() => Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Tooltip(
message: _calendarView.value == CalendarView.month
? 'Beralih ke tampilan agenda'
: 'Beralih ke tampilan kalender',
child: InkWell(
onTap: () {
_calendarView.value =
_calendarView.value == CalendarView.month
? CalendarView.schedule
: CalendarView.month;
// Update calendar controller dengan tampilan baru
_calendarController.view = _calendarView.value;
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_calendarView.value == CalendarView.month
? Icons.view_agenda
: Icons.calendar_month,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4),
Text(
_calendarView.value == CalendarView.month
? 'Agenda'
: 'Bulan',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
)),
const SizedBox(width: 8),
// Tombol filter
Obx(() => Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Tooltip(
message: _showAllSchedules.value
? 'Tampilkan hanya jadwal bulan ini'
: 'Tampilkan semua jadwal',
child: InkWell(
onTap: () {
_showAllSchedules.value =
!_showAllSchedules.value;
},
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_showAllSchedules.value
? Icons.filter_list
: Icons.filter_alt,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4),
Text(
_showAllSchedules.value
? 'Semua'
: 'Bulan Ini',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
)),
],
),
],
),
),
Padding(
padding: const EdgeInsets.all(12.0),
@ -71,7 +192,7 @@ class CalendarViewWidget extends StatelessWidget {
height: MediaQuery.of(context).size.height * 0.65,
child: Obx(() {
return SfCalendar(
view: CalendarView.month,
view: _calendarView.value,
controller: _calendarController,
initialSelectedDate: DateTime.now(),
initialDisplayDate: DateTime.now(),
@ -119,6 +240,42 @@ class CalendarViewWidget extends StatelessWidget {
),
),
),
scheduleViewSettings: ScheduleViewSettings(
appointmentItemHeight: 70,
monthHeaderSettings: MonthHeaderSettings(
height: 50,
backgroundColor: AppTheme.primaryColor,
monthTextStyle: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
weekHeaderSettings: WeekHeaderSettings(
height: 40,
textAlign: TextAlign.center,
backgroundColor: Colors.grey.shade100,
weekTextStyle: TextStyle(
color: AppTheme.primaryColor,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
dayHeaderSettings: DayHeaderSettings(
dayFormat: 'EEEE',
width: 70,
dayTextStyle: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: AppTheme.primaryColor,
),
dateTextStyle: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppTheme.primaryColor,
),
),
),
cellBorderColor: Colors.grey.withOpacity(0.2),
todayHighlightColor: AppTheme.primaryColor,
selectionDecoration: BoxDecoration(
@ -291,6 +448,7 @@ class CalendarViewWidget extends StatelessWidget {
...controller.jadwalTerlaksana,
];
// Tambahkan filter berdasarkan _showAllSchedules
DateTime now = DateTime.now();
DateTime firstDayOfMonth = DateTime(now.year, now.month, 1);
DateTime lastDayOfMonth = DateTime(now.year, now.month + 1, 0);
@ -300,9 +458,14 @@ class CalendarViewWidget extends StatelessWidget {
DateTime jadwalDate =
FormatHelper.toLocalDateTime(jadwal.tanggalPenyaluran!);
if (jadwalDate
.isAfter(firstDayOfMonth.subtract(const Duration(days: 1))) &&
jadwalDate.isBefore(lastDayOfMonth.add(const Duration(days: 1)))) {
// Filter berdasarkan bulan saat ini jika _showAllSchedules.value = false
bool shouldShow = _showAllSchedules.value ||
(jadwalDate.isAfter(
firstDayOfMonth.subtract(const Duration(days: 1))) &&
jadwalDate
.isBefore(lastDayOfMonth.add(const Duration(days: 1))));
if (shouldShow) {
Color appointmentColor;
// Periksa status jadwal menggunakan switch-case untuk konsistensi

View File

@ -1,55 +1,223 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class GreetingHeader extends StatelessWidget {
final String name;
final String role;
final String? desa;
final String? nip;
final String? profileImageUrl;
const GreetingHeader({
super.key,
required this.name,
required this.role,
this.desa,
this.nip,
this.profileImageUrl,
});
String _getGreeting() {
final hour = DateTime.now().hour;
if (hour < 12) {
return 'Selamat Pagi';
} else if (hour < 15) {
return 'Selamat Siang';
} else if (hour < 19) {
return 'Selamat Sore';
} else {
return 'Selamat Malam';
}
}
String _getCurrentDate() {
final now = DateTime.now();
return DateFormat('EEEE, d MMMM yyyy', 'id_ID').format(now);
}
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
gradient: LinearGradient(
colors: [
Colors.blue.shade800,
Colors.blue.shade600,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.grey.withAlpha(26), // 0.1 * 255 ≈ 26
color: Colors.blue.withOpacity(0.3),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 1),
blurRadius: 15,
offset: const Offset(0, 5),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
),
child: Text(
_getGreeting(),
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
const SizedBox(height: 8),
Text(
'Selamat Datang, $name!',
style: textTheme.headlineSmall?.copyWith(
fontSize: 24,
name,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(height: 5),
Text(
const SizedBox(width: 12),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 2,
),
image: profileImageUrl != null && profileImageUrl!.isNotEmpty
? DecorationImage(
image: NetworkImage(profileImageUrl!),
fit: BoxFit.cover,
)
: null,
),
child: profileImageUrl == null || profileImageUrl!.isEmpty
? const Icon(
Icons.person,
size: 30,
color: Colors.white,
)
: null,
),
],
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.location_on,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Flexible(
child: Text(
desa != null && desa!.isNotEmpty
? 'Kamu Login Sebagai $role $desa.'
: 'Kamu Login Sebagai $role.',
style: textTheme.bodyMedium?.copyWith(
fontSize: 14,
color: Colors.grey[600],
? '$role - Desa $desa'
: role,
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
if (nip != null && nip!.isNotEmpty)
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.badge,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Text(
'NIP: $nip',
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
),
],
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.calendar_today,
size: 14,
color: Colors.white70,
),
const SizedBox(width: 4),
Text(
_getCurrentDate(),
style: const TextStyle(
fontSize: 12,
color: Colors.white70,
),
),
],
),
),
],
),
],
),

View File

@ -57,6 +57,15 @@ class PetugasDesaDashboardController extends GetxController {
userProfile['desa']?['nama'] ??
(userProfile['desa_id'] != null ? 'Desa' : 'Desa');
// Getter untuk NIP dari profil pengguna
String? get nip =>
userProfile['role_data']?['nip'] ?? _authController.roleData?.nip;
// Getter untuk foto profil dari profil pengguna
String? get profileImageUrl =>
userProfile['role_data']?['foto_profil'] ??
_authController.roleData?.fotoProfil;
// Getter untuk counter dari CounterService
RxInt get jumlahMenunggu => _counterService.jumlahMenunggu;
RxInt get jumlahDiproses => _counterService.jumlahDiproses;

View File

@ -263,7 +263,7 @@ class DaftarDonaturView extends GetView<DonaturController> {
child: Row(
children: [
const Icon(
Icons.attach_money,
Icons.payment_rounded,
size: 14,
color: Colors.green,
),

File diff suppressed because it is too large Load Diff

View File

@ -277,7 +277,7 @@ class DetailDonaturView extends GetView<DonaturController> {
child: _buildSummaryCard(
title: 'Donasi Uang',
value: jumlahDonasiUang.toString(),
icon: Icons.attach_money,
icon: Icons.payment_rounded,
color: jenisColor,
),
),

View File

@ -5,14 +5,10 @@ import 'package:penyaluran_app/app/data/models/tindakan_pengaduan_model.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/pengaduan_controller.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'package:penyaluran_app/app/widgets/cards/info_card.dart';
import 'package:penyaluran_app/app/widgets/indicators/status_pill.dart';
import 'package:penyaluran_app/app/services/supabase_service.dart';
import 'package:timeline_tile/timeline_tile.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:penyaluran_app/app/widgets/inputs/dropdown_input.dart';
import 'package:penyaluran_app/app/widgets/inputs/text_input.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:penyaluran_app/app/widgets/widgets.dart';
@ -1391,9 +1387,39 @@ class DetailPengaduanView extends GetView<PengaduanController> {
? List<String>.from(tindakan.buktiTindakan!)
: [];
// Fungsi untuk memilih bukti tindakan
// Fungsi untuk menampilkan dialog pilih sumber foto
void showPilihSumberFoto(
BuildContext dialogContext, Function(BuildContext, bool) pickFunction) {
showDialog(
context: dialogContext,
builder: (innerContext) => AlertDialog(
title: const Text('Pilih Sumber Foto'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Kamera'),
onTap: () => pickFunction(innerContext, true),
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Galeri'),
onTap: () => pickFunction(innerContext, false),
),
],
),
),
);
}
showDialog(
context: context,
builder: (dialogContext) =>
StatefulBuilder(builder: (stateContext, setState) {
// Fungsi untuk memilih bukti tindakan dipindahkan ke dalam StatefulBuilder
Future<void> pickBuktiTindakan(
BuildContext dialogContext, bool fromCamera) async {
BuildContext innerContext, bool fromCamera) async {
try {
final ImagePicker picker = ImagePicker();
final XFile? pickedFile = await picker.pickImage(
@ -1408,7 +1434,7 @@ class DetailPengaduanView extends GetView<PengaduanController> {
if (pickedFile != null) {
// Tampilkan loading dialog
showDialog(
context: dialogContext,
context: innerContext,
barrierDismissible: false,
builder: (BuildContext context) {
return const Center(
@ -1418,17 +1444,19 @@ class DetailPengaduanView extends GetView<PengaduanController> {
);
try {
// Tambahkan gambar ke daftar
// Tambahkan gambar ke daftar dan update state
setState(() {
buktiTindakanPaths.add(pickedFile.path);
});
// Tutup loading dialog
Navigator.of(dialogContext, rootNavigator: true).pop();
Navigator.of(innerContext, rootNavigator: true).pop();
// Tutup dialog pilih sumber foto
Navigator.of(dialogContext).pop();
Navigator.of(innerContext).pop();
} catch (e) {
// Tutup loading dialog jika terjadi error
Navigator.of(dialogContext, rootNavigator: true).pop();
Navigator.of(innerContext, rootNavigator: true).pop();
rethrow;
}
}
@ -1444,35 +1472,6 @@ class DetailPengaduanView extends GetView<PengaduanController> {
}
}
// Fungsi untuk menampilkan dialog pilih sumber foto
void showPilihSumberFoto(BuildContext dialogContext) {
showDialog(
context: dialogContext,
builder: (innerContext) => AlertDialog(
title: const Text('Pilih Sumber Foto'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Kamera'),
onTap: () => pickBuktiTindakan(innerContext, true),
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Galeri'),
onTap: () => pickBuktiTindakan(innerContext, false),
),
],
),
),
);
}
showDialog(
context: context,
builder: (dialogContext) =>
StatefulBuilder(builder: (stateContext, setState) {
return AlertDialog(
title: Row(
children: [
@ -1581,7 +1580,8 @@ class DetailPengaduanView extends GetView<PengaduanController> {
children: [
if (buktiTindakanPaths.isEmpty)
InkWell(
onTap: () => showPilihSumberFoto(stateContext),
onTap: () => showPilihSumberFoto(
stateContext, pickBuktiTindakan),
child: Container(
height: 150,
width: double.infinity,
@ -1619,15 +1619,16 @@ class DetailPengaduanView extends GetView<PengaduanController> {
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: buktiTindakanPaths
.length, //tombol tambah jika tidak selesai
itemCount: buktiTindakanPaths.length +
1, // Tambah 1 untuk tombol tambah
itemBuilder: (context, index) {
if (index ==
buktiTindakanPaths.length) {
// Tombol tambah foto
return InkWell(
onTap: () => showPilihSumberFoto(
stateContext),
stateContext,
pickBuktiTindakan),
child: Container(
width: 100,
margin: const EdgeInsets.only(

View File

@ -675,7 +675,7 @@ class _KonfirmasiPenerimaPageState extends State<KonfirmasiPenerimaPage> {
borderRadius: BorderRadius.circular(6),
),
child: Icon(
isUang ? Icons.attach_money : Icons.scale_outlined,
isUang ? Icons.payment_rounded : Icons.scale_outlined,
color: Colors.green,
size: 20,
),

View File

@ -521,7 +521,15 @@ class PengaduanView extends GetView<PengaduanController> {
),
),
Text(
'${item.jumlahBantuan} ${item.stokBantuan?['satuan'] ?? ''}',
item.stokBantuan?['is_uang'] == true
? FormatHelper.formatRupiah(
item.jumlahBantuan is num
? item.jumlahBantuan
: double.tryParse(item
.jumlahBantuan
.toString()) ??
0)
: '${item.jumlahBantuan} ${item.stokBantuan?['satuan'] ?? ''}',
style: TextStyle(
fontWeight: FontWeight.w500,
color: Colors.grey.shade800,

View File

@ -10,6 +10,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/penitipan_view.dar
import 'package:penyaluran_app/app/modules/petugas_desa/views/pengaduan_view.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/riwayat_stok_controller.dart';
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
class PetugasDesaView extends GetView<PetugasDesaController> {
const PetugasDesaView({super.key});
@ -190,360 +191,119 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
}
Widget _buildDrawer(BuildContext context) {
return Drawer(
child: Column(
children: [
Container(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
),
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 16,
bottom: 24,
left: 16,
right: 16),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Hero(
tag: 'profile-photo',
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white70,
backgroundImage: controller.profilePhotoUrl != null &&
controller.profilePhotoUrl!.isNotEmpty
? NetworkImage(controller.profilePhotoUrl!)
: null,
child: (controller.profilePhotoUrl == null ||
controller.profilePhotoUrl!.isEmpty)
? Text(
controller.nama.isNotEmpty
? controller.nama
.substring(0, 1)
.toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: AppTheme.primaryColor,
fontSize: 30,
),
)
: null,
),
),
),
SizedBox(height: 16),
Text(
'Halo,',
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
Text(
controller.nama,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
controller.formattedRole,
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
SizedBox(width: 8),
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.location_on,
color: Colors.white,
size: 14,
),
SizedBox(width: 4),
Text(
controller.desa,
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
],
),
),
],
),
],
),
),
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
_buildMenuCategory('Menu Utama'),
Obx(() => _buildMenuItem(
return Obx(() {
Map<String, List<DrawerMenuItem>> menuCategories = {
'Menu Utama': [
DrawerMenuItem(
icon: Icons.dashboard_outlined,
activeIcon: Icons.dashboard,
title: 'Dashboard',
isSelected: controller.activeTabIndex.value == 0,
onTap: () {
Navigator.pop(context);
controller.changeTab(0);
controller.activeTabIndex.value = 0;
},
)),
Obx(() => _buildMenuItem(
icon: Icons.handshake_outlined,
activeIcon: Icons.handshake,
),
DrawerMenuItem(
icon: Icons.volunteer_activism_outlined,
activeIcon: Icons.volunteer_activism,
title: 'Penyaluran',
isSelected: controller.activeTabIndex.value == 1,
badgeCount: controller.jumlahMenunggu.value > 0
? controller.jumlahMenunggu.value
: null,
badgeColor: Colors.green,
onTap: () {
Navigator.pop(context);
controller.changeTab(1);
controller.activeTabIndex.value = 1;
},
)),
Obx(() => _buildMenuItem(
icon: Icons.inventory_2_outlined,
activeIcon: Icons.inventory_2,
),
DrawerMenuItem(
icon: Icons.inbox_outlined,
activeIcon: Icons.inbox,
title: 'Penitipan',
isSelected: controller.activeTabIndex.value == 2,
onTap: () {
Navigator.pop(context);
controller.changeTab(2);
controller.activeTabIndex.value = 2;
},
)),
Obx(() => _buildMenuItem(
icon: Icons.warning_amber_outlined,
activeIcon: Icons.warning_amber,
),
DrawerMenuItem(
icon: Icons.report_problem_outlined,
activeIcon: Icons.report_problem,
title: 'Pengaduan',
isSelected: controller.activeTabIndex.value == 3,
badgeCount: controller.jumlahDiproses.value > 0
? controller.jumlahDiproses.value
: null,
badgeColor: Colors.orange,
onTap: () {
Navigator.pop(context);
controller.changeTab(3);
controller.activeTabIndex.value = 3;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.inventory_outlined,
activeIcon: Icons.inventory,
title: 'Stok Bantuan',
isSelected: controller.activeTabIndex.value == 4,
onTap: () {
Navigator.pop(context);
controller.changeTab(4);
},
)),
_buildMenuCategory('Kelola Data'),
_buildMenuItem(
icon: Icons.person_add_outlined,
activeIcon: Icons.person_add,
title: 'Kelola Penerima',
onTap: () {
Navigator.pop(context);
Get.toNamed('/daftar-penerima');
controller.activeTabIndex.value = 4;
},
),
_buildMenuItem(
icon: Icons.people_outlined,
activeIcon: Icons.people,
title: 'Kelola Donatur',
],
'Pengaturan': [
DrawerMenuItem(
icon: Icons.notifications_outlined,
activeIcon: Icons.notifications,
title: 'Notifikasi',
badgeCount: controller.jumlahNotifikasiBelumDibaca.value > 0
? controller.jumlahNotifikasiBelumDibaca.value
: null,
badgeColor: Colors.red,
onTap: () {
Navigator.pop(context);
Get.toNamed('/daftar-donatur');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NotifikasiView(),
),
);
},
),
_buildMenuItem(
icon: Icons.location_on_outlined,
activeIcon: Icons.location_on,
title: 'Lokasi Penyaluran',
onTap: () {
Navigator.pop(context);
Get.toNamed('/lokasi-penyaluran');
},
),
_buildMenuItem(
icon: Icons.description_outlined,
activeIcon: Icons.description,
title: 'Laporan Penyaluran',
onTap: () {
Navigator.pop(context);
Get.toNamed('/laporan-penyaluran');
},
),
_buildMenuCategory('Pengaturan'),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.person_outline,
activeIcon: Icons.person,
title: 'Profil',
onTap: () {
Navigator.pop(context);
Get.toNamed('/profile');
},
),
const Divider(),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.info_outline,
activeIcon: Icons.info,
title: 'Tentang Kami',
onTap: () {
Navigator.pop(context);
Get.toNamed('/about');
},
),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.logout,
title: 'Keluar',
isLogout: true,
onTap: () {
Navigator.pop(context);
controller.logout();
},
isLogout: true,
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'© ${DateTime.now().year} DisalurKita',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
),
],
),
);
}
};
Widget _buildMenuCategory(String title) {
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
);
}
Widget _buildMenuItem({
required IconData icon,
IconData? activeIcon,
required String title,
bool isSelected = false,
String? badge,
required Function() onTap,
bool isLogout = false,
}) {
return AnimatedContainer(
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: isSelected
? AppTheme.primaryColor.withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: ListTile(
leading: Stack(
alignment: Alignment.center,
children: [
Icon(
isSelected ? (activeIcon ?? icon) : icon,
color: isSelected
? AppTheme.primaryColor
: isLogout
? Colors.red
: Colors.grey[700],
size: 24,
),
if (badge != null)
Positioned(
top: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints: BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: Text(
badge,
style: TextStyle(
fontSize: 10,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
),
],
),
title: Text(
title,
style: TextStyle(
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected
? AppTheme.primaryColor
: isLogout
? Colors.red
: Colors.grey[800],
fontSize: 14,
),
),
onTap: onTap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
visualDensity: VisualDensity.compact,
selectedTileColor: AppTheme.primaryColor.withOpacity(0.1),
selected: isSelected,
),
return AppDrawer(
nama: controller.namaLengkap,
role: 'Petugas Desa',
desa: controller.desa,
avatar: controller.profilePhotoUrl,
menuItems: const [], // Tidak digunakan karena menggunakan menuCategories
menuCategories: menuCategories,
onLogout: controller.logout,
footerText: '© ${DateTime.now().year} DisalurKita',
);
});
}
Widget _buildBottomNavigationBar() {

View File

@ -347,10 +347,16 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
),
),
...controller.daftarStokBantuan.map((stok) {
final bool isUang = stok.isUang ?? false;
final String formattedJumlah = isUang
? FormatHelper.formatRupiah(
stok.totalStok ?? 0)
: '${stok.totalStok} ${stok.satuan}';
return DropdownMenuItem(
value: stok.id,
child: Text(
stok.nama ?? '-',
'${stok.nama ?? '-'} ($formattedJumlah)',
overflow: TextOverflow.ellipsis,
),
);
@ -380,6 +386,9 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
: 'Tidak diketahui';
final stokBantuanSatuan =
riwayat.stokBantuan != null ? riwayat.stokBantuan!['satuan'] ?? '' : '';
final bool isUang = riwayat.stokBantuan != null
? riwayat.stokBantuan!['is_uang'] ?? false
: false;
final sumberLabels = {
'penitipan': 'Penitipan',
'penerimaan': 'Penerimaan',
@ -464,7 +473,9 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
),
const SizedBox(width: 4),
Text(
'${riwayat.jumlah?.toStringAsFixed(0) ?? '0'} $stokBantuanSatuan',
isUang
? FormatHelper.formatRupiah(riwayat.jumlah ?? 0)
: '${riwayat.jumlah?.toStringAsFixed(0) ?? '0'} $stokBantuanSatuan',
style: TextStyle(
fontWeight: FontWeight.bold,
color: isPenambahan ? Colors.green : Colors.red,
@ -761,10 +772,13 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
value: controller.selectedStokBantuan.value,
items: controller.daftarStokBantuan
.map((StokBantuanModel stok) {
final bool isUang = stok.isUang ?? false;
final String formattedStok = isUang
? FormatHelper.formatRupiah(stok.totalStok ?? 0)
: '${stok.totalStok} ${stok.satuan}';
return DropdownMenuItem<StokBantuanModel>(
value: stok,
child: Text(
'${stok.nama} (${stok.totalStok} ${stok.satuan})'),
child: Text('${stok.nama} ($formattedStok)'),
);
}).toList(),
onChanged: (StokBantuanModel? value) {

View File

@ -24,7 +24,8 @@ class StokBantuanView extends GetView<StokBantuanController> {
},
backgroundColor: AppTheme.primaryColor,
icon: const Icon(Icons.add, color: Colors.white),
label: const Text('Tambah Stok', style: TextStyle(color: Colors.white)),
label: const Text('Tambah Jenis Stok',
style: TextStyle(color: Colors.white)),
elevation: 2,
),
);
@ -636,7 +637,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tambah Stok Bantuan',
'Tambah Jenis Stok Bantuan',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),

View File

@ -363,7 +363,7 @@ class TambahPenyaluranView extends GetView<JadwalPenyaluranController> {
child: Row(
children: [
isUang.value
? const Icon(Icons.attach_money)
? const Icon(Icons.payment_rounded)
: const Icon(Icons.inventory_2),
const SizedBox(width: 8),
Expanded(

View File

@ -7,8 +7,6 @@ import 'package:penyaluran_app/app/theme/app_theme.dart';
import 'package:penyaluran_app/app/utils/format_helper.dart';
import 'package:timeline_tile/timeline_tile.dart';
import 'package:image_picker/image_picker.dart';
import 'package:penyaluran_app/app/widgets/indicators/status_pill.dart';
import 'package:penyaluran_app/app/widgets/cards/info_card.dart';
import 'dart:io';
import 'package:penyaluran_app/app/widgets/widgets.dart';
@ -652,6 +650,76 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
),
),
),
// Menampilkan foto pengaduan jika ada
if (pengaduan.fotoPengaduan != null &&
pengaduan.fotoPengaduan!.isNotEmpty) ...[
const SizedBox(height: 16),
Text(
'Foto Pengaduan:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 8),
Container(
height: 120,
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(8),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
child: Row(
children: pengaduan.fotoPengaduan!.map((foto) {
return GestureDetector(
onTap: () => _showFullScreenImage(context, foto),
child: Container(
width: 120,
height: 120,
margin: const EdgeInsets.only(right: 8),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
image: DecorationImage(
image: foto.startsWith('http')
? NetworkImage(foto)
: FileImage(File(foto)) as ImageProvider,
fit: BoxFit.cover,
),
),
child: Stack(
alignment: Alignment.bottomRight,
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: const Icon(
Icons.zoom_in,
color: Colors.white,
size: 16,
),
),
],
),
),
);
}).toList(),
),
),
),
],
const SizedBox(height: 16),
Row(
children: [
@ -926,7 +994,12 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
'Nama Penyaluran', pengaduan.namaPenyaluran, Icons.assignment),
_buildInfoItem(
'Jenis Bantuan', pengaduan.jenisBantuan, Icons.category),
_buildInfoItem('Jumlah Bantuan', pengaduan.jumlahBantuan,
_buildInfoItem(
'Jumlah Bantuan',
pengaduan.isUang == true
? FormatHelper.formatRupiah(
double.tryParse(pengaduan.jumlahBantuan ?? '0'))
: pengaduan.jumlahBantuan,
Icons.shopping_basket),
_buildInfoItem(
'Deskripsi', pengaduan.deskripsiPenyaluran, Icons.description),

View File

@ -23,54 +23,6 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header DisalurKita dengan logo dan slogan
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.blue.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Image.asset(
'assets/images/logo-disalurkita.png',
width: 50,
height: 50,
),
const SizedBox(width: 15),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DisalurKita',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1565C0),
),
),
SizedBox(height: 5),
Text(
'Salurkan dengan Pasti, Pantau dengan Bukti',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
_buildWelcomeSection(),
const SizedBox(height: 24),
_buildStatisticSection(),
@ -540,7 +492,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
children: [
if (totalUang > 0)
_buildSummaryItem(
icon: Icons.attach_money,
icon: Icons.payment_rounded,
color: Colors.green,
title: 'Total Bantuan Uang',
value: FormatHelper.formatRupiah(totalUang),

View File

@ -158,18 +158,67 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// nama penyaluran
Text(
penyaluran.namaPenyaluran ?? 'Nama Penyaluran',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
// deskripsi penyaluran
if (penyaluran.deskripsiPenyaluran != null &&
penyaluran.deskripsiPenyaluran!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
Container(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(0, 1),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.description_outlined,
size: 18,
color: Colors.grey.shade600,
),
const SizedBox(width: 8),
Text(
'Deskripsi',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 8),
Text(
penyaluran.deskripsiPenyaluran!,
style: const TextStyle(
color: Colors.black,
fontSize: 16,
color: Colors.black87,
fontSize: 15,
height: 1.5,
),
),
],
),
),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
@ -197,7 +246,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
),
child: Icon(
penyaluran.isUang == true
? Icons.attach_money
? Icons.payment_rounded
: Icons.inventory_2,
color: penyaluran.isUang == true
? Colors.green
@ -736,13 +785,17 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
// Pastikan menggunakan data terbaru dari model dan cetak ke log untuk debugging
final qrData = penyaluran.qrCodeHash ?? 'invalid-qr-code';
print('penyaluran.statusPenyaluran ${penyaluran.statusPenyaluran}');
print('penyaluran.statusPenerimaan ${penyaluran.statusPenerimaan}');
// Cek status penyaluran untuk disabled state
final bool isDisabled = penyaluran.statusPenyaluran != null &&
// Cek status penyaluran dan penerimaan untuk disabled state
final bool isDisabled = (penyaluran.statusPenyaluran != null &&
(penyaluran.statusPenyaluran!.toUpperCase() == 'DIJADWALKAN' ||
penyaluran.statusPenyaluran!.toUpperCase() == 'DISETUJUI' ||
penyaluran.statusPenyaluran!.toUpperCase() == 'BATALTERLAKSANA' ||
penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA');
penyaluran.statusPenyaluran!.toUpperCase() ==
'BATALTERLAKSANA' ||
penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA')) ||
(penyaluran.statusPenerimaan != null &&
penyaluran.statusPenerimaan!.toUpperCase() == 'DITERIMA');
final String statusMessage;
if (isDisabled) {
@ -752,6 +805,9 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
} else if (penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA') {
statusMessage =
'QR Code sudah digunakan pada penyaluran yang telah terlaksana';
} else if (penyaluran.statusPenerimaan != null &&
penyaluran.statusPenerimaan!.toUpperCase() == 'DITERIMA') {
statusMessage = 'QR Code sudah digunakan karena bantuan telah diterima';
} else {
statusMessage =
'QR Code belum dapat digunakan karena penyaluran belum terlaksana';

View File

@ -15,10 +15,6 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
return const Center(child: CircularProgressIndicator());
}
// Debug print untuk melihat jumlah item
print(
'DEBUG: Jumlah penerimaan tersedia: ${controller.penerimaPenyaluran.length}');
return RefreshIndicator(
onRefresh: () async {
// Tambahkan delay untuk memastikan refresh indicator terlihat
@ -102,9 +98,7 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
}
Widget _buildPenerimaanList(BuildContext context) {
// Debug print untuk melihat jumlah item
print(
'DEBUG: Membangun ListView dengan ${controller.penerimaPenyaluran.length} item bantuan');
// Menggunakan CustomScrollView dan SliverList untuk layout yang lebih stabil
// Menggunakan CustomScrollView dan SliverList untuk layout yang lebih stabil
return CustomScrollView(
@ -122,9 +116,6 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
final item = controller.penerimaPenyaluran[index];
// Debug
print('DEBUG: Membangun item $index dengan id: ${item.id}');
// Menggunakan SizedBox untuk memberikan batas lebar dan tinggi
return SizedBox(
width: MediaQuery.of(context).size.width,

View File

@ -289,6 +289,8 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
Expanded(
child: _buildInfoItem(
'Jenis',
item.stokBantuan?[
'nama'] ??
item.jenisBantuan ??
"Tidak tersedia",
),
@ -296,8 +298,13 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
Expanded(
child: _buildInfoItem(
'Jumlah',
item.jumlahBantuan ??
"Tidak tersedia",
item.isUang
? FormatHelper.formatRupiah(
double.tryParse(item
.jumlahBantuan
.toString()) ??
0)
: '${item.jumlahBantuan} ${item.stokBantuan?['satuan'] ?? ''}',
),
),
],

View File

@ -5,6 +5,7 @@ import 'package:penyaluran_app/app/modules/warga/views/warga_dashboard_view.dart
import 'package:penyaluran_app/app/modules/warga/views/warga_penerimaan_view.dart';
import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_view.dart';
import 'package:penyaluran_app/app/widgets/app_bottom_navigation_bar.dart';
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class WargaView extends GetView<WargaDashboardController> {
@ -133,276 +134,83 @@ class WargaView extends GetView<WargaDashboardController> {
}
});
return Drawer(
child: Column(
children: [
Container(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
),
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 16,
bottom: 24,
left: 16,
right: 16),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white70,
backgroundImage: controller.fotoProfil.value.isNotEmpty
? NetworkImage(controller.fotoProfil.value)
: null,
child: controller.fotoProfil.isEmpty
? Text(
controller.nama.isNotEmpty
? controller.nama.substring(0, 1).toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 24,
),
)
: null,
),
),
SizedBox(height: 16),
Text(
'Halo,',
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
Text(
controller.nama,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
'Warga',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
SizedBox(width: 8),
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.location_on,
color: Colors.white,
size: 14,
),
SizedBox(width: 4),
Text(
controller.desa ?? 'Tidak ada desa',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
],
),
),
],
),
],
),
),
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
_buildMenuCategory('Menu Utama'),
Obx(() => _buildMenuItem(
return Obx(() {
Map<String, List<DrawerMenuItem>> menuCategories = {
'Menu Utama': [
DrawerMenuItem(
icon: Icons.dashboard_outlined,
activeIcon: Icons.dashboard,
title: 'Dashboard',
isSelected: controller.activeTabIndex.value == 0,
onTap: () {
Navigator.pop(context);
controller.changeTab(0);
controller.activeTabIndex.value = 0;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.volunteer_activism_outlined,
activeIcon: Icons.volunteer_activism,
title: 'Penerimaan',
title: 'Penerimaan Bantuan',
isSelected: controller.activeTabIndex.value == 1,
badgeCount: controller.totalPenyaluranDiterima.value > 0
? controller.totalPenyaluranDiterima.value
: null,
badgeColor: Colors.green,
onTap: () {
Navigator.pop(context);
controller.changeTab(1);
controller.activeTabIndex.value = 1;
},
)),
Obx(() => _buildMenuItem(
),
DrawerMenuItem(
icon: Icons.report_problem_outlined,
activeIcon: Icons.report_problem,
title: 'Pengaduan',
isSelected: controller.activeTabIndex.value == 2,
badgeCount: controller.totalPengaduanProses.value > 0
? controller.totalPengaduanProses.value
: null,
badgeColor: Colors.orange,
onTap: () {
Navigator.pop(context);
controller.changeTab(2);
controller.activeTabIndex.value = 2;
},
)),
_buildMenuCategory('Pengaturan'),
_buildMenuItem(
),
],
'Pengaturan': [
DrawerMenuItem(
icon: Icons.person_outline,
activeIcon: Icons.person,
title: 'Profil',
onTap: () async {
Navigator.pop(context);
await Get.toNamed('/profile');
// Refresh data ketika kembali dari profil
controller.refreshData();
onTap: () {
Get.toNamed('/profile');
},
),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.info_outline,
activeIcon: Icons.info,
title: 'Tentang Kami',
onTap: () {
Navigator.pop(context);
Get.toNamed('/about');
},
),
_buildMenuItem(
DrawerMenuItem(
icon: Icons.logout,
title: 'Keluar',
isLogout: true,
onTap: () {
Navigator.pop(context);
controller.logout();
},
isLogout: true,
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'© ${DateTime.now().year} DisalurKita',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
),
],
),
);
}
};
Widget _buildMenuCategory(String title) {
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
);
}
Widget _buildMenuItem({
required IconData icon,
IconData? activeIcon,
required String title,
bool isSelected = false,
String? badge,
required Function() onTap,
bool isLogout = false,
}) {
return AnimatedContainer(
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: isSelected
? AppTheme.primaryColor.withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: ListTile(
leading: Icon(
isSelected ? (activeIcon ?? icon) : icon,
color: isSelected
? AppTheme.primaryColor
: (isLogout ? Colors.red : null),
),
title: Text(
title,
style: TextStyle(
color: isSelected
? AppTheme.primaryColor
: (isLogout ? Colors.red : null),
fontWeight: isSelected ? FontWeight.bold : null,
),
),
trailing: badge != null
? Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(10),
),
constraints: BoxConstraints(
minWidth: 20,
minHeight: 20,
),
child: Text(
badge,
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
textAlign: TextAlign.center,
),
)
: null,
onTap: onTap,
),
return AppDrawer(
nama: controller.nama,
role: 'Warga',
desa: controller.desa,
avatar: controller.fotoProfil.value,
menuItems: const [], // Tidak digunakan karena menggunakan menuCategories
menuCategories: menuCategories,
onLogout: controller.logout,
footerText: '© ${DateTime.now().year} DisalurKita',
);
});
}
}

View File

@ -42,6 +42,7 @@ abstract class Routes {
static const riwayatPengaduan = _Paths.riwayatPengaduan;
static const qrScanner = _Paths.qrScanner;
static const konfirmasiPenerimaQr = _Paths.konfirmasiPenerimaQr;
static const jadwalPenyaluran = _Paths.jadwalPenyaluran;
static const PENGADUAN = '/pengaduan';
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
static const PENGADUAN_DETAIL = '/pengaduan-detail';
@ -91,6 +92,7 @@ abstract class _Paths {
static const riwayatPengaduan = '/petugas-desa/riwayat-pengaduan';
static const qrScanner = '/petugas-desa/qr-scanner';
static const konfirmasiPenerimaQr = '/petugas-desa/konfirmasi-penerima/:id';
static const jadwalPenyaluran = '/petugas-desa/jadwal-penyaluran';
static const PENGADUAN = '/pengaduan';
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
static const PENGADUAN_DETAIL = '/pengaduan-detail';

View File

@ -2370,4 +2370,39 @@ class SupabaseService extends GetxService {
return null;
}
}
// Metode untuk mengupdate role user dengan SQL langsung
Future<void> updateUserRole(String userId, int roleId) async {
try {
// Coba update via RPC jika sudah diimplementasikan
try {
await client.rpc(
'update_user_role',
params: {
'user_id': userId,
'role_id_value': roleId,
},
);
print('DEBUG: Berhasil mengupdate role_id user via RPC');
return;
} catch (e) {
print('WARN: RPC update_user_role tidak tersedia: $e');
}
// Jika RPC tidak tersedia, gunakan query SQL langsung ke auth.users jika diizinkan
try {
print('DEBUG: Mencoba update auth.users langsung');
await client
.from('auth.users')
.update({'role_id': roleId}).eq('id', userId);
print('DEBUG: Berhasil mengupdate role_id user via SQL langsung');
} catch (e) {
print('ERROR: Gagal mengupdate auth.users: $e');
print('INFO: Role perlu diupdate manual melalui dashboard Supabase');
}
} catch (e) {
print('ERROR: Gagal mengupdate role user: $e');
// Tidak perlu throw exception, kegagalan update role tidak fatal
}
}
}

View File

@ -10,6 +10,9 @@ class AppDrawer extends StatelessWidget {
final int? notificationCount;
final VoidCallback onLogout;
final List<DrawerMenuItem> menuItems;
final Map<String, List<DrawerMenuItem>>? menuCategories;
final String? footerText;
final Widget? headerExtraContent;
const AppDrawer({
super.key,
@ -20,60 +23,24 @@ class AppDrawer extends StatelessWidget {
this.notificationCount,
required this.onLogout,
required this.menuItems,
this.menuCategories,
this.footerText,
this.headerExtraContent,
});
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
_buildHeader(context),
if (menuCategories != null && menuCategories!.isNotEmpty)
_buildCategorizedMenu()
else
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
CircleAvatar(
radius: 30,
backgroundColor: AppTheme.primaryColor.withOpacity(0.2),
backgroundImage: avatar != null && avatar!.isNotEmpty
? NetworkImage(avatar!)
: null,
child: (avatar == null || avatar!.isEmpty)
? Text(
nama.isNotEmpty
? nama.substring(0, 1).toUpperCase()
: '?',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: AppTheme.primaryColor,
fontSize: 24,
),
)
: null,
),
const SizedBox(height: 10),
Text(
nama,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
desa != null ? '$role - $desa' : role,
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 14,
),
),
],
),
),
...menuItems.map((item) => _buildMenuItem(context, item)),
const Divider(),
ListTile(
@ -85,11 +52,11 @@ class AppDrawer extends StatelessWidget {
},
),
ListTile(
leading: const Icon(Icons.settings_outlined),
title: const Text('Pengaturan'),
leading: const Icon(Icons.info_outline),
title: const Text('Tentang Kami'),
onTap: () {
Navigator.pop(context);
// TODO: Navigasi ke halaman pengaturan
Get.toNamed('/about');
},
),
ListTile(
@ -102,16 +69,229 @@ class AppDrawer extends StatelessWidget {
),
],
),
),
if (footerText != null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
footerText!,
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
),
],
),
);
}
Widget _buildMenuItem(BuildContext context, DrawerMenuItem item) {
return ListTile(
Widget _buildHeader(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
),
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 16,
bottom: 24,
left: 16,
right: 16),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: CircleAvatar(
radius: 40,
backgroundColor: Colors.white70,
backgroundImage: avatar != null && avatar!.isNotEmpty
? NetworkImage(avatar!)
: null,
child: (avatar == null || avatar!.isEmpty)
? Text(
nama.isNotEmpty
? nama.substring(0, 1).toUpperCase()
: '?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
fontSize: 24,
),
)
: null,
),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'DisalurKita',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 3),
const Text(
'Salurkan dengan Pasti, Pantau dengan Bukti',
style: TextStyle(
fontSize: 10,
color: Colors.white70,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
const SizedBox(height: 16),
const Text(
'Halo,',
style: TextStyle(
color: Colors.white70,
fontSize: 16,
),
),
Text(
nama,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
const SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
role,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
if (desa != null) ...[
const SizedBox(width: 8),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.location_on,
color: Colors.white,
size: 14,
),
const SizedBox(width: 4),
Text(
desa!,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
),
],
),
),
],
],
),
if (headerExtraContent != null) ...[
const SizedBox(height: 8),
headerExtraContent!,
],
],
),
);
}
Widget _buildCategorizedMenu() {
return Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: [
...menuCategories!.entries.map((entry) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildMenuCategory(entry.key),
...entry.value.map((item) => _buildMenuItem(null, item)),
],
)),
],
),
);
}
Widget _buildMenuCategory(String title) {
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
);
}
Widget _buildMenuItem(BuildContext? context, DrawerMenuItem item) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: item.isSelected
? AppTheme.primaryColor.withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: ListTile(
leading: item.badgeCount != null && item.badgeCount! > 0
? Stack(
alignment: Alignment.center,
children: [
Icon(item.icon),
Icon(
item.isSelected
? (item.activeIcon ?? item.icon)
: item.icon,
color: item.isSelected
? AppTheme.primaryColor
: (item.isLogout ? Colors.red : null),
),
Positioned(
top: 0,
right: 0,
@ -137,32 +317,58 @@ class AppDrawer extends StatelessWidget {
),
],
)
: Icon(item.icon),
title: Text(item.title),
selected: item.isSelected,
selectedColor: AppTheme.primaryColor,
: Icon(
item.isSelected ? (item.activeIcon ?? item.icon) : item.icon,
color: item.isSelected
? AppTheme.primaryColor
: (item.isLogout ? Colors.red : null),
),
title: Text(
item.title,
style: TextStyle(
color: item.isSelected
? AppTheme.primaryColor
: (item.isLogout ? Colors.red : null),
fontWeight: item.isSelected ? FontWeight.bold : null,
),
),
trailing: item.trailing,
onTap: () {
if (context != null) {
Navigator.pop(context);
} else {
Navigator.of(Get.overlayContext!).pop();
}
Future.delayed(const Duration(milliseconds: 100), () {
item.onTap();
});
},
),
);
}
}
class DrawerMenuItem {
final IconData icon;
final IconData? activeIcon;
final String title;
final bool isSelected;
final VoidCallback onTap;
final int? badgeCount;
final Color? badgeColor;
final bool isLogout;
final Widget? trailing;
DrawerMenuItem({
required this.icon,
this.activeIcon,
required this.title,
this.isSelected = false,
required this.onTap,
this.badgeCount,
this.badgeColor,
this.isLogout = false,
this.trailing,
});
}

View File

@ -68,7 +68,7 @@ class BantuanCard extends StatelessWidget {
),
child: Icon(
item.isUang == true
? Icons.attach_money
? Icons.payment_rounded
: Icons.inventory_2,
color: item.isUang == true ? Colors.green : Colors.blue,
size: 28,
@ -81,16 +81,37 @@ class BantuanCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (item.namaPenyaluran != null &&
item.namaPenyaluran!.isNotEmpty)
Text(
item.namaPenyaluran!,
style: TextStyle(
color: (item.isUang == true)
? Colors.green.shade800
: Colors.blue.shade800,
fontSize: 15,
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Padding(
padding: EdgeInsets.only(
top: item.namaPenyaluran != null &&
item.namaPenyaluran!.isNotEmpty
? 4
: 0),
child: Text(
item.kategoriNama ?? 'Bantuan',
style: TextStyle(
color: Colors.grey.shade700,
fontSize: 14,
fontSize: 13,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
const SizedBox(height: 6),
Row(
mainAxisSize: MainAxisSize.min,
@ -229,12 +250,26 @@ class BantuanCard extends StatelessWidget {
children: [
Icon(
item.isUang == true
? Icons.attach_money
? Icons.payment_rounded
: Icons.inventory_2,
color: item.isUang == true
? Colors.green.shade700
: Colors.blue.shade700,
),
const SizedBox(width: 12),
Expanded(
child: Text(
item.namaPenyaluran != null &&
item.namaPenyaluran!.isNotEmpty
? item.namaPenyaluran!
: "Bantuan ${item.kategoriNama ?? ''}",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),

View File

@ -169,6 +169,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
equatable:
dependency: transitive
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
@ -241,6 +249,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
fl_chart:
dependency: "direct main"
description:
name: fl_chart
sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237"
url: "https://pub.dev"
source: hosted
version: "0.70.2"
flutter:
dependency: "direct main"
description: flutter

View File

@ -96,6 +96,7 @@ dependencies:
cached_network_image: ^3.3.1
share_plus: ^10.1.4
path: ^1.9.1
fl_chart: ^0.70.2
dev_dependencies:
flutter_test: