card dashboard produksi

This commit is contained in:
2026-10-14 20:18:15 +07:00
parent 3a9b3dbd7b
commit 3e172cf915
2 changed files with 30 additions and 68 deletions
@@ -183,7 +183,8 @@ class SummaryController extends Controller
$landMetrics = $this->karakteristikLandMetrics($request); $landMetrics = $this->karakteristikLandMetrics($request);
$totalLuasLahan = $landMetrics['total_luas_lahan']; $totalLuasLahan = $landMetrics['total_luas_lahan'];
$avgLuasPerPetani = $landMetrics['ratarata_luas_lahan']; $avgLuasPerPetani = $landMetrics['ratarata_luas_lahan'];
$productivityCards = $this->productivityKpiCards($lahanIds); $actualProductivity = $this->actualProductivityByCommodityChart($request);
$productivityCards = $this->productivityKpiCards($actualProductivity);
$praktikBudidaya = $this->filteredPraktikBudidaya($request); $praktikBudidaya = $this->filteredPraktikBudidaya($request);
$surveyPanen = $this->filteredSurveyPanen($request); $surveyPanen = $this->filteredSurveyPanen($request);
$aksesPasar = $this->filteredAksesPasar($request); $aksesPasar = $this->filteredAksesPasar($request);
@@ -243,7 +244,7 @@ class SummaryController extends Controller
), ),
'praktikSortasiPanen' => $this->enumOptionChart($surveyPanen, 'praktik_sortasi', PraktikSortasi::class), 'praktikSortasiPanen' => $this->enumOptionChart($surveyPanen, 'praktik_sortasi', PraktikSortasi::class),
'praktikFermentasiPascapanen' => $this->fermentationPracticeChart($aksesPasar), 'praktikFermentasiPascapanen' => $this->fermentationPracticeChart($aksesPasar),
'produktivitasAktualPerKomoditas' => $this->actualProductivityByCommodityChart($request), 'produktivitasAktualPerKomoditas' => $actualProductivity,
], ],
]; ];
} }
@@ -706,43 +707,18 @@ class SummaryController extends Controller
]; ];
} }
private function productivityKpiCards($lahanIds): array private function productivityKpiCards(array $actualProductivity): array
{ {
$komoditas = Komoditas::query()
->where('is_utama', true)
->orderBy('nama')
->limit(4)
->get(['id', 'nama']);
$produksiRows = DB::table('produksi')
->whereNull('produksi.deleted_at')
->leftJoin('map_lahan as lahan_produksi', 'lahan_produksi.id', '=', 'produksi.lahan_id')
->when($lahanIds->isNotEmpty(), fn ($query) => $query->whereIn('produksi.lahan_id', $lahanIds))
->when($lahanIds->isEmpty(), fn ($query) => $query->whereRaw('1 = 0'))
->selectRaw('COALESCE(produksi.komoditas_id, lahan_produksi.komoditas_id) as komoditas_id')
->addSelect([
'produksi.jumlah_panen',
'produksi.satuan',
'produksi.luas_panen',
'produksi.produktivitas',
])
->get()
->groupBy('komoditas_id');
$variants = ['primary', 'warning', 'info', 'secondary']; $variants = ['primary', 'warning', 'info', 'secondary'];
$icon = 'tabler-chart-bar'; $icon = 'tabler-chart-bar';
return $komoditas return collect($actualProductivity['labels'])
->values() ->map(function (string $name, int $index) use ($actualProductivity, $variants, $icon) {
->map(function ($item, int $index) use ($produksiRows, $variants, $icon) {
$rows = $produksiRows->get($item->id, collect());
$average = $this->averageProductivity($rows);
return [ return [
'value' => number_format($average, 0, ',', '.'), 'value' => number_format((float) ($actualProductivity['series'][$index] ?? 0), 2, ',', '.'),
'unit' => 'kg/ha', 'unit' => 'kg/ha',
'label' => 'Produktivitas Rata-rata '.$item->nama, 'label' => 'Produktivitas Aktual '.$name,
'description' => 'Rata-rata produktivitas per komoditas', 'description' => 'Hasil panen per musim dibagi total luas lahan',
'icon' => $icon, 'icon' => $icon,
'variant' => $variants[$index % count($variants)], 'variant' => $variants[$index % count($variants)],
]; ];
@@ -750,32 +726,6 @@ class SummaryController extends Controller
->all(); ->all();
} }
private function averageProductivity($rows): float
{
if ($rows->isEmpty()) {
return 0;
}
$storedAverage = $rows
->pluck('produktivitas')
->filter(fn ($value) => (float) $value > 0)
->avg();
if ($storedAverage > 0) {
return round((float) $storedAverage, 2);
}
$totalProduksiKg = $rows->sum(fn ($row) => $this->toKilograms(
(float) ($row->jumlah_panen ?? 0),
(string) ($row->satuan ?? 'kg')
));
$totalLuasPanen = $rows->sum(fn ($row) => (float) ($row->luas_panen ?? 0));
return $totalLuasPanen > 0
? round($totalProduksiKg / $totalLuasPanen, 2)
: 0;
}
private function actualProductivityByCommodityChart(Request $request): array private function actualProductivityByCommodityChart(Request $request): array
{ {
$preferredOrder = ['Kakao', 'Vanili', 'Kemiri', 'Kopi']; $preferredOrder = ['Kakao', 'Vanili', 'Kemiri', 'Kopi'];
@@ -824,15 +774,6 @@ class SummaryController extends Controller
]; ];
} }
private function toKilograms(float $value, string $unit): float
{
return match (strtolower($unit)) {
'ton' => $value * 1000,
'kuintal', 'kwintal' => $value * 100,
default => $value,
};
}
private function filteredLahanQuery(Request $request) private function filteredLahanQuery(Request $request)
{ {
return DB::table('map_lahan') return DB::table('map_lahan')
@@ -106,3 +106,24 @@ test('actual productivity rounds kg per hectare to two decimals', function () {
expect(($this->chart)()['series'][1])->toBe(33.33); expect(($this->chart)()['series'][1])->toBe(33.33);
}); });
test('productivity cards display the same values and commodities as the chart', function () {
DB::table('map_lahan')->where('petani_id', 5)->update(['luas_lahan' => 0.3]);
$controller = app(SummaryController::class);
$method = new ReflectionMethod($controller, 'productivityKpiCards');
foreach ([[], ['desaKelurahan_id' => 2], ['komoditas_id' => 1]] as $filters) {
$chart = ($this->chart)($filters);
$cards = $method->invoke($controller, $chart);
expect(array_column($cards, 'label'))->toBe(array_map(
fn ($name) => 'Produktivitas Aktual '.$name,
$chart['labels'],
));
expect(array_column($cards, 'value'))->toBe(array_map(
fn ($value) => number_format($value, 2, ',', '.'),
$chart['series'],
));
expect(array_column($cards, 'unit'))->toBe(array_fill(0, count($chart['labels']), 'kg/ha'));
}
});