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:
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
<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
|
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
|
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
|
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
|
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 ~
|
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
|
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>
|
||||||
<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
|
<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
|
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|
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
<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>
|
||||||
<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
|
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
|
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
|
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|
|
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>
|
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
|
|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>
|
||||||
<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
|
<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
|
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~
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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
|
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
|
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
|
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
|
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
|
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 {
|
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
|
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>
|
<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
|
}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
|
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
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
||||||
<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>
|
||||||
<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>
|
||||||
<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
|
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>
|
<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>
|
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>
|
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>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>
|
||||||
<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>
|
<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
|
|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>
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
||||||
<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>
|
||||||
<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>
|
||||||
<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
|
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>
|
||||||
<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>
|
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>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>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>
|
||||||
<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>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
|
~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>
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
||||||
<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>
|
||||||
<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
|
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
|
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
|
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}
|
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>
|
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
|
}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>
|
||||||
<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
|
<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
|
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
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
|||||||
M
|
M
|
||||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||||
A
|
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>
|
||||||
<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>
|
||||||
<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>
|
||||||
<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
|
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
|
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>
|
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>
|
|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>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>
|
||||||
<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}
|
<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
|
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>
|
@ -111,4 +111,9 @@ class PengaduanModel {
|
|||||||
if (jumlah == null) return 'Tidak diketahui';
|
if (jumlah == null) return 'Tidak diketahui';
|
||||||
return '$jumlah $satuan';
|
return '$jumlah $satuan';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getter untuk memeriksa apakah bantuan berupa uang
|
||||||
|
bool get isUang {
|
||||||
|
return stokBantuan?['is_uang'] == true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,14 +177,13 @@ class AuthProvider {
|
|||||||
String? jenis,
|
String? jenis,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
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(
|
final response = await _supabaseService.client.auth.signUp(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
data: {
|
|
||||||
'role_id': 3, // Otomatis set sebagai donatur
|
|
||||||
},
|
|
||||||
// Tidak menggunakan email redirect karena kita akan auto-confirm
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final user = response.user;
|
final user = response.user;
|
||||||
@ -192,59 +191,63 @@ class AuthProvider {
|
|||||||
throw Exception('Gagal membuat akun donatur');
|
throw Exception('Gagal membuat akun donatur');
|
||||||
}
|
}
|
||||||
|
|
||||||
print('User berhasil terdaftar dengan ID: ${user.id}');
|
print('DEBUG: User berhasil terdaftar dengan ID: ${user.id}');
|
||||||
|
|
||||||
// Step 2: Buat data donatur di tabel donatur
|
|
||||||
await _supabaseService.client.from('donatur').insert({
|
|
||||||
'id': user.id,
|
|
||||||
'nama_lengkap': namaLengkap,
|
|
||||||
'alamat': alamat,
|
|
||||||
'no_hp': noHp,
|
|
||||||
'email': email,
|
|
||||||
'jenis': jenis ?? 'Individu',
|
|
||||||
'created_at': DateTime.now().toIso8601String(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Step 3: Pastikan di tabel profiles_backup juga ada data ini (jika digunakan)
|
|
||||||
try {
|
try {
|
||||||
await _supabaseService.client.from('profiles_backup').insert({
|
// Step 2: Buat data donatur di tabel donatur
|
||||||
|
print('DEBUG: Mencoba menyimpan data donatur ke tabel donatur');
|
||||||
|
|
||||||
|
final currentTime = DateTime.now().toIso8601String();
|
||||||
|
|
||||||
|
// Data untuk tabel donatur
|
||||||
|
final donaturData = {
|
||||||
'id': user.id,
|
'id': user.id,
|
||||||
'updated_at': DateTime.now().toIso8601String(),
|
'nama_lengkap': namaLengkap,
|
||||||
'role_id': 3,
|
'alamat': alamat,
|
||||||
});
|
'no_hp': noHp,
|
||||||
|
'email': email,
|
||||||
|
'jenis': jenis ?? 'Individu',
|
||||||
|
'created_at': currentTime,
|
||||||
|
'updated_at': currentTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) {
|
} catch (e) {
|
||||||
print('Error menyimpan di profiles_backup: $e');
|
print('ERROR: Gagal menyimpan data donatur: $e');
|
||||||
// Lanjutkan proses meskipun ada error di step ini
|
throw Exception('Gagal menyimpan data donatur. Silakan coba lagi.');
|
||||||
}
|
}
|
||||||
|
|
||||||
print('Berhasil mendaftarkan donatur: $namaLengkap');
|
|
||||||
|
|
||||||
// Pesan untuk pengembang
|
|
||||||
print(
|
print(
|
||||||
'CATATAN: User akan perlu login manual. Jika email konfirmasi masih diperlukan,');
|
'DEBUG: Berhasil mendaftarkan donatur: $namaLengkap dengan ID: ${user.id}');
|
||||||
print(
|
|
||||||
'nonaktifkan verifikasi email di dashboard Supabase: Authentication > Email Templates > Disable Email Confirmation');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error pada signUpDonatur: $e');
|
print('ERROR: Error pada signUpDonatur: $e');
|
||||||
|
|
||||||
if (e.toString().contains('User already registered')) {
|
if (e.toString().contains('User already registered')) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Email sudah terdaftar. Silakan gunakan email lain atau login dengan email tersebut.');
|
'Email sudah terdaftar. Silakan gunakan email lain atau login dengan email tersebut.');
|
||||||
|
} 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.');
|
||||||
|
print(
|
||||||
|
'Untuk mengatasi ini, nonaktifkan konfirmasi email di dashboard Supabase:');
|
||||||
|
print(
|
||||||
|
'1. Buka project Supabase > Authentication > Email Templates > Confirmation');
|
||||||
|
print('2. Matikan toggle "Enable email confirmations"');
|
||||||
|
print('==============================');
|
||||||
|
throw Exception(
|
||||||
|
'Gagal mengirim email konfirmasi. Mohon hubungi administrator.');
|
||||||
} else {
|
} else {
|
||||||
// Untuk error konfirmasi email, berikan instruksi yang jelas
|
|
||||||
if (e.toString().contains('Error sending confirmation mail')) {
|
|
||||||
print('===== PERHATIAN PENGEMBANG =====');
|
|
||||||
print(
|
|
||||||
'Error ini terjadi karena Supabase gagal mengirim email konfirmasi.');
|
|
||||||
print(
|
|
||||||
'Untuk mengatasi ini, nonaktifkan konfirmasi email di dashboard Supabase:');
|
|
||||||
print(
|
|
||||||
'1. Buka project Supabase > Authentication > Email Templates > Confirmation');
|
|
||||||
print('2. Matikan toggle "Enable email confirmations"');
|
|
||||||
print('==============================');
|
|
||||||
throw Exception(
|
|
||||||
'Gagal mengirim email konfirmasi. Mohon hubungi administrator.');
|
|
||||||
}
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,62 +537,114 @@ class AuthController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
|
||||||
// Proses registrasi donatur dengan role_id 3
|
|
||||||
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,
|
|
||||||
);
|
|
||||||
|
|
||||||
Get.snackbar(
|
// Kita akan mencoba maksimal 2 kali jika terjadi error
|
||||||
'Sukses',
|
int maxAttempts = 2;
|
||||||
'Registrasi donatur berhasil! Silakan login dengan akun Anda.',
|
int attempt = 0;
|
||||||
snackPosition: SnackPosition.TOP,
|
bool success = false;
|
||||||
backgroundColor: Colors.green,
|
Exception? lastError;
|
||||||
colorText: Colors.white,
|
|
||||||
duration: const Duration(seconds: 5),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Bersihkan form
|
while (attempt < maxAttempts && !success) {
|
||||||
clearDonaturRegistrationForm();
|
attempt++;
|
||||||
|
print(
|
||||||
|
'DEBUG: Mencoba pendaftaran donatur (percobaan ke-$attempt dari $maxAttempts)');
|
||||||
|
|
||||||
// Arahkan ke halaman login
|
try {
|
||||||
Get.offAllNamed(Routes.login);
|
print('DEBUG: Validasi form berhasil, melanjutkan proses registrasi');
|
||||||
} catch (e) {
|
print('DEBUG: Email: ${emailController.text}');
|
||||||
print('Error registrasi donatur: $e');
|
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}');
|
||||||
|
|
||||||
String errorMessage = 'Gagal melakukan registrasi';
|
// 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,
|
||||||
|
);
|
||||||
|
|
||||||
// Tangani error sesuai jenisnya
|
print('DEBUG: Registrasi donatur berhasil');
|
||||||
if (e.toString().contains('email konfirmasi')) {
|
success = true;
|
||||||
errorMessage =
|
|
||||||
'Gagal mengirim email konfirmasi. Mohon periksa alamat email Anda dan coba lagi nanti.';
|
Get.snackbar(
|
||||||
} else if (e.toString().contains('Email sudah terdaftar')) {
|
'Sukses',
|
||||||
errorMessage =
|
'Registrasi donatur berhasil! Silakan login dengan akun Anda.',
|
||||||
'Email sudah terdaftar. Silakan gunakan email lain atau login dengan email tersebut.';
|
snackPosition: SnackPosition.TOP,
|
||||||
} else if (e.toString().contains('weak-password')) {
|
backgroundColor: Colors.green,
|
||||||
errorMessage =
|
colorText: Colors.white,
|
||||||
'Password terlalu lemah. Gunakan kombinasi huruf, angka, dan simbol.';
|
duration: const Duration(seconds: 5),
|
||||||
} else if (e.toString().contains('invalid-email')) {
|
);
|
||||||
errorMessage = 'Format email tidak valid.';
|
|
||||||
} else {
|
// Bersihkan form
|
||||||
errorMessage = 'Gagal melakukan registrasi: ${e.toString()}';
|
clearDonaturRegistrationForm();
|
||||||
|
|
||||||
|
// Arahkan ke halaman login
|
||||||
|
Get.offAllNamed(Routes.login);
|
||||||
|
} catch (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
|
||||||
|
if (e.toString().contains('email konfirmasi')) {
|
||||||
|
errorMessage =
|
||||||
|
'Gagal mengirim email konfirmasi. Mohon periksa alamat email Anda dan coba lagi nanti.';
|
||||||
|
} else if (e.toString().contains('Email sudah terdaftar')) {
|
||||||
|
errorMessage =
|
||||||
|
'Email sudah terdaftar. Silakan gunakan email lain atau login dengan email tersebut.';
|
||||||
|
} else if (e.toString().contains('weak-password')) {
|
||||||
|
errorMessage =
|
||||||
|
'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()}';
|
||||||
|
}
|
||||||
|
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
errorMessage,
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
duration: const Duration(seconds: 7),
|
||||||
|
);
|
||||||
|
break; // Keluar dari loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Get.snackbar(
|
isLoading.value = false;
|
||||||
'Error',
|
|
||||||
errorMessage,
|
// Jika gagal setelah mencoba beberapa kali
|
||||||
snackPosition: SnackPosition.TOP,
|
if (!success && lastError != null) {
|
||||||
backgroundColor: Colors.red,
|
print('ERROR: Pendaftaran gagal setelah $attempt percobaan');
|
||||||
colorText: Colors.white,
|
// Error sudah ditampilkan di dalam loop
|
||||||
duration: const Duration(seconds: 5),
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.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/routes/app_pages.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
class RegisterDonaturView extends GetView<AuthController> {
|
class RegisterDonaturView extends GetView<AuthController> {
|
||||||
const RegisterDonaturView({super.key});
|
const RegisterDonaturView({super.key});
|
||||||
@ -13,7 +14,7 @@ class RegisterDonaturView extends GetView<AuthController> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Daftar Donatur'),
|
title: const Text('Daftar Donatur'),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: AppTheme.primaryColor,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
@ -22,436 +23,469 @@ class RegisterDonaturView extends GetView<AuthController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: Container(
|
||||||
child: Padding(
|
decoration: const BoxDecoration(
|
||||||
padding: const EdgeInsets.all(20.0),
|
gradient: LinearGradient(
|
||||||
child: SingleChildScrollView(
|
begin: Alignment.topCenter,
|
||||||
child: Form(
|
end: Alignment.bottomCenter,
|
||||||
key: controller.registerDonaturFormKey,
|
colors: [Color(0xFFE3F2FD), Colors.white],
|
||||||
child: Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
),
|
||||||
children: [
|
child: SafeArea(
|
||||||
const SizedBox(height: 10),
|
child: Padding(
|
||||||
// Header dengan icon dan judul
|
padding: const EdgeInsets.all(20.0),
|
||||||
Container(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(15),
|
child: Form(
|
||||||
decoration: BoxDecoration(
|
key: controller.registerDonaturFormKey,
|
||||||
color: Colors.blue.shade50,
|
child: Column(
|
||||||
borderRadius: BorderRadius.circular(15),
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
),
|
children: [
|
||||||
child: Column(
|
const SizedBox(height: 10),
|
||||||
children: [
|
// Header dengan icon dan judul
|
||||||
Image.asset(
|
Container(
|
||||||
'assets/images/logo-disalurkita.png',
|
padding: const EdgeInsets.all(15),
|
||||||
width: 120,
|
decoration: BoxDecoration(
|
||||||
height: 120,
|
color: Colors.white,
|
||||||
),
|
borderRadius: BorderRadius.circular(15),
|
||||||
const Text(
|
boxShadow: [
|
||||||
'Daftar Sebagai Donatur',
|
BoxShadow(
|
||||||
style: TextStyle(
|
color: AppTheme.primaryColor.withOpacity(0.2),
|
||||||
fontSize: 24,
|
spreadRadius: 1,
|
||||||
fontWeight: FontWeight.bold,
|
blurRadius: 5,
|
||||||
color: Colors.blue,
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
const SizedBox(height: 4),
|
),
|
||||||
const Text(
|
child: Column(
|
||||||
'Bergabunglah dengan kami untuk membantu mereka yang membutuhkan',
|
children: [
|
||||||
textAlign: TextAlign.center,
|
Image.asset(
|
||||||
style: TextStyle(
|
'assets/images/logo-disalurkita.png',
|
||||||
fontSize: 16,
|
width: 120,
|
||||||
color: Colors.blueGrey,
|
height: 120,
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
],
|
'Daftar Sebagai Donatur',
|
||||||
),
|
style: TextStyle(
|
||||||
),
|
fontSize: 24,
|
||||||
const SizedBox(height: 20),
|
fontWeight: FontWeight.bold,
|
||||||
// Step indicator
|
color: AppTheme.primaryColor,
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.person_add, color: Colors.blue),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
'Informasi Akun',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
|
|
||||||
// Nama Lengkap
|
|
||||||
TextFormField(
|
|
||||||
controller: controller.namaController,
|
|
||||||
keyboardType: TextInputType.name,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Nama Lengkap',
|
|
||||||
hintText: 'Masukkan nama lengkap Anda',
|
|
||||||
prefixIcon: const Icon(Icons.person, color: Colors.blue),
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.grey.shade100,
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: controller.validateDonaturNama,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// Email
|
|
||||||
TextFormField(
|
|
||||||
controller: controller.emailController,
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Email',
|
|
||||||
hintText: 'contoh@email.com',
|
|
||||||
prefixIcon: const Icon(Icons.email, color: Colors.blue),
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.grey.shade100,
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: controller.validateEmail,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// Password
|
|
||||||
Obx(() => TextFormField(
|
|
||||||
controller: controller.passwordController,
|
|
||||||
obscureText: controller.isPasswordHidden.value,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Password',
|
|
||||||
hintText: 'Minimal 8 karakter',
|
|
||||||
prefixIcon:
|
|
||||||
const Icon(Icons.lock, color: Colors.blue),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
controller.isPasswordHidden.value
|
|
||||||
? Icons.visibility_off
|
|
||||||
: Icons.visibility,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
|
||||||
controller.togglePasswordVisibility(),
|
|
||||||
),
|
),
|
||||||
filled: true,
|
const SizedBox(height: 4),
|
||||||
fillColor: Colors.grey.shade100,
|
Text(
|
||||||
enabledBorder: OutlineInputBorder(
|
'Bergabunglah dengan kami untuk membantu mereka yang membutuhkan',
|
||||||
borderRadius: BorderRadius.circular(10),
|
textAlign: TextAlign.center,
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
style: TextStyle(
|
||||||
),
|
fontSize: 16,
|
||||||
focusedBorder: OutlineInputBorder(
|
color: const Color(0xFF546E7A),
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide:
|
|
||||||
const BorderSide(color: Colors.blue, width: 2),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: controller.validatePassword,
|
|
||||||
)),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// Confirm Password
|
|
||||||
Obx(() => TextFormField(
|
|
||||||
controller: controller.confirmPasswordController,
|
|
||||||
obscureText: controller.isConfirmPasswordHidden.value,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Konfirmasi Password',
|
|
||||||
hintText: 'Masukkan password yang sama',
|
|
||||||
prefixIcon: const Icon(Icons.lock_outline,
|
|
||||||
color: Colors.blue),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
controller.isConfirmPasswordHidden.value
|
|
||||||
? Icons.visibility_off
|
|
||||||
: Icons.visibility,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
),
|
||||||
onPressed: () =>
|
|
||||||
controller.toggleConfirmPasswordVisibility(),
|
|
||||||
),
|
),
|
||||||
filled: true,
|
],
|
||||||
fillColor: Colors.grey.shade100,
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: controller.validateConfirmPassword,
|
|
||||||
)),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// Section heading
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.person_pin_circle, color: Colors.blue),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
'Informasi Profil',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
|
|
||||||
// No HP
|
|
||||||
TextFormField(
|
|
||||||
controller: controller.noHpController,
|
|
||||||
keyboardType: TextInputType.phone,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Nomor HP',
|
|
||||||
hintText: 'Masukkan nomor HP aktif',
|
|
||||||
prefixIcon: const Icon(Icons.phone, color: Colors.blue),
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.grey.shade100,
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
validator: controller.validateDonaturNoHp,
|
const SizedBox(height: 20),
|
||||||
),
|
// Step indicator
|
||||||
const SizedBox(height: 15),
|
Row(
|
||||||
|
|
||||||
// Alamat
|
|
||||||
TextFormField(
|
|
||||||
controller: controller.alamatController,
|
|
||||||
keyboardType: TextInputType.streetAddress,
|
|
||||||
maxLines: 2,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Alamat Lengkap',
|
|
||||||
hintText: 'Masukkan alamat lengkap Anda',
|
|
||||||
prefixIcon: const Icon(Icons.home, color: Colors.blue),
|
|
||||||
filled: true,
|
|
||||||
fillColor: Colors.grey.shade100,
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
validator: controller.validateDonaturAlamat,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
|
|
||||||
// Jenis Donatur (Dropdown)
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
border: Border.all(color: Colors.grey.shade300),
|
|
||||||
),
|
|
||||||
child: DropdownButtonFormField<String>(
|
|
||||||
value: controller.jenisController.text.isEmpty
|
|
||||||
? 'Individu'
|
|
||||||
: controller.jenisController.text,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'Jenis Donatur',
|
|
||||||
prefixIcon:
|
|
||||||
const Icon(Icons.category, color: Colors.blue),
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
),
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Individu', child: Text('Individu')),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Organisasi', child: Text('Organisasi')),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Perusahaan', child: Text('Perusahaan')),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: 'Lainnya', child: Text('Lainnya')),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
controller.jenisController.text = value ?? 'Individu';
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
|
|
||||||
// Catatan Informasi
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(15),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.blue.shade50,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
border: Border.all(color: Colors.blue.shade200),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.info_outline, color: Colors.blue),
|
Icon(Icons.person_add, color: AppTheme.primaryColor),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Text(
|
||||||
child: Column(
|
'Informasi Akun',
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
style: TextStyle(
|
||||||
children: const [
|
fontSize: 18,
|
||||||
Text(
|
fontWeight: FontWeight.bold,
|
||||||
'Informasi',
|
color: AppTheme.primaryColor,
|
||||||
style: TextStyle(
|
),
|
||||||
fontWeight: FontWeight.bold,
|
),
|
||||||
color: Colors.blue,
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
|
// Nama Lengkap
|
||||||
|
TextFormField(
|
||||||
|
controller: controller.namaController,
|
||||||
|
keyboardType: TextInputType.name,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Nama Lengkap',
|
||||||
|
hintText: 'Masukkan nama lengkap Anda',
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.person, color: AppTheme.primaryColor),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validateDonaturNama,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Email
|
||||||
|
TextFormField(
|
||||||
|
controller: controller.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
hintText: 'contoh@email.com',
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.email, color: AppTheme.primaryColor),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validateEmail,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Password
|
||||||
|
Obx(() => TextFormField(
|
||||||
|
controller: controller.passwordController,
|
||||||
|
obscureText: controller.isPasswordHidden.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
hintText: 'Minimal 8 karakter',
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.lock, color: AppTheme.primaryColor),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
controller.isPasswordHidden.value
|
||||||
|
? Icons.visibility_off
|
||||||
|
: Icons.visibility,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
),
|
),
|
||||||
SizedBox(height: 5),
|
onPressed: () =>
|
||||||
Text(
|
controller.togglePasswordVisibility(),
|
||||||
'Data Anda akan terverifikasi dan terlindungi. Kami menjaga privasi dan keamanan data Anda.',
|
),
|
||||||
style: TextStyle(
|
filled: true,
|
||||||
fontSize: 14,
|
fillColor: Colors.white,
|
||||||
color: Colors.blueGrey,
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validatePassword,
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Confirm Password
|
||||||
|
Obx(() => TextFormField(
|
||||||
|
controller: controller.confirmPasswordController,
|
||||||
|
obscureText: controller.isConfirmPasswordHidden.value,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Konfirmasi Password',
|
||||||
|
hintText: 'Masukkan password yang sama',
|
||||||
|
prefixIcon: Icon(Icons.lock_outline,
|
||||||
|
color: AppTheme.primaryColor),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
controller.isConfirmPasswordHidden.value
|
||||||
|
? Icons.visibility_off
|
||||||
|
: Icons.visibility,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
controller.toggleConfirmPasswordVisibility(),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide:
|
||||||
|
BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validateConfirmPassword,
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Section heading
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.person_pin_circle,
|
||||||
|
color: AppTheme.primaryColor),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
'Informasi Profil',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
|
// No HP
|
||||||
|
TextFormField(
|
||||||
|
controller: controller.noHpController,
|
||||||
|
keyboardType: TextInputType.phone,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Nomor HP',
|
||||||
|
hintText: 'Masukkan nomor HP aktif',
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.phone, color: AppTheme.primaryColor),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validateDonaturNoHp,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Alamat
|
||||||
|
TextFormField(
|
||||||
|
controller: controller.alamatController,
|
||||||
|
keyboardType: TextInputType.streetAddress,
|
||||||
|
maxLines: 2,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Alamat Lengkap',
|
||||||
|
hintText: 'Masukkan alamat lengkap Anda',
|
||||||
|
prefixIcon:
|
||||||
|
Icon(Icons.home, color: AppTheme.primaryColor),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: AppTheme.primaryColor, width: 2),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: controller.validateDonaturAlamat,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
|
// Jenis Donatur (Dropdown)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
value: controller.jenisController.text.isEmpty
|
||||||
|
? 'Individu'
|
||||||
|
: controller.jenisController.text,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Jenis Donatur',
|
||||||
|
labelStyle: TextStyle(color: AppTheme.primaryColor),
|
||||||
|
prefixIcon: Icon(Icons.category,
|
||||||
|
color: AppTheme.primaryColor),
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
),
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'Individu', child: Text('Individu')),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'Organisasi', child: Text('Organisasi')),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'Perusahaan', child: Text('Perusahaan')),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: 'Lainnya', child: Text('Lainnya')),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
controller.jenisController.text = value ?? 'Individu';
|
||||||
|
},
|
||||||
|
dropdownColor: Colors.white,
|
||||||
|
icon: Icon(Icons.arrow_drop_down,
|
||||||
|
color: AppTheme.primaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
|
||||||
|
// Catatan Informasi
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF1F8E9),
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(color: const Color(0xFFAED581)),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.info_outline,
|
||||||
|
color: Color(0xFF558B2F)),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: const [
|
||||||
|
Text(
|
||||||
|
'Informasi',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF558B2F),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'Data Anda akan terverifikasi dan terlindungi. Kami menjaga privasi dan keamanan data Anda.',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF33691E),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
|
||||||
|
// Register Button
|
||||||
|
Obx(() => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: AppTheme.primaryColor.withOpacity(0.3),
|
||||||
|
spreadRadius: 1,
|
||||||
|
blurRadius: 3,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
child: ElevatedButton(
|
||||||
],
|
onPressed: controller.isLoading.value
|
||||||
),
|
? null
|
||||||
),
|
: controller.registerDonatur,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
const SizedBox(height: 25),
|
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
// Register Button
|
shape: RoundedRectangleBorder(
|
||||||
Obx(() => Container(
|
borderRadius: BorderRadius.circular(10),
|
||||||
decoration: BoxDecoration(
|
),
|
||||||
borderRadius: BorderRadius.circular(10),
|
elevation: 0,
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.blue.withOpacity(0.3),
|
|
||||||
spreadRadius: 1,
|
|
||||||
blurRadius: 3,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
),
|
||||||
],
|
child: controller.isLoading.value
|
||||||
),
|
? const SpinKitThreeBounce(
|
||||||
child: ElevatedButton(
|
color: Colors.white,
|
||||||
onPressed: controller.isLoading.value
|
size: 24,
|
||||||
? null
|
)
|
||||||
: controller.registerDonatur,
|
: const Row(
|
||||||
style: ElevatedButton.styleFrom(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
children: [
|
||||||
backgroundColor: Colors.blue,
|
Icon(Icons.how_to_reg,
|
||||||
shape: RoundedRectangleBorder(
|
color: Colors.white),
|
||||||
borderRadius: BorderRadius.circular(10),
|
SizedBox(width: 10),
|
||||||
),
|
Text(
|
||||||
elevation: 0,
|
'DAFTAR SEKARANG',
|
||||||
),
|
style: TextStyle(
|
||||||
child: controller.isLoading.value
|
fontSize: 16,
|
||||||
? const SpinKitThreeBounce(
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
size: 24,
|
letterSpacing: 1,
|
||||||
)
|
),
|
||||||
: const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.how_to_reg, color: Colors.white),
|
|
||||||
SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
'DAFTAR SEKARANG',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
)),
|
||||||
)),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Login Link
|
// Login Link
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(15),
|
padding: const EdgeInsets.all(15),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.shade50,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(color: Colors.grey.shade200),
|
border: Border.all(color: Colors.grey.shade200),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Sudah punya akun?',
|
'Sudah punya akun?',
|
||||||
style: TextStyle(color: Colors.grey),
|
style: TextStyle(color: Color(0xFF546E7A)),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get.offAllNamed(Routes.login),
|
onPressed: () => Get.offAllNamed(Routes.login),
|
||||||
child: const Text(
|
style: TextButton.styleFrom(
|
||||||
'Masuk',
|
foregroundColor: AppTheme.primaryColor,
|
||||||
style: TextStyle(
|
),
|
||||||
fontWeight: FontWeight.bold,
|
child: const Text(
|
||||||
color: Colors.blue,
|
'Masuk',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -36,54 +36,6 @@ class DonaturDashboardView extends GetView<DonaturDashboardController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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(),
|
_buildWelcomeSection(),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildStatisticSection(),
|
_buildStatisticSection(),
|
||||||
@ -242,29 +194,14 @@ class DonaturDashboardView extends GetView<DonaturDashboardController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
_buildActionButton(
|
||||||
children: [
|
icon: Icons.add_box_rounded,
|
||||||
Expanded(
|
label: 'Titip Bantuan',
|
||||||
child: _buildActionButton(
|
color: Colors.green.shade700,
|
||||||
icon: Icons.edit_rounded,
|
onTap: () {
|
||||||
label: 'Edit Profil',
|
// Navigasi ke form penitipan bantuan
|
||||||
color: Colors.blue.shade700,
|
controller.activeTabIndex.value = 3;
|
||||||
onTap: () => Get.toNamed(Routes.profile),
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: _buildActionButton(
|
|
||||||
icon: Icons.add_box_rounded,
|
|
||||||
label: 'Titip Bantuan',
|
|
||||||
color: Colors.green.shade700,
|
|
||||||
onTap: () {
|
|
||||||
// Navigasi ke form penitipan bantuan
|
|
||||||
controller.activeTabIndex.value = 3;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -661,7 +661,8 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoItem(
|
child: _buildInfoItem(
|
||||||
icon: isUang ? Icons.attach_money : Icons.category,
|
icon:
|
||||||
|
isUang ? Icons.payment_rounded : Icons.category,
|
||||||
label: 'Jenis Bantuan',
|
label: 'Jenis Bantuan',
|
||||||
value: stokNama,
|
value: stokNama,
|
||||||
iconColor: isUang
|
iconColor: isUang
|
||||||
@ -944,7 +945,8 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoItem(
|
child: _buildInfoItem(
|
||||||
icon: isUang ? Icons.attach_money : Icons.category,
|
icon:
|
||||||
|
isUang ? Icons.payment_rounded : Icons.category,
|
||||||
label: 'Jenis Bantuan',
|
label: 'Jenis Bantuan',
|
||||||
value: stokNama,
|
value: stokNama,
|
||||||
iconColor: isUang
|
iconColor: isUang
|
||||||
@ -1042,7 +1044,7 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
|
|||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
// Tampilkan ikon uang jika stok bantuan berbentuk uang
|
// Tampilkan ikon uang jika stok bantuan berbentuk uang
|
||||||
isUang ? Icons.attach_money : Icons.numbers,
|
isUang ? Icons.payment_rounded : Icons.numbers,
|
||||||
color: Colors.grey.shade700),
|
color: Colors.grey.shade700),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
@ -1660,7 +1662,7 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
|
|||||||
const Divider(),
|
const Divider(),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: isUangBantuan
|
icon: isUangBantuan
|
||||||
? Icons.attach_money
|
? Icons.payment_rounded
|
||||||
: Icons.shopping_bag,
|
: Icons.shopping_bag,
|
||||||
label: 'Jumlah',
|
label: 'Jumlah',
|
||||||
value: jumlahBantuan + (isUangBantuan ? '' : ' item'),
|
value: jumlahBantuan + (isUangBantuan ? '' : ' item'),
|
||||||
|
@ -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_penitipan_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/donatur/views/donatur_riwayat_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_bottom_navigation_bar.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
class DonaturView extends GetView<DonaturDashboardController> {
|
class DonaturView extends GetView<DonaturDashboardController> {
|
||||||
@ -166,301 +167,93 @@ class DonaturView extends GetView<DonaturDashboardController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDrawer(BuildContext context) {
|
Widget _buildDrawer(BuildContext context) {
|
||||||
return Drawer(
|
return Obx(() {
|
||||||
child: Column(
|
Map<String, List<DrawerMenuItem>> menuCategories = {
|
||||||
children: [
|
'Menu Utama': [
|
||||||
Container(
|
DrawerMenuItem(
|
||||||
decoration: BoxDecoration(
|
icon: Icons.dashboard_outlined,
|
||||||
gradient: AppTheme.primaryGradient,
|
activeIcon: Icons.dashboard,
|
||||||
),
|
title: 'Dashboard',
|
||||||
padding: EdgeInsets.only(
|
isSelected: controller.activeTabIndex.value == 0,
|
||||||
top: MediaQuery.of(context).padding.top + 16,
|
onTap: () {
|
||||||
bottom: 24,
|
controller.activeTabIndex.value = 0;
|
||||||
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(
|
DrawerMenuItem(
|
||||||
child: ListView(
|
icon: Icons.description_outlined,
|
||||||
padding: EdgeInsets.zero,
|
activeIcon: Icons.description,
|
||||||
children: [
|
title: 'Skema Bantuan',
|
||||||
_buildMenuCategory('Menu Utama'),
|
isSelected: controller.activeTabIndex.value == 1,
|
||||||
Obx(() => _buildMenuItem(
|
onTap: () {
|
||||||
icon: Icons.dashboard_outlined,
|
controller.activeTabIndex.value = 1;
|
||||||
activeIcon: Icons.dashboard,
|
},
|
||||||
title: 'Dashboard',
|
|
||||||
isSelected: controller.activeTabIndex.value == 0,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.activeTabIndex.value = 0;
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
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(
|
|
||||||
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(
|
|
||||||
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(
|
|
||||||
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(
|
|
||||||
icon: Icons.person_outline,
|
|
||||||
activeIcon: Icons.person,
|
|
||||||
title: 'Profil',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/profile');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.info_outline,
|
|
||||||
activeIcon: Icons.info,
|
|
||||||
title: 'Tentang Kami',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/about');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.logout,
|
|
||||||
title: 'Keluar',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.logout();
|
|
||||||
},
|
|
||||||
isLogout: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
DrawerMenuItem(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
icon: Icons.calendar_today_outlined,
|
||||||
child: Text(
|
activeIcon: Icons.calendar_today,
|
||||||
'© ${DateTime.now().year} DisalurKita',
|
title: 'Jadwal Penyaluran',
|
||||||
style: TextStyle(
|
isSelected: controller.activeTabIndex.value == 2,
|
||||||
fontSize: 12,
|
onTap: () {
|
||||||
color: Colors.grey,
|
controller.activeTabIndex.value = 2;
|
||||||
),
|
},
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
DrawerMenuItem(
|
||||||
|
icon: Icons.add_box_outlined,
|
||||||
|
activeIcon: Icons.add_box,
|
||||||
|
title: 'Penitipan Bantuan',
|
||||||
|
isSelected: controller.activeTabIndex.value == 3,
|
||||||
|
onTap: () {
|
||||||
|
controller.activeTabIndex.value = 3;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.assignment_outlined,
|
||||||
|
activeIcon: Icons.assignment,
|
||||||
|
title: 'Laporan Penyaluran',
|
||||||
|
isSelected: controller.activeTabIndex.value == 4,
|
||||||
|
onTap: () {
|
||||||
|
controller.activeTabIndex.value = 4;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
'Pengaturan': [
|
||||||
);
|
DrawerMenuItem(
|
||||||
}
|
icon: Icons.person_outline,
|
||||||
|
activeIcon: Icons.person,
|
||||||
Widget _buildMenuCategory(String title) {
|
title: 'Profil',
|
||||||
return Padding(
|
onTap: () {
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
Get.toNamed('/profile');
|
||||||
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,
|
|
||||||
),
|
),
|
||||||
),
|
DrawerMenuItem(
|
||||||
trailing: badge != null
|
icon: Icons.info_outline,
|
||||||
? Container(
|
activeIcon: Icons.info,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
title: 'Tentang Kami',
|
||||||
decoration: BoxDecoration(
|
onTap: () {
|
||||||
color: Colors.orange,
|
Get.toNamed('/about');
|
||||||
borderRadius: BorderRadius.circular(10),
|
},
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints(
|
DrawerMenuItem(
|
||||||
minWidth: 20,
|
icon: Icons.logout,
|
||||||
minHeight: 20,
|
title: 'Keluar',
|
||||||
),
|
isLogout: true,
|
||||||
child: Text(
|
onTap: () {
|
||||||
badge,
|
controller.logout();
|
||||||
style: const TextStyle(
|
},
|
||||||
color: Colors.white,
|
),
|
||||||
fontSize: 12,
|
],
|
||||||
),
|
};
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
return AppDrawer(
|
||||||
)
|
nama: controller.nama,
|
||||||
: null,
|
role: 'Donatur',
|
||||||
onTap: onTap,
|
desa: controller.desa,
|
||||||
),
|
avatar: controller.profilePhotoUrl,
|
||||||
);
|
menuItems: const [], // Tidak digunakan karena menggunakan menuCategories
|
||||||
|
menuCategories: menuCategories,
|
||||||
|
onLogout: controller.logout,
|
||||||
|
footerText: '© ${DateTime.now().year} DisalurKita',
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,12 @@ class CalendarViewWidget extends StatelessWidget {
|
|||||||
final JadwalPenyaluranController controller;
|
final JadwalPenyaluranController controller;
|
||||||
final CalendarController _calendarController = CalendarController();
|
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({
|
CalendarViewWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
@ -47,20 +53,135 @@ class CalendarViewWidget extends StatelessWidget {
|
|||||||
topRight: Radius.circular(16),
|
topRight: Radius.circular(16),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
// Title pada baris pertama
|
||||||
Icons.calendar_month_rounded,
|
Row(
|
||||||
color: Colors.white,
|
children: [
|
||||||
size: 28,
|
const Icon(
|
||||||
|
Icons.calendar_month_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
'Kalender Penyaluran',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
const SizedBox(height: 12),
|
||||||
'Kalender Penyaluran',
|
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
// Tombol filter pada baris kedua
|
||||||
fontWeight: FontWeight.bold,
|
Row(
|
||||||
color: Colors.white,
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -71,7 +192,7 @@ class CalendarViewWidget extends StatelessWidget {
|
|||||||
height: MediaQuery.of(context).size.height * 0.65,
|
height: MediaQuery.of(context).size.height * 0.65,
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return SfCalendar(
|
return SfCalendar(
|
||||||
view: CalendarView.month,
|
view: _calendarView.value,
|
||||||
controller: _calendarController,
|
controller: _calendarController,
|
||||||
initialSelectedDate: DateTime.now(),
|
initialSelectedDate: DateTime.now(),
|
||||||
initialDisplayDate: 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),
|
cellBorderColor: Colors.grey.withOpacity(0.2),
|
||||||
todayHighlightColor: AppTheme.primaryColor,
|
todayHighlightColor: AppTheme.primaryColor,
|
||||||
selectionDecoration: BoxDecoration(
|
selectionDecoration: BoxDecoration(
|
||||||
@ -291,6 +448,7 @@ class CalendarViewWidget extends StatelessWidget {
|
|||||||
...controller.jadwalTerlaksana,
|
...controller.jadwalTerlaksana,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Tambahkan filter berdasarkan _showAllSchedules
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime firstDayOfMonth = DateTime(now.year, now.month, 1);
|
DateTime firstDayOfMonth = DateTime(now.year, now.month, 1);
|
||||||
DateTime lastDayOfMonth = DateTime(now.year, now.month + 1, 0);
|
DateTime lastDayOfMonth = DateTime(now.year, now.month + 1, 0);
|
||||||
@ -300,9 +458,14 @@ class CalendarViewWidget extends StatelessWidget {
|
|||||||
DateTime jadwalDate =
|
DateTime jadwalDate =
|
||||||
FormatHelper.toLocalDateTime(jadwal.tanggalPenyaluran!);
|
FormatHelper.toLocalDateTime(jadwal.tanggalPenyaluran!);
|
||||||
|
|
||||||
if (jadwalDate
|
// Filter berdasarkan bulan saat ini jika _showAllSchedules.value = false
|
||||||
.isAfter(firstDayOfMonth.subtract(const Duration(days: 1))) &&
|
bool shouldShow = _showAllSchedules.value ||
|
||||||
jadwalDate.isBefore(lastDayOfMonth.add(const Duration(days: 1)))) {
|
(jadwalDate.isAfter(
|
||||||
|
firstDayOfMonth.subtract(const Duration(days: 1))) &&
|
||||||
|
jadwalDate
|
||||||
|
.isBefore(lastDayOfMonth.add(const Duration(days: 1))));
|
||||||
|
|
||||||
|
if (shouldShow) {
|
||||||
Color appointmentColor;
|
Color appointmentColor;
|
||||||
|
|
||||||
// Periksa status jadwal menggunakan switch-case untuk konsistensi
|
// Periksa status jadwal menggunakan switch-case untuk konsistensi
|
||||||
|
@ -1,55 +1,223 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class GreetingHeader extends StatelessWidget {
|
class GreetingHeader extends StatelessWidget {
|
||||||
final String name;
|
final String name;
|
||||||
final String role;
|
final String role;
|
||||||
final String? desa;
|
final String? desa;
|
||||||
|
final String? nip;
|
||||||
|
final String? profileImageUrl;
|
||||||
|
|
||||||
const GreetingHeader({
|
const GreetingHeader({
|
||||||
super.key,
|
super.key,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.role,
|
required this.role,
|
||||||
this.desa,
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textTheme = Theme.of(context).textTheme;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
gradient: LinearGradient(
|
||||||
borderRadius: BorderRadius.circular(12),
|
colors: [
|
||||||
|
Colors.blue.shade800,
|
||||||
|
Colors.blue.shade600,
|
||||||
|
],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.grey.withAlpha(26), // 0.1 * 255 ≈ 26
|
color: Colors.blue.withOpacity(0.3),
|
||||||
spreadRadius: 1,
|
spreadRadius: 1,
|
||||||
blurRadius: 3,
|
blurRadius: 15,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 5),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
'Selamat Datang, $name!',
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
style: textTheme.headlineSmall?.copyWith(
|
children: [
|
||||||
fontSize: 24,
|
Expanded(
|
||||||
fontWeight: FontWeight.bold,
|
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(
|
||||||
|
name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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: 5),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Wrap(
|
||||||
desa != null && desa!.isNotEmpty
|
spacing: 8,
|
||||||
? 'Kamu Login Sebagai $role $desa.'
|
runSpacing: 8,
|
||||||
: 'Kamu Login Sebagai $role.',
|
children: [
|
||||||
style: textTheme.bodyMedium?.copyWith(
|
Container(
|
||||||
fontSize: 14,
|
padding:
|
||||||
color: Colors.grey[600],
|
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
|
||||||
|
? '$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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -57,6 +57,15 @@ class PetugasDesaDashboardController extends GetxController {
|
|||||||
userProfile['desa']?['nama'] ??
|
userProfile['desa']?['nama'] ??
|
||||||
(userProfile['desa_id'] != null ? 'Desa' : 'Desa');
|
(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
|
// Getter untuk counter dari CounterService
|
||||||
RxInt get jumlahMenunggu => _counterService.jumlahMenunggu;
|
RxInt get jumlahMenunggu => _counterService.jumlahMenunggu;
|
||||||
RxInt get jumlahDiproses => _counterService.jumlahDiproses;
|
RxInt get jumlahDiproses => _counterService.jumlahDiproses;
|
||||||
|
@ -263,7 +263,7 @@ class DaftarDonaturView extends GetView<DonaturController> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.attach_money,
|
Icons.payment_rounded,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
),
|
),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -277,7 +277,7 @@ class DetailDonaturView extends GetView<DonaturController> {
|
|||||||
child: _buildSummaryCard(
|
child: _buildSummaryCard(
|
||||||
title: 'Donasi Uang',
|
title: 'Donasi Uang',
|
||||||
value: jumlahDonasiUang.toString(),
|
value: jumlahDonasiUang.toString(),
|
||||||
icon: Icons.attach_money,
|
icon: Icons.payment_rounded,
|
||||||
color: jenisColor,
|
color: jenisColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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/modules/petugas_desa/controllers/pengaduan_controller.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
import 'package:penyaluran_app/app/utils/format_helper.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:penyaluran_app/app/services/supabase_service.dart';
|
||||||
import 'package:timeline_tile/timeline_tile.dart';
|
import 'package:timeline_tile/timeline_tile.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'dart:io';
|
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:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:penyaluran_app/app/widgets/widgets.dart';
|
import 'package:penyaluran_app/app/widgets/widgets.dart';
|
||||||
|
|
||||||
@ -1391,61 +1387,9 @@ class DetailPengaduanView extends GetView<PengaduanController> {
|
|||||||
? List<String>.from(tindakan.buktiTindakan!)
|
? List<String>.from(tindakan.buktiTindakan!)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Fungsi untuk memilih bukti tindakan
|
|
||||||
Future<void> pickBuktiTindakan(
|
|
||||||
BuildContext dialogContext, bool fromCamera) async {
|
|
||||||
try {
|
|
||||||
final ImagePicker picker = ImagePicker();
|
|
||||||
final XFile? pickedFile = await picker.pickImage(
|
|
||||||
source: fromCamera ? ImageSource.camera : ImageSource.gallery,
|
|
||||||
imageQuality: 80,
|
|
||||||
maxWidth: 1200,
|
|
||||||
maxHeight: 1200,
|
|
||||||
preferredCameraDevice:
|
|
||||||
fromCamera ? CameraDevice.rear : CameraDevice.front,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pickedFile != null) {
|
|
||||||
// Tampilkan loading dialog
|
|
||||||
showDialog(
|
|
||||||
context: dialogContext,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Tambahkan gambar ke daftar
|
|
||||||
buktiTindakanPaths.add(pickedFile.path);
|
|
||||||
|
|
||||||
// Tutup loading dialog
|
|
||||||
Navigator.of(dialogContext, rootNavigator: true).pop();
|
|
||||||
|
|
||||||
// Tutup dialog pilih sumber foto
|
|
||||||
Navigator.of(dialogContext).pop();
|
|
||||||
} catch (e) {
|
|
||||||
// Tutup loading dialog jika terjadi error
|
|
||||||
Navigator.of(dialogContext, rootNavigator: true).pop();
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print('Error picking image: $e');
|
|
||||||
Get.snackbar(
|
|
||||||
'Error',
|
|
||||||
'Gagal mengambil gambar: ${e.toString()}',
|
|
||||||
snackPosition: SnackPosition.TOP,
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fungsi untuk menampilkan dialog pilih sumber foto
|
// Fungsi untuk menampilkan dialog pilih sumber foto
|
||||||
void showPilihSumberFoto(BuildContext dialogContext) {
|
void showPilihSumberFoto(
|
||||||
|
BuildContext dialogContext, Function(BuildContext, bool) pickFunction) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: dialogContext,
|
context: dialogContext,
|
||||||
builder: (innerContext) => AlertDialog(
|
builder: (innerContext) => AlertDialog(
|
||||||
@ -1456,12 +1400,12 @@ class DetailPengaduanView extends GetView<PengaduanController> {
|
|||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.camera_alt),
|
leading: const Icon(Icons.camera_alt),
|
||||||
title: const Text('Kamera'),
|
title: const Text('Kamera'),
|
||||||
onTap: () => pickBuktiTindakan(innerContext, true),
|
onTap: () => pickFunction(innerContext, true),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.photo_library),
|
leading: const Icon(Icons.photo_library),
|
||||||
title: const Text('Galeri'),
|
title: const Text('Galeri'),
|
||||||
onTap: () => pickBuktiTindakan(innerContext, false),
|
onTap: () => pickFunction(innerContext, false),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -1473,6 +1417,61 @@ class DetailPengaduanView extends GetView<PengaduanController> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) =>
|
builder: (dialogContext) =>
|
||||||
StatefulBuilder(builder: (stateContext, setState) {
|
StatefulBuilder(builder: (stateContext, setState) {
|
||||||
|
// Fungsi untuk memilih bukti tindakan dipindahkan ke dalam StatefulBuilder
|
||||||
|
Future<void> pickBuktiTindakan(
|
||||||
|
BuildContext innerContext, bool fromCamera) async {
|
||||||
|
try {
|
||||||
|
final ImagePicker picker = ImagePicker();
|
||||||
|
final XFile? pickedFile = await picker.pickImage(
|
||||||
|
source: fromCamera ? ImageSource.camera : ImageSource.gallery,
|
||||||
|
imageQuality: 80,
|
||||||
|
maxWidth: 1200,
|
||||||
|
maxHeight: 1200,
|
||||||
|
preferredCameraDevice:
|
||||||
|
fromCamera ? CameraDevice.rear : CameraDevice.front,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedFile != null) {
|
||||||
|
// Tampilkan loading dialog
|
||||||
|
showDialog(
|
||||||
|
context: innerContext,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Tambahkan gambar ke daftar dan update state
|
||||||
|
setState(() {
|
||||||
|
buktiTindakanPaths.add(pickedFile.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tutup loading dialog
|
||||||
|
Navigator.of(innerContext, rootNavigator: true).pop();
|
||||||
|
|
||||||
|
// Tutup dialog pilih sumber foto
|
||||||
|
Navigator.of(innerContext).pop();
|
||||||
|
} catch (e) {
|
||||||
|
// Tutup loading dialog jika terjadi error
|
||||||
|
Navigator.of(innerContext, rootNavigator: true).pop();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error picking image: $e');
|
||||||
|
Get.snackbar(
|
||||||
|
'Error',
|
||||||
|
'Gagal mengambil gambar: ${e.toString()}',
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -1581,7 +1580,8 @@ class DetailPengaduanView extends GetView<PengaduanController> {
|
|||||||
children: [
|
children: [
|
||||||
if (buktiTindakanPaths.isEmpty)
|
if (buktiTindakanPaths.isEmpty)
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => showPilihSumberFoto(stateContext),
|
onTap: () => showPilihSumberFoto(
|
||||||
|
stateContext, pickBuktiTindakan),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 150,
|
height: 150,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -1619,15 +1619,16 @@ class DetailPengaduanView extends GetView<PengaduanController> {
|
|||||||
height: 100,
|
height: 100,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: buktiTindakanPaths
|
itemCount: buktiTindakanPaths.length +
|
||||||
.length, //tombol tambah jika tidak selesai
|
1, // Tambah 1 untuk tombol tambah
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index ==
|
if (index ==
|
||||||
buktiTindakanPaths.length) {
|
buktiTindakanPaths.length) {
|
||||||
// Tombol tambah foto
|
// Tombol tambah foto
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => showPilihSumberFoto(
|
onTap: () => showPilihSumberFoto(
|
||||||
stateContext),
|
stateContext,
|
||||||
|
pickBuktiTindakan),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 100,
|
width: 100,
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
|
@ -675,7 +675,7 @@ class _KonfirmasiPenerimaPageState extends State<KonfirmasiPenerimaPage> {
|
|||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
isUang ? Icons.attach_money : Icons.scale_outlined,
|
isUang ? Icons.payment_rounded : Icons.scale_outlined,
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
|
@ -521,7 +521,15 @@ class PengaduanView extends GetView<PengaduanController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
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(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.grey.shade800,
|
color: Colors.grey.shade800,
|
||||||
|
@ -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/modules/petugas_desa/views/pengaduan_view.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.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/modules/petugas_desa/controllers/riwayat_stok_controller.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
|
||||||
|
|
||||||
class PetugasDesaView extends GetView<PetugasDesaController> {
|
class PetugasDesaView extends GetView<PetugasDesaController> {
|
||||||
const PetugasDesaView({super.key});
|
const PetugasDesaView({super.key});
|
||||||
@ -190,360 +191,119 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDrawer(BuildContext context) {
|
Widget _buildDrawer(BuildContext context) {
|
||||||
return Drawer(
|
return Obx(() {
|
||||||
child: Column(
|
Map<String, List<DrawerMenuItem>> menuCategories = {
|
||||||
children: [
|
'Menu Utama': [
|
||||||
Container(
|
DrawerMenuItem(
|
||||||
decoration: BoxDecoration(
|
icon: Icons.dashboard_outlined,
|
||||||
gradient: AppTheme.primaryGradient,
|
activeIcon: Icons.dashboard,
|
||||||
),
|
title: 'Dashboard',
|
||||||
padding: EdgeInsets.only(
|
isSelected: controller.activeTabIndex.value == 0,
|
||||||
top: MediaQuery.of(context).padding.top + 16,
|
onTap: () {
|
||||||
bottom: 24,
|
controller.activeTabIndex.value = 0;
|
||||||
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(
|
DrawerMenuItem(
|
||||||
child: ListView(
|
icon: Icons.volunteer_activism_outlined,
|
||||||
padding: EdgeInsets.zero,
|
activeIcon: Icons.volunteer_activism,
|
||||||
children: [
|
title: 'Penyaluran',
|
||||||
_buildMenuCategory('Menu Utama'),
|
isSelected: controller.activeTabIndex.value == 1,
|
||||||
Obx(() => _buildMenuItem(
|
badgeCount: controller.jumlahMenunggu.value > 0
|
||||||
icon: Icons.dashboard_outlined,
|
? controller.jumlahMenunggu.value
|
||||||
activeIcon: Icons.dashboard,
|
: null,
|
||||||
title: 'Dashboard',
|
badgeColor: Colors.green,
|
||||||
isSelected: controller.activeTabIndex.value == 0,
|
onTap: () {
|
||||||
onTap: () {
|
controller.activeTabIndex.value = 1;
|
||||||
Navigator.pop(context);
|
},
|
||||||
controller.changeTab(0);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
icon: Icons.handshake_outlined,
|
|
||||||
activeIcon: Icons.handshake,
|
|
||||||
title: 'Penyaluran',
|
|
||||||
isSelected: controller.activeTabIndex.value == 1,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.changeTab(1);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
icon: Icons.inventory_2_outlined,
|
|
||||||
activeIcon: Icons.inventory_2,
|
|
||||||
title: 'Penitipan',
|
|
||||||
isSelected: controller.activeTabIndex.value == 2,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.changeTab(2);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
icon: Icons.warning_amber_outlined,
|
|
||||||
activeIcon: Icons.warning_amber,
|
|
||||||
title: 'Pengaduan',
|
|
||||||
isSelected: controller.activeTabIndex.value == 3,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.changeTab(3);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
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');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.people_outlined,
|
|
||||||
activeIcon: Icons.people,
|
|
||||||
title: 'Kelola Donatur',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/daftar-donatur');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_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(
|
|
||||||
icon: Icons.person_outline,
|
|
||||||
activeIcon: Icons.person,
|
|
||||||
title: 'Profil',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/profile');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.info_outline,
|
|
||||||
activeIcon: Icons.info,
|
|
||||||
title: 'Tentang Kami',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/about');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.logout,
|
|
||||||
title: 'Keluar',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.logout();
|
|
||||||
},
|
|
||||||
isLogout: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
DrawerMenuItem(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
icon: Icons.inbox_outlined,
|
||||||
child: Text(
|
activeIcon: Icons.inbox,
|
||||||
'© ${DateTime.now().year} DisalurKita',
|
title: 'Penitipan',
|
||||||
style: TextStyle(
|
isSelected: controller.activeTabIndex.value == 2,
|
||||||
fontSize: 12,
|
onTap: () {
|
||||||
color: Colors.grey,
|
controller.activeTabIndex.value = 2;
|
||||||
),
|
},
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
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: () {
|
||||||
|
controller.activeTabIndex.value = 3;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.inventory_outlined,
|
||||||
|
activeIcon: Icons.inventory,
|
||||||
|
title: 'Stok Bantuan',
|
||||||
|
isSelected: controller.activeTabIndex.value == 4,
|
||||||
|
onTap: () {
|
||||||
|
controller.activeTabIndex.value = 4;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
'Pengaturan': [
|
||||||
);
|
DrawerMenuItem(
|
||||||
}
|
icon: Icons.notifications_outlined,
|
||||||
|
activeIcon: Icons.notifications,
|
||||||
Widget _buildMenuCategory(String title) {
|
title: 'Notifikasi',
|
||||||
return Padding(
|
badgeCount: controller.jumlahNotifikasiBelumDibaca.value > 0
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
? controller.jumlahNotifikasiBelumDibaca.value
|
||||||
child: Text(
|
: null,
|
||||||
title,
|
badgeColor: Colors.red,
|
||||||
style: TextStyle(
|
onTap: () {
|
||||||
fontSize: 12,
|
Navigator.push(
|
||||||
fontWeight: FontWeight.bold,
|
context,
|
||||||
color: Colors.grey[600],
|
MaterialPageRoute(
|
||||||
),
|
builder: (context) => const NotifikasiView(),
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
),
|
),
|
||||||
),
|
DrawerMenuItem(
|
||||||
onTap: onTap,
|
icon: Icons.person_outline,
|
||||||
shape: RoundedRectangleBorder(
|
activeIcon: Icons.person,
|
||||||
borderRadius: BorderRadius.circular(8),
|
title: 'Profil',
|
||||||
),
|
onTap: () {
|
||||||
visualDensity: VisualDensity.compact,
|
Get.toNamed('/profile');
|
||||||
selectedTileColor: AppTheme.primaryColor.withOpacity(0.1),
|
},
|
||||||
selected: isSelected,
|
),
|
||||||
),
|
DrawerMenuItem(
|
||||||
);
|
icon: Icons.info_outline,
|
||||||
|
activeIcon: Icons.info,
|
||||||
|
title: 'Tentang Kami',
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed('/about');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.logout,
|
||||||
|
title: 'Keluar',
|
||||||
|
isLogout: true,
|
||||||
|
onTap: () {
|
||||||
|
controller.logout();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
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() {
|
Widget _buildBottomNavigationBar() {
|
||||||
|
@ -347,10 +347,16 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
...controller.daftarStokBantuan.map((stok) {
|
...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(
|
return DropdownMenuItem(
|
||||||
value: stok.id,
|
value: stok.id,
|
||||||
child: Text(
|
child: Text(
|
||||||
stok.nama ?? '-',
|
'${stok.nama ?? '-'} ($formattedJumlah)',
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -380,6 +386,9 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
|
|||||||
: 'Tidak diketahui';
|
: 'Tidak diketahui';
|
||||||
final stokBantuanSatuan =
|
final stokBantuanSatuan =
|
||||||
riwayat.stokBantuan != null ? riwayat.stokBantuan!['satuan'] ?? '' : '';
|
riwayat.stokBantuan != null ? riwayat.stokBantuan!['satuan'] ?? '' : '';
|
||||||
|
final bool isUang = riwayat.stokBantuan != null
|
||||||
|
? riwayat.stokBantuan!['is_uang'] ?? false
|
||||||
|
: false;
|
||||||
final sumberLabels = {
|
final sumberLabels = {
|
||||||
'penitipan': 'Penitipan',
|
'penitipan': 'Penitipan',
|
||||||
'penerimaan': 'Penerimaan',
|
'penerimaan': 'Penerimaan',
|
||||||
@ -464,7 +473,9 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'${riwayat.jumlah?.toStringAsFixed(0) ?? '0'} $stokBantuanSatuan',
|
isUang
|
||||||
|
? FormatHelper.formatRupiah(riwayat.jumlah ?? 0)
|
||||||
|
: '${riwayat.jumlah?.toStringAsFixed(0) ?? '0'} $stokBantuanSatuan',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: isPenambahan ? Colors.green : Colors.red,
|
color: isPenambahan ? Colors.green : Colors.red,
|
||||||
@ -761,10 +772,13 @@ class RiwayatStokView extends GetView<RiwayatStokController> {
|
|||||||
value: controller.selectedStokBantuan.value,
|
value: controller.selectedStokBantuan.value,
|
||||||
items: controller.daftarStokBantuan
|
items: controller.daftarStokBantuan
|
||||||
.map((StokBantuanModel stok) {
|
.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>(
|
return DropdownMenuItem<StokBantuanModel>(
|
||||||
value: stok,
|
value: stok,
|
||||||
child: Text(
|
child: Text('${stok.nama} ($formattedStok)'),
|
||||||
'${stok.nama} (${stok.totalStok} ${stok.satuan})'),
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (StokBantuanModel? value) {
|
onChanged: (StokBantuanModel? value) {
|
||||||
|
@ -24,7 +24,8 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
|||||||
},
|
},
|
||||||
backgroundColor: AppTheme.primaryColor,
|
backgroundColor: AppTheme.primaryColor,
|
||||||
icon: const Icon(Icons.add, color: Colors.white),
|
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,
|
elevation: 2,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -636,7 +637,7 @@ class StokBantuanView extends GetView<StokBantuanController> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Tambah Stok Bantuan',
|
'Tambah Jenis Stok Bantuan',
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -363,7 +363,7 @@ class TambahPenyaluranView extends GetView<JadwalPenyaluranController> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
isUang.value
|
isUang.value
|
||||||
? const Icon(Icons.attach_money)
|
? const Icon(Icons.payment_rounded)
|
||||||
: const Icon(Icons.inventory_2),
|
: const Icon(Icons.inventory_2),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -7,8 +7,6 @@ import 'package:penyaluran_app/app/theme/app_theme.dart';
|
|||||||
import 'package:penyaluran_app/app/utils/format_helper.dart';
|
import 'package:penyaluran_app/app/utils/format_helper.dart';
|
||||||
import 'package:timeline_tile/timeline_tile.dart';
|
import 'package:timeline_tile/timeline_tile.dart';
|
||||||
import 'package:image_picker/image_picker.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 'dart:io';
|
||||||
import 'package:penyaluran_app/app/widgets/widgets.dart';
|
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),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -926,7 +994,12 @@ class WargaDetailPengaduanView extends GetView<WargaDashboardController> {
|
|||||||
'Nama Penyaluran', pengaduan.namaPenyaluran, Icons.assignment),
|
'Nama Penyaluran', pengaduan.namaPenyaluran, Icons.assignment),
|
||||||
_buildInfoItem(
|
_buildInfoItem(
|
||||||
'Jenis Bantuan', pengaduan.jenisBantuan, Icons.category),
|
'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),
|
Icons.shopping_basket),
|
||||||
_buildInfoItem(
|
_buildInfoItem(
|
||||||
'Deskripsi', pengaduan.deskripsiPenyaluran, Icons.description),
|
'Deskripsi', pengaduan.deskripsiPenyaluran, Icons.description),
|
||||||
|
@ -23,54 +23,6 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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(),
|
_buildWelcomeSection(),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildStatisticSection(),
|
_buildStatisticSection(),
|
||||||
@ -540,7 +492,7 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
|
|||||||
children: [
|
children: [
|
||||||
if (totalUang > 0)
|
if (totalUang > 0)
|
||||||
_buildSummaryItem(
|
_buildSummaryItem(
|
||||||
icon: Icons.attach_money,
|
icon: Icons.payment_rounded,
|
||||||
color: Colors.green,
|
color: Colors.green,
|
||||||
title: 'Total Bantuan Uang',
|
title: 'Total Bantuan Uang',
|
||||||
value: FormatHelper.formatRupiah(totalUang),
|
value: FormatHelper.formatRupiah(totalUang),
|
||||||
|
@ -158,18 +158,67 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
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 &&
|
if (penyaluran.deskripsiPenyaluran != null &&
|
||||||
penyaluran.deskripsiPenyaluran!.isNotEmpty)
|
penyaluran.deskripsiPenyaluran!.isNotEmpty)
|
||||||
Padding(
|
Container(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding:
|
||||||
child: Text(
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
penyaluran.deskripsiPenyaluran!,
|
decoration: BoxDecoration(
|
||||||
style: const TextStyle(
|
color: Colors.white,
|
||||||
color: Colors.black,
|
borderRadius: BorderRadius.circular(12),
|
||||||
fontSize: 16,
|
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.black87,
|
||||||
|
fontSize: 15,
|
||||||
|
height: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -197,7 +246,7 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
penyaluran.isUang == true
|
penyaluran.isUang == true
|
||||||
? Icons.attach_money
|
? Icons.payment_rounded
|
||||||
: Icons.inventory_2,
|
: Icons.inventory_2,
|
||||||
color: penyaluran.isUang == true
|
color: penyaluran.isUang == true
|
||||||
? Colors.green
|
? Colors.green
|
||||||
@ -736,13 +785,17 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
// Pastikan menggunakan data terbaru dari model dan cetak ke log untuk debugging
|
// Pastikan menggunakan data terbaru dari model dan cetak ke log untuk debugging
|
||||||
final qrData = penyaluran.qrCodeHash ?? 'invalid-qr-code';
|
final qrData = penyaluran.qrCodeHash ?? 'invalid-qr-code';
|
||||||
print('penyaluran.statusPenyaluran ${penyaluran.statusPenyaluran}');
|
print('penyaluran.statusPenyaluran ${penyaluran.statusPenyaluran}');
|
||||||
|
print('penyaluran.statusPenerimaan ${penyaluran.statusPenerimaan}');
|
||||||
|
|
||||||
// Cek status penyaluran untuk disabled state
|
// Cek status penyaluran dan penerimaan untuk disabled state
|
||||||
final bool isDisabled = penyaluran.statusPenyaluran != null &&
|
final bool isDisabled = (penyaluran.statusPenyaluran != null &&
|
||||||
(penyaluran.statusPenyaluran!.toUpperCase() == 'DIJADWALKAN' ||
|
(penyaluran.statusPenyaluran!.toUpperCase() == 'DIJADWALKAN' ||
|
||||||
penyaluran.statusPenyaluran!.toUpperCase() == 'DISETUJUI' ||
|
penyaluran.statusPenyaluran!.toUpperCase() == 'DISETUJUI' ||
|
||||||
penyaluran.statusPenyaluran!.toUpperCase() == 'BATALTERLAKSANA' ||
|
penyaluran.statusPenyaluran!.toUpperCase() ==
|
||||||
penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA');
|
'BATALTERLAKSANA' ||
|
||||||
|
penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA')) ||
|
||||||
|
(penyaluran.statusPenerimaan != null &&
|
||||||
|
penyaluran.statusPenerimaan!.toUpperCase() == 'DITERIMA');
|
||||||
|
|
||||||
final String statusMessage;
|
final String statusMessage;
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
@ -752,6 +805,9 @@ class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
} else if (penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA') {
|
} else if (penyaluran.statusPenyaluran!.toUpperCase() == 'TERLAKSANA') {
|
||||||
statusMessage =
|
statusMessage =
|
||||||
'QR Code sudah digunakan pada penyaluran yang telah terlaksana';
|
'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 {
|
} else {
|
||||||
statusMessage =
|
statusMessage =
|
||||||
'QR Code belum dapat digunakan karena penyaluran belum terlaksana';
|
'QR Code belum dapat digunakan karena penyaluran belum terlaksana';
|
||||||
|
@ -15,10 +15,6 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug print untuk melihat jumlah item
|
|
||||||
print(
|
|
||||||
'DEBUG: Jumlah penerimaan tersedia: ${controller.penerimaPenyaluran.length}');
|
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
// Tambahkan delay untuk memastikan refresh indicator terlihat
|
// Tambahkan delay untuk memastikan refresh indicator terlihat
|
||||||
@ -102,9 +98,7 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPenerimaanList(BuildContext context) {
|
Widget _buildPenerimaanList(BuildContext context) {
|
||||||
// Debug print untuk melihat jumlah item
|
// Menggunakan CustomScrollView dan SliverList untuk layout yang lebih stabil
|
||||||
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(
|
return CustomScrollView(
|
||||||
@ -122,9 +116,6 @@ class WargaPenerimaanView extends GetView<WargaDashboardController> {
|
|||||||
|
|
||||||
final item = controller.penerimaPenyaluran[index];
|
final item = controller.penerimaPenyaluran[index];
|
||||||
|
|
||||||
// Debug
|
|
||||||
print('DEBUG: Membangun item $index dengan id: ${item.id}');
|
|
||||||
|
|
||||||
// Menggunakan SizedBox untuk memberikan batas lebar dan tinggi
|
// Menggunakan SizedBox untuk memberikan batas lebar dan tinggi
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
|
@ -289,15 +289,22 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoItem(
|
child: _buildInfoItem(
|
||||||
'Jenis',
|
'Jenis',
|
||||||
item.jenisBantuan ??
|
item.stokBantuan?[
|
||||||
|
'nama'] ??
|
||||||
|
item.jenisBantuan ??
|
||||||
"Tidak tersedia",
|
"Tidak tersedia",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildInfoItem(
|
child: _buildInfoItem(
|
||||||
'Jumlah',
|
'Jumlah',
|
||||||
item.jumlahBantuan ??
|
item.isUang
|
||||||
"Tidak tersedia",
|
? FormatHelper.formatRupiah(
|
||||||
|
double.tryParse(item
|
||||||
|
.jumlahBantuan
|
||||||
|
.toString()) ??
|
||||||
|
0)
|
||||||
|
: '${item.jumlahBantuan} ${item.stokBantuan?['satuan'] ?? ''}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -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_penerimaan_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_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_bottom_navigation_bar.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
|
||||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
class WargaView extends GetView<WargaDashboardController> {
|
class WargaView extends GetView<WargaDashboardController> {
|
||||||
@ -133,276 +134,83 @@ class WargaView extends GetView<WargaDashboardController> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Drawer(
|
return Obx(() {
|
||||||
child: Column(
|
Map<String, List<DrawerMenuItem>> menuCategories = {
|
||||||
children: [
|
'Menu Utama': [
|
||||||
Container(
|
DrawerMenuItem(
|
||||||
decoration: BoxDecoration(
|
icon: Icons.dashboard_outlined,
|
||||||
gradient: AppTheme.primaryGradient,
|
activeIcon: Icons.dashboard,
|
||||||
),
|
title: 'Dashboard',
|
||||||
padding: EdgeInsets.only(
|
isSelected: controller.activeTabIndex.value == 0,
|
||||||
top: MediaQuery.of(context).padding.top + 16,
|
onTap: () {
|
||||||
bottom: 24,
|
controller.activeTabIndex.value = 0;
|
||||||
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(
|
DrawerMenuItem(
|
||||||
child: ListView(
|
icon: Icons.volunteer_activism_outlined,
|
||||||
padding: EdgeInsets.zero,
|
activeIcon: Icons.volunteer_activism,
|
||||||
children: [
|
title: 'Penerimaan Bantuan',
|
||||||
_buildMenuCategory('Menu Utama'),
|
isSelected: controller.activeTabIndex.value == 1,
|
||||||
Obx(() => _buildMenuItem(
|
badgeCount: controller.totalPenyaluranDiterima.value > 0
|
||||||
icon: Icons.dashboard_outlined,
|
? controller.totalPenyaluranDiterima.value
|
||||||
activeIcon: Icons.dashboard,
|
: null,
|
||||||
title: 'Dashboard',
|
badgeColor: Colors.green,
|
||||||
isSelected: controller.activeTabIndex.value == 0,
|
onTap: () {
|
||||||
onTap: () {
|
controller.activeTabIndex.value = 1;
|
||||||
Navigator.pop(context);
|
},
|
||||||
controller.changeTab(0);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
icon: Icons.volunteer_activism_outlined,
|
|
||||||
activeIcon: Icons.volunteer_activism,
|
|
||||||
title: 'Penerimaan',
|
|
||||||
isSelected: controller.activeTabIndex.value == 1,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.changeTab(1);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Obx(() => _buildMenuItem(
|
|
||||||
icon: Icons.report_problem_outlined,
|
|
||||||
activeIcon: Icons.report_problem,
|
|
||||||
title: 'Pengaduan',
|
|
||||||
isSelected: controller.activeTabIndex.value == 2,
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.changeTab(2);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
_buildMenuCategory('Pengaturan'),
|
|
||||||
_buildMenuItem(
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.info_outline,
|
|
||||||
activeIcon: Icons.info,
|
|
||||||
title: 'Tentang Kami',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/about');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_buildMenuItem(
|
|
||||||
icon: Icons.logout,
|
|
||||||
title: 'Keluar',
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
controller.logout();
|
|
||||||
},
|
|
||||||
isLogout: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
DrawerMenuItem(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
icon: Icons.report_problem_outlined,
|
||||||
child: Text(
|
activeIcon: Icons.report_problem,
|
||||||
'© ${DateTime.now().year} DisalurKita',
|
title: 'Pengaduan',
|
||||||
style: TextStyle(
|
isSelected: controller.activeTabIndex.value == 2,
|
||||||
fontSize: 12,
|
badgeCount: controller.totalPengaduanProses.value > 0
|
||||||
color: Colors.grey,
|
? controller.totalPengaduanProses.value
|
||||||
),
|
: null,
|
||||||
textAlign: TextAlign.center,
|
badgeColor: Colors.orange,
|
||||||
),
|
onTap: () {
|
||||||
|
controller.activeTabIndex.value = 2;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
'Pengaturan': [
|
||||||
);
|
DrawerMenuItem(
|
||||||
}
|
icon: Icons.person_outline,
|
||||||
|
activeIcon: Icons.person,
|
||||||
Widget _buildMenuCategory(String title) {
|
title: 'Profil',
|
||||||
return Padding(
|
onTap: () {
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
Get.toNamed('/profile');
|
||||||
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,
|
|
||||||
),
|
),
|
||||||
),
|
DrawerMenuItem(
|
||||||
trailing: badge != null
|
icon: Icons.info_outline,
|
||||||
? Container(
|
activeIcon: Icons.info,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
title: 'Tentang Kami',
|
||||||
decoration: BoxDecoration(
|
onTap: () {
|
||||||
color: Colors.orange,
|
Get.toNamed('/about');
|
||||||
borderRadius: BorderRadius.circular(10),
|
},
|
||||||
),
|
),
|
||||||
constraints: BoxConstraints(
|
DrawerMenuItem(
|
||||||
minWidth: 20,
|
icon: Icons.logout,
|
||||||
minHeight: 20,
|
title: 'Keluar',
|
||||||
),
|
isLogout: true,
|
||||||
child: Text(
|
onTap: () {
|
||||||
badge,
|
controller.logout();
|
||||||
style: TextStyle(
|
},
|
||||||
color: Colors.white,
|
),
|
||||||
fontSize: 12,
|
],
|
||||||
),
|
};
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
return AppDrawer(
|
||||||
)
|
nama: controller.nama,
|
||||||
: null,
|
role: 'Warga',
|
||||||
onTap: onTap,
|
desa: controller.desa,
|
||||||
),
|
avatar: controller.fotoProfil.value,
|
||||||
);
|
menuItems: const [], // Tidak digunakan karena menggunakan menuCategories
|
||||||
|
menuCategories: menuCategories,
|
||||||
|
onLogout: controller.logout,
|
||||||
|
footerText: '© ${DateTime.now().year} DisalurKita',
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ abstract class Routes {
|
|||||||
static const riwayatPengaduan = _Paths.riwayatPengaduan;
|
static const riwayatPengaduan = _Paths.riwayatPengaduan;
|
||||||
static const qrScanner = _Paths.qrScanner;
|
static const qrScanner = _Paths.qrScanner;
|
||||||
static const konfirmasiPenerimaQr = _Paths.konfirmasiPenerimaQr;
|
static const konfirmasiPenerimaQr = _Paths.konfirmasiPenerimaQr;
|
||||||
|
static const jadwalPenyaluran = _Paths.jadwalPenyaluran;
|
||||||
static const PENGADUAN = '/pengaduan';
|
static const PENGADUAN = '/pengaduan';
|
||||||
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
|
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
|
||||||
static const PENGADUAN_DETAIL = '/pengaduan-detail';
|
static const PENGADUAN_DETAIL = '/pengaduan-detail';
|
||||||
@ -91,6 +92,7 @@ abstract class _Paths {
|
|||||||
static const riwayatPengaduan = '/petugas-desa/riwayat-pengaduan';
|
static const riwayatPengaduan = '/petugas-desa/riwayat-pengaduan';
|
||||||
static const qrScanner = '/petugas-desa/qr-scanner';
|
static const qrScanner = '/petugas-desa/qr-scanner';
|
||||||
static const konfirmasiPenerimaQr = '/petugas-desa/konfirmasi-penerima/:id';
|
static const konfirmasiPenerimaQr = '/petugas-desa/konfirmasi-penerima/:id';
|
||||||
|
static const jadwalPenyaluran = '/petugas-desa/jadwal-penyaluran';
|
||||||
static const PENGADUAN = '/pengaduan';
|
static const PENGADUAN = '/pengaduan';
|
||||||
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
|
static const TAMBAH_PENGADUAN = '/tambah-pengaduan';
|
||||||
static const PENGADUAN_DETAIL = '/pengaduan-detail';
|
static const PENGADUAN_DETAIL = '/pengaduan-detail';
|
||||||
|
@ -2370,4 +2370,39 @@ class SupabaseService extends GetxService {
|
|||||||
return null;
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ class AppDrawer extends StatelessWidget {
|
|||||||
final int? notificationCount;
|
final int? notificationCount;
|
||||||
final VoidCallback onLogout;
|
final VoidCallback onLogout;
|
||||||
final List<DrawerMenuItem> menuItems;
|
final List<DrawerMenuItem> menuItems;
|
||||||
|
final Map<String, List<DrawerMenuItem>>? menuCategories;
|
||||||
|
final String? footerText;
|
||||||
|
final Widget? headerExtraContent;
|
||||||
|
|
||||||
const AppDrawer({
|
const AppDrawer({
|
||||||
super.key,
|
super.key,
|
||||||
@ -20,25 +23,101 @@ class AppDrawer extends StatelessWidget {
|
|||||||
this.notificationCount,
|
this.notificationCount,
|
||||||
required this.onLogout,
|
required this.onLogout,
|
||||||
required this.menuItems,
|
required this.menuItems,
|
||||||
|
this.menuCategories,
|
||||||
|
this.footerText,
|
||||||
|
this.headerExtraContent,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Drawer(
|
return Drawer(
|
||||||
child: ListView(
|
child: Column(
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
_buildHeader(context),
|
||||||
decoration: BoxDecoration(
|
if (menuCategories != null && menuCategories!.isNotEmpty)
|
||||||
gradient: AppTheme.primaryGradient,
|
_buildCategorizedMenu()
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
...menuItems.map((item) => _buildMenuItem(context, item)),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.person_outline),
|
||||||
|
title: const Text('Profil'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Get.toNamed('/profile');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.info_outline),
|
||||||
|
title: const Text('Tentang Kami'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
Get.toNamed('/about');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.logout),
|
||||||
|
title: const Text('Keluar'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
onLogout();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
if (footerText != null)
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
children: [
|
child: Text(
|
||||||
CircleAvatar(
|
footerText!,
|
||||||
radius: 30,
|
style: const TextStyle(
|
||||||
backgroundColor: AppTheme.primaryColor.withOpacity(0.2),
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
backgroundImage: avatar != null && avatar!.isNotEmpty
|
||||||
? NetworkImage(avatar!)
|
? NetworkImage(avatar!)
|
||||||
: null,
|
: null,
|
||||||
@ -47,122 +126,249 @@ class AppDrawer extends StatelessWidget {
|
|||||||
nama.isNotEmpty
|
nama.isNotEmpty
|
||||||
? nama.substring(0, 1).toUpperCase()
|
? nama.substring(0, 1).toUpperCase()
|
||||||
: '?',
|
: '?',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: AppTheme.primaryColor,
|
color: Colors.blue.shade700,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
),
|
||||||
Text(
|
const SizedBox(width: 15),
|
||||||
nama,
|
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(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 18,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
desa != null ? '$role - $desa' : role,
|
if (desa != null) ...[
|
||||||
style: TextStyle(
|
const SizedBox(width: 8),
|
||||||
color: Colors.white.withOpacity(0.8),
|
Container(
|
||||||
fontSize: 14,
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
),
|
|
||||||
...menuItems.map((item) => _buildMenuItem(context, item)),
|
|
||||||
const Divider(),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.person_outline),
|
|
||||||
title: const Text('Profil'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed('/profile');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.settings_outlined),
|
|
||||||
title: const Text('Pengaturan'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
// TODO: Navigasi ke halaman pengaturan
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.logout),
|
|
||||||
title: const Text('Keluar'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
onLogout();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
if (headerExtraContent != null) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
headerExtraContent!,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMenuItem(BuildContext context, DrawerMenuItem item) {
|
Widget _buildCategorizedMenu() {
|
||||||
return ListTile(
|
return Expanded(
|
||||||
leading: item.badgeCount != null && item.badgeCount! > 0
|
child: ListView(
|
||||||
? Stack(
|
padding: EdgeInsets.zero,
|
||||||
alignment: Alignment.center,
|
children: [
|
||||||
children: [
|
...menuCategories!.entries.map((entry) => Column(
|
||||||
Icon(item.icon),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Positioned(
|
children: [
|
||||||
top: 0,
|
_buildMenuCategory(entry.key),
|
||||||
right: 0,
|
...entry.value.map((item) => _buildMenuItem(null, item)),
|
||||||
child: Container(
|
],
|
||||||
padding: const EdgeInsets.all(2),
|
)),
|
||||||
decoration: BoxDecoration(
|
],
|
||||||
color: item.badgeColor ?? Colors.red,
|
),
|
||||||
borderRadius: BorderRadius.circular(10),
|
);
|
||||||
),
|
}
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minWidth: 12,
|
Widget _buildMenuCategory(String title) {
|
||||||
minHeight: 12,
|
return Padding(
|
||||||
),
|
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
item.badgeCount.toString(),
|
title,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
fontSize: 12,
|
||||||
fontSize: 8,
|
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.isSelected
|
||||||
|
? (item.activeIcon ?? item.icon)
|
||||||
|
: item.icon,
|
||||||
|
color: item.isSelected
|
||||||
|
? AppTheme.primaryColor
|
||||||
|
: (item.isLogout ? Colors.red : null),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.badgeColor ?? Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
item.badgeCount.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
)
|
||||||
)
|
: Icon(
|
||||||
: Icon(item.icon),
|
item.isSelected ? (item.activeIcon ?? item.icon) : item.icon,
|
||||||
title: Text(item.title),
|
color: item.isSelected
|
||||||
selected: item.isSelected,
|
? AppTheme.primaryColor
|
||||||
selectedColor: AppTheme.primaryColor,
|
: (item.isLogout ? Colors.red : null),
|
||||||
onTap: () {
|
),
|
||||||
Navigator.pop(context);
|
title: Text(
|
||||||
item.onTap();
|
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 {
|
class DrawerMenuItem {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
final IconData? activeIcon;
|
||||||
final String title;
|
final String title;
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
final int? badgeCount;
|
final int? badgeCount;
|
||||||
final Color? badgeColor;
|
final Color? badgeColor;
|
||||||
|
final bool isLogout;
|
||||||
|
final Widget? trailing;
|
||||||
|
|
||||||
DrawerMenuItem({
|
DrawerMenuItem({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
this.activeIcon,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.isSelected = false,
|
this.isSelected = false,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
this.badgeCount,
|
this.badgeCount,
|
||||||
this.badgeColor,
|
this.badgeColor,
|
||||||
|
this.isLogout = false,
|
||||||
|
this.trailing,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class BantuanCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
item.isUang == true
|
item.isUang == true
|
||||||
? Icons.attach_money
|
? Icons.payment_rounded
|
||||||
: Icons.inventory_2,
|
: Icons.inventory_2,
|
||||||
color: item.isUang == true ? Colors.green : Colors.blue,
|
color: item.isUang == true ? Colors.green : Colors.blue,
|
||||||
size: 28,
|
size: 28,
|
||||||
@ -81,15 +81,36 @@ class BantuanCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
if (item.namaPenyaluran != null &&
|
||||||
item.kategoriNama ?? 'Bantuan',
|
item.namaPenyaluran!.isNotEmpty)
|
||||||
style: TextStyle(
|
Text(
|
||||||
color: Colors.grey.shade700,
|
item.namaPenyaluran!,
|
||||||
fontSize: 14,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
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: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Row(
|
Row(
|
||||||
@ -229,12 +250,26 @@ class BantuanCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
item.isUang == true
|
item.isUang == true
|
||||||
? Icons.attach_money
|
? Icons.payment_rounded
|
||||||
: Icons.inventory_2,
|
: Icons.inventory_2,
|
||||||
color: item.isUang == true
|
color: item.isUang == true
|
||||||
? Colors.green.shade700
|
? Colors.green.shade700
|
||||||
: Colors.blue.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,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@ -169,6 +169,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -241,6 +249,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
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:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -96,6 +96,7 @@ dependencies:
|
|||||||
cached_network_image: ^3.3.1
|
cached_network_image: ^3.3.1
|
||||||
share_plus: ^10.1.4
|
share_plus: ^10.1.4
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
|
fl_chart: ^0.70.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user