contentData($request); return view('dashboard.produktivitas', [ 'title' => $title, 'dashboardFilters' => $request->only([ 'provinsi_id', 'kabupatenKota_id', 'kecamatan_id', 'desaKelurahan_id', 'komoditas_id', 'segmen', ]), ] + $filterData + $this->productionDashboardData($request)); } private function productionDashboardData(Request $request): array { $lahanQuery = $this->filteredLahanQuery($request); $lahan = (clone $lahanQuery) ->leftJoin('profiles', 'profiles.id', '=', 'map_lahan.profile_id') ->leftJoin('master_komoditas', 'master_komoditas.id', '=', 'map_lahan.komoditas_id') ->leftJoin('master_desa_kelurahan', 'master_desa_kelurahan.id', '=', 'map_lahan.desa_kelurahan_id') ->select([ 'map_lahan.*', 'profiles.nama as profile_nama', 'master_komoditas.nama as komoditas_nama', 'master_desa_kelurahan.nama as desa_nama', ]) ->get(); $lahanIds = $lahan->pluck('id')->filter()->values(); $tanaman = DB::table('map_tanaman') ->whereNull('deleted_at') ->when($lahanIds->isNotEmpty(), fn ($query) => $query->whereIn('lahan_id', $lahanIds)) ->when($lahanIds->isEmpty(), fn ($query) => $query->whereRaw('1 = 0')) ->get([ 'id', 'lahan_id', 'kondisi_tanaman', 'umur_tanaman', 'serangan_hama', 'penyakit_tanaman', 'keparahan_serangan', 'jenis_hama', 'jenis_penyakit', ]); $conditionSource = $tanaman->whereNotNull('kondisi_tanaman')->isNotEmpty() ? $tanaman : $lahan; $conditionSourceName = $tanaman->whereNotNull('kondisi_tanaman')->isNotEmpty() ? 'tanaman' : 'lahan'; $healthSource = $this->healthSource($lahan, $tanaman); $healthSourceName = $tanaman->isNotEmpty() ? 'tanaman' : 'lahan'; $landMetrics = $this->karakteristikLandMetrics($request); $totalLuasLahan = $landMetrics['total_luas_lahan']; $petaniCount = $landMetrics['total_petani']; $avgLuasPerPetani = $landMetrics['ratarata_luas_lahan']; $jumlahTanaman = $conditionSource->whereNotNull('kondisi_tanaman')->count(); $kpiCards = [ [ 'value' => number_format($totalLuasLahan, 2, ',', '.'), 'unit' => 'Ha', 'label' => 'Total Luas Lahan', 'description' => 'Luas keseluruhan lahan terfilter', 'icon' => 'tabler-chart-area-line', 'variant' => 'success', ], [ 'value' => number_format($avgLuasPerPetani, 2, ',', '.'), 'unit' => 'Ha/petani', 'label' => 'Rata-Rata Luas Lahan per Petani', 'description' => number_format($petaniCount, 0, ',', '.').' petani terfilter', 'icon' => 'tabler-ruler-measure', 'variant' => 'danger', ], [ 'value' => number_format($jumlahTanaman, 0, ',', '.'), 'unit' => 'tanaman', 'label' => 'Jumlah Tanaman', 'description' => 'Total tanaman pada chart kondisi tanaman', 'icon' => 'tabler-plant-2', 'variant' => 'secondary', ], ]; return [ 'kpiCards' => $kpiCards, 'chartData' => [ 'umurLahan' => $this->ageBucketChart($lahan, 'umur_lahan'), 'umurTanaman' => $this->ageBucketChart($healthSource, 'umur_tanaman'), 'kondisiTanaman' => $this->attributeChart($conditionSource, 'kondisi_tanaman', fn ($value) => $this->conditionLabel($value)), 'keparahanSerangan' => $this->attributeChart($healthSource, 'keparahan_serangan', fn ($value) => $this->severityLabel($value)), 'gejalaSerangan' => $this->symptomChart($healthSource), ], ]; } private function filteredLahanQuery(Request $request) { return DB::table('map_lahan') ->whereNull('map_lahan.deleted_at') ->leftJoin('master_desa_kelurahan as desa_filter', 'map_lahan.desa_kelurahan_id', '=', 'desa_filter.id') ->leftJoin('master_kecamatan as kecamatan_filter', 'desa_filter.kecamatan_id', '=', 'kecamatan_filter.id') ->leftJoin('master_kabupaten_kota as kabupaten_filter', 'kecamatan_filter.kabupaten_kota_id', '=', 'kabupaten_filter.id') ->when($request->filled('provinsi_id'), fn ($query) => $query->where('kabupaten_filter.provinsi_id', $request->provinsi_id)) ->when($request->filled('kabupatenKota_id'), fn ($query) => $query->where('kecamatan_filter.kabupaten_kota_id', $request->kabupatenKota_id)) ->when($request->filled('kecamatan_id'), fn ($query) => $query->where('desa_filter.kecamatan_id', $request->kecamatan_id)) ->when($request->filled('desaKelurahan_id'), fn ($query) => $query->where('map_lahan.desa_kelurahan_id', $request->desaKelurahan_id)) ->when($request->filled('komoditas_id'), fn ($query) => $query->where('map_lahan.komoditas_id', $request->komoditas_id)) ->when($request->filled('segmen'), function ($query) use ($request) { $query->whereIn('map_lahan.petani_id', $this->petaniIdsBySegment($request->segmen)); }); } private function karakteristikLandMetrics(Request $request): array { $filteredPetaniIds = $this->filteredPetaniIdsQuery($request); $totalPetani = (int) DB::table('petani') ->whereNull('petani.deleted_at') ->whereIn('petani.id', clone $filteredPetaniIds) ->count(); $totalLuasLahan = (float) DB::table('map_lahan') ->whereNull('map_lahan.deleted_at') ->whereIn('map_lahan.petani_id', clone $filteredPetaniIds) ->sum('map_lahan.luas_lahan'); return [ 'total_petani' => $totalPetani, 'total_luas_lahan' => $totalLuasLahan, 'ratarata_luas_lahan' => $totalLuasLahan && $totalPetani ? $totalLuasLahan / $totalPetani : 0, ]; } private function filteredPetaniIdsQuery(Request $request) { $query = DB::table('petani') ->whereNull('petani.deleted_at') ->leftJoin('master_desa_kelurahan as desa', 'petani.desa_kelurahan_id', '=', 'desa.id') ->leftJoin('master_kecamatan as kecamatan', 'desa.kecamatan_id', '=', 'kecamatan.id') ->leftJoin('master_kabupaten_kota as kabupaten', 'kecamatan.kabupaten_kota_id', '=', 'kabupaten.id') ->when($request->filled('provinsi_id'), fn ($query) => $query->where('kabupaten.provinsi_id', $request->provinsi_id)) ->when($request->filled('kabupatenKota_id'), fn ($query) => $query->where('kecamatan.kabupaten_kota_id', $request->kabupatenKota_id)) ->when($request->filled('kecamatan_id'), fn ($query) => $query->where('desa.kecamatan_id', $request->kecamatan_id)) ->when($request->filled('desaKelurahan_id'), fn ($query) => $query->where('petani.desa_kelurahan_id', $request->desaKelurahan_id)) ->when($request->filled('komoditas_id'), fn ($query) => $query->where('petani.komoditas_id', $request->komoditas_id)); if ($request->filled('segmen')) { $query->whereExists(function ($subQuery) use ($request) { $subQuery->from('petani_skor') ->whereNull('petani_skor.deleted_at') ->whereColumn('petani_skor.petani_id', 'petani.id'); match ($request->segmen) { 'Penggerak' => $subQuery->where('skor_kesiapan_adopsi', '>=', 4), 'Perintis' => $subQuery->where('skor_kesiapan_adopsi', '>=', 3) ->where('skor_kesiapan_adopsi', '<', 4), 'Pemerhati' => $subQuery->where('skor_kesiapan_adopsi', '>=', 2) ->where('skor_kesiapan_adopsi', '<', 3), 'Pemula' => $subQuery->where('skor_kesiapan_adopsi', '>=', 1) ->where('skor_kesiapan_adopsi', '<', 2), 'Belum Lengkap' => $subQuery->where(function ($query) { $query->whereNull('skor_kesiapan_adopsi') ->orWhere('skor_kesiapan_adopsi', '<', 1); }), default => null, }; }); } return $query->select('petani.id'); } private function petaniIdsBySegment(string $segment) { $query = DB::table('petani_skor') ->whereNull('deleted_at') ->select('petani_id'); match ($segment) { '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), 'Belum Lengkap' => $query->where(fn ($subQuery) => $subQuery ->whereNull('skor_kesiapan_adopsi') ->orWhere('skor_kesiapan_adopsi', '<', 1)), default => $query->whereRaw('1 = 1'), }; return $query; } private function healthSource($lahan, $tanaman) { return $tanaman->isNotEmpty() ? $tanaman : $lahan; } private function ageBucketChart($rows, string $field): array { $order = ['< 3 tahun', '3-7 tahun', '8-15 tahun', '> 15 tahun']; $counts = $rows ->pluck($field) ->filter(fn ($age) => $age !== null) ->map(function ($age) { $age = (float) $age; return match (true) { $age < 3 => '< 3 tahun', $age <= 7 => '3-7 tahun', $age <= 15 => '8-15 tahun', default => '> 15 tahun', }; }) ->countBy(); return [ 'labels' => $order, 'series' => collect($order)->map(fn ($label) => (int) ($counts[$label] ?? 0))->all(), ]; } private function attributeChart($rows, string $field, callable $labeler): array { $counts = $rows ->pluck($field) ->filter(fn ($value) => $this->enumValue($value) !== null && $this->enumValue($value) !== '') ->map(fn ($value) => $labeler($value)) ->countBy() ->sortDesc(); return [ 'labels' => $counts->keys()->values()->all(), 'series' => $counts->values()->map(fn ($value) => (int) $value)->values()->all(), ]; } private function symptomChart($rows): array { $counts = collect(); $noSymptoms = 0; foreach ($rows as $row) { $symptoms = collect([ $this->enumLabel(SeranganHama::class, $row->serangan_hama ?? null), $this->enumLabel(PenyakitTanaman::class, $row->penyakit_tanaman ?? null), ]) ->merge($this->arrayValues($row->jenis_hama ?? null)) ->merge($this->arrayValues($row->jenis_penyakit ?? null)) ->filter() ->values(); if ($symptoms->isEmpty()) { $noSymptoms++; continue; } foreach ($symptoms as $symptom) { $counts[$symptom] = (int) ($counts[$symptom] ?? 0) + 1; } } if ($noSymptoms > 0) { $counts['Tidak ada gejala'] = $noSymptoms; } $counts = $counts->sortDesc()->take(10); return [ 'labels' => $counts->keys()->values()->all(), 'series' => $counts->values()->map(fn ($value) => (int) $value)->values()->all(), ]; } private function conditionLabel($value): string { return $this->enumLabel(KondisiTanaman::class, $value, 'Belum ditentukan', true); } private function severityLabel($value): string { return $this->enumLabel(KeparahanSerangan::class, $value, 'Belum ditentukan', true); } private function enumLabel(string $enumClass, $value, string $fallback = '', bool $short = false): string { $rawValue = $this->enumValue($value); if ($rawValue === null || $rawValue === '') { return $fallback; } if (method_exists($enumClass, 'tryFrom')) { $case = $enumClass::tryFrom($rawValue); if ($case && method_exists($case, 'label')) { $label = $case->label(); return $short ? explode(',', $label)[0] : $label; } } return str($rawValue)->replace('_', ' ')->title()->toString(); } private function enumValue($value): ?string { if ($value instanceof \BackedEnum) { return (string) $value->value; } if ($value === null) { return null; } return (string) $value; } private function arrayValues($value): array { if ($value === null || $value === '') { return []; } if ($value instanceof \Illuminate\Support\Collection) { return $value->values()->all(); } if (is_array($value)) { return array_values(array_filter($value, fn ($item) => $item !== null && $item !== '')); } $decoded = json_decode((string) $value, true); if (is_array($decoded)) { return array_values(array_filter($decoded, fn ($item) => $item !== null && $item !== '')); } return [(string) $value]; } }