Perbarui judul aplikasi dari 'Penyaluran App' menjadi 'Penerimaan App'. Tambahkan properti baru pada model PenerimaPenyaluranModel untuk mendukung informasi tambahan terkait penyaluran. Modifikasi tampilan di WargaDashboardView dan WargaPengaduanView untuk meningkatkan pengalaman pengguna. Hapus WargaPenyaluranView yang tidak digunakan dan perbarui rute aplikasi untuk mencerminkan perubahan ini.
This commit is contained in:
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
||||
M
|
||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||
A
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
|
||||
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
<EFBFBD>
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2p
|
||||
n
|
||||
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
||||
lD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
||||
r
|
||||
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||
w
|
||||
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2z
|
||||
x
|
||||
x
|
||||
vD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json <08><><EFBFBD><EFBFBD><EFBFBD>2 ~
|
||||
|
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\metadata_generation_command.txt <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2w
|
||||
u
|
||||
u
|
||||
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\prefab_config.json <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
||||
M
|
||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||
A
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
<EFBFBD>
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
~
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
|
||||
p
|
||||
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
||||
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
||||
t
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2|
|
||||
z
|
||||
z
|
||||
xD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json <08><><EFBFBD><EFBFBD><EFBFBD>2 <09>
|
||||
~
|
||||
~
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json.bin <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\metadata_generation_command.txt <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||
w
|
||||
w
|
||||
uD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\prefab_config.json <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
||||
M
|
||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||
A
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2x
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\additional_project_files.txt <08><><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2x
|
||||
v
|
||||
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
|
||||
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
|
||||
{
|
||||
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json <EFBFBD><EFBFBD><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 ů<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
|
||||
h
|
||||
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja <EFBFBD><EFBFBD><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 ů<EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
|
||||
l
|
||||
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2s
|
||||
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt ů<EFBFBD><EFBFBD><EFBFBD>2s
|
||||
q
|
||||
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2
|
||||
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt ů<EFBFBD><EFBFBD><EFBFBD>2
|
||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
||||
r
|
||||
r
|
||||
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json ϯ<><CFAF><EFBFBD>2 x
|
||||
v
|
||||
v
|
||||
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json.bin ϯ<><CFAF><EFBFBD>2
|
||||
~
|
||||
|
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\metadata_generation_command.txt ϯ<><CFAF><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 ϯ<><CFAF><EFBFBD>2
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
||||
M
|
||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||
A
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint ̱<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2~
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\additional_project_files.txt ̱<EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build.json ̱<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
~
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2m
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\android_gradle_build_mini.json ̱<EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2m
|
||||
k
|
||||
iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2q
|
||||
iD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja ̱<EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2q
|
||||
o
|
||||
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2v
|
||||
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build.ninja.txt ̱<EFBFBD><EFBFBD><EFBFBD>2v
|
||||
t
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\build_file_index.txt ̱<EFBFBD><EFBFBD><EFBFBD>2
|
||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2w
|
||||
u
|
||||
u
|
||||
sD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json ̱<><CCB1><EFBFBD>2 {
|
||||
y
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json.bin ̱<><CCB1><EFBFBD>2
|
||||
<EFBFBD>
|
||||
|
||||
|
||||
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\metadata_generation_command.txt ̱<><CCB1><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 ̱<><CCB1><EFBFBD>2
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2y
|
@ -13,6 +13,15 @@ class PenerimaPenyaluranModel {
|
||||
final String? stokBantuanId;
|
||||
final Map<String, dynamic>? warga; // Data warga yang terkait
|
||||
final String? tandaTangan;
|
||||
final bool? isUang; // Apakah bantuan berupa uang
|
||||
final String? satuan; // Satuan bantuan
|
||||
final Map<String, dynamic>? stokBantuan; // Data stok bantuan
|
||||
final Map<String, dynamic>? penyaluranBantuan; // Data penyaluran bantuan
|
||||
final String? kategoriNama; // Nama kategori bantuan
|
||||
final String? namaPenyaluran; // Nama penyaluran
|
||||
final String? deskripsiPenyaluran; // Deskripsi penyaluran
|
||||
final String? lokasiPenyaluranNama; // Nama lokasi penyaluran
|
||||
final String? lokasiPenyaluranAlamat; // Alamat lokasi penyaluran
|
||||
|
||||
PenerimaPenyaluranModel({
|
||||
this.id,
|
||||
@ -27,6 +36,15 @@ class PenerimaPenyaluranModel {
|
||||
this.stokBantuanId,
|
||||
this.warga,
|
||||
this.tandaTangan,
|
||||
this.isUang,
|
||||
this.satuan,
|
||||
this.stokBantuan,
|
||||
this.penyaluranBantuan,
|
||||
this.kategoriNama,
|
||||
this.namaPenyaluran,
|
||||
this.deskripsiPenyaluran,
|
||||
this.lokasiPenyaluranNama,
|
||||
this.lokasiPenyaluranAlamat,
|
||||
});
|
||||
|
||||
factory PenerimaPenyaluranModel.fromRawJson(String str) =>
|
||||
@ -52,6 +70,15 @@ class PenerimaPenyaluranModel {
|
||||
stokBantuanId: json["stok_bantuan_id"],
|
||||
warga: json["warga"],
|
||||
tandaTangan: json["tanda_tangan"],
|
||||
isUang: json["is_uang"],
|
||||
satuan: json["satuan"],
|
||||
stokBantuan: json["stok_bantuan"],
|
||||
penyaluranBantuan: json["penyaluran_bantuan"],
|
||||
kategoriNama: json["kategori_nama"],
|
||||
namaPenyaluran: json["nama_penyaluran"],
|
||||
deskripsiPenyaluran: json["deskripsi_penyaluran"],
|
||||
lokasiPenyaluranNama: json["lokasi_penyaluran_nama"],
|
||||
lokasiPenyaluranAlamat: json["lokasi_penyaluran_alamat"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@ -67,5 +94,14 @@ class PenerimaPenyaluranModel {
|
||||
"stok_bantuan_id": stokBantuanId,
|
||||
"warga": warga,
|
||||
"tanda_tangan": tandaTangan,
|
||||
"is_uang": isUang,
|
||||
"satuan": satuan,
|
||||
"stok_bantuan": stokBantuan,
|
||||
"penyaluran_bantuan": penyaluranBantuan,
|
||||
"kategori_nama": kategoriNama,
|
||||
"nama_penyaluran": namaPenyaluran,
|
||||
"deskripsi_penyaluran": deskripsiPenyaluran,
|
||||
"lokasi_penyaluran_nama": lokasiPenyaluranNama,
|
||||
"lokasi_penyaluran_alamat": lokasiPenyaluranAlamat,
|
||||
};
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class DetailPenyaluranPage extends StatelessWidget {
|
||||
title: const Text('Detail Penyaluran'),
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
),
|
||||
|
@ -4,9 +4,11 @@ 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';
|
||||
import 'package:penyaluran_app/app/services/supabase_service.dart';
|
||||
|
||||
class WargaDashboardController extends GetxController {
|
||||
final AuthController _authController = Get.find<AuthController>();
|
||||
final SupabaseService _supabaseService = SupabaseService.to;
|
||||
|
||||
final Rx<UserModel?> currentUser = Rx<UserModel?>(null);
|
||||
|
||||
@ -58,84 +60,22 @@ class WargaDashboardController extends GetxController {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
// TODO: Implementasi fetch data dari API
|
||||
// Contoh data dummy untuk pengembangan UI
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
// Pastikan user sudah login dan memiliki ID
|
||||
if (user?.id == null) {
|
||||
throw Exception('User tidak terautentikasi');
|
||||
}
|
||||
|
||||
// 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',
|
||||
),
|
||||
];
|
||||
// Ambil data penerima penyaluran dari server
|
||||
await fetchPenerimaPenyaluran();
|
||||
|
||||
totalPenyaluranDiterima.value = penerimaPenyaluran.length;
|
||||
// Ambil data pengajuan kelayakan dari server
|
||||
await fetchPengajuanKelayakan();
|
||||
|
||||
// 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',
|
||||
),
|
||||
];
|
||||
// Ambil data pengaduan dari server
|
||||
await fetchPengaduan();
|
||||
|
||||
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;
|
||||
// Ambil data notifikasi
|
||||
await fetchNotifikasi();
|
||||
} catch (e) {
|
||||
print('Error fetching data: $e');
|
||||
} finally {
|
||||
@ -143,6 +83,239 @@ class WargaDashboardController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk mengambil data penerima penyaluran
|
||||
Future<void> fetchPenerimaPenyaluran() async {
|
||||
try {
|
||||
print('Memulai fetchPenerimaPenyaluran()');
|
||||
print('User ID: ${user?.id}');
|
||||
|
||||
// Pertama, cari warga_id berdasarkan user_id
|
||||
final wargaResponse = await _supabaseService.client
|
||||
.from('warga')
|
||||
.select('id')
|
||||
.eq('user_id', user!.id)
|
||||
.single();
|
||||
|
||||
print('Warga response: $wargaResponse');
|
||||
|
||||
if (wargaResponse == null) {
|
||||
print('Tidak ditemukan data warga untuk user_id: ${user!.id}');
|
||||
return;
|
||||
}
|
||||
|
||||
final wargaId = wargaResponse['id'];
|
||||
print('Warga ID: $wargaId');
|
||||
|
||||
// Ambil data penerima penyaluran dengan join ke warga, stok bantuan, dan penyaluran bantuan
|
||||
final response =
|
||||
await _supabaseService.client.from('penerima_penyaluran').select('''
|
||||
*,
|
||||
warga:warga_id(*),
|
||||
stok_bantuan:stok_bantuan_id(
|
||||
*,
|
||||
kategori_bantuan(*)
|
||||
),
|
||||
penyaluran_bantuan:penyaluran_bantuan_id(
|
||||
*,
|
||||
lokasi_penyaluran(*)
|
||||
)
|
||||
''').eq('warga_id', wargaId).order('created_at', ascending: false);
|
||||
|
||||
print('Response dari API: $response');
|
||||
|
||||
if (response != null) {
|
||||
final List<PenerimaPenyaluranModel> penerima = [];
|
||||
for (var item in response) {
|
||||
print('Memproses item: $item');
|
||||
|
||||
Map<String, dynamic> sanitizedPenerimaData =
|
||||
Map<String, dynamic>.from(item);
|
||||
|
||||
print('Data yang disanitasi: $sanitizedPenerimaData');
|
||||
|
||||
if (sanitizedPenerimaData['jumlah_bantuan'] is String) {
|
||||
var jumlahBantuan = double.tryParse(
|
||||
sanitizedPenerimaData['jumlah_bantuan'] as String);
|
||||
print(
|
||||
'Konversi jumlah_bantuan dari String ke double: $jumlahBantuan');
|
||||
sanitizedPenerimaData['jumlah_bantuan'] = jumlahBantuan;
|
||||
}
|
||||
|
||||
// Tambahkan informasi apakah bantuan uang atau bukan dan satuan
|
||||
if (sanitizedPenerimaData['stok_bantuan'] != null) {
|
||||
print('Stok bantuan: ${sanitizedPenerimaData['stok_bantuan']}');
|
||||
|
||||
// Cek apakah bantuan uang
|
||||
final isUang =
|
||||
sanitizedPenerimaData['stok_bantuan']['is_uang'] ?? false;
|
||||
sanitizedPenerimaData['is_uang'] = isUang;
|
||||
|
||||
// Ambil satuan
|
||||
final satuan =
|
||||
sanitizedPenerimaData['stok_bantuan']['satuan'] ?? '';
|
||||
sanitizedPenerimaData['satuan'] = satuan;
|
||||
|
||||
// Ambil nama kategori bantuan jika tersedia
|
||||
if (sanitizedPenerimaData['stok_bantuan']['kategori_bantuan'] !=
|
||||
null) {
|
||||
final kategoriNama = sanitizedPenerimaData['stok_bantuan']
|
||||
['kategori_bantuan']['nama'] ??
|
||||
'';
|
||||
sanitizedPenerimaData['kategori_nama'] = kategoriNama;
|
||||
}
|
||||
|
||||
print('Is Uang: $isUang, Satuan: $satuan');
|
||||
}
|
||||
|
||||
// Tambahkan informasi dari penyaluran bantuan
|
||||
if (sanitizedPenerimaData['penyaluran_bantuan'] != null) {
|
||||
print(
|
||||
'Penyaluran bantuan: ${sanitizedPenerimaData['penyaluran_bantuan']}');
|
||||
|
||||
// Ambil nama penyaluran
|
||||
final namaPenyaluran =
|
||||
sanitizedPenerimaData['penyaluran_bantuan']['nama'] ?? '';
|
||||
sanitizedPenerimaData['nama_penyaluran'] = namaPenyaluran;
|
||||
|
||||
// Ambil deskripsi penyaluran
|
||||
final deskripsiPenyaluran =
|
||||
sanitizedPenerimaData['penyaluran_bantuan']['deskripsi'] ?? '';
|
||||
sanitizedPenerimaData['deskripsi_penyaluran'] = deskripsiPenyaluran;
|
||||
|
||||
// Ambil lokasi penyaluran jika tersedia
|
||||
if (sanitizedPenerimaData['penyaluran_bantuan']
|
||||
['lokasi_penyaluran'] !=
|
||||
null) {
|
||||
final lokasiNama = sanitizedPenerimaData['penyaluran_bantuan']
|
||||
['lokasi_penyaluran']['nama'] ??
|
||||
'';
|
||||
sanitizedPenerimaData['lokasi_penyaluran_nama'] = lokasiNama;
|
||||
|
||||
final lokasiAlamat = sanitizedPenerimaData['penyaluran_bantuan']
|
||||
['lokasi_penyaluran']['alamat_lengkap'] ??
|
||||
'';
|
||||
sanitizedPenerimaData['lokasi_penyaluran_alamat'] = lokasiAlamat;
|
||||
}
|
||||
|
||||
print('Nama Penyaluran: $namaPenyaluran');
|
||||
}
|
||||
|
||||
var model = PenerimaPenyaluranModel.fromJson(sanitizedPenerimaData);
|
||||
print('Model yang dibuat: $model');
|
||||
penerima.add(model);
|
||||
}
|
||||
|
||||
print('Total data yang diproses: ${penerima.length}');
|
||||
penerimaPenyaluran.assignAll(penerima);
|
||||
|
||||
var diterima =
|
||||
penerima.where((p) => p.statusPenerimaan == 'DITERIMA').length;
|
||||
print('Total penyaluran diterima: $diterima');
|
||||
totalPenyaluranDiterima.value = diterima;
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
print('Error fetchPenerimaPenyaluran: $e');
|
||||
print('Stack trace: $stackTrace');
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk mengambil data pengajuan kelayakan
|
||||
Future<void> fetchPengajuanKelayakan() async {
|
||||
try {
|
||||
// Pertama, cari warga_id berdasarkan user_id
|
||||
final wargaResponse = await _supabaseService.client
|
||||
.from('warga')
|
||||
.select('id')
|
||||
.eq('user_id', user!.id)
|
||||
.single();
|
||||
|
||||
if (wargaResponse == null) {
|
||||
print('Tidak ditemukan data warga untuk user_id: ${user!.id}');
|
||||
return;
|
||||
}
|
||||
|
||||
final wargaId = wargaResponse['id'];
|
||||
print('Warga ID untuk pengajuan kelayakan: $wargaId');
|
||||
|
||||
final response = await _supabaseService.client
|
||||
.from('xx02_pengajuan_kelayakan_bantuan')
|
||||
.select('*')
|
||||
.eq('warga_id', wargaId)
|
||||
.order('created_at', ascending: false);
|
||||
|
||||
if (response != null) {
|
||||
final List<PengajuanKelayakanBantuanModel> pengajuan = [];
|
||||
for (var item in response) {
|
||||
// Konversi status ke enum
|
||||
if (item['status'] != null) {
|
||||
final statusStr = item['status'].toString();
|
||||
item['status'] = statusStr; // Pastikan status dalam format string
|
||||
}
|
||||
|
||||
pengajuan.add(PengajuanKelayakanBantuanModel.fromJson(item));
|
||||
}
|
||||
pengajuanKelayakan.assignAll(pengajuan);
|
||||
|
||||
// Hitung jumlah berdasarkan status
|
||||
totalPengajuanMenunggu.value =
|
||||
pengajuan.where((p) => p.status == StatusKelayakan.MENUNGGU).length;
|
||||
totalPengajuanTerverifikasi.value = pengajuan
|
||||
.where((p) => p.status == StatusKelayakan.TERVERIFIKASI)
|
||||
.length;
|
||||
totalPengajuanDitolak.value =
|
||||
pengajuan.where((p) => p.status == StatusKelayakan.DITOLAK).length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error fetching pengajuan kelayakan: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk mengambil data pengaduan
|
||||
Future<void> fetchPengaduan() async {
|
||||
try {
|
||||
final response = await _supabaseService.client
|
||||
.from('pengaduan')
|
||||
.select('*')
|
||||
.eq('pelapor', user!.id)
|
||||
.order('created_at', ascending: false);
|
||||
|
||||
if (response != null) {
|
||||
final List<PengaduanModel> pengaduanList = [];
|
||||
for (var item in response) {
|
||||
pengaduanList.add(PengaduanModel.fromJson(item));
|
||||
}
|
||||
pengaduan.assignAll(pengaduanList);
|
||||
|
||||
// Hitung jumlah berdasarkan status
|
||||
totalPengaduan.value = pengaduanList.length;
|
||||
totalPengaduanProses.value = pengaduanList
|
||||
.where((p) => p.status == 'PROSES' || p.status == 'DIPROSES')
|
||||
.length;
|
||||
totalPengaduanSelesai.value =
|
||||
pengaduanList.where((p) => p.status == 'SELESAI').length;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error fetching pengaduan: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk mengambil data notifikasi
|
||||
Future<void> fetchNotifikasi() async {
|
||||
try {
|
||||
final response = await _supabaseService.client
|
||||
.from('notifikasi')
|
||||
.select('*')
|
||||
.eq('user_id', user!.id)
|
||||
.eq('dibaca', false)
|
||||
.count();
|
||||
|
||||
jumlahNotifikasiBelumDibaca.value = response.count;
|
||||
} catch (e) {
|
||||
print('Error fetching notifikasi: $e');
|
||||
jumlahNotifikasiBelumDibaca.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Navigasi ke halaman detail
|
||||
void goToPenyaluranDetail() {
|
||||
changeTab(1);
|
||||
|
@ -2,91 +2,147 @@ 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';
|
||||
import 'package:penyaluran_app/app/widgets/bantuan_card.dart';
|
||||
import 'package:penyaluran_app/app/widgets/section_header.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 Scaffold(
|
||||
body: 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(),
|
||||
],
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
controller.fetchData();
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildWelcomeSection(),
|
||||
const SizedBox(height: 24),
|
||||
_buildPenerimaanSummary(),
|
||||
const SizedBox(height: 24),
|
||||
_buildRecentPenerimaan(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWelcomeSection() {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 30,
|
||||
backgroundColor: Colors.blue.shade100,
|
||||
child: const Icon(
|
||||
Icons.person,
|
||||
size: 40,
|
||||
color: Colors.blue,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: Colors.blue.shade100,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: Colors.blue.shade700,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Selamat Datang,',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
controller.nama,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Selamat Datang,',
|
||||
const SizedBox(height: 16),
|
||||
const Divider(),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.home,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Alamat tidak tersedia',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
controller.nama,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.phone,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'No. HP tidak tersedia',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
controller.desa ?? 'Warga Desa',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_city,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
controller.desa ?? 'Desa tidak tersedia',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -94,393 +150,179 @@ class WargaDashboardView extends GetView<WargaDashboardController> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPenyaluranSummary() {
|
||||
Widget _buildPenerimaanSummary() {
|
||||
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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
// Hitung total bantuan uang dan non-uang
|
||||
double totalUang = 0;
|
||||
Map<String, double> totalNonUang = {};
|
||||
|
||||
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;
|
||||
for (var item in controller.penerimaPenyaluran) {
|
||||
if (item.statusPenerimaan == 'DITERIMA') {
|
||||
if (item.isUang == true && item.jumlahBantuan != null) {
|
||||
totalUang += item.jumlahBantuan!;
|
||||
} else if (item.isUang == false &&
|
||||
item.jumlahBantuan != null &&
|
||||
item.satuan != null) {
|
||||
if (totalNonUang.containsKey(item.satuan)) {
|
||||
totalNonUang[item.satuan!] =
|
||||
(totalNonUang[item.satuan] ?? 0) + item.jumlahBantuan!;
|
||||
} else {
|
||||
totalNonUang[item.satuan!] = item.jumlahBantuan!;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SectionHeader(
|
||||
title: 'Ringkasan Bantuan',
|
||||
titleStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (totalUang > 0)
|
||||
_buildSummaryItem(
|
||||
icon: Icons.attach_money,
|
||||
color: Colors.green,
|
||||
title: 'Total Bantuan Uang',
|
||||
value: currencyFormat.format(totalUang),
|
||||
),
|
||||
if (totalNonUang.isNotEmpty) ...[
|
||||
if (totalUang > 0) const SizedBox(height: 12),
|
||||
...totalNonUang.entries.map((entry) {
|
||||
return _buildSummaryItem(
|
||||
icon: Icons.inventory_2,
|
||||
color: Colors.blue,
|
||||
title: 'Total Bantuan ${entry.key}',
|
||||
value: '${entry.value} ${entry.key}',
|
||||
);
|
||||
}),
|
||||
],
|
||||
if (totalUang == 0 && totalNonUang.isEmpty)
|
||||
_buildSummaryItem(
|
||||
icon: Icons.info_outline,
|
||||
color: Colors.grey,
|
||||
title: 'Belum Ada Bantuan',
|
||||
value: 'Anda belum menerima bantuan',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPengaduanSummary() {
|
||||
Widget _buildSummaryItem({
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
required String title,
|
||||
required String value,
|
||||
}) {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: color,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRecentPenerimaan() {
|
||||
if (controller.penerimaPenyaluran.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final maxItems = controller.penerimaPenyaluran.length > 2
|
||||
? 2
|
||||
: controller.penerimaPenyaluran.length;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Pengaduan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
SectionHeader(
|
||||
title: 'Bantuan Terbaru',
|
||||
viewAllText: 'Lihat Semua',
|
||||
onViewAll: () {
|
||||
Get.toNamed('/warga-penerimaan');
|
||||
},
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: maxItems,
|
||||
itemBuilder: (context, index) {
|
||||
final item = controller.penerimaPenyaluran[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: BantuanCard(
|
||||
item: item,
|
||||
isCompact: true,
|
||||
onTap: () {
|
||||
Get.toNamed('/warga/detail-penerimaan',
|
||||
arguments: {'id': item.id});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (controller.penerimaPenyaluran.length > 2)
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
Get.toNamed('/warga-penerimaan');
|
||||
},
|
||||
icon: const Icon(Icons.list),
|
||||
label: const Text('Lihat Semua Bantuan'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
632
lib/app/modules/warga/views/warga_detail_penerimaan_view.dart
Normal file
632
lib/app/modules/warga/views/warga_detail_penerimaan_view.dart
Normal file
@ -0,0 +1,632 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
|
||||
import 'package:penyaluran_app/app/modules/warga/controllers/warga_dashboard_controller.dart';
|
||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||
import 'package:penyaluran_app/app/widgets/status_badge.dart';
|
||||
|
||||
class WargaDetailPenerimaanView extends GetView<WargaDashboardController> {
|
||||
const WargaDetailPenerimaanView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Map<String, dynamic> args = Get.arguments ?? {};
|
||||
final id = args['id'];
|
||||
|
||||
if (id == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Detail Penerimaan'),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('ID Penerimaan tidak ditemukan'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Konversi id ke string untuk memastikan kompatibilitas dengan model
|
||||
final String penyaluranId = id.toString();
|
||||
|
||||
// Cari data penerimaan berdasarkan ID
|
||||
final PenerimaPenyaluranModel? penyaluran = controller.penerimaPenyaluran
|
||||
.firstWhereOrNull((item) => item.id == penyaluranId);
|
||||
|
||||
if (penyaluran == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Detail Penerimaan'),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('Data penerimaan tidak ditemukan'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final bool isDiterima = penyaluran.statusPenerimaan == 'DITERIMA';
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Detail Penerimaan'),
|
||||
elevation: 0,
|
||||
backgroundColor: Get.theme.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
),
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Get.theme.primaryColor.withOpacity(0.05),
|
||||
Colors.white,
|
||||
],
|
||||
),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeaderSection(penyaluran),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailSection(penyaluran),
|
||||
const SizedBox(height: 16),
|
||||
_buildLocationSection(penyaluran),
|
||||
const SizedBox(height: 16),
|
||||
if (isDiterima) _buildBuktiPenerimaanSection(penyaluran),
|
||||
if (isDiterima) const SizedBox(height: 16),
|
||||
_buildAdditionalInfoSection(penyaluran),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderSection(PenerimaPenyaluranModel penyaluran) {
|
||||
final currencyFormat = NumberFormat.currency(
|
||||
locale: 'id',
|
||||
symbol: 'Rp ',
|
||||
decimalDigits: 0,
|
||||
);
|
||||
|
||||
// Format jumlah bantuan berdasarkan tipe (uang atau bukan)
|
||||
String formattedJumlah = '';
|
||||
if (penyaluran.jumlahBantuan != null) {
|
||||
if (penyaluran.isUang == true) {
|
||||
formattedJumlah = currencyFormat.format(penyaluran.jumlahBantuan);
|
||||
} else {
|
||||
formattedJumlah =
|
||||
'${penyaluran.jumlahBantuan} ${penyaluran.satuan ?? ''}';
|
||||
}
|
||||
} else {
|
||||
formattedJumlah = '-';
|
||||
}
|
||||
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
gradient: AppTheme.primaryGradient,
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
penyaluran.namaPenyaluran ??
|
||||
penyaluran.keterangan ??
|
||||
'Bantuan',
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
StatusBadge(
|
||||
status: penyaluran.statusPenerimaan ?? 'MENUNGGU',
|
||||
fontSize: 14,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (penyaluran.deskripsiPenyaluran != null &&
|
||||
penyaluran.deskripsiPenyaluran!.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Text(
|
||||
penyaluran.deskripsiPenyaluran!,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 3,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: (penyaluran.isUang == true
|
||||
? Colors.green
|
||||
: Colors.blue)
|
||||
.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
penyaluran.isUang == true
|
||||
? Icons.attach_money
|
||||
: Icons.inventory_2,
|
||||
color: penyaluran.isUang == true
|
||||
? Colors.green
|
||||
: Colors.blue,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Jumlah Bantuan',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
formattedJumlah,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: penyaluran.isUang == true
|
||||
? Colors.green.shade700
|
||||
: Colors.blue.shade700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailSection(PenerimaPenyaluranModel penyaluran) {
|
||||
return Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_outline,
|
||||
color: Get.theme.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Detail Bantuan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailItem(
|
||||
icon: Icons.category,
|
||||
title: 'Kategori',
|
||||
value: penyaluran.kategoriNama ?? 'Tidak tersedia',
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.calendar_today,
|
||||
title: 'Tanggal Penerimaan',
|
||||
value: penyaluran.tanggalPenerimaan != null
|
||||
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||
.format(penyaluran.tanggalPenerimaan!)
|
||||
: 'Belum diterima',
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.access_time,
|
||||
title: 'Waktu Penerimaan',
|
||||
value: penyaluran.tanggalPenerimaan != null
|
||||
? DateFormat('HH:mm', 'id_ID')
|
||||
.format(penyaluran.tanggalPenerimaan!)
|
||||
: 'Belum diterima',
|
||||
),
|
||||
if (penyaluran.keterangan != null &&
|
||||
penyaluran.keterangan!.isNotEmpty) ...[
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.note,
|
||||
title: 'Keterangan',
|
||||
value: penyaluran.keterangan!,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLocationSection(PenerimaPenyaluranModel penyaluran) {
|
||||
return Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_on,
|
||||
color: Get.theme.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Lokasi Penerimaan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailItem(
|
||||
icon: Icons.place,
|
||||
title: 'Tempat Penerimaan',
|
||||
value: penyaluran.lokasiPenyaluranNama ?? 'Tidak tersedia',
|
||||
),
|
||||
if (penyaluran.lokasiPenyaluranAlamat != null &&
|
||||
penyaluran.lokasiPenyaluranAlamat!.isNotEmpty) ...[
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.map,
|
||||
title: 'Alamat',
|
||||
value: penyaluran.lokasiPenyaluranAlamat!,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
// TODO: Implementasi peta lokasi jika koordinat tersedia
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBuktiPenerimaanSection(PenerimaPenyaluranModel penyaluran) {
|
||||
return Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.verified,
|
||||
color: Colors.green.shade700,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Bukti Penerimaan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Bukti Penerimaan (Foto)
|
||||
if (penyaluran.buktiPenerimaan != null &&
|
||||
penyaluran.buktiPenerimaan!.isNotEmpty) ...[
|
||||
const Text(
|
||||
'Foto Bukti Penerimaan',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.network(
|
||||
penyaluran.buktiPenerimaan!,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.broken_image,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Gambar tidak dapat dimuat',
|
||||
style: TextStyle(color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
] else ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.image_not_supported,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
'Bukti penerimaan belum diunggah',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
|
||||
// Tanda Tangan
|
||||
const Divider(height: 24),
|
||||
const Text(
|
||||
'Tanda Tangan Penerima',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (penyaluran.tandaTangan != null &&
|
||||
penyaluran.tandaTangan!.isNotEmpty) ...[
|
||||
Container(
|
||||
height: 150,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.network(
|
||||
penyaluran.tandaTangan!,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.broken_image,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Tanda tangan tidak dapat dimuat',
|
||||
style: TextStyle(color: Colors.grey.shade600),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.draw,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
'Tanda tangan belum tersedia',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAdditionalInfoSection(PenerimaPenyaluranModel penyaluran) {
|
||||
return Card(
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.more_horiz,
|
||||
color: Get.theme.primaryColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Informasi Tambahan',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailItem(
|
||||
icon: Icons.numbers,
|
||||
title: 'ID Penerimaan',
|
||||
value: '${penyaluran.id}',
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.person,
|
||||
title: 'Penerima',
|
||||
value: controller.nama,
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildDetailItem(
|
||||
icon: Icons.update,
|
||||
title: 'Terakhir Diperbarui',
|
||||
value: penyaluran.tanggalPenerimaan != null
|
||||
? DateFormat('dd MMMM yyyy HH:mm', 'id_ID')
|
||||
.format(penyaluran.tanggalPenerimaan!)
|
||||
: 'Tidak tersedia',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailItem({
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String value,
|
||||
}) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
112
lib/app/modules/warga/views/warga_penerimaan_view.dart
Normal file
112
lib/app/modules/warga/views/warga_penerimaan_view.dart
Normal file
@ -0,0 +1,112 @@
|
||||
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/widgets/bantuan_card.dart';
|
||||
|
||||
class WargaPenerimaanView extends GetView<WargaDashboardController> {
|
||||
const WargaPenerimaanView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
controller.fetchData();
|
||||
},
|
||||
child: controller.penerimaPenyaluran.isEmpty
|
||||
? _buildEmptyState()
|
||||
: _buildPenerimaanList(),
|
||||
);
|
||||
}),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
// Navigasi ke halaman riwayat penerimaan
|
||||
Get.toNamed('/riwayat-penyaluran');
|
||||
},
|
||||
backgroundColor: Colors.blue,
|
||||
child: const Icon(Icons.history),
|
||||
tooltip: 'Riwayat Penerimaan',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.volunteer_activism,
|
||||
size: 80,
|
||||
color: Colors.blue.shade400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Belum Ada Penerimaan',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Text(
|
||||
'Anda belum menerima bantuan. Bantuan akan muncul di sini ketika Anda menerimanya.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
controller.fetchData();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Muat Ulang'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPenerimaanList() {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: controller.penerimaPenyaluran.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = controller.penerimaPenyaluran[index];
|
||||
|
||||
return BantuanCard(
|
||||
item: item,
|
||||
onTap: () {
|
||||
// Navigasi ke detail penerimaan
|
||||
Get.toNamed('/warga/detail-penerimaan', arguments: {'id': item.id});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -77,7 +77,32 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
|
||||
itemCount: controller.pengaduan.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = controller.pengaduan[index];
|
||||
final isProses = item.status == 'PROSES';
|
||||
|
||||
// Tentukan status dan warna berdasarkan status pengaduan
|
||||
Color statusColor;
|
||||
String statusText;
|
||||
|
||||
switch (item.status?.toUpperCase()) {
|
||||
case 'PROSES':
|
||||
case 'DIPROSES':
|
||||
case 'TINDAKAN':
|
||||
statusColor = Colors.orange;
|
||||
statusText = 'Proses';
|
||||
break;
|
||||
case 'SELESAI':
|
||||
statusColor = Colors.green;
|
||||
statusText = 'Selesai';
|
||||
break;
|
||||
case 'DITOLAK':
|
||||
statusColor = Colors.red;
|
||||
statusText = 'Ditolak';
|
||||
break;
|
||||
default:
|
||||
statusColor = Colors.grey;
|
||||
statusText = item.status ?? 'Tidak Diketahui';
|
||||
}
|
||||
|
||||
final isProses = statusText == 'Proses';
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
@ -115,18 +140,16 @@ class WargaPengaduanView extends GetView<WargaDashboardController> {
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isProses
|
||||
? Colors.orange.withOpacity(0.1)
|
||||
: Colors.green.withOpacity(0.1),
|
||||
color: statusColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: isProses ? Colors.orange : Colors.green,
|
||||
color: statusColor,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
isProses ? 'Proses' : 'Selesai',
|
||||
statusText,
|
||||
style: TextStyle(
|
||||
color: isProses ? Colors.orange : Colors.green,
|
||||
color: statusColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
|
@ -1,187 +0,0 @@
|
||||
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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ 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_penerimaan_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';
|
||||
@ -23,7 +23,7 @@ class WargaView extends GetView<WargaDashboardController> {
|
||||
case 0:
|
||||
return const Text('Dashboard Warga');
|
||||
case 1:
|
||||
return const Text('Penyaluran Bantuan');
|
||||
return const Text('Penerimaan Bantuan');
|
||||
case 2:
|
||||
return const Text('Pengaduan');
|
||||
default:
|
||||
@ -96,7 +96,7 @@ class WargaView extends GetView<WargaDashboardController> {
|
||||
),
|
||||
DrawerMenuItem(
|
||||
icon: Icons.volunteer_activism_outlined,
|
||||
title: 'Penyaluran',
|
||||
title: 'Penerimaan',
|
||||
isSelected: controller.activeTabIndex.value == 1,
|
||||
onTap: () => controller.changeTab(1),
|
||||
),
|
||||
@ -125,7 +125,7 @@ class WargaView extends GetView<WargaDashboardController> {
|
||||
case 0:
|
||||
return const WargaDashboardView();
|
||||
case 1:
|
||||
return const WargaPenyaluranView();
|
||||
return const WargaPenerimaanView();
|
||||
case 2:
|
||||
return const WargaPengaduanView();
|
||||
default:
|
||||
@ -144,7 +144,7 @@ class WargaView extends GetView<WargaDashboardController> {
|
||||
AppBottomNavigationBarItem(
|
||||
icon: Icons.volunteer_activism_outlined,
|
||||
activeIcon: Icons.volunteer_activism,
|
||||
label: 'Penyaluran',
|
||||
label: 'Penerimaan',
|
||||
badgeCount: controller.totalPenyaluranDiterima.value > 0
|
||||
? controller.totalPenyaluranDiterima.value
|
||||
: null,
|
||||
|
@ -22,10 +22,11 @@ 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/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_penerimaan_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';
|
||||
import 'package:penyaluran_app/app/modules/warga/views/warga_detail_penerimaan_view.dart';
|
||||
|
||||
part 'app_routes.dart';
|
||||
|
||||
@ -46,12 +47,12 @@ class AppPages {
|
||||
binding: AuthBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.wargaDashboard,
|
||||
name: Routes.wargaDashboard,
|
||||
page: () => const WargaView(),
|
||||
binding: WargaBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.wargaPenyaluran,
|
||||
name: Routes.wargaPenerimaan,
|
||||
page: () {
|
||||
final controller = Get.find<WargaDashboardController>();
|
||||
controller.activeTabIndex.value = 1;
|
||||
@ -60,7 +61,7 @@ class AppPages {
|
||||
binding: WargaBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.wargaPengaduan,
|
||||
name: Routes.wargaPengaduan,
|
||||
page: () {
|
||||
final controller = Get.find<WargaDashboardController>();
|
||||
controller.activeTabIndex.value = 2;
|
||||
@ -123,5 +124,10 @@ class AppPages {
|
||||
page: () => DetailPenyaluranPage(),
|
||||
binding: PenyaluranBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: Routes.wargaDetailPenerimaan,
|
||||
page: () => const WargaDetailPenerimaanView(),
|
||||
binding: WargaBinding(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ abstract class Routes {
|
||||
static const login = _Paths.login;
|
||||
static const register = _Paths.register;
|
||||
static const wargaDashboard = _Paths.wargaDashboard;
|
||||
static const wargaPenyaluran = _Paths.wargaPenyaluran;
|
||||
static const wargaPenerimaan = _Paths.wargaPenerimaan;
|
||||
static const wargaPengaduan = _Paths.wargaPengaduan;
|
||||
static const petugasVerifikasiDashboard = _Paths.petugasVerifikasiDashboard;
|
||||
static const petugasDesaDashboard = _Paths.petugasDesaDashboard;
|
||||
@ -27,6 +27,7 @@ abstract class Routes {
|
||||
static const laporanPenyaluran = _Paths.laporanPenyaluran;
|
||||
static const detailPenyaluran = _Paths.detailPenyaluran;
|
||||
static const riwayatPenyaluran = _Paths.riwayatPenyaluran;
|
||||
static const wargaDetailPenerimaan = _Paths.wargaDetailPenerimaan;
|
||||
}
|
||||
|
||||
abstract class _Paths {
|
||||
@ -35,7 +36,7 @@ abstract class _Paths {
|
||||
static const login = '/login';
|
||||
static const register = '/register';
|
||||
static const wargaDashboard = '/warga-dashboard';
|
||||
static const wargaPenyaluran = '/warga-penyaluran';
|
||||
static const wargaPenerimaan = '/warga-penerimaan';
|
||||
static const wargaPengaduan = '/warga-pengaduan';
|
||||
static const petugasVerifikasiDashboard = '/petugas-verifikasi-dashboard';
|
||||
static const petugasDesaDashboard = '/petugas-desa-dashboard';
|
||||
@ -56,4 +57,5 @@ abstract class _Paths {
|
||||
static const laporanPenyaluran = '/laporan-penyaluran';
|
||||
static const detailPenyaluran = '/detail-penyaluran';
|
||||
static const riwayatPenyaluran = '/petugas-desa/riwayat-penyaluran';
|
||||
static const wargaDetailPenerimaan = '/warga/detail-penerimaan';
|
||||
}
|
||||
|
280
lib/app/widgets/bantuan_card.dart
Normal file
280
lib/app/widgets/bantuan_card.dart
Normal file
@ -0,0 +1,280 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:penyaluran_app/app/data/models/penerima_penyaluran_model.dart';
|
||||
import 'package:penyaluran_app/app/widgets/status_badge.dart';
|
||||
|
||||
class BantuanCard extends StatelessWidget {
|
||||
final PenerimaPenyaluranModel item;
|
||||
final VoidCallback? onTap;
|
||||
final bool isCompact;
|
||||
|
||||
const BantuanCard({
|
||||
Key? key,
|
||||
required this.item,
|
||||
this.onTap,
|
||||
this.isCompact = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currencyFormat = NumberFormat.currency(
|
||||
locale: 'id',
|
||||
symbol: 'Rp ',
|
||||
decimalDigits: 0,
|
||||
);
|
||||
|
||||
// Format jumlah bantuan berdasarkan tipe (uang atau bukan)
|
||||
String formattedJumlah = '';
|
||||
if (item.jumlahBantuan != null) {
|
||||
if (item.isUang == true) {
|
||||
formattedJumlah = currencyFormat.format(item.jumlahBantuan);
|
||||
} else {
|
||||
formattedJumlah = '${item.jumlahBantuan} ${item.satuan ?? ''}';
|
||||
}
|
||||
} else {
|
||||
formattedJumlah = '-';
|
||||
}
|
||||
|
||||
// Tampilan kompak untuk daftar ringkasan
|
||||
if (isCompact) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: (item.isUang == true ? Colors.green : Colors.blue)
|
||||
.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(
|
||||
item.isUang == true
|
||||
? Icons.attach_money
|
||||
: Icons.inventory_2,
|
||||
color: item.isUang == true ? Colors.green : Colors.blue,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.namaPenyaluran ?? item.keterangan ?? 'Bantuan',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.kategoriNama ?? 'Bantuan',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade700,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.tanggalPenerimaan != null
|
||||
? DateFormat('dd MMMM yyyy', 'id_ID')
|
||||
.format(item.tanggalPenerimaan!)
|
||||
: '-',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
StatusBadge(
|
||||
status: item.statusPenerimaan ?? 'MENUNGGU',
|
||||
fontSize: 10,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
formattedJumlah,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: item.isUang == true ? Colors.green : Colors.blue,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Tampilan detail untuk halaman daftar lengkap
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 2,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
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.namaPenyaluran ?? item.keterangan ?? 'Bantuan',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
StatusBadge(
|
||||
status: item.statusPenerimaan ?? 'MENUNGGU',
|
||||
),
|
||||
],
|
||||
),
|
||||
if (item.deskripsiPenyaluran != null &&
|
||||
item.deskripsiPenyaluran!.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
item.deskripsiPenyaluran!,
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade700,
|
||||
fontSize: 14,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.category,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
item.kategoriNama ?? 'Bantuan',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade700,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
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.location_on,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.lokasiPenyaluranNama ?? 'Lokasi tidak tersedia',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
item.isUang == true
|
||||
? Icons.attach_money
|
||||
: Icons.inventory_2,
|
||||
size: 16,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
formattedJumlah,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: item.isUang == true ? Colors.green : Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(height: 1),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: onTap,
|
||||
icon: const Icon(Icons.visibility),
|
||||
label: const Text('Lihat Detail'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
51
lib/app/widgets/section_header.dart
Normal file
51
lib/app/widgets/section_header.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback? onViewAll;
|
||||
final String? viewAllText;
|
||||
final Widget? trailing;
|
||||
final EdgeInsets padding;
|
||||
final TextStyle? titleStyle;
|
||||
|
||||
const SectionHeader({
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.onViewAll,
|
||||
this.viewAllText = 'Lihat Semua',
|
||||
this.trailing,
|
||||
this.padding = const EdgeInsets.only(bottom: 12),
|
||||
this.titleStyle,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: titleStyle ??
|
||||
const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (trailing != null)
|
||||
trailing!
|
||||
else if (onViewAll != null)
|
||||
TextButton(
|
||||
onPressed: onViewAll,
|
||||
child: Text(viewAllText!),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
minimumSize: const Size(0, 36),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
79
lib/app/widgets/status_badge.dart
Normal file
79
lib/app/widgets/status_badge.dart
Normal file
@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StatusBadge extends StatelessWidget {
|
||||
final String status;
|
||||
final Map<String, Color>? customColors;
|
||||
final Map<String, String>? customLabels;
|
||||
final double fontSize;
|
||||
final EdgeInsets padding;
|
||||
|
||||
const StatusBadge({
|
||||
Key? key,
|
||||
required this.status,
|
||||
this.customColors,
|
||||
this.customLabels,
|
||||
this.fontSize = 12,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final String statusUpper = status.toUpperCase();
|
||||
|
||||
// Default colors for common statuses
|
||||
final Map<String, Color> defaultColors = {
|
||||
'DITERIMA': Colors.green,
|
||||
'MENUNGGU': Colors.orange,
|
||||
'DITOLAK': Colors.red,
|
||||
'PROSES': Colors.orange,
|
||||
'DIPROSES': Colors.orange,
|
||||
'TINDAKAN': Colors.orange,
|
||||
'SELESAI': Colors.green,
|
||||
'TERVERIFIKASI': Colors.green,
|
||||
};
|
||||
|
||||
// Default labels for common statuses
|
||||
final Map<String, String> defaultLabels = {
|
||||
'DITERIMA': 'Diterima',
|
||||
'MENUNGGU': 'Menunggu',
|
||||
'DITOLAK': 'Ditolak',
|
||||
'PROSES': 'Proses',
|
||||
'DIPROSES': 'Proses',
|
||||
'TINDAKAN': 'Tindakan',
|
||||
'SELESAI': 'Selesai',
|
||||
'TERVERIFIKASI': 'Terverifikasi',
|
||||
};
|
||||
|
||||
// Determine color and label based on status
|
||||
final Color color =
|
||||
(customColors != null && customColors!.containsKey(statusUpper))
|
||||
? customColors![statusUpper]!
|
||||
: defaultColors.containsKey(statusUpper)
|
||||
? defaultColors[statusUpper]!
|
||||
: Colors.grey;
|
||||
|
||||
final String label =
|
||||
(customLabels != null && customLabels!.containsKey(statusUpper))
|
||||
? customLabels![statusUpper]!
|
||||
: defaultLabels.containsKey(statusUpper)
|
||||
? defaultLabels[statusUpper]!
|
||||
: status;
|
||||
|
||||
return Container(
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: color),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetMaterialApp(
|
||||
title: 'Penyaluran App',
|
||||
title: 'Penerimaan App',
|
||||
theme: AppTheme.lightTheme,
|
||||
darkTheme: AppTheme.darkTheme,
|
||||
themeMode: ThemeMode.light, // Default ke tema terang
|
||||
|
Reference in New Issue
Block a user