Tambahkan rute baru untuk modul Warga, termasuk WargaDashboard, WargaPenyaluran, dan WargaPengaduan. Perbarui referensi import di app_pages.dart dan app_routes.dart untuk mencerminkan perubahan ini, meningkatkan struktur dan navigasi aplikasi.
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><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||||
|
|
||||||
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||||
|
|
|
|
||||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||||
<EFBFBD>
|
<EFBFBD>
|
||||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
|
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
|
||||||
n
|
n
|
||||||
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
||||||
r
|
r
|
||||||
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2y
|
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||||
w
|
w
|
||||||
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2z
|
K <20><><EFBFBD><EFBFBD><EFBFBD>2z
|
||||||
x
|
x
|
||||||
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json <08><><EFBFBD><EFBFBD><EFBFBD>2 ~
|
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json <08><><EFBFBD><EFBFBD><EFBFBD>2 ~
|
||||||
|
|
|
|
||||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin <08><><EFBFBD><EFBFBD><EFBFBD>2
|
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||||
<EFBFBD>
|
<EFBFBD>
|
||||||
<EFBFBD>
|
<EFBFBD>
|
||||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2w
|
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2w
|
||||||
u
|
u
|
||||||
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json <08><><EFBFBD><EFBFBD><EFBFBD>2
|
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
( <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
@ -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>
|
||||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||||
~
|
~
|
||||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||||
<EFBFBD>
|
<EFBFBD>
|
||||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
|
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
|
||||||
p
|
p
|
||||||
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
||||||
t
|
t
|
||||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2{
|
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||||
y
|
y
|
||||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
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><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||||
y
|
y
|
||||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2x
|
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2x
|
||||||
v
|
v
|
||||||
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
|
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
|
||||||
{
|
{
|
||||||
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
|
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
|
||||||
h
|
h
|
||||||
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
|
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
|
||||||
l
|
l
|
||||||
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2s
|
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2s
|
||||||
q
|
q
|
||||||
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
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 <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||||
|
|
|
|
||||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\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\x86_64\additional_project_files.txt <08><><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 <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json <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\x86_64\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2m
|
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json <08><><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 <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2q
|
iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja <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_64\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2v
|
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt <08><><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 <08><><EFBFBD><EFBFBD><EFBFBD>2
|
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt <08><><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
|
1
lib/app/modules/penyaluran/detail_penyaluran_page.dart
Normal file
1
lib/app/modules/penyaluran/detail_penyaluran_page.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
11
lib/app/modules/warga/bindings/warga_binding.dart
Normal file
11
lib/app/modules/warga/bindings/warga_binding.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
|
||||||
|
class WargaBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut<WargaDashboardController>(
|
||||||
|
() => WargaDashboardController(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/pengaduan_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/pengajuan_kelayakan_bantuan_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/auth/controllers/auth_controller.dart';
|
||||||
|
|
||||||
|
class WargaDashboardController extends GetxController {
|
||||||
|
final AuthController _authController = Get.find<AuthController>();
|
||||||
|
|
||||||
|
final Rx<UserModel?> currentUser = Rx<UserModel?>(null);
|
||||||
|
|
||||||
|
// Indeks tab yang aktif di bottom navigation bar
|
||||||
|
final RxInt activeTabIndex = 0.obs;
|
||||||
|
|
||||||
|
// Data untuk summary penerima penyaluran
|
||||||
|
final RxList<PenerimaPenyaluranModel> penerimaPenyaluran =
|
||||||
|
<PenerimaPenyaluranModel>[].obs;
|
||||||
|
final RxInt totalPenyaluranDiterima = 0.obs;
|
||||||
|
|
||||||
|
// Data untuk daftar pengajuan kelayakan bantuan
|
||||||
|
final RxList<PengajuanKelayakanBantuanModel> pengajuanKelayakan =
|
||||||
|
<PengajuanKelayakanBantuanModel>[].obs;
|
||||||
|
final RxInt totalPengajuanMenunggu = 0.obs;
|
||||||
|
final RxInt totalPengajuanTerverifikasi = 0.obs;
|
||||||
|
final RxInt totalPengajuanDitolak = 0.obs;
|
||||||
|
|
||||||
|
// Data untuk summary pengaduan
|
||||||
|
final RxList<PengaduanModel> pengaduan = <PengaduanModel>[].obs;
|
||||||
|
final RxInt totalPengaduan = 0.obs;
|
||||||
|
final RxInt totalPengaduanProses = 0.obs;
|
||||||
|
final RxInt totalPengaduanSelesai = 0.obs;
|
||||||
|
|
||||||
|
// Indikator loading
|
||||||
|
final RxBool isLoading = false.obs;
|
||||||
|
|
||||||
|
// Jumlah notifikasi belum dibaca
|
||||||
|
final RxInt jumlahNotifikasiBelumDibaca = 0.obs;
|
||||||
|
|
||||||
|
// Getter untuk data user
|
||||||
|
UserModel? get user => _authController.user;
|
||||||
|
String get role => user?.role ?? 'WARGA';
|
||||||
|
String get nama => user?.name ?? 'Warga';
|
||||||
|
String? get desa => user?.desa?.nama;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
fetchData();
|
||||||
|
loadUserData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadUserData() {
|
||||||
|
currentUser.value = _authController.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fetchData() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: Implementasi fetch data dari API
|
||||||
|
// Contoh data dummy untuk pengembangan UI
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
// Dummy data penerima penyaluran
|
||||||
|
penerimaPenyaluran.value = [
|
||||||
|
PenerimaPenyaluranModel(
|
||||||
|
id: '1',
|
||||||
|
statusPenerimaan: 'DITERIMA',
|
||||||
|
tanggalPenerimaan: DateTime.now().subtract(const Duration(days: 5)),
|
||||||
|
jumlahBantuan: 50000,
|
||||||
|
keterangan: 'Bantuan Tunai',
|
||||||
|
),
|
||||||
|
PenerimaPenyaluranModel(
|
||||||
|
id: '2',
|
||||||
|
statusPenerimaan: 'DITERIMA',
|
||||||
|
tanggalPenerimaan: DateTime.now().subtract(const Duration(days: 15)),
|
||||||
|
jumlahBantuan: 100000,
|
||||||
|
keterangan: 'Bantuan Sembako',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
totalPenyaluranDiterima.value = penerimaPenyaluran.length;
|
||||||
|
|
||||||
|
// Dummy data pengajuan kelayakan
|
||||||
|
pengajuanKelayakan.value = [
|
||||||
|
PengajuanKelayakanBantuanModel(
|
||||||
|
id: '1',
|
||||||
|
status: StatusKelayakan.MENUNGGU,
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(days: 2)),
|
||||||
|
),
|
||||||
|
PengajuanKelayakanBantuanModel(
|
||||||
|
id: '2',
|
||||||
|
status: StatusKelayakan.TERVERIFIKASI,
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(days: 10)),
|
||||||
|
),
|
||||||
|
PengajuanKelayakanBantuanModel(
|
||||||
|
id: '3',
|
||||||
|
status: StatusKelayakan.DITOLAK,
|
||||||
|
createdAt: DateTime.now().subtract(const Duration(days: 20)),
|
||||||
|
alasanVerifikasi: 'Dokumen tidak lengkap',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
totalPengajuanMenunggu.value = pengajuanKelayakan
|
||||||
|
.where((p) => p.status == StatusKelayakan.MENUNGGU)
|
||||||
|
.length;
|
||||||
|
totalPengajuanTerverifikasi.value = pengajuanKelayakan
|
||||||
|
.where((p) => p.status == StatusKelayakan.TERVERIFIKASI)
|
||||||
|
.length;
|
||||||
|
totalPengajuanDitolak.value = pengajuanKelayakan
|
||||||
|
.where((p) => p.status == StatusKelayakan.DITOLAK)
|
||||||
|
.length;
|
||||||
|
|
||||||
|
// Dummy data pengaduan
|
||||||
|
pengaduan.value = [
|
||||||
|
PengaduanModel(
|
||||||
|
id: '1',
|
||||||
|
judul: 'Bantuan tidak sesuai',
|
||||||
|
status: 'PROSES',
|
||||||
|
tanggalPengaduan: DateTime.now().subtract(const Duration(days: 3)),
|
||||||
|
),
|
||||||
|
PengaduanModel(
|
||||||
|
id: '2',
|
||||||
|
judul: 'Keterlambatan penyaluran',
|
||||||
|
status: 'SELESAI',
|
||||||
|
tanggalPengaduan: DateTime.now().subtract(const Duration(days: 25)),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
totalPengaduan.value = pengaduan.length;
|
||||||
|
totalPengaduanProses.value =
|
||||||
|
pengaduan.where((p) => p.status == 'PROSES').length;
|
||||||
|
totalPengaduanSelesai.value =
|
||||||
|
pengaduan.where((p) => p.status == 'SELESAI').length;
|
||||||
|
|
||||||
|
// Dummy data notifikasi
|
||||||
|
jumlahNotifikasiBelumDibaca.value = 3;
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching data: $e');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigasi ke halaman detail
|
||||||
|
void goToPenyaluranDetail() {
|
||||||
|
changeTab(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToPengajuanDetail() {
|
||||||
|
// TODO: Implementasi navigasi ke halaman detail pengajuan
|
||||||
|
// Untuk saat ini, belum ada halaman detail pengajuan
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToPengaduanDetail() {
|
||||||
|
changeTab(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk mengubah tab
|
||||||
|
void changeTab(int index) {
|
||||||
|
activeTabIndex.value = index;
|
||||||
|
|
||||||
|
// Tidak perlu navigasi ke halaman lain, cukup ubah indeks tab
|
||||||
|
// yang akan mengubah konten body di WargaView
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk logout
|
||||||
|
void logout() {
|
||||||
|
_authController.logout();
|
||||||
|
}
|
||||||
|
}
|
487
lib/app/modules/warga/views/warga_dashboard_view.dart
Normal file
487
lib/app/modules/warga/views/warga_dashboard_view.dart
Normal file
@ -0,0 +1,487 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
import 'package:penyaluran_app/app/data/models/pengajuan_kelayakan_bantuan_model.dart';
|
||||||
|
|
||||||
|
class WargaDashboardView extends GetView<WargaDashboardController> {
|
||||||
|
const WargaDashboardView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
controller.fetchData();
|
||||||
|
},
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildWelcomeSection(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildPenyaluranSummary(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildPengajuanSection(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_buildPengaduanSummary(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWelcomeSection() {
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 30,
|
||||||
|
backgroundColor: Colors.blue.shade100,
|
||||||
|
child: const Icon(
|
||||||
|
Icons.person,
|
||||||
|
size: 40,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Selamat Datang,',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
controller.nama,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
controller.desa ?? 'Warga Desa',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPenyaluranSummary() {
|
||||||
|
final currencyFormat = NumberFormat.currency(
|
||||||
|
locale: 'id',
|
||||||
|
symbol: 'Rp ',
|
||||||
|
decimalDigits: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Penyaluran Bantuan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Total Bantuan Diterima',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'${controller.totalPenyaluranDiterima.value}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.volunteer_activism,
|
||||||
|
size: 40,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(height: 32),
|
||||||
|
if (controller.penerimaPenyaluran.isNotEmpty)
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: controller.penerimaPenyaluran.length > 2
|
||||||
|
? 2
|
||||||
|
: controller.penerimaPenyaluran.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = controller.penerimaPenyaluran[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(
|
||||||
|
item.keterangan ?? 'Bantuan',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
item.tanggalPenerimaan != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(item.tanggalPenerimaan!)
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
item.jumlahBantuan != null
|
||||||
|
? currencyFormat.format(item.jumlahBantuan)
|
||||||
|
: '-',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Text('Belum ada penyaluran bantuan'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (controller.penerimaPenyaluran.length > 2)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => controller.changeTab(1),
|
||||||
|
child: const Text('Lihat Semua'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengajuanSection() {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Pengajuan Kelayakan Bantuan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Menunggu',
|
||||||
|
controller.totalPengajuanMenunggu.value,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Terverifikasi',
|
||||||
|
controller.totalPengajuanTerverifikasi.value,
|
||||||
|
Colors.green,
|
||||||
|
),
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Ditolak',
|
||||||
|
controller.totalPengajuanDitolak.value,
|
||||||
|
Colors.red,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(height: 32),
|
||||||
|
if (controller.pengajuanKelayakan.isNotEmpty)
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: controller.pengajuanKelayakan.length > 3
|
||||||
|
? 3
|
||||||
|
: controller.pengajuanKelayakan.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = controller.pengajuanKelayakan[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(
|
||||||
|
'Pengajuan #${index + 1}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
item.createdAt != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(item.createdAt!)
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
trailing: _buildStatusBadge(item.status),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Text('Belum ada pengajuan kelayakan'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (controller.pengajuanKelayakan.length > 3)
|
||||||
|
TextButton(
|
||||||
|
onPressed: controller.goToPengajuanDetail,
|
||||||
|
child: const Text('Lihat Semua'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusCounter(String label, int count, Color color) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'$count',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusBadge(StatusKelayakan? status) {
|
||||||
|
if (status == null) return const SizedBox();
|
||||||
|
|
||||||
|
Color color;
|
||||||
|
String text;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case StatusKelayakan.MENUNGGU:
|
||||||
|
color = Colors.orange;
|
||||||
|
text = 'Menunggu';
|
||||||
|
break;
|
||||||
|
case StatusKelayakan.TERVERIFIKASI:
|
||||||
|
color = Colors.green;
|
||||||
|
text = 'Terverifikasi';
|
||||||
|
break;
|
||||||
|
case StatusKelayakan.DITOLAK:
|
||||||
|
color = Colors.red;
|
||||||
|
text = 'Ditolak';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(color: color),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(
|
||||||
|
color: color,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengaduanSummary() {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Pengaduan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Total',
|
||||||
|
controller.totalPengaduan.value,
|
||||||
|
Colors.blue,
|
||||||
|
),
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Proses',
|
||||||
|
controller.totalPengaduanProses.value,
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
_buildStatusCounter(
|
||||||
|
'Selesai',
|
||||||
|
controller.totalPengaduanSelesai.value,
|
||||||
|
Colors.green,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(height: 32),
|
||||||
|
if (controller.pengaduan.isNotEmpty)
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: controller.pengaduan.length > 2
|
||||||
|
? 2
|
||||||
|
: controller.pengaduan.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = controller.pengaduan[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(
|
||||||
|
item.judul ?? 'Pengaduan #${index + 1}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
item.tanggalPengaduan != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(item.tanggalPengaduan!)
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
trailing: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.status == 'PROSES'
|
||||||
|
? Colors.orange.withOpacity(0.1)
|
||||||
|
: Colors.green.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(
|
||||||
|
color: item.status == 'PROSES'
|
||||||
|
? Colors.orange
|
||||||
|
: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
item.status == 'PROSES' ? 'Proses' : 'Selesai',
|
||||||
|
style: TextStyle(
|
||||||
|
color: item.status == 'PROSES'
|
||||||
|
? Colors.orange
|
||||||
|
: Colors.green,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
child: Text('Belum ada pengaduan'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (controller.pengaduan.length > 2)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => controller.changeTab(2),
|
||||||
|
child: const Text('Lihat Semua'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implementasi navigasi ke halaman buat pengaduan
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: const Text('Buat Pengaduan Baru'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
minimumSize: const Size(double.infinity, 48),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
194
lib/app/modules/warga/views/warga_pengaduan_view.dart
Normal file
194
lib/app/modules/warga/views/warga_pengaduan_view.dart
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
|
||||||
|
class WargaPengaduanView extends GetView<WargaDashboardController> {
|
||||||
|
const WargaPengaduanView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
controller.fetchData();
|
||||||
|
},
|
||||||
|
child: controller.pengaduan.isEmpty
|
||||||
|
? _buildEmptyState()
|
||||||
|
: _buildPengaduanList(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyState() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.report_problem,
|
||||||
|
size: 80,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Belum Ada Pengaduan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Anda belum membuat pengaduan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implementasi navigasi ke halaman buat pengaduan
|
||||||
|
Get.toNamed('/buat-pengaduan');
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: const Text('Buat Pengaduan Baru'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPengaduanList() {
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
itemCount: controller.pengaduan.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = controller.pengaduan[index];
|
||||||
|
final isProses = item.status == 'PROSES';
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// TODO: Navigasi ke detail pengaduan
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
item.judul ?? 'Pengaduan #${index + 1}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isProses
|
||||||
|
? Colors.orange.withOpacity(0.1)
|
||||||
|
: Colors.green.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(
|
||||||
|
color: isProses ? Colors.orange : Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
isProses ? 'Proses' : 'Selesai',
|
||||||
|
style: TextStyle(
|
||||||
|
color: isProses ? Colors.orange : Colors.green,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
if (item.deskripsi != null && item.deskripsi!.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: Text(
|
||||||
|
item.deskripsi!,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_today,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
item.tanggalPengaduan != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(item.tanggalPengaduan!)
|
||||||
|
: '-',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(height: 1),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implementasi navigasi ke detail pengaduan
|
||||||
|
Get.toNamed('/detail-pengaduan',
|
||||||
|
arguments: {'id': item.id});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.visibility),
|
||||||
|
label: const Text('Lihat Detail'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
187
lib/app/modules/warga/views/warga_penyaluran_view.dart
Normal file
187
lib/app/modules/warga/views/warga_penyaluran_view.dart
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
|
||||||
|
class WargaPenyaluranView extends GetView<WargaDashboardController> {
|
||||||
|
const WargaPenyaluranView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () async {
|
||||||
|
controller.fetchData();
|
||||||
|
},
|
||||||
|
child: controller.penerimaPenyaluran.isEmpty
|
||||||
|
? _buildEmptyState()
|
||||||
|
: _buildPenyaluranList(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyState() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.volunteer_activism,
|
||||||
|
size: 80,
|
||||||
|
color: Colors.grey.shade400,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Belum Ada Penyaluran',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Anda belum menerima penyaluran bantuan',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPenyaluranList() {
|
||||||
|
final currencyFormat = NumberFormat.currency(
|
||||||
|
locale: 'id',
|
||||||
|
symbol: 'Rp ',
|
||||||
|
decimalDigits: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
itemCount: controller.penerimaPenyaluran.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = controller.penerimaPenyaluran[index];
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
elevation: 2,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// TODO: Navigasi ke detail penyaluran
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
item.keterangan ?? 'Bantuan',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(color: Colors.green),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Diterima',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.green,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.calendar_today,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
item.tanggalPenerimaan != null
|
||||||
|
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||||
|
.format(item.tanggalPenerimaan!)
|
||||||
|
: '-',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.attach_money,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
item.jumlahBantuan != null
|
||||||
|
? currencyFormat.format(item.jumlahBantuan)
|
||||||
|
: '-',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(height: 1),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implementasi navigasi ke detail penyaluran
|
||||||
|
Get.toNamed('/detail-penyaluran',
|
||||||
|
arguments: {'id': item.id});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.visibility),
|
||||||
|
label: const Text('Lihat Detail'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
180
lib/app/modules/warga/views/warga_view.dart
Normal file
180
lib/app/modules/warga/views/warga_view.dart
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_dashboard_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_penyaluran_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/app_drawer.dart';
|
||||||
|
import 'package:penyaluran_app/app/widgets/app_bottom_navigation_bar.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class WargaView extends GetView<WargaDashboardController> {
|
||||||
|
const WargaView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
key: scaffoldKey,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Obx(() {
|
||||||
|
switch (controller.activeTabIndex.value) {
|
||||||
|
case 0:
|
||||||
|
return const Text('Dashboard Warga');
|
||||||
|
case 1:
|
||||||
|
return const Text('Penyaluran Bantuan');
|
||||||
|
case 2:
|
||||||
|
return const Text('Pengaduan');
|
||||||
|
default:
|
||||||
|
return const Text('Dashboard Warga');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.menu),
|
||||||
|
onPressed: () {
|
||||||
|
scaffoldKey.currentState?.openDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
// Tombol notifikasi
|
||||||
|
Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.notifications_outlined),
|
||||||
|
onPressed: () {
|
||||||
|
// Navigasi ke halaman notifikasi
|
||||||
|
Get.toNamed('/notifikasi');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Obx(() {
|
||||||
|
if (controller.jumlahNotifikasiBelumDibaca.value > 0) {
|
||||||
|
return Positioned(
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 16,
|
||||||
|
minHeight: 16,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
controller.jumlahNotifikasiBelumDibaca.value.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
drawer: Obx(() => AppDrawer(
|
||||||
|
nama: controller.nama,
|
||||||
|
role: 'Warga',
|
||||||
|
desa: controller.desa,
|
||||||
|
notificationCount: controller.jumlahNotifikasiBelumDibaca.value,
|
||||||
|
onLogout: controller.logout,
|
||||||
|
menuItems: [
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.dashboard_outlined,
|
||||||
|
title: 'Dashboard',
|
||||||
|
isSelected: controller.activeTabIndex.value == 0,
|
||||||
|
onTap: () => controller.changeTab(0),
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.volunteer_activism_outlined,
|
||||||
|
title: 'Penyaluran',
|
||||||
|
isSelected: controller.activeTabIndex.value == 1,
|
||||||
|
onTap: () => controller.changeTab(1),
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.report_problem_outlined,
|
||||||
|
title: 'Pengaduan',
|
||||||
|
isSelected: controller.activeTabIndex.value == 2,
|
||||||
|
badgeCount: controller.totalPengaduanProses.value,
|
||||||
|
badgeColor: Colors.orange,
|
||||||
|
onTap: () => controller.changeTab(2),
|
||||||
|
),
|
||||||
|
DrawerMenuItem(
|
||||||
|
icon: Icons.assignment_outlined,
|
||||||
|
title: 'Pengajuan Kelayakan',
|
||||||
|
onTap: () {
|
||||||
|
// TODO: Navigasi ke halaman pengajuan kelayakan
|
||||||
|
Get.toNamed('/pengajuan-kelayakan');
|
||||||
|
},
|
||||||
|
badgeCount: controller.totalPengajuanMenunggu.value,
|
||||||
|
badgeColor: Colors.blue,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
body: Obx(() {
|
||||||
|
switch (controller.activeTabIndex.value) {
|
||||||
|
case 0:
|
||||||
|
return const WargaDashboardView();
|
||||||
|
case 1:
|
||||||
|
return const WargaPenyaluranView();
|
||||||
|
case 2:
|
||||||
|
return const WargaPengaduanView();
|
||||||
|
default:
|
||||||
|
return const WargaDashboardView();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
bottomNavigationBar: Obx(() => AppBottomNavigationBar(
|
||||||
|
currentIndex: controller.activeTabIndex.value,
|
||||||
|
onTap: controller.changeTab,
|
||||||
|
items: [
|
||||||
|
AppBottomNavigationBarItem(
|
||||||
|
icon: Icons.dashboard_outlined,
|
||||||
|
activeIcon: Icons.dashboard,
|
||||||
|
label: 'Beranda',
|
||||||
|
),
|
||||||
|
AppBottomNavigationBarItem(
|
||||||
|
icon: Icons.volunteer_activism_outlined,
|
||||||
|
activeIcon: Icons.volunteer_activism,
|
||||||
|
label: 'Penyaluran',
|
||||||
|
badgeCount: controller.totalPenyaluranDiterima.value > 0
|
||||||
|
? controller.totalPenyaluranDiterima.value
|
||||||
|
: null,
|
||||||
|
badgeColor: Colors.green,
|
||||||
|
),
|
||||||
|
AppBottomNavigationBarItem(
|
||||||
|
icon: Icons.report_problem_outlined,
|
||||||
|
activeIcon: Icons.report_problem,
|
||||||
|
label: 'Pengaduan',
|
||||||
|
badgeCount: controller.totalPengaduanProses.value > 0
|
||||||
|
? controller.totalPengaduanProses.value
|
||||||
|
: null,
|
||||||
|
badgeColor: Colors.orange,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
floatingActionButton: Obx(() {
|
||||||
|
// Tampilkan FAB hanya di halaman pengaduan
|
||||||
|
if (controller.activeTabIndex.value == 2) {
|
||||||
|
return FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
// TODO: Implementasi navigasi ke halaman buat pengaduan
|
||||||
|
Get.toNamed('/buat-pengaduan');
|
||||||
|
},
|
||||||
|
backgroundColor: AppTheme.primaryColor,
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,12 @@ import 'package:penyaluran_app/app/modules/profile/bindings/profile_binding.dart
|
|||||||
import 'package:penyaluran_app/app/modules/profile/views/profile_view.dart';
|
import 'package:penyaluran_app/app/modules/profile/views/profile_view.dart';
|
||||||
import 'package:penyaluran_app/app/modules/splash/bindings/splash_binding.dart';
|
import 'package:penyaluran_app/app/modules/splash/bindings/splash_binding.dart';
|
||||||
import 'package:penyaluran_app/app/modules/splash/views/splash_view.dart';
|
import 'package:penyaluran_app/app/modules/splash/views/splash_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/bindings/warga_binding.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_dashboard_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_penyaluran_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_pengaduan_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/views/warga_view.dart';
|
||||||
|
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||||
|
|
||||||
part 'app_routes.dart';
|
part 'app_routes.dart';
|
||||||
|
|
||||||
@ -39,6 +45,29 @@ class AppPages {
|
|||||||
page: () => const LoginView(),
|
page: () => const LoginView(),
|
||||||
binding: AuthBinding(),
|
binding: AuthBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: _Paths.wargaDashboard,
|
||||||
|
page: () => const WargaView(),
|
||||||
|
binding: WargaBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: _Paths.wargaPenyaluran,
|
||||||
|
page: () {
|
||||||
|
final controller = Get.find<WargaDashboardController>();
|
||||||
|
controller.activeTabIndex.value = 1;
|
||||||
|
return const WargaView();
|
||||||
|
},
|
||||||
|
binding: WargaBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: _Paths.wargaPengaduan,
|
||||||
|
page: () {
|
||||||
|
final controller = Get.find<WargaDashboardController>();
|
||||||
|
controller.activeTabIndex.value = 2;
|
||||||
|
return const WargaView();
|
||||||
|
},
|
||||||
|
binding: WargaBinding(),
|
||||||
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: _Paths.petugasDesaDashboard,
|
name: _Paths.petugasDesaDashboard,
|
||||||
page: () => const PetugasDesaView(),
|
page: () => const PetugasDesaView(),
|
||||||
|
@ -6,6 +6,8 @@ abstract class Routes {
|
|||||||
static const login = _Paths.login;
|
static const login = _Paths.login;
|
||||||
static const register = _Paths.register;
|
static const register = _Paths.register;
|
||||||
static const wargaDashboard = _Paths.wargaDashboard;
|
static const wargaDashboard = _Paths.wargaDashboard;
|
||||||
|
static const wargaPenyaluran = _Paths.wargaPenyaluran;
|
||||||
|
static const wargaPengaduan = _Paths.wargaPengaduan;
|
||||||
static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard;
|
static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard;
|
||||||
static const petugasDesaDashboard = _Paths.petugasDesaDashboard;
|
static const petugasDesaDashboard = _Paths.petugasDesaDashboard;
|
||||||
static const donaturDashboard = _Paths.donaturDashboard;
|
static const donaturDashboard = _Paths.donaturDashboard;
|
||||||
@ -33,6 +35,8 @@ abstract class _Paths {
|
|||||||
static const login = '/login';
|
static const login = '/login';
|
||||||
static const register = '/register';
|
static const register = '/register';
|
||||||
static const wargaDashboard = '/warga-dashboard';
|
static const wargaDashboard = '/warga-dashboard';
|
||||||
|
static const wargaPenyaluran = '/warga-penyaluran';
|
||||||
|
static const wargaPengaduan = '/warga-pengaduan';
|
||||||
static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard';
|
static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard';
|
||||||
static const petugasDesaDashboard = '/petugas-desa-dashboard';
|
static const petugasDesaDashboard = '/petugas-desa-dashboard';
|
||||||
static const donaturDashboard = '/donatur-dashboard';
|
static const donaturDashboard = '/donatur-dashboard';
|
||||||
|
112
lib/app/widgets/app_bottom_navigation_bar.dart
Normal file
112
lib/app/widgets/app_bottom_navigation_bar.dart
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class AppBottomNavigationBar extends StatelessWidget {
|
||||||
|
final int currentIndex;
|
||||||
|
final Function(int) onTap;
|
||||||
|
final List<AppBottomNavigationBarItem> items;
|
||||||
|
|
||||||
|
const AppBottomNavigationBar({
|
||||||
|
Key? key,
|
||||||
|
required this.currentIndex,
|
||||||
|
required this.onTap,
|
||||||
|
required this.items,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BottomNavigationBar(
|
||||||
|
currentIndex: currentIndex,
|
||||||
|
onTap: onTap,
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
selectedItemColor: AppTheme.primaryColor,
|
||||||
|
unselectedItemColor: Colors.grey,
|
||||||
|
items: items.map((item) => _buildNavigationBarItem(item)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BottomNavigationBarItem _buildNavigationBarItem(
|
||||||
|
AppBottomNavigationBarItem item) {
|
||||||
|
return BottomNavigationBarItem(
|
||||||
|
icon: item.badgeCount != null && item.badgeCount! > 0
|
||||||
|
? Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(item.icon),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.badgeColor ?? Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
item.badgeCount.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Icon(item.icon),
|
||||||
|
activeIcon: item.badgeCount != null && item.badgeCount! > 0
|
||||||
|
? Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(item.activeIcon ?? item.icon),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: item.badgeColor ?? Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
item.badgeCount.toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Icon(item.activeIcon ?? item.icon),
|
||||||
|
label: item.label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppBottomNavigationBarItem {
|
||||||
|
final IconData icon;
|
||||||
|
final IconData? activeIcon;
|
||||||
|
final String label;
|
||||||
|
final int? badgeCount;
|
||||||
|
final Color? badgeColor;
|
||||||
|
|
||||||
|
AppBottomNavigationBarItem({
|
||||||
|
required this.icon,
|
||||||
|
this.activeIcon,
|
||||||
|
required this.label,
|
||||||
|
this.badgeCount,
|
||||||
|
this.badgeColor,
|
||||||
|
});
|
||||||
|
}
|
162
lib/app/widgets/app_drawer.dart
Normal file
162
lib/app/widgets/app_drawer.dart
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class AppDrawer extends StatelessWidget {
|
||||||
|
final String nama;
|
||||||
|
final String role;
|
||||||
|
final String? desa;
|
||||||
|
final String? avatar;
|
||||||
|
final int? notificationCount;
|
||||||
|
final VoidCallback onLogout;
|
||||||
|
final List<DrawerMenuItem> menuItems;
|
||||||
|
|
||||||
|
const AppDrawer({
|
||||||
|
Key? key,
|
||||||
|
required this.nama,
|
||||||
|
required this.role,
|
||||||
|
this.desa,
|
||||||
|
this.avatar,
|
||||||
|
this.notificationCount,
|
||||||
|
required this.onLogout,
|
||||||
|
required this.menuItems,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Drawer(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
children: [
|
||||||
|
DrawerHeader(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: AppTheme.primaryGradient,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 30,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
backgroundImage:
|
||||||
|
avatar != null ? NetworkImage(avatar!) : null,
|
||||||
|
child: avatar == null
|
||||||
|
? const Icon(
|
||||||
|
Icons.person,
|
||||||
|
size: 40,
|
||||||
|
color: AppTheme.primaryColor,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
nama,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
desa != null ? '$role - $desa' : role,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.8),
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...menuItems.map((item) => _buildMenuItem(context, item)),
|
||||||
|
const Divider(),
|
||||||
|
ListTile(
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMenuItem(BuildContext context, DrawerMenuItem item) {
|
||||||
|
return ListTile(
|
||||||
|
leading: item.badgeCount != null && item.badgeCount! > 0
|
||||||
|
? Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(item.icon),
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Icon(item.icon),
|
||||||
|
title: Text(item.title),
|
||||||
|
selected: item.isSelected,
|
||||||
|
selectedColor: AppTheme.primaryColor,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
item.onTap();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DrawerMenuItem {
|
||||||
|
final IconData icon;
|
||||||
|
final String title;
|
||||||
|
final bool isSelected;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final int? badgeCount;
|
||||||
|
final Color? badgeColor;
|
||||||
|
|
||||||
|
DrawerMenuItem({
|
||||||
|
required this.icon,
|
||||||
|
required this.title,
|
||||||
|
this.isSelected = false,
|
||||||
|
required this.onTap,
|
||||||
|
this.badgeCount,
|
||||||
|
this.badgeColor,
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user