This commit is contained in:
2026-08-19 16:42:31 +07:00
3 changed files with 204 additions and 36 deletions
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Web\Dashboard;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@@ -14,6 +15,9 @@ use App\Models\Pertanian\Lahan;
use App\Models\Pertanian\Tanaman; use App\Models\Pertanian\Tanaman;
use App\Models\Pertanian\SurveyPelatihan; use App\Models\Pertanian\SurveyPelatihan;
use App\Models\Pertanian\SurveySertifikasi; use App\Models\Pertanian\SurveySertifikasi;
use App\Enums\AksesPasar\PraktikFermentasi;
use App\Enums\AksesPasar\SaluranTerdahulu;
use App\Enums\AksesPasar\SkorPengalaman;
use BackedEnum; use BackedEnum;
@@ -92,36 +96,34 @@ class SurveyPetaniController extends Controller
// Progres pengisian data per petani // Progres pengisian data per petani
$progressPetani = Petani::query() $progressPetani = Petani::query()
->select([ ->select(['id', 'nama', 'komoditas_id'])
'id', ->with([
'nama', 'komoditas:id,nama',
]) 'lahans',
->withExists([ 'praktikBudidaya',
'lahans as has_lahan', 'sosialEkonomi',
'praktikBudidaya as has_praktik_budidaya', 'aksesPasar',
'sosialEkonomi as has_sosial_ekonomi',
'aksesPasar as has_akses_pasar',
]) ])
->orderBy('nama') ->orderBy('nama')
->get() ->get()
->map(function ($petani) { ->map(function ($petani) {
$status = [ $status = [
'lahan' => [ 'lahan' => $this->moduleStatus(
'label' => 'Lahan', 'Lahan',
'done' => (bool) $petani->has_lahan, $this->hasCompleteLahan($petani)
], ),
'praktik_budidaya' => [ 'praktik_budidaya' => $this->moduleStatus(
'label' => 'Praktik Budidaya', 'Praktik Budidaya',
'done' => (bool) $petani->has_praktik_budidaya, $this->hasCompletePraktikBudidaya($petani->praktikBudidaya)
], ),
'sosial_ekonomi' => [ 'sosial_ekonomi' => $this->moduleStatus(
'label' => 'Sosial Ekonomi', 'Sosial Ekonomi',
'done' => (bool) $petani->has_sosial_ekonomi, $this->hasCompleteSosialEkonomi($petani->sosialEkonomi)
], ),
'akses_pasar' => [ 'akses_pasar' => $this->moduleStatus(
'label' => 'Akses Pasar', 'Akses Pasar',
'done' => (bool) $petani->has_akses_pasar, $this->hasCompleteAksesPasar($petani->aksesPasar)
], ),
]; ];
$totalModul = count($status); $totalModul = count($status);
@@ -131,12 +133,13 @@ class SurveyPetaniController extends Controller
->count(); ->count();
return [ return [
'id' => $petani->id, 'id' => $petani->id,
'name' => $petani->nama, 'name' => $petani->nama,
'status' => $status, 'komoditas' => $petani->komoditas?->nama ?? 'Belum ditentukan',
'done' => $modulSelesai, 'status' => $status,
'total' => $totalModul, 'done' => $modulSelesai,
'percent' => $totalModul > 0 'total' => $totalModul,
'percent' => $totalModul > 0
? (int) round(($modulSelesai / $totalModul) * 100) ? (int) round(($modulSelesai / $totalModul) * 100)
: 0, : 0,
]; ];
@@ -278,6 +281,147 @@ class SurveyPetaniController extends Controller
]); ]);
} }
private function moduleStatus(string $label, bool $done): array
{
return [
'label' => $label,
'done' => $done,
];
}
private function hasCompleteLahan(Petani $petani): bool
{
return $petani->lahans->contains(function ($lahan) {
return $this->allFieldsFilled($lahan, [
'luas_lahan',
'komoditas_id',
'jenis_komoditas_id',
'umur_tanaman',
'kondisi_tanaman',
]);
});
}
private function hasCompletePraktikBudidaya(?Model $praktikBudidaya): bool
{
return $this->allFieldsFilled($praktikBudidaya, [
'frekuensi_pemangkasan_utama',
'pemberian_pupuk',
'metode_penanganan_opt',
'frekuensi_sanitasi_lahan',
'penanganan_gulma',
'kondisi_pengelolaan_air',
]);
}
private function hasCompleteSosialEkonomi(?Model $sosialEkonomi): bool
{
return $this->allFieldsFilled($sosialEkonomi, [
'skor_periode_paceklik',
'skor_kemampuan_menabung',
'skor_kecukupan_penghasilan',
'skor_kejelasan_status_lahan',
'kemauan_mencoba_praktik_baru',
'pemahaman_mutu_harga',
'panutan_petani_sukses',
'kemauan_ikut_program',
'orientasi_rencana_jangka_panjang',
'keaktifan_pertemuan_kelompok',
'peran_dalam_kelompok',
'manfaat_kelompok_tani',
'status_rekening_tabungan',
'pengalaman_kredit_kebun',
'kondisi_aset_produktif',
'keterlibatan_program_keuangan_formal',
'keterikatan_hutang_panen',
'praktik_ijon',
'kebebasan_memilih_pembeli',
]);
}
private function hasCompleteAksesPasar(?Model $aksesPasar): bool
{
if (! $this->allFieldsFilled($aksesPasar, [
'skor_pengalaman',
'praktik_fermentasi',
'skor_kesadaran_premium',
'tingkat_kepercayaan_pembeli_baru',
])) {
return false;
}
$skorPengalaman = (int) $aksesPasar->getAttribute('skor_pengalaman');
if (
$skorPengalaman !== SkorPengalaman::BELUM_PERNAH->value
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu'))
) {
return false;
}
if (
in_array($skorPengalaman, [
SkorPengalaman::PERNAH_BERHENTI_TIDAK_ADA_AKSES->value,
SkorPengalaman::PERNAH_BERHENTI_MUNGKIN_KEMBALI->value,
], true)
&& ! $this->valueIsFilled($aksesPasar->getAttribute('alasan_berhenti'))
) {
return false;
}
if (
in_array(
SaluranTerdahulu::LAINNYA->value,
$aksesPasar->getAttribute('saluran_terdahulu') ?? [],
true
)
&& ! $this->valueIsFilled($aksesPasar->getAttribute('saluran_terdahulu_lainnya'))
) {
return false;
}
if (
$aksesPasar->getAttribute('praktik_fermentasi') !== PraktikFermentasi::BELUM_PERNAH->value
&& ! $this->allFieldsFilled($aksesPasar, [
'proses_fermentasi',
'model_fermentasi_memungkinkan',
])
) {
return false;
}
return true;
}
private function allFieldsFilled(?Model $model, array $fields): bool
{
if (! $model) {
return false;
}
foreach ($fields as $field) {
if (! $this->valueIsFilled($model->getAttribute($field))) {
return false;
}
}
return true;
}
private function valueIsFilled(mixed $value): bool
{
if ($value instanceof BackedEnum) {
$value = $value->value;
}
if (is_array($value)) {
return collect($value)
->contains(fn ($item) => $this->valueIsFilled($item));
}
return ! is_null($value) && $value !== '';
}
private function pelatihanGapOptions(): array private function pelatihanGapOptions(): array
{ {
$enumClass = self::GAP_ENUM_CLASS; $enumClass = self::GAP_ENUM_CLASS;
@@ -4,6 +4,15 @@
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/> crossorigin=""/>
<style>
.clickable-row {
cursor: pointer;
}
.clickable-row:hover {
background-color: rgba(0, 0, 0, 0.03);
}
</style>
@endpush @endpush
@section('content') @section('content')
@@ -388,7 +397,7 @@ $mapDesaSegmentasi = $segmentasiByDesa->map(function ($segmentasi, $desaId) use
@php @php
$style = $segmentStyles[$item->segmen()] ?? null; $style = $segmentStyles[$item->segmen()] ?? null;
@endphp @endphp
<tr data-desa="{{ $item->desa_kelurahan_id }}"> <tr data-desa="{{ $item->desa_kelurahan_id }}" data-url="{{ route('dashboard.profil-petani', $item->id) }}" class="clickable-row">
<td class="pe-0">{{ $item->nama }}</td> <td class="pe-0">{{ $item->nama }}</td>
<td class="pe-0">{{ $item->desaKelurahan?->nama }}</td> <td class="pe-0">{{ $item->desaKelurahan?->nama }}</td>
<td class="pe-0">{{ $item->komoditas?->nama }}</td> <td class="pe-0">{{ $item->komoditas?->nama }}</td>
@@ -452,6 +461,8 @@ $mapDesaSegmentasi = $segmentasiByDesa->map(function ($segmentasi, $desaId) use
"autoWidth": false, "autoWidth": false,
"responsive": false, "responsive": false,
"scrollX": true "scrollX": true
}).on('click', 'tbody tr', function () {
window.location.href = $(this).data('url');
}); });
let selectedDesa = null; let selectedDesa = null;
@@ -401,6 +401,10 @@
Petani Petani
</th> </th>
<th style="font-size:10px;letter-spacing:.7px;text-transform:uppercase">
Komoditas
</th>
<th class="text-center" <th class="text-center"
style="font-size:10px;letter-spacing:.7px;text-transform:uppercase"> style="font-size:10px;letter-spacing:.7px;text-transform:uppercase">
Lahan Lahan
@@ -474,19 +478,28 @@
</div> </div>
</td> </td>
<td>
<span
class="badge bg-label-secondary text-secondary"
title="{{ $petani['komoditas'] }}"
>
{{ $petani['komoditas'] }}
</span>
</td>
@foreach ($petani['status'] as $status) @foreach ($petani['status'] as $status)
<td class="text-center"> <td class="text-center">
@if ($status['done']) @if ($status['done'])
<span <span
class="badge rounded-pill bg-label-success text-success" class="badge rounded-pill bg-label-success text-success"
title="{{ $status['label'] }} sudah diisi" title="{{ $status['label'] }} lengkap"
> >
<i class="ti tabler-check"></i> <i class="ti tabler-check"></i>
</span> </span>
@else @else
<span <span
class="badge rounded-pill bg-label-secondary text-secondary" class="badge rounded-pill bg-label-secondary text-secondary"
title="{{ $status['label'] }} belum diisi" title="{{ $status['label'] }} belum lengkap"
> >
<i class="ti tabler-minus"></i> <i class="ti tabler-minus"></i>
</span> </span>
@@ -533,7 +546,7 @@
</tr> </tr>
@empty @empty
<tr> <tr>
<td colspan="7" class="text-center text-muted py-4"> <td colspan="8" class="text-center text-muted py-4">
Belum ada data petani. Belum ada data petani.
</td> </td>
</tr> </tr>