Perbarui model LokasiPenyaluran dengan mengganti properti alamat menjadi alamatLengkap. Modifikasi tampilan dan controller di modul donatur dan petugas desa untuk menggunakan properti baru ini. Tambahkan fungsionalitas untuk mengelola lokasi penyaluran, termasuk penghapusan dan pengeditan lokasi. Perbarui rute aplikasi untuk menambahkan halaman lokasi penyaluran baru dan pastikan controller terdaftar dengan benar.
This commit is contained in:
0
JadwalPenyaluranController())
Normal file
0
JadwalPenyaluranController())
Normal file
@ -2,27 +2,27 @@ C/C++ Structured LogO
|
||||
M
|
||||
KC:\dev\flutter\packages\flutter_tools\gradle\src\main\groovy\CMakeLists.txtC
|
||||
A
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint <08><>Ƹ<EFBFBD>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>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>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>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>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>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>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>2 ~
|
||||
|
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\arm64-v8a\compile_commands.json.bin <08><>Ƹ<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>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>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 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
<EFBFBD>
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\additional_project_files.txt Ƹ<EFBFBD>2 <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
~
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build.json Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\android_gradle_build_mini.json <EFBFBD><EFBFBD><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 Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2r
|
||||
p
|
||||
nD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja <EFBFBD><EFBFBD><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 Ƹ<EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2v
|
||||
t
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2{
|
||||
rD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build.ninja.txt Ƹ<EFBFBD>2{
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\build_file_index.txt Ƹ<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 Ƹ<CE81>2 <09>
|
||||
~
|
||||
~
|
||||
|D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\compile_commands.json.bin Ƹ<CE81>2
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>
|
||||
<EFBFBD>D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\armeabi-v7a\metadata_generation_command.txt Ƹ<CE81>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 Ƹ<CE81>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>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>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>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2}
|
||||
{
|
||||
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json <08><><EFBFBD><EFBFBD><EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
|
||||
yD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\android_gradle_build_mini.json <08><>Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2j
|
||||
h
|
||||
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja <08><><EFBFBD><EFBFBD><EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
|
||||
fD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja <08><>Ƹ<EFBFBD>2<18><> <20><><EFBFBD><EFBFBD><EFBFBD>2n
|
||||
l
|
||||
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt <08><><EFBFBD><EFBFBD><EFBFBD>2s
|
||||
jD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build.ninja.txt <08><>Ƹ<EFBFBD>2s
|
||||
q
|
||||
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt <08><><EFBFBD><EFBFBD><EFBFBD>2
|
||||
oD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\build_file_index.txt <08><>Ƹ<EFBFBD>2
|
||||
K <20><><EFBFBD><EFBFBD><EFBFBD>2t
|
||||
r
|
||||
r
|
||||
pD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json <08><>Ƹ<EFBFBD>2 x
|
||||
v
|
||||
v
|
||||
tD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\compile_commands.json.bin <08><>Ƹ<EFBFBD>2
|
||||
~
|
||||
|
|
||||
|
|
||||
zD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\metadata_generation_command.txt <08><>Ƹ<EFBFBD>2<18> <20><><EFBFBD><EFBFBD><EFBFBD>2q
|
||||
o
|
||||
o
|
||||
mD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86\prefab_config.json <08><>Ƹ<EFBFBD>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>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>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>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>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>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>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>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 ʃƸ<CA83>2 {
|
||||
y
|
||||
y
|
||||
wD:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\compile_commands.json.bin ʃƸ<CA83>2
|
||||
<EFBFBD>
|
||||
|
||||
|
||||
}D:\KULIAH\Matkul\SKRIPSI\penyaluran_app\penyaluran_app\android\app\.cxx\Debug\626b5o2n\x86_64\metadata_generation_command.txt ʃƸ<CA83>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 ʃƸ<CA83>2
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2y
|
||||
( <20><><EFBFBD><EFBFBD><EFBFBD>2y
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
||||
class LokasiPenyaluranModel {
|
||||
final String id;
|
||||
final String nama;
|
||||
final String? alamat;
|
||||
final String? alamatLengkap;
|
||||
final String? desa;
|
||||
final String? kecamatan;
|
||||
final String? kabupaten;
|
||||
@ -19,7 +19,7 @@ class LokasiPenyaluranModel {
|
||||
LokasiPenyaluranModel({
|
||||
required this.id,
|
||||
required this.nama,
|
||||
this.alamat,
|
||||
this.alamatLengkap,
|
||||
this.desa,
|
||||
this.kecamatan,
|
||||
this.kabupaten,
|
||||
@ -42,7 +42,7 @@ class LokasiPenyaluranModel {
|
||||
LokasiPenyaluranModel(
|
||||
id: json["id"],
|
||||
nama: json["nama"],
|
||||
alamat: json["alamat"],
|
||||
alamatLengkap: json["alamat_lengkap"],
|
||||
desa: json["desa"],
|
||||
kecamatan: json["kecamatan"],
|
||||
kabupaten: json["kabupaten"],
|
||||
@ -61,7 +61,7 @@ class LokasiPenyaluranModel {
|
||||
Map<String, dynamic> toJson() => {
|
||||
"id": id,
|
||||
"nama": nama,
|
||||
"alamat": alamat,
|
||||
"alamat_lengkap": alamatLengkap,
|
||||
"desa": desa,
|
||||
"kecamatan": kecamatan,
|
||||
"kabupaten": kabupaten,
|
||||
|
@ -1196,7 +1196,7 @@ class _FormPenitipanBantuanState extends State<FormPenitipanBantuan> {
|
||||
value: selectedLokasiPenyaluranId,
|
||||
items: controller.lokasiPenyaluran.map((lokasi) {
|
||||
String alamatLengkap = [
|
||||
lokasi.alamat,
|
||||
lokasi.alamatLengkap,
|
||||
lokasi.desa,
|
||||
lokasi.kecamatan,
|
||||
lokasi.kabupaten,
|
||||
|
@ -101,7 +101,8 @@ class DetailPenyaluranController extends GetxController {
|
||||
// Ambil data penerima penyaluran
|
||||
final penerimaPenyaluranData = await _supabaseService.client
|
||||
.from('penerima_penyaluran')
|
||||
.select('*, warga:warga_id(*)')
|
||||
.select(
|
||||
'*, warga:warga_id(*, desa:desa_id(*)), stok_bantuan:stok_bantuan_id(*)')
|
||||
.eq('penyaluran_bantuan_id', penyaluranId);
|
||||
|
||||
final List<PenerimaPenyaluranModel> penerima = [];
|
||||
|
@ -588,19 +588,21 @@ class JadwalPenyaluranController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> loadLokasiPenyaluranData() async {
|
||||
isLokasiLoading.value = true;
|
||||
try {
|
||||
isLokasiLoading(true);
|
||||
final lokasiData = await _supabaseService.getAllLokasiPenyaluran();
|
||||
if (lokasiData != null) {
|
||||
for (var lokasi in lokasiData) {
|
||||
final lokasiModel = LokasiPenyaluranModel.fromJson(lokasi);
|
||||
lokasiPenyaluranCache[lokasiModel.id] = lokasiModel;
|
||||
}
|
||||
final data = await _supabaseService.getLokasiPenyaluran(
|
||||
petugasId: user?.id,
|
||||
);
|
||||
|
||||
// Bersihkan cache dan tambahkan data baru
|
||||
lokasiPenyaluranCache.clear();
|
||||
for (final lokasi in data) {
|
||||
lokasiPenyaluranCache[lokasi.id] = lokasi;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error loading lokasi penyaluran data: $e');
|
||||
print('Error loading lokasi penyaluran: $e');
|
||||
} finally {
|
||||
isLokasiLoading(false);
|
||||
isLokasiLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -869,4 +871,92 @@ class JadwalPenyaluranController extends GetxController {
|
||||
// Kembalikan representasi string dari hash
|
||||
return hash.toString();
|
||||
}
|
||||
|
||||
// Mengedit lokasi penyaluran
|
||||
void editLokasiPenyaluran(String lokasiId) {
|
||||
if (lokasiPenyaluranCache.containsKey(lokasiId)) {
|
||||
// Ambil data lokasi yang akan diedit
|
||||
final lokasi = lokasiPenyaluranCache[lokasiId];
|
||||
|
||||
// Navigasi ke halaman edit dengan membawa data lokasi
|
||||
Get.toNamed('/petugas-desa/edit-lokasi-penyaluran', arguments: {
|
||||
'lokasi_id': lokasiId,
|
||||
'lokasi': lokasi,
|
||||
});
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Gagal',
|
||||
'Data lokasi tidak ditemukan',
|
||||
backgroundColor: Colors.red.shade100,
|
||||
colorText: Colors.red.shade800,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Menghapus lokasi penyaluran
|
||||
void hapusLokasiPenyaluran(String lokasiId) {
|
||||
// Tampilkan dialog konfirmasi penghapusan
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: const Text('Konfirmasi Hapus'),
|
||||
content: const Text('Apakah Anda yakin ingin menghapus lokasi ini?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: const Text('BATAL'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Get.back(); // Tutup dialog
|
||||
|
||||
// Tampilkan loading
|
||||
Get.dialog(
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
|
||||
try {
|
||||
// Lakukan penghapusan di database
|
||||
await _supabaseService.deleteLokasiPenyaluran(lokasiId);
|
||||
|
||||
// Hapus data dari cache lokal
|
||||
lokasiPenyaluranCache.remove(lokasiId);
|
||||
|
||||
// Tutup dialog loading
|
||||
Get.back();
|
||||
|
||||
// Tampilkan notifikasi berhasil
|
||||
Get.snackbar(
|
||||
'Berhasil',
|
||||
'Lokasi penyaluran berhasil dihapus',
|
||||
backgroundColor: Colors.green.shade100,
|
||||
colorText: Colors.green.shade800,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
} catch (e) {
|
||||
// Tutup dialog loading
|
||||
Get.back();
|
||||
|
||||
// Tampilkan pesan error
|
||||
Get.snackbar(
|
||||
'Gagal',
|
||||
'Terjadi kesalahan: ${e.toString()}',
|
||||
backgroundColor: Colors.red.shade100,
|
||||
colorText: Colors.red.shade800,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
),
|
||||
child: const Text('HAPUS'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
507
lib/app/modules/petugas_desa/views/lokasi_penyaluran_view.dart
Normal file
507
lib/app/modules/petugas_desa/views/lokasi_penyaluran_view.dart
Normal file
@ -0,0 +1,507 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/controllers/jadwal_penyaluran_controller.dart';
|
||||
import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
||||
|
||||
class LokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
const LokasiPenyaluranView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Lokasi Penyaluran'),
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: _buildLokasiPenyaluranList(),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () => Get.toNamed(Routes.tambahLokasiPenyaluran),
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
icon: const Icon(Icons.add_location, color: Colors.white),
|
||||
label:
|
||||
const Text('Tambah Lokasi', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLokasiPenyaluranList() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => controller.loadLokasiPenyaluranData(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header dengan gradient yang lebih menarik
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.blue.shade700,
|
||||
Colors.blue.shade500,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.blue.shade200.withOpacity(0.5),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 5),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'Daftar Lokasi Penyaluran',
|
||||
style: Theme.of(Get.context!)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Kelola lokasi penyaluran bantuan untuk masyarakat dengan lebih mudah',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Counter jumlah lokasi
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Obx(() => Text(
|
||||
'${controller.lokasiPenyaluranCache.length} Lokasi Terdaftar',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Daftar Lokasi
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
if (controller.isLokasiLoading.value) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (controller.lokasiPenyaluranCache.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.location_off,
|
||||
size: 64,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Belum ada lokasi penyaluran',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Tambahkan lokasi penyaluran untuk memulai',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () =>
|
||||
Get.toNamed(Routes.tambahLokasiPenyaluran),
|
||||
icon: const Icon(Icons.add_location),
|
||||
label: const Text('Tambah Lokasi'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue.shade600,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 100),
|
||||
itemCount: controller.lokasiPenyaluranCache.length +
|
||||
1, // +1 untuk footer
|
||||
itemBuilder: (context, index) {
|
||||
// Footer item
|
||||
if (index == controller.lokasiPenyaluranCache.length) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Text(
|
||||
'Tidak ada lokasi lainnya',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey.shade500,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Item lokasi normal
|
||||
final lokasi = controller.lokasiPenyaluranCache.values
|
||||
.elementAt(index);
|
||||
final lokasiId =
|
||||
controller.lokasiPenyaluranCache.keys.elementAt(index);
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
onTap: () {
|
||||
// Tambahkan aksi ketika card diklik
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Icon Lokasi dengan latar belakang
|
||||
Hero(
|
||||
tag: 'lokasi_icon_$lokasiId',
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade50,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
Colors.blue.withOpacity(0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.blue.shade700,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Informasi utama lokasi
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Hero(
|
||||
tag: 'lokasi_nama_$lokasiId',
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Text(
|
||||
lokasi.nama,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
if (lokasi.alamatLengkap != null &&
|
||||
lokasi.alamatLengkap!.isNotEmpty)
|
||||
Text(
|
||||
lokasi.alamatLengkap!,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
// Indikator koordinat tersedia
|
||||
if (lokasi.latitude != null &&
|
||||
lokasi.longitude != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.gps_fixed,
|
||||
size: 14,
|
||||
color: Colors.blue.shade500,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Koordinat tersedia',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color:
|
||||
Colors.blue.shade500,
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Badge "NEW" jika lokasi baru dibuat (kurang dari 3 hari)
|
||||
if (_isNewLocation(lokasi.createdAt))
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade500,
|
||||
borderRadius:
|
||||
BorderRadius.circular(12),
|
||||
),
|
||||
child: const Text(
|
||||
'BARU',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Divider dan tag kategori
|
||||
if (lokasi.isLokasiTitip ||
|
||||
(lokasi.desa != null &&
|
||||
lokasi.desa!.isNotEmpty))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Divider(color: Colors.grey.shade200),
|
||||
),
|
||||
|
||||
// Informasi tambahan dan tag
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
// Tag lokasi penitipan jika ada
|
||||
if (lokasi.isLokasiTitip)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade100,
|
||||
borderRadius:
|
||||
BorderRadius.circular(30),
|
||||
border: Border.all(
|
||||
color: Colors.green.shade300),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 16,
|
||||
color: Colors.green.shade800,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Lokasi Penitipan',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.green.shade800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Tag informasi desa jika ada
|
||||
if (lokasi.desa != null &&
|
||||
lokasi.desa!.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade50,
|
||||
borderRadius:
|
||||
BorderRadius.circular(30),
|
||||
border: Border.all(
|
||||
color: Colors.blue.shade200),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_city,
|
||||
size: 16,
|
||||
color: Colors.blue.shade800,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
lokasi.desa!,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue.shade800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Tombol aksi
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
// Tombol Hapus
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// Aksi hapus lokasi
|
||||
controller.hapusLokasiPenyaluran(
|
||||
lokasiId);
|
||||
},
|
||||
borderRadius:
|
||||
BorderRadius.circular(30),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0, vertical: 4.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.delete_outline,
|
||||
color: Colors.red.shade600,
|
||||
size: 18),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Hapus',
|
||||
style: TextStyle(
|
||||
color: Colors.red.shade600,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method untuk menentukan apakah lokasi baru dibuat (kurang dari 3 hari)
|
||||
bool _isNewLocation(DateTime createdAt) {
|
||||
final now = DateTime.now();
|
||||
final difference = now.difference(createdAt);
|
||||
return difference.inDays < 3;
|
||||
}
|
||||
}
|
@ -5,12 +5,16 @@ import 'package:penyaluran_app/app/theme/app_theme.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/components/jadwal_section_widget.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/components/calendar_view_widget.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/tambah_penyaluran_view.dart';
|
||||
import 'package:penyaluran_app/app/routes/app_pages.dart';
|
||||
|
||||
class PenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
const PenyaluranView({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Memastikan controller tersedia
|
||||
if (!Get.isRegistered<JadwalPenyaluranController>()) {
|
||||
Get.put(JadwalPenyaluranController());
|
||||
}
|
||||
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
@ -84,11 +88,6 @@ class PenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
// Ringkasan jadwal
|
||||
_buildJadwalSummary(Get.context!),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Tombol untuk mengelola lokasi penyaluran
|
||||
_buildLokasiPenyaluranSection(),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Jadwal hari ini
|
||||
@ -237,240 +236,4 @@ class PenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Widget untuk menampilkan section lokasi penyaluran
|
||||
Widget _buildLokasiPenyaluranSection() {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(color: Colors.blue.shade100, width: 1),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Lokasi Penyaluran',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue.shade800,
|
||||
),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
// Menampilkan dialog daftar lokasi penyaluran
|
||||
_showLokasiPenyaluranDialog();
|
||||
},
|
||||
icon: const Icon(Icons.map, size: 16),
|
||||
label: const Text('Lihat Lokasi'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.blue,
|
||||
side: BorderSide(color: Colors.blue.shade300),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Kelola lokasi penyaluran bantuan untuk masyarakat dengan lebih mudah',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => Get.toNamed(Routes.tambahLokasiPenyaluran),
|
||||
icon: const Icon(Icons.add_location, size: 16),
|
||||
label: const Text('Tambah Lokasi Penyaluran Baru'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue.shade50,
|
||||
foregroundColor: Colors.blue.shade700,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blue.shade200),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Fungsi untuk menampilkan dialog daftar lokasi penyaluran
|
||||
void _showLokasiPenyaluranDialog() {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Daftar Lokasi Penyaluran',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue.shade800,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Get.back(),
|
||||
icon: const Icon(Icons.close),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: Get.height * 0.5,
|
||||
),
|
||||
width: double.infinity,
|
||||
child: Obx(() {
|
||||
if (controller.isLokasiLoading.value) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (controller.lokasiPenyaluranCache.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.location_off,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Belum ada lokasi penyaluran',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
Get.toNamed(Routes.tambahLokasiPenyaluran);
|
||||
},
|
||||
icon: const Icon(Icons.add_location),
|
||||
label: const Text('Tambah Lokasi'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: controller.lokasiPenyaluranCache.length,
|
||||
itemBuilder: (context, index) {
|
||||
final lokasi = controller.lokasiPenyaluranCache.values
|
||||
.elementAt(index);
|
||||
final lokasiId = controller.lokasiPenyaluranCache.keys
|
||||
.elementAt(index);
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
lokasi.nama,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (lokasi.alamat != null &&
|
||||
lokasi.alamat!.isNotEmpty)
|
||||
Text(lokasi.alamat!),
|
||||
Row(
|
||||
children: [
|
||||
if (lokasi.isLokasiTitip)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade100,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
'Lokasi Penitipan',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.green.shade800,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade50,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.blue.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
Get.toNamed(Routes.tambahLokasiPenyaluran);
|
||||
},
|
||||
child: const Text('Tambah Lokasi Baru'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -386,6 +386,15 @@ class PetugasDesaView extends GetView<PetugasDesaController> {
|
||||
Get.toNamed('/daftar-donatur');
|
||||
},
|
||||
),
|
||||
_buildMenuItem(
|
||||
icon: Icons.location_on_outlined,
|
||||
activeIcon: Icons.location_on,
|
||||
title: 'Lokasi Penyaluran',
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Get.toNamed('/lokasi-penyaluran');
|
||||
},
|
||||
),
|
||||
_buildMenuItem(
|
||||
icon: Icons.description_outlined,
|
||||
activeIcon: Icons.description,
|
||||
|
@ -15,16 +15,28 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: _buildTambahLokasiPenyaluranForm(context),
|
||||
body: _LokasiPenyaluranForm(controller: controller),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTambahLokasiPenyaluranForm(BuildContext context) {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final TextEditingController namaController = TextEditingController();
|
||||
final TextEditingController alamatLengkapController =
|
||||
TextEditingController();
|
||||
class _LokasiPenyaluranForm extends StatefulWidget {
|
||||
final JadwalPenyaluranController controller;
|
||||
|
||||
const _LokasiPenyaluranForm({required this.controller});
|
||||
|
||||
@override
|
||||
State<_LokasiPenyaluranForm> createState() => _LokasiPenyaluranFormState();
|
||||
}
|
||||
|
||||
class _LokasiPenyaluranFormState extends State<_LokasiPenyaluranForm> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final TextEditingController namaController = TextEditingController();
|
||||
final TextEditingController alamatLengkapController = TextEditingController();
|
||||
bool isLokasiTitip = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
@ -95,6 +107,29 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Checkbox Is Lokasi Titip
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: isLokasiTitip,
|
||||
activeColor: AppTheme.primaryColor,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
isLokasiTitip = newValue ?? false;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('Lokasi Titip'),
|
||||
const SizedBox(width: 4),
|
||||
const Tooltip(
|
||||
message:
|
||||
'Centang jika lokasi ini merupakan lokasi yang dapat menerima penitipan',
|
||||
child: Icon(Icons.info_outline, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Tombol Submit
|
||||
@ -107,6 +142,7 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
_tambahLokasiPenyaluran(
|
||||
nama: namaController.text,
|
||||
alamatLengkap: alamatLengkapController.text,
|
||||
isLokasiTitip: isLokasiTitip,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -137,6 +173,7 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
Future<void> _tambahLokasiPenyaluran({
|
||||
required String nama,
|
||||
required String alamatLengkap,
|
||||
required bool isLokasiTitip,
|
||||
}) async {
|
||||
try {
|
||||
// Tampilkan loading
|
||||
@ -152,7 +189,8 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
final String id = uuid.v4();
|
||||
|
||||
// Ambil ID petugas desa yang sedang login dari controller
|
||||
final String? petugasDesaId = controller.supabaseService.currentUser?.id;
|
||||
final String? petugasDesaId =
|
||||
widget.controller.supabaseService.currentUser?.id;
|
||||
|
||||
if (petugasDesaId == null) {
|
||||
Get.back(); // Tutup dialog loading
|
||||
@ -167,7 +205,7 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
|
||||
// Dapatkan desa_id dari data petugas desa
|
||||
// Ambil data petugas desa dari Supabase untuk mendapatkan desa_id
|
||||
final petugasDesaData = await controller.supabaseService.client
|
||||
final petugasDesaData = await widget.controller.supabaseService.client
|
||||
.from('petugas_desa')
|
||||
.select('desa_id')
|
||||
.eq('id', petugasDesaId)
|
||||
@ -193,11 +231,12 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
'nama': nama,
|
||||
'alamat_lengkap': alamatLengkap,
|
||||
'desa_id': desaId,
|
||||
'is_lokasi_titip': isLokasiTitip,
|
||||
'created_at': DateTime.now().toIso8601String(),
|
||||
};
|
||||
|
||||
// Insert data ke tabel lokasi_penyaluran
|
||||
await controller.supabaseService.client
|
||||
await widget.controller.supabaseService.client
|
||||
.from('lokasi_penyaluran')
|
||||
.insert(data);
|
||||
|
||||
@ -216,7 +255,7 @@ class TambahLokasiPenyaluranView extends GetView<JadwalPenyaluranController> {
|
||||
Get.back();
|
||||
|
||||
// Refresh data di controller
|
||||
controller.refreshData();
|
||||
widget.controller.refreshData();
|
||||
} catch (e) {
|
||||
// Tutup dialog loading
|
||||
Get.back();
|
||||
|
@ -13,6 +13,7 @@ import 'package:penyaluran_app/app/modules/petugas_desa/views/daftar_donatur_vie
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_donatur_view.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/tambah_penyaluran_view.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/tambah_lokasi_penyaluran_view.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/lokasi_penyaluran_view.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/riwayat_penyaluran_view.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/views/detail_penyaluran_page.dart';
|
||||
import 'package:penyaluran_app/app/modules/petugas_desa/bindings/penyaluran_binding.dart';
|
||||
@ -155,6 +156,11 @@ class AppPages {
|
||||
page: () => const TambahLokasiPenyaluranView(),
|
||||
binding: PetugasDesaBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.lokasiPenyaluran,
|
||||
page: () => const LokasiPenyaluranView(),
|
||||
binding: PetugasDesaBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.detailPenyaluran,
|
||||
page: () => DetailPenyaluranPage(),
|
||||
|
@ -30,6 +30,7 @@ abstract class Routes {
|
||||
static const detailDonatur = _Paths.detailDonatur;
|
||||
static const tambahPenyaluran = _Paths.tambahPenyaluran;
|
||||
static const tambahLokasiPenyaluran = _Paths.tambahLokasiPenyaluran;
|
||||
static const lokasiPenyaluran = _Paths.lokasiPenyaluran;
|
||||
static const daftarPenerimaPenyaluran = _Paths.daftarPenerimaPenyaluran;
|
||||
static const detailPenerimaPenyaluran = _Paths.detailPenerimaPenyaluran;
|
||||
static const laporanPenyaluran = _Paths.laporanPenyaluran;
|
||||
@ -78,6 +79,7 @@ abstract class _Paths {
|
||||
static const detailDonatur = '/daftar-donatur/detail';
|
||||
static const tambahPenyaluran = '/tambah-penyaluran';
|
||||
static const tambahLokasiPenyaluran = '/tambah-lokasi-penyaluran';
|
||||
static const lokasiPenyaluran = '/lokasi-penyaluran';
|
||||
static const daftarPenerimaPenyaluran = '/daftar-penerima-penyaluran';
|
||||
static const detailPenerimaPenyaluran = '/detail-penerima-penyaluran';
|
||||
static const laporanPenyaluran = '/laporan-penyaluran';
|
||||
|
@ -3,6 +3,7 @@ import 'package:penyaluran_app/app/data/models/donatur_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/petugas_desa_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/user_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/warga_model.dart';
|
||||
import 'package:penyaluran_app/app/data/models/lokasi_penyaluran_model.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'dart:io';
|
||||
|
||||
@ -1736,6 +1737,33 @@ class SupabaseService extends GetxService {
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk mendapatkan lokasi penyaluran berdasarkan ID petugas
|
||||
Future<List<LokasiPenyaluranModel>> getLokasiPenyaluran(
|
||||
{String? petugasId}) async {
|
||||
try {
|
||||
var query = client.from('lokasi_penyaluran').select('*');
|
||||
|
||||
final response = await query.order('nama');
|
||||
|
||||
return response
|
||||
.map((data) => LokasiPenyaluranModel.fromJson(data))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print('Error getting lokasi penyaluran: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk menghapus lokasi penyaluran
|
||||
Future<void> deleteLokasiPenyaluran(String lokasiId) async {
|
||||
try {
|
||||
await client.from('lokasi_penyaluran').delete().eq('id', lokasiId);
|
||||
} catch (e) {
|
||||
print('Error deleting lokasi penyaluran: $e');
|
||||
throw e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Metode untuk mendapatkan daftar penerima penyaluran berdasarkan ID penyaluran
|
||||
Future<List<Map<String, dynamic>>?> getPenerimaPenyaluran(
|
||||
String penyaluranId) async {
|
||||
|
Reference in New Issue
Block a user