kabupatenOptions(); $ngadaOption = collect($kabupatenOptions)->first( fn ($option) => str_contains(strtolower($option['label']), 'ngada') ); $initialFilters = $this->selectedFilters($request, $ngadaOption['value'] ?? 'all'); $totalPetani = $this->applyPetaniFilters(Petani::query(), $initialFilters)->count(); $totalLuasLahan = ((float) $this->applyLahanFilters(Lahan::query(), $initialFilters)->sum('luas_lahan')) / 10000; $rataRataLuasLahan = $totalPetani > 0 ? $totalLuasLahan / $totalPetani : 0; $totalProduksiKg = $this->totalProduksiKg($initialFilters); $rataRataProduksiKg = $totalPetani > 0 ? $totalProduksiKg / $totalPetani : 0; $rataRataProduktivitas = $totalLuasLahan > 0 ? $totalProduksiKg / $totalLuasLahan : 0; $filterOptions = [ [ 'key' => 'kabupaten', 'label' => 'Kabupaten', 'options' => $kabupatenOptions, 'icon' => 'tabler-map-pin', 'readonly' => true, ], [ 'key' => 'kecamatan', 'label' => 'Kecamatan', 'options' => $this->kecamatanOptions(), ], [ 'key' => 'desa', 'label' => 'Desa', 'options' => $this->desaOptions(), ], [ 'key' => 'komoditas', 'label' => 'Komoditas', 'options' => $this->komoditasOptions(), ], [ 'key' => 'segmentasi', 'label' => 'Segmentasi', 'options' => $this->segmentasiOptions(), ], ]; $summaryData = [ [ 'value' => number_format($totalPetani, 0, ',', '.'), 'label' => 'Petani Terdata', 'description' => 'Total petani yang masuk sistem', 'icon' => 'tabler-users', 'variant' => 'primary', ], [ 'value' => number_format($totalLuasLahan, 2, ',', '.').' ha', 'label' => 'Total Luas Lahan', 'description' => 'Rata-rata '.number_format($rataRataLuasLahan, 2, ',', '.').' ha/petani', 'icon' => 'tabler-mountain', 'variant' => 'success', ], [ 'value' => number_format($totalProduksiKg, 0, ',', '.').' kg', 'label' => 'Total Produksi (Semua Komoditas)', 'description' => 'Rata-rata '.number_format($rataRataProduksiKg, 0, ',', '.').' kg/petani', 'icon' => 'tabler-wheat', 'variant' => 'warning', ], [ 'value' => number_format($rataRataProduktivitas, 0, ',', '.').' kg/ha', 'label' => 'Rata-rata Produktivitas', 'description' => 'Semua komoditas berdasarkan total produksi dan luas lahan', 'icon' => 'tabler-chart-bar', 'variant' => 'info', ], ]; return view('dashboard.indikator-pertanian', [ 'title' => 'Indikator Pertanian', 'initialFilters' => $initialFilters, 'filterOptions' => $filterOptions, 'summaryData' => $summaryData, 'chartData' => [ 'petaniPerKomoditas' => $this->petaniPerKomoditasChart($initialFilters), 'luasLahanPerKecamatan' => $this->luasLahanPerKecamatanChart($initialFilters), 'kondisiTanaman' => $this->kondisiTanamanChart($initialFilters), 'statusKepemilikanLahan' => $this->statusKepemilikanLahanChart($initialFilters), 'seranganHama' => $this->seranganHamaChart($initialFilters), 'penyakitTanaman' => $this->penyakitTanamanChart($initialFilters), 'keparahanSerangan' => $this->keparahanSeranganChart($initialFilters), 'statusHakKelolaLahan' => $this->statusHakKelolaLahanChart($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'), ], ]); } private function selectedFilters(Request $request, string $defaultKabupaten): array { $filters = [ 'kabupaten' => $defaultKabupaten, 'kecamatan' => $this->validFilterValue($request->query('kecamatan'), Kecamatan::class), 'desa' => $this->validFilterValue($request->query('desa'), DesaKelurahan::class), 'komoditas' => $this->validFilterValue($request->query('komoditas'), Komoditas::class), 'segmentasi' => $this->validSegmentasi((string) $request->query('segmentasi', 'all')), ]; if ($filters['desa'] !== 'all') { $desa = DesaKelurahan::query()->find($filters['desa']); if (! $desa || ($filters['kecamatan'] !== 'all' && (string) $desa->kecamatan_id !== $filters['kecamatan'])) { $filters['desa'] = 'all'; } } if ($filters['kecamatan'] === 'all') { $filters['desa'] = 'all'; } return $filters; } private function validFilterValue(mixed $value, string $modelClass): string { if (! $value || $value === 'all') { return 'all'; } return $modelClass::query()->whereKey($value)->exists() ? (string) $value : 'all'; } private function validSegmentasi(string $value): string { return in_array($value, [ 'Penggerak', 'Perintis', 'Pemerhati', 'Pemula', 'Belum tersegmentasi', 'all', ], true) ? $value : 'all'; } private function kabupatenOptions(): array { return $this->withAllOption( 'Semua Kabupaten', KabupatenKota::query() ->orderBy('nama') ->get(['id', 'kode', 'nama']) ->map(fn (KabupatenKota $kabupaten) => [ 'value' => (string) $kabupaten->id, 'label' => $this->kabupatenLabel($kabupaten), ]) ->values() ->all() ); } private function kecamatanOptions(): array { return $this->withAllOption( 'Semua Kecamatan', Kecamatan::query() ->orderBy('nama') ->get(['id', 'nama']) ->map(fn (Kecamatan $kecamatan) => [ 'value' => (string) $kecamatan->id, 'label' => $kecamatan->nama, ]) ->values() ->all() ); } private function desaOptions(): array { return $this->withAllOption( 'Semua Desa', DesaKelurahan::query() ->orderBy('nama') ->get(['id', 'nama', 'kecamatan_id']) ->map(fn (DesaKelurahan $desa) => [ 'value' => (string) $desa->id, 'label' => $desa->nama, 'parent' => (string) $desa->kecamatan_id, ]) ->values() ->all() ); } private function komoditasOptions(): array { return $this->withAllOption( 'Semua Komoditas', Komoditas::query() ->orderBy('nama') ->get(['id', 'nama']) ->map(fn (Komoditas $komoditas) => [ 'value' => (string) $komoditas->id, 'label' => $komoditas->nama, ]) ->values() ->all() ); } private function segmentasiOptions(): array { // Aggregate bucket counts in SQL instead of pulling every skor_kesiapan_adopsi // value into PHP just to map/filter/unique it. $row = SkorPetani::query()->selectRaw(" COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 4) as penggerak, COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 3 AND skor_kesiapan_adopsi < 4) as perintis, COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 2 AND skor_kesiapan_adopsi < 3) as pemerhati, COUNT(*) FILTER (WHERE skor_kesiapan_adopsi >= 1 AND skor_kesiapan_adopsi < 2) as pemula, COUNT(*) FILTER (WHERE skor_kesiapan_adopsi IS NOT NULL AND skor_kesiapan_adopsi < 1) as belum_tersegmentasi ")->first(); $order = [ 'Penggerak' => $row->penggerak, 'Perintis' => $row->perintis, 'Pemerhati' => $row->pemerhati, 'Pemula' => $row->pemula, 'Belum tersegmentasi' => $row->belum_tersegmentasi, ]; $segments = collect($order) ->filter(fn ($count) => $count > 0) ->keys() ->map(fn ($segment) => [ 'value' => $segment, 'label' => $segment, ]) ->values() ->all(); return $this->withAllOption('Semua Segmentasi', $segments); } private function totalProduksiKg(array $filters): float { $expr = $this->kgCaseExpr('jumlah_panen', 'satuan'); return (float) ($this->applyProduksiFilters(Produksi::query(), $filters) ->whereNotNull('jumlah_panen') ->selectRaw("SUM({$expr}) as total") ->value('total') ?? 0); } private function petaniPerKomoditasChart(array $filters): array { $rows = $this->applyPetaniFilters(Petani::query(), $filters) ->leftJoin('master_komoditas as k', 'k.id', '=', 'petani.komoditas_id') ->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label") ->selectRaw('COUNT(petani.id) as total') ->groupBy('k.id', 'k.nama') ->orderByDesc('total') ->get(); return [ 'labels' => $rows->pluck('label')->values()->all(), 'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(), ]; } private function luasLahanPerKecamatanChart(array $filters): array { $rows = $this->applyLahanFilters(Lahan::query(), $filters) ->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'map_lahan.desa_kelurahan_id') ->leftJoin('master_kecamatan as k', 'k.id', '=', 'd.kecamatan_id') ->selectRaw("COALESCE(k.nama, 'Belum ditentukan') as label") ->selectRaw('SUM(map_lahan.luas_lahan) / 10000 as total') ->groupBy('k.id', 'k.nama') ->havingRaw('SUM(map_lahan.luas_lahan) > 0') ->orderByDesc('total') ->limit(8) ->get() ->filter(fn ($row) => round((float) $row->total, 2) > 0) ->values(); return [ 'labels' => $rows->pluck('label')->values()->all(), 'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(), ]; } private function kondisiTanamanChart(array $filters): array { $rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters) ->whereNull('map_tanaman.deleted_at') ->whereNotNull('map_tanaman.kondisi_tanaman') ->selectRaw('map_tanaman.kondisi_tanaman as label') ->selectRaw('COUNT(map_tanaman.id) as total') ->groupBy('map_tanaman.kondisi_tanaman') ->orderByDesc('total') ->get(); if ($rows->isEmpty()) { $rows = $this->applyLahanFilters(Lahan::query(), $filters) ->whereNull('deleted_at') ->whereNotNull('kondisi_tanaman') ->selectRaw('kondisi_tanaman as label') ->selectRaw('COUNT(map_lahan.id) as total') ->groupBy('kondisi_tanaman') ->orderByDesc('total') ->get(); } return [ 'labels' => $rows->pluck('label')->map(fn ($label) => $this->shortEnumLabel((string) $label))->values()->all(), 'series' => $rows->pluck('total')->map(fn ($value) => (int) $value)->values()->all(), ]; } private function seranganHamaChart(array $filters): array { return $this->tanamanAttributeChart( $filters, 'serangan_hama', fn ($label) => $this->enumLabel(SeranganHama::class, (string) $label) ); } private function penyakitTanamanChart(array $filters): array { return $this->tanamanAttributeChart( $filters, 'penyakit_tanaman', fn ($label) => $this->enumLabel(PenyakitTanaman::class, (string) $label) ); } private function keparahanSeranganChart(array $filters): array { return $this->tanamanAttributeChart( $filters, 'keparahan_serangan', fn ($label) => $this->keparahanSeranganLabel((string) $label) ); } private function statusKepemilikanLahanChart(array $filters): array { $rows = $this->applyLahanFilters(Lahan::query(), $filters) ->whereNotNull('status_kepemilikan') ->selectRaw('status_kepemilikan as label') ->selectRaw('COUNT(map_lahan.id) as total') ->groupBy('status_kepemilikan') ->orderByDesc('total') ->get(); return $this->chartFromRows($rows, fn ($label) => $this->shortEnumLabel((string) $label)); } private function statusHakKelolaLahanChart(array $filters): array { $rows = $this->applySosialEkonomiFilters(SosialEkonomi::query(), $filters) ->whereNotNull('status_kepemilikan_lahan') ->selectRaw('status_kepemilikan_lahan as label') ->selectRaw('COUNT(sosial_ekonomi.id) as total') ->groupBy('status_kepemilikan_lahan') ->orderByDesc('total') ->get(); return $this->chartFromRows( $rows, fn ($label) => $this->enumLabel(StatusKepemilikanLahanSosialEkonomi::class, (string) $label) ); } private function praktikPemupukanChart(array $filters): array { $rows = $this->applyPraktikBudidayaFilters(PraktikBudidaya::query(), $filters) ->whereNotNull('pemberian_pupuk') ->selectRaw('pemberian_pupuk as label') ->selectRaw('COUNT(praktik_budidaya.id) as total') ->groupBy('pemberian_pupuk') ->orderBy('pemberian_pupuk') ->get(); return $this->chartFromRows( $rows, fn ($label) => $this->pemberianPupukLabel((int) $label) ); } private function produksiPerKomoditasChart(array $filters): array { $expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan'); $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("SUM({$expr}) as total") ->groupBy('k.id', 'k.nama') ->get() ->filter(fn ($row) => round((float) $row->total, 2) > 0) ->sortByDesc('total') ->values(); return [ 'labels' => $rows->pluck('label')->values()->all(), 'series' => $rows->pluck('total')->map(fn ($value) => round((float) $value, 2))->values()->all(), ]; } private function produktivitasPerKomoditasChart(array $filters): array { $expr = $this->kgCaseExpr('produksi.jumlah_panen', 'produksi.satuan'); $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('AVG(produksi.produktivitas) FILTER (WHERE produksi.produktivitas > 0) as avg_prod') ->selectRaw("SUM({$expr}) as total_kg") ->selectRaw('SUM(produksi.luas_panen) as total_luas') ->groupBy('k.id', 'k.nama') ->get() ->map(function ($row) { $avgProd = (float) $row->avg_prod; $totalLuas = (float) $row->total_luas; $totalKg = (float) $row->total_kg; $value = $avgProd > 0 ? $avgProd : ($totalLuas > 0 ? $totalKg / $totalLuas : 0); return (object) ['label' => $row->label, 'total' => round($value, 2)]; }) ->filter(fn ($row) => $row->total > 0) ->sortByDesc('total') ->values(); return [ 'labels' => $rows->pluck('label')->values()->all(), 'series' => $rows->pluck('total')->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 { return [ 'labels' => $rows ->pluck('label') ->map(fn ($label) => $labelResolver($label)) ->values() ->all(), 'series' => $rows ->pluck('total') ->map(fn ($value) => (int) $value) ->values() ->all(), ]; } private function tanamanAttributeChart(array $filters, string $field, callable $labelResolver): array { $qualifiedField = 'map_tanaman.'.$field; $rows = $this->applyTanamanTableFilters(DB::table('map_tanaman'), $filters) ->whereNull('map_tanaman.deleted_at') ->whereNotNull($qualifiedField) ->selectRaw($qualifiedField.' as label') ->selectRaw('COUNT(map_tanaman.id) as total') ->groupBy($qualifiedField) ->orderByDesc('total') ->get(); if ($rows->isEmpty()) { $rows = $this->applyLahanFilters(Lahan::query(), $filters) ->whereNotNull($field) ->selectRaw($field.' as label') ->selectRaw('COUNT(map_lahan.id) as total') ->groupBy($field) ->orderByDesc('total') ->get(); } return $this->chartFromRows($rows, $labelResolver); } private function applyPetaniFilters($query, array $filters) { return $query ->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) { $query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan'])); }) ->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa'])) ->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas'])) ->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) { $query->whereHas('skor', function ($skor) use ($filters) { $this->applySegmentasiRange($skor, $filters['segmentasi']); }); }); } private function applyLahanFilters($query, array $filters) { return $query ->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) { $query->whereHas('desaKelurahan', fn ($desa) => $desa->where('kecamatan_id', $filters['kecamatan'])); }) ->when($filters['desa'] !== 'all', fn ($query) => $query->where('desa_kelurahan_id', $filters['desa'])) ->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas'])) ->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) { $query->whereIn('petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi'])); }); } private function applyProduksiFilters($query, array $filters) { return $query ->when($filters['kecamatan'] !== 'all' || $filters['desa'] !== 'all' || $filters['segmentasi'] !== 'all', function ($query) use ($filters) { $query->whereHas('lahan', function ($lahan) use ($filters) { $this->applyLahanFilters($lahan, $filters); }); }) ->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('komoditas_id', $filters['komoditas'])); } private function applySosialEkonomiFilters($query, array $filters) { return $query->whereHas('petani', function ($petani) use ($filters) { $this->applyPetaniFilters($petani, $filters); }); } private function applyPraktikBudidayaFilters($query, array $filters) { return $query->whereHas('petani', function ($petani) use ($filters) { $this->applyPetaniFilters($petani, $filters); }); } 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) { return $query ->leftJoin('map_lahan as l', 'l.id', '=', 'map_tanaman.lahan_id') ->when($filters['kecamatan'] !== 'all', function ($query) use ($filters) { $query ->leftJoin('master_desa_kelurahan as d', 'd.id', '=', 'l.desa_kelurahan_id') ->where('d.kecamatan_id', $filters['kecamatan']); }) ->when($filters['desa'] !== 'all', fn ($query) => $query->where('l.desa_kelurahan_id', $filters['desa'])) ->when($filters['komoditas'] !== 'all', fn ($query) => $query->where('map_tanaman.komoditas_id', $filters['komoditas'])) ->when($filters['segmentasi'] !== 'all', function ($query) use ($filters) { $query->where(function ($query) use ($filters) { $query ->whereIn('map_tanaman.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi'])) ->orWhereIn('l.petani_id', $this->segmentasiPetaniIdsQuery($filters['segmentasi'])); }); }); } private function segmentasiPetaniIdsQuery(string $segmentasi) { return Petani::query() ->select('id') ->whereHas('skor', function ($skor) use ($segmentasi) { $this->applySegmentasiRange($skor, $segmentasi); }); } private function applySegmentasiRange($query, string $segmentasi) { return match ($segmentasi) { 'Penggerak' => $query->where('skor_kesiapan_adopsi', '>=', 4), 'Perintis' => $query->where('skor_kesiapan_adopsi', '>=', 3)->where('skor_kesiapan_adopsi', '<', 4), 'Pemerhati' => $query->where('skor_kesiapan_adopsi', '>=', 2)->where('skor_kesiapan_adopsi', '<', 3), 'Pemula' => $query->where('skor_kesiapan_adopsi', '>=', 1)->where('skor_kesiapan_adopsi', '<', 2), default => $query->where(function ($query) { $query->whereNull('skor_kesiapan_adopsi')->orWhere('skor_kesiapan_adopsi', '<', 1); }), }; } /** * SQL CASE expression converting a jumlah/satuan pair to kilograms, * so aggregation happens in the database instead of pulling every * row of `produksi` into PHP to sum with toKilograms(). */ private function kgCaseExpr(string $jumlahColumn, string $satuanColumn): string { return "CASE LOWER({$satuanColumn}) WHEN 'ton' THEN {$jumlahColumn} * 1000 WHEN 'kuintal' THEN {$jumlahColumn} * 100 WHEN 'kwintal' THEN {$jumlahColumn} * 100 ELSE {$jumlahColumn} END"; } private function shortEnumLabel(string $value): string { return str($value) ->replace('_', ' ') ->headline() ->toString(); } private function enumLabel(string $enumClass, int|string $value): string { $case = $enumClass::tryFrom($value); if (! $case) { return $this->shortEnumLabel((string) $value); } return method_exists($case, 'label') ? $case->label() : $this->shortEnumLabel((string) $case->value); } private function keparahanSeranganLabel(string $value): string { return match (KeparahanSerangan::tryFrom($value)) { KeparahanSerangan::RINGAN => 'Ringan', KeparahanSerangan::SEDANG => 'Sedang', KeparahanSerangan::BERAT => 'Parah', default => $this->shortEnumLabel($value), }; } private function pemberianPupukLabel(int $value): string { return match (PemberianPupuk::tryFrom($value)) { PemberianPupuk::TIDAK_PERNAH => 'Tidak pernah', PemberianPupuk::JENIS_DOSIS_WAKTU_TIDAK_MENENTU => 'Tidak menentu', PemberianPupuk::JENIS_SESUAI_DOSIS_TIDAK_MENENTU => 'Jenis sesuai', PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT => 'Cukup tepat', PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_TEPAT => 'Tepat & rutin', default => $this->shortEnumLabel((string) $value), }; } private function toKilograms(float $value, string $unit): float { return match (strtolower($unit)) { 'ton' => $value * 1000, 'kuintal', 'kwintal' => $value * 100, default => $value, }; } private function withAllOption(string $label, array $options): array { return array_merge([ [ 'value' => 'all', 'label' => $label, ], ], $options); } private function kabupatenLabel(KabupatenKota $kabupaten): string { return preg_replace('/^(kabupaten|kota)\s+/i', '', $kabupaten->nama); } private function segmentLabel(float $score): string { return match (true) { $score >= 4.0 => 'Penggerak', $score >= 3.0 => 'Perintis', $score >= 2.0 => 'Pemerhati', $score >= 1.0 => 'Pemula', default => 'Belum tersegmentasi', }; } }