dashboard indikator pertanian

This commit is contained in:
2026-08-07 16:09:24 +07:00
parent 0d9f6c0b64
commit a4f38998d5
2 changed files with 307 additions and 2 deletions
@@ -15,6 +15,7 @@ use App\Models\Pertanian\PraktikBudidaya;
use App\Models\Pertanian\Produksi; use App\Models\Pertanian\Produksi;
use App\Models\Pertanian\SkorPetani; use App\Models\Pertanian\SkorPetani;
use App\Models\Pertanian\SosialEkonomi; use App\Models\Pertanian\SosialEkonomi;
use App\Models\Pertanian\SurveyProduktivitas;
use App\Models\Wilayah\DesaKelurahan; use App\Models\Wilayah\DesaKelurahan;
use App\Models\Wilayah\KabupatenKota; use App\Models\Wilayah\KabupatenKota;
use App\Models\Wilayah\Kecamatan; use App\Models\Wilayah\Kecamatan;
@@ -120,6 +121,12 @@ class IndikatorPertanianController extends Controller
'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters), 'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters),
'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($initialFilters), 'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($initialFilters),
'praktikPemupukan' => $this->praktikPemupukanChart($initialFilters), 'praktikPemupukan' => $this->praktikPemupukanChart($initialFilters),
'produksiPerKomoditas' => $this->produksiPerKomoditasChart($initialFilters),
'produktivitasPerKomoditas' => $this->produktivitasPerKomoditasChart($initialFilters),
'luasPanenPerKomoditas' => $this->luasPanenPerKomoditasChart($initialFilters),
'statusPanen' => $this->statusPanenChart($initialFilters),
'surveyProduktivitasHa' => $this->surveyProduktivitasByUnitChart($initialFilters, 'ha_tahun'),
'surveyProduktivitasPohon' => $this->surveyProduktivitasByUnitChart($initialFilters, 'pohon_tahun'),
], ],
]); ]);
} }
@@ -410,6 +417,123 @@ class IndikatorPertanianController extends Controller
); );
} }
private function produksiPerKomoditasChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->whereNotNull('jumlah_panen')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('produksi.jumlah_panen')
->selectRaw('produksi.satuan')
->get()
->groupBy('label')
->map(fn ($items) => round($items->sum(
fn (Produksi $produksi) => $this->toKilograms(
(float) $produksi->jumlah_panen,
(string) $produksi->satuan
)
), 2))
->filter(fn ($total) => $total > 0)
->sortDesc();
return [
'labels' => $rows->keys()->values()->all(),
'series' => $rows->values()->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function produktivitasPerKomoditasChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('produksi.jumlah_panen')
->selectRaw('produksi.satuan')
->selectRaw('produksi.luas_panen')
->selectRaw('produksi.produktivitas')
->get()
->groupBy('label')
->map(function ($items) {
$storedAverage = $items
->pluck('produktivitas')
->filter(fn ($value) => (float) $value > 0)
->avg();
if ($storedAverage > 0) {
return round((float) $storedAverage, 2);
}
$totalProduksiKg = $items->sum(
fn (Produksi $produksi) => $this->toKilograms(
(float) $produksi->jumlah_panen,
(string) $produksi->satuan
)
);
$totalLuasPanen = $items->sum(fn (Produksi $produksi) => (float) $produksi->luas_panen);
return $totalLuasPanen > 0
? round($totalProduksiKg / $totalLuasPanen, 2)
: 0;
})
->filter(fn ($total) => $total > 0)
->sortDesc();
return [
'labels' => $rows->keys()->values()->all(),
'series' => $rows->values()->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function luasPanenPerKomoditasChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'produksi.komoditas_id')
->whereNotNull('luas_panen')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('SUM(produksi.luas_panen) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('SUM(produksi.luas_panen) > 0')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function statusPanenChart(array $filters): array
{
$rows = $this->applyProduksiFilters(Produksi::query(), $filters)
->whereNotNull('status_panen')
->selectRaw("COALESCE(status_panen, 'Belum ditentukan') as label")
->selectRaw('COUNT(produksi.id) as total')
->groupBy('status_panen')
->orderByDesc('total')
->get();
return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label));
}
private function surveyProduktivitasByUnitChart(array $filters, string $unit): array
{
$rows = $this->applySurveyProduktivitasFilters(SurveyProduktivitas::query(), $filters)
->leftJoin('master_komoditas as k', 'k.id', '=', 'survey_produktivitas.komoditas_id')
->where('satuan', $unit)
->whereNotNull('kuantitas')
->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label")
->selectRaw('AVG(survey_produktivitas.kuantitas) as total')
->groupBy('k.id', 'k.nama')
->havingRaw('AVG(survey_produktivitas.kuantitas) > 0')
->orderByDesc('total')
->get();
return [
'labels' => $rows->pluck('label')->values()->all(),
'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(),
];
}
private function chartFromRows($rows, callable $labelResolver): array private function chartFromRows($rows, callable $labelResolver): array
{ {
return [ return [
@@ -505,6 +629,15 @@ class IndikatorPertanianController extends Controller
}); });
} }
private function applySurveyProduktivitasFilters($query, array $filters)
{
return $query
->whereHas('petani', function ($petani) use ($filters) {
$this->applyPetaniFilters($petani, $filters);
})
->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas']));
}
private function applyTanamanTableFilters($query, array $filters) private function applyTanamanTableFilters($query, array $filters)
{ {
return $query return $query
@@ -146,7 +146,13 @@
#chart-penyakit-tanaman .apexcharts-tooltip, #chart-penyakit-tanaman .apexcharts-tooltip,
#chart-keparahan-serangan .apexcharts-tooltip, #chart-keparahan-serangan .apexcharts-tooltip,
#chart-status-hak-kelola-lahan .apexcharts-tooltip, #chart-status-hak-kelola-lahan .apexcharts-tooltip,
#chart-praktik-pemupukan .apexcharts-tooltip { #chart-praktik-pemupukan .apexcharts-tooltip,
#chart-produksi-komoditas .apexcharts-tooltip,
#chart-produktivitas-komoditas .apexcharts-tooltip,
#chart-luas-panen-komoditas .apexcharts-tooltip,
#chart-status-panen .apexcharts-tooltip,
#chart-survey-produktivitas-ha .apexcharts-tooltip,
#chart-survey-produktivitas-pohon .apexcharts-tooltip {
background: transparent !important; background: transparent !important;
border: none !important; border: none !important;
box-shadow: none !important; box-shadow: none !important;
@@ -531,12 +537,100 @@
</div> </div>
</div> </div>
<div class="tab-pane fade" id="indicator-tab-produksi" role="tabpanel">
<div class="row g-6 mt-1">
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Total Produksi per Komoditas</h5>
<small class="text-body-secondary">Akumulasi hasil panen aktual</small>
</div>
</div>
<div class="card-body">
<div id="chart-produksi-komoditas" class="indicator-chart"></div>
</div>
</div>
</div>
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Produktivitas Aktual</h5>
<small class="text-body-secondary">Rata-rata kg/ha per komoditas</small>
</div>
</div>
<div class="card-body">
<div id="chart-produktivitas-komoditas" class="indicator-chart"></div>
</div>
</div>
</div>
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Luas Panen per Komoditas</h5>
<small class="text-body-secondary">Akumulasi luas panen terdata</small>
</div>
</div>
<div class="card-body">
<div id="chart-luas-panen-komoditas" class="indicator-chart"></div>
</div>
</div>
</div>
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Status Panen</h5>
<small class="text-body-secondary">Jumlah catatan produksi per status</small>
</div>
</div>
<div class="card-body">
<div id="chart-status-panen" class="indicator-chart-sm"></div>
</div>
</div>
</div>
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Produktivitas Survey (ha/tahun)</h5>
<small class="text-body-secondary">Rata-rata produktivitas per komoditas</small>
</div>
</div>
<div class="card-body">
<div id="chart-survey-produktivitas-ha" class="indicator-chart"></div>
</div>
</div>
</div>
<div class="col-xxl-4 col-lg-6">
<div class="card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Produktivitas Survey (pohon/tahun)</h5>
<small class="text-body-secondary">Rata-rata produktivitas per komoditas</small>
</div>
</div>
<div class="card-body">
<div id="chart-survey-produktivitas-pohon" class="indicator-chart"></div>
</div>
</div>
</div>
</div>
</div>
@foreach ([ @foreach ([
'indicator-tab-produksi' => 'Produksi dan Produktivitas',
'indicator-tab-input-produksi' => 'Input Produksi', 'indicator-tab-input-produksi' => 'Input Produksi',
'indicator-tab-panen' => 'Panen dan Pascapanen', 'indicator-tab-panen' => 'Panen dan Pascapanen',
'indicator-tab-akses-pasar' => 'Akses Pasar', 'indicator-tab-akses-pasar' => 'Akses Pasar',
] as $tabId => $tabLabel) ] as $tabId => $tabLabel)
<div class="tab-pane fade" id="{{ $tabId }}" role="tabpanel"> <div class="tab-pane fade" id="{{ $tabId }}" role="tabpanel">
<div class="card"> <div class="card">
<div class="card-body text-center text-body-secondary py-5"> <div class="card-body text-center text-body-secondary py-5">
@@ -1154,6 +1248,84 @@
labelWidth: 180, labelWidth: 180,
valueDigits: 0, valueDigits: 0,
}); });
let productionChartsRendered = false;
function renderProductionCharts() {
if (productionChartsRendered) {
return;
}
productionChartsRendered = true;
renderBar('#chart-produksi-komoditas', chartData.produksiPerKomoditas, {
name: 'Produksi',
suffix: 'kg',
colors: colorsFor(0),
height: 330,
horizontal: true,
labelWidth: 150,
valueDigits: 0,
});
renderBar('#chart-produktivitas-komoditas', chartData.produktivitasPerKomoditas, {
name: 'Produktivitas',
suffix: 'kg/ha',
colors: colorsFor(2),
height: 330,
horizontal: true,
labelWidth: 150,
valueDigits: 2,
});
renderBar('#chart-luas-panen-komoditas', chartData.luasPanenPerKomoditas, {
name: 'Luas panen',
suffix: 'ha',
colors: colorsFor(4),
height: 330,
horizontal: true,
labelWidth: 150,
valueDigits: 2,
});
renderDonut(
'#chart-status-panen',
chartData.statusPanen,
colorsFor(6),
{
totalLabel: 'Catatan',
valueDigits: 0,
}
);
renderBar('#chart-survey-produktivitas-ha', chartData.surveyProduktivitasHa, {
name: 'Produktivitas',
suffix: 'ha/tahun',
colors: colorsFor(1),
height: 330,
horizontal: true,
labelWidth: 150,
valueDigits: 2,
});
renderBar('#chart-survey-produktivitas-pohon', chartData.surveyProduktivitasPohon, {
name: 'Produktivitas',
suffix: 'pohon/tahun',
colors: colorsFor(3),
height: 330,
horizontal: true,
labelWidth: 150,
valueDigits: 2,
});
}
document
.querySelector('[data-bs-target="#indicator-tab-produksi"]')
?.addEventListener('shown.bs.tab', renderProductionCharts);
if (document.querySelector('#indicator-tab-produksi')?.classList.contains('active')) {
renderProductionCharts();
}
}); });
</script> </script>
@endpush @endpush