From f95f43999afe18f56d986d2737c57e89539e4351 Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Thu, 15 Oct 2026 11:25:38 +0700 Subject: [PATCH 1/5] peta sebaran dan praktek budaya --- .../Web/Dashboard/SummaryController.php | 110 ++++++ .../views/dashboard/produktivitas.blade.php | 327 +++++++++++++++--- 2 files changed, 397 insertions(+), 40 deletions(-) diff --git a/app/Http/Controllers/Web/Dashboard/SummaryController.php b/app/Http/Controllers/Web/Dashboard/SummaryController.php index dd7028f..b3ffa01 100644 --- a/app/Http/Controllers/Web/Dashboard/SummaryController.php +++ b/app/Http/Controllers/Web/Dashboard/SummaryController.php @@ -262,6 +262,116 @@ class SummaryController extends Controller ], 'levels' => $levels, 'cultivationLevels' => $this->productionCultivationMapLevelData($request, $levels), + 'information' => $this->productionMapInformationData($request), + ]; + } + + private function productionMapInformationData(Request $request): array + { + $landMetrics = $this->karakteristikLandMetrics($request); + $filteredPetaniIds = $this->filteredPetaniIdsQuery($request); + $commodityRows = DB::table('petani') + ->whereNull('petani.deleted_at') + ->whereIn('petani.id', clone $filteredPetaniIds) + ->leftJoin('master_komoditas', 'master_komoditas.id', '=', 'petani.komoditas_id') + ->selectRaw("COALESCE(master_komoditas.nama, 'Belum ditentukan') as nama") + ->selectRaw('COUNT(DISTINCT petani.id) as jumlah') + ->groupBy('master_komoditas.nama') + ->orderByDesc('jumlah') + ->get() + ->map(fn ($row) => ['nama' => $row->nama, 'jumlah' => (int) $row->jumlah]) + ->all(); + + $lahan = (clone $this->filteredLahanQuery($request)) + ->leftJoin('profiles', 'profiles.id', '=', 'map_lahan.profile_id') + ->leftJoin('petani', 'petani.id', '=', 'map_lahan.petani_id') + ->tap(fn ($query) => $this->whereActiveMapOwner($query)) + ->get([ + 'map_lahan.id', + 'map_lahan.serangan_hama', + 'map_lahan.penyakit_tanaman', + 'map_lahan.jenis_hama', + 'map_lahan.jenis_penyakit', + ]); + $tanamanByLahan = DB::table('map_tanaman') + ->whereNull('map_tanaman.deleted_at') + ->whereIn('map_tanaman.lahan_id', $lahan->pluck('id')) + ->get([ + 'map_tanaman.lahan_id', + 'map_tanaman.serangan_hama', + 'map_tanaman.penyakit_tanaman', + 'map_tanaman.jenis_hama', + 'map_tanaman.jenis_penyakit', + ]) + ->groupBy('lahan_id'); + $optCounts = []; + + foreach ($lahan as $land) { + foreach ($this->productionOptDetails($land, $tanamanByLahan->get($land->id, collect())) as $opt) { + $optCounts[$opt] = ($optCounts[$opt] ?? 0) + 1; + } + } + + arsort($optCounts); + + $mainCommodityIds = DB::table('master_komoditas') + ->where('is_utama', true) + ->whereNull('deleted_at') + ->select('id'); + $scoreQuery = DB::table('petani') + ->leftJoin('petani_skor', function ($join) { + $join->on('petani_skor.petani_id', '=', 'petani.id') + ->whereNull('petani_skor.deleted_at'); + }) + ->whereNull('petani.deleted_at') + ->whereIn('petani.id', clone $filteredPetaniIds) + ->whereIn('petani.komoditas_id', $mainCommodityIds); + $averageIndex = (clone $scoreQuery)->avg('petani_skor.skor_kesiapan_adopsi'); + $segmentCase = "CASE + WHEN petani_skor.skor_kesiapan_adopsi >= 4 THEN 'Penggerak' + WHEN petani_skor.skor_kesiapan_adopsi >= 3 THEN 'Perintis' + WHEN petani_skor.skor_kesiapan_adopsi >= 2 THEN 'Pemerhati' + WHEN petani_skor.skor_kesiapan_adopsi >= 1 THEN 'Pemula' + ELSE 'Belum Lengkap' + END"; + $segmentCounts = (clone $scoreQuery) + ->selectRaw("{$segmentCase} as segmen") + ->selectRaw('COUNT(DISTINCT petani.id) as jumlah') + ->groupByRaw($segmentCase) + ->get(); + $segmentPriority = [ + 'Penggerak' => 0, + 'Perintis' => 1, + 'Pemerhati' => 2, + 'Pemula' => 3, + 'Belum Lengkap' => 4, + ]; + $dominantSegment = $segmentCounts + ->sort(function ($left, $right) use ($segmentPriority) { + return ((int) $right->jumlah <=> (int) $left->jumlah) + ?: (($segmentPriority[$left->segmen] ?? 99) <=> ($segmentPriority[$right->segmen] ?? 99)); + }) + ->first(); + $averageSegment = match (true) { + $averageIndex === null => 'Belum Lengkap', + $averageIndex >= 4 => 'Penggerak', + $averageIndex >= 3 => 'Perintis', + $averageIndex >= 2 => 'Pemerhati', + $averageIndex >= 1 => 'Pemula', + default => 'Belum Lengkap', + }; + + return [ + 'jumlah_petani' => $landMetrics['total_petani'], + 'total_luas_lahan' => $landMetrics['total_luas_lahan'], + 'petani_per_komoditas' => $commodityRows, + 'opt' => collect($optCounts) + ->map(fn ($jumlah, $nama) => ['nama' => $nama, 'jumlah' => $jumlah]) + ->values() + ->all(), + 'indeks' => $averageIndex === null ? null : round((float) $averageIndex, 2), + 'segmen' => $averageSegment, + 'segmen_dominan' => $dominantSegment->segmen ?? 'Belum Lengkap', ]; } diff --git a/resources/views/dashboard/produktivitas.blade.php b/resources/views/dashboard/produktivitas.blade.php index ae90cb0..d6b3b29 100644 --- a/resources/views/dashboard/produktivitas.blade.php +++ b/resources/views/dashboard/produktivitas.blade.php @@ -241,6 +241,145 @@ width: 100%; } + .produksi-map-information-overlay { + background: rgba(255, 255, 255, .94); + border: 1px solid rgba(0, 0, 0, .08); + border-radius: 12px; + box-shadow: 0 6px 24px rgba(0, 0, 0, .2); + left: auto; + max-height: calc(100% - 2rem); + overflow-y: auto; + padding: .875rem; + pointer-events: none; + position: absolute; + right: 1rem; + top: 1rem; + width: min(350px, calc(100% - 5.5rem)); + z-index: 700; + } + + .produksi-map-information { + display: flex; + flex-direction: column; + gap: .75rem; + } + + .produksi-map-information-summary { + align-items: center; + background: rgba(var(--bs-primary-rgb, 115, 103, 240), .07); + border-radius: 10px; + display: flex; + gap: .75rem; + padding: .875rem; + } + + .produksi-map-information-summary-icon { + align-items: center; + background: var(--bs-primary, #7367f0); + border-radius: 10px; + color: #fff; + display: inline-flex; + flex: 0 0 38px; + height: 38px; + justify-content: center; + width: 38px; + } + + .produksi-map-information-summary-value { + color: var(--bs-heading-color); + font-size: 1.25rem; + font-weight: 700; + line-height: 1.2; + } + + .produksi-map-information-summary-label, + .produksi-map-information-section-title { + color: var(--bs-heading-color); + font-size: .8125rem; + font-weight: 600; + } + + .produksi-map-information-metrics { + display: grid; + gap: .4rem; + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .produksi-map-information-metric { + background: rgba(var(--bs-primary-rgb, 115, 103, 240), .07); + border-radius: 8px; + min-width: 0; + padding: .5rem .625rem; + } + + .produksi-map-information-metric-label { + color: var(--bs-secondary-color); + display: block; + font-size: .65rem; + line-height: 1.2; + margin-bottom: .2rem; + } + + .produksi-map-information-metric-value { + color: var(--bs-heading-color); + display: block; + font-size: .75rem; + font-weight: 700; + line-height: 1.25; + overflow-wrap: anywhere; + } + + .produksi-map-information-section-title { + display: block; + margin-bottom: .625rem; + } + + .produksi-map-information-bars { + display: flex; + flex-direction: column; + gap: .5rem; + } + + .produksi-map-information-bar-label { + align-items: baseline; + color: var(--bs-body-color); + display: flex; + font-size: .75rem; + gap: .5rem; + justify-content: space-between; + margin-bottom: .25rem; + } + + .produksi-map-information-bar-label span:first-child { + min-width: 0; + overflow-wrap: anywhere; + } + + .produksi-map-information-bar-label strong { + color: var(--bs-heading-color); + flex: 0 0 auto; + font-weight: 600; + } + + .produksi-map-information-track { + background: var(--bs-secondary-bg, rgba(128, 128, 128, .14)); + border-radius: 999px; + height: 6px; + overflow: hidden; + } + + .produksi-map-information-fill { + background: var(--bs-primary, #7367f0); + border-radius: inherit; + height: 100%; + min-width: 3px; + } + + .produksi-map-information-empty { + color: var(--bs-secondary-color); + font-size: .75rem; + } + .produksi-map-back { display: none; left: 50px; @@ -479,6 +618,13 @@ left: 16px; top: 16px; } + + .produksi-map-information-overlay { + left: auto; + right: .75rem; + top: 1rem; + width: min(320px, calc(100% - 1.5rem)); + } } @endpush @@ -618,51 +764,61 @@
-
+
-
Peta Sebaran
- Lahan dan tanaman berdasarkan filter wilayah, komoditas, dan segmen -
-
- - - +
Peta Sebaran & Praktik Budidaya
+ Petani, luas lahan, komoditas, OPT, dan praktik budidaya berdasarkan filter aktif
-
- -
Belum ada poligon lahan untuk filter ini.
-
-
-
-
-
+
+
+
+
+
Peta Sebaran Lahan & Tanaman
+ Lahan dan tanaman berdasarkan filter wilayah, komoditas, dan segmen +
+
+ + + +
+
+
+ +
Belum ada poligon lahan untuk filter ini.
+
+
+
+
+
-
-
-
-
-
-
Peta Praktik Budidaya
- Intensitas pemangkasan, pemupukan, dan kebiasaan praktik baik berdasarkan filter wilayah -
-
- - - -
-
-
-
- -
Belum ada poligon lahan untuk filter ini.
+
+
+
+
Peta Praktik Budidaya
+ Intensitas pemangkasan, pemupukan, dan kebiasaan praktik baik +
+
+ + + +
+
+
+ +
Belum ada poligon lahan untuk filter ini.
+
+
+
+
+
@@ -921,6 +1077,7 @@ document.addEventListener('DOMContentLoaded', function () { const chartData = @json($chartData); const mapData = @json($mapData); + const mapInformation = mapData.information || {}; const wilayah = @json($wilayah); const headingColor = config.colors.headingColor; const legendColor = config.colors.bodyColor; @@ -950,6 +1107,93 @@ document.addEventListener('DOMContentLoaded', function () { }); } + function renderProductionMapInformation(elementId, includeOpt = true) { + const element = document.getElementById(elementId); + + if (!element) return; + + const commodityRows = Array.isArray(mapInformation.petani_per_komoditas) + ? mapInformation.petani_per_komoditas + : []; + const optRows = Array.isArray(mapInformation.opt) ? mapInformation.opt : []; + const indexValue = mapInformation.indeks === null || mapInformation.indeks === undefined + ? '-' + : `${formatNumber(mapInformation.indeks, 2)} / 5`; + const renderBars = function (rows, emptyLabel) { + if (!rows.length) { + return `
${emptyLabel}
`; + } + + const visibleRows = rows.slice(0, 5); + const maxValue = Math.max(...visibleRows.map(row => Number(row.jumlah || 0)), 1); + + const bars = visibleRows.map(function (row) { + const value = Number(row.jumlah || 0); + const width = Math.max(value > 0 ? 3 : 0, (value / maxValue) * 100); + + return ` +
+
+ ${escapeHtml(row.nama || 'Belum ditentukan')} + ${formatNumber(value, 0)} +
+ +
+ `; + }).join(''); + + const remaining = rows.length - visibleRows.length; + return bars + (remaining > 0 + ? `
+ ${formatNumber(remaining, 0)} kategori lainnya
` + : ''); + }; + + element.innerHTML = ` +
+
+ +
+
${formatNumber(mapInformation.jumlah_petani || 0, 0)}
+
Jumlah Petani
+
+
+
+ +
+
${formatNumber(mapInformation.total_luas_lahan || 0, 2)} ha
+
Total Luas Lahan
+
+
+
+
+
+ Indeks + ${indexValue} +
+
+ Segmen + ${escapeHtml(mapInformation.segmen || 'Belum Lengkap')} +
+
+ Segmen Dominan + ${escapeHtml(mapInformation.segmen_dominan || 'Belum Lengkap')} +
+
+
+ Jumlah Petani / Komoditas +
${renderBars(commodityRows, 'Belum ada data komoditas.')}
+
+ ${includeOpt ? ` +
+ OPT (Organisme Pengganggu Tanaman) +
${renderBars(optRows, 'Belum ada data OPT.')}
+
+ ` : ''} + `; + } + function toGroupedBarData(data, seriesName) { if (!data || !Array.isArray(data.labels) || !Array.isArray(data.series)) { return data; @@ -1955,6 +2199,9 @@ document.addEventListener('DOMContentLoaded', function () { cultivationMapRendered = true; } + renderProductionMapInformation('produksi-map-information'); + renderProductionMapInformation('produksi-budidaya-map-information', false); + if (typeof L === 'undefined') { const mapEmpty = document.getElementById('produksi-map-empty'); const cultivationMapEmpty = document.getElementById('produksi-budidaya-map-empty'); From 9d56b67998a8ef892cac57348c526651e33baa13 Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Thu, 15 Oct 2026 11:31:57 +0700 Subject: [PATCH 2/5] sroll untuk --- resources/views/dashboard/produktivitas.blade.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/resources/views/dashboard/produktivitas.blade.php b/resources/views/dashboard/produktivitas.blade.php index d6b3b29..d46008e 100644 --- a/resources/views/dashboard/produktivitas.blade.php +++ b/resources/views/dashboard/produktivitas.blade.php @@ -250,7 +250,7 @@ max-height: calc(100% - 2rem); overflow-y: auto; padding: .875rem; - pointer-events: none; + pointer-events: auto; position: absolute; right: 1rem; top: 1rem; @@ -1124,10 +1124,9 @@ document.addEventListener('DOMContentLoaded', function () { return `
${emptyLabel}
`; } - const visibleRows = rows.slice(0, 5); - const maxValue = Math.max(...visibleRows.map(row => Number(row.jumlah || 0)), 1); + const maxValue = Math.max(...rows.map(row => Number(row.jumlah || 0)), 1); - const bars = visibleRows.map(function (row) { + return rows.map(function (row) { const value = Number(row.jumlah || 0); const width = Math.max(value > 0 ? 3 : 0, (value / maxValue) * 100); @@ -1143,11 +1142,6 @@ document.addEventListener('DOMContentLoaded', function () {
`; }).join(''); - - const remaining = rows.length - visibleRows.length; - return bars + (remaining > 0 - ? `
+ ${formatNumber(remaining, 0)} kategori lainnya
` - : ''); }; element.innerHTML = ` From 9b5c0a730972b3e4be358c44113b8785abfb7fcf Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Thu, 15 Oct 2026 13:02:42 +0700 Subject: [PATCH 3/5] perubahan query --- .../Web/Dashboard/SummaryController.php | 107 +++++++++++++----- .../views/dashboard/produktivitas.blade.php | 70 ++++++++++-- routes/web.php | 1 + 3 files changed, 145 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Web/Dashboard/SummaryController.php b/app/Http/Controllers/Web/Dashboard/SummaryController.php index b3ffa01..7ff615d 100644 --- a/app/Http/Controllers/Web/Dashboard/SummaryController.php +++ b/app/Http/Controllers/Web/Dashboard/SummaryController.php @@ -55,6 +55,48 @@ class SummaryController extends Controller ] + $filterData + $this->productionDashboardData($request)); } + public function mapInformation(Request $request) + { + if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('fasilitator')) { + abort(403); + } + + $validated = $request->validate([ + 'level' => ['nullable', 'in:provinsi,kabupaten,kecamatan,desa'], + 'id' => ['required_with:level', 'integer'], + 'provinsi_id' => ['nullable', 'integer'], + 'kabupatenKota_id' => ['nullable', 'integer'], + 'kecamatan_id' => ['nullable', 'integer'], + 'desaKelurahan_id' => ['nullable', 'integer'], + 'komoditas_id' => ['nullable', 'integer'], + 'segmen' => ['nullable', 'string'], + ]); + + $filters = [ + 'provinsi_id' => null, + 'kabupatenKota_id' => null, + 'kecamatan_id' => null, + 'desaKelurahan_id' => null, + 'komoditas_id' => $validated['komoditas_id'] ?? null, + 'segmen' => $validated['segmen'] ?? null, + ]; + $regionFilter = match ($validated['level'] ?? null) { + 'provinsi' => 'provinsi_id', + 'kabupaten' => 'kabupatenKota_id', + 'kecamatan' => 'kecamatan_id', + 'desa' => 'desaKelurahan_id', + default => null, + }; + + if ($regionFilter) { + $filters[$regionFilter] = $validated['id']; + } + + $scopedRequest = Request::create('/dashboard/produktivitas/map-information', 'GET', $filters); + + return response()->json($this->productionMapInformationData($scopedRequest)); + } + public function pasar(Request $request) { if (!Gate::allows('super_admin') && !Gate::allows('admin') && !Gate::allows('fasilitator')) { @@ -181,6 +223,7 @@ class SummaryController extends Controller $healthSourceName = $tanaman->isNotEmpty() ? 'tanaman' : 'lahan'; $landMetrics = $this->karakteristikLandMetrics($request); + $totalPetani = $landMetrics['total_petani']; $totalLuasLahan = $landMetrics['total_luas_lahan']; $avgLuasPerPetani = $landMetrics['ratarata_luas_lahan']; $actualProductivity = $this->actualProductivityByCommodityChart($request); @@ -190,6 +233,14 @@ class SummaryController extends Controller $aksesPasar = $this->filteredAksesPasar($request); $kpiCards = [ + [ + 'value' => number_format($totalPetani, 0, ',', '.'), + 'unit' => 'orang', + 'label' => 'Jumlah Petani', + 'description' => 'Petani sesuai filter yang dipilih', + 'icon' => 'tabler-users', + 'variant' => 'primary', + ], [ 'value' => number_format($totalLuasLahan, 2, ',', '.'), 'unit' => 'ha', @@ -268,34 +319,34 @@ class SummaryController extends Controller private function productionMapInformationData(Request $request): array { - $landMetrics = $this->karakteristikLandMetrics($request); - $filteredPetaniIds = $this->filteredPetaniIdsQuery($request); - $commodityRows = DB::table('petani') - ->whereNull('petani.deleted_at') - ->whereIn('petani.id', clone $filteredPetaniIds) - ->leftJoin('master_komoditas', 'master_komoditas.id', '=', 'petani.komoditas_id') - ->selectRaw("COALESCE(master_komoditas.nama, 'Belum ditentukan') as nama") - ->selectRaw('COUNT(DISTINCT petani.id) as jumlah') - ->groupBy('master_komoditas.nama') + $filteredMapLahan = (clone $this->filteredLahanQuery($request)) + ->leftJoin('profiles', 'profiles.id', '=', 'map_lahan.profile_id') + ->leftJoin('petani', 'petani.id', '=', 'map_lahan.petani_id') + ->tap(fn ($query) => $this->whereActiveMapOwner($query)); + $mapLahanRows = (clone $filteredMapLahan)->get([ + 'map_lahan.id', + 'map_lahan.petani_id', + 'map_lahan.luas_lahan', + 'map_lahan.serangan_hama', + 'map_lahan.penyakit_tanaman', + 'map_lahan.jenis_hama', + 'map_lahan.jenis_penyakit', + ]); + $petaniIds = $mapLahanRows->pluck('petani_id')->filter()->unique()->values(); + $commodityRows = (clone $filteredMapLahan) + ->leftJoin('master_komoditas as map_komoditas', 'map_komoditas.id', '=', 'map_lahan.komoditas_id') + ->whereNotNull('map_lahan.petani_id') + ->selectRaw("COALESCE(map_komoditas.nama, 'Belum ditentukan') as nama") + ->selectRaw('COUNT(DISTINCT map_lahan.petani_id) as jumlah') + ->groupBy('map_komoditas.nama') ->orderByDesc('jumlah') ->get() ->map(fn ($row) => ['nama' => $row->nama, 'jumlah' => (int) $row->jumlah]) ->all(); - $lahan = (clone $this->filteredLahanQuery($request)) - ->leftJoin('profiles', 'profiles.id', '=', 'map_lahan.profile_id') - ->leftJoin('petani', 'petani.id', '=', 'map_lahan.petani_id') - ->tap(fn ($query) => $this->whereActiveMapOwner($query)) - ->get([ - 'map_lahan.id', - 'map_lahan.serangan_hama', - 'map_lahan.penyakit_tanaman', - 'map_lahan.jenis_hama', - 'map_lahan.jenis_penyakit', - ]); $tanamanByLahan = DB::table('map_tanaman') ->whereNull('map_tanaman.deleted_at') - ->whereIn('map_tanaman.lahan_id', $lahan->pluck('id')) + ->whereIn('map_tanaman.lahan_id', $mapLahanRows->pluck('id')) ->get([ 'map_tanaman.lahan_id', 'map_tanaman.serangan_hama', @@ -306,7 +357,7 @@ class SummaryController extends Controller ->groupBy('lahan_id'); $optCounts = []; - foreach ($lahan as $land) { + foreach ($mapLahanRows as $land) { foreach ($this->productionOptDetails($land, $tanamanByLahan->get($land->id, collect())) as $opt) { $optCounts[$opt] = ($optCounts[$opt] ?? 0) + 1; } @@ -318,14 +369,18 @@ class SummaryController extends Controller ->where('is_utama', true) ->whereNull('deleted_at') ->select('id'); + $scorePetaniIds = DB::table('petani') + ->whereIn('petani.id', $petaniIds) + ->whereIn('petani.komoditas_id', $mainCommodityIds) + ->select('petani.id') + ->pluck('petani.id'); $scoreQuery = DB::table('petani') ->leftJoin('petani_skor', function ($join) { $join->on('petani_skor.petani_id', '=', 'petani.id') ->whereNull('petani_skor.deleted_at'); }) ->whereNull('petani.deleted_at') - ->whereIn('petani.id', clone $filteredPetaniIds) - ->whereIn('petani.komoditas_id', $mainCommodityIds); + ->whereIn('petani.id', $scorePetaniIds); $averageIndex = (clone $scoreQuery)->avg('petani_skor.skor_kesiapan_adopsi'); $segmentCase = "CASE WHEN petani_skor.skor_kesiapan_adopsi >= 4 THEN 'Penggerak' @@ -362,8 +417,8 @@ class SummaryController extends Controller }; return [ - 'jumlah_petani' => $landMetrics['total_petani'], - 'total_luas_lahan' => $landMetrics['total_luas_lahan'], + 'jumlah_petani' => $petaniIds->count(), + 'total_luas_lahan' => (float) $mapLahanRows->sum('luas_lahan'), 'petani_per_komoditas' => $commodityRows, 'opt' => collect($optCounts) ->map(fn ($jumlah, $nama) => ['nama' => $nama, 'jumlah' => $jumlah]) diff --git a/resources/views/dashboard/produktivitas.blade.php b/resources/views/dashboard/produktivitas.blade.php index d46008e..2833828 100644 --- a/resources/views/dashboard/produktivitas.blade.php +++ b/resources/views/dashboard/produktivitas.blade.php @@ -717,7 +717,7 @@
@foreach ($kpiCards as $card) -
+
@@ -764,12 +764,12 @@
-
+
@@ -1077,6 +1077,8 @@ document.addEventListener('DOMContentLoaded', function () { const chartData = @json($chartData); const mapData = @json($mapData); + const dashboardFilters = @json($dashboardFilters); + const mapInformationUrl = @json(route('dashboard.produktivitas.map-information')); const mapInformation = mapData.information || {}; const wilayah = @json($wilayah); const headingColor = config.colors.headingColor; @@ -1090,6 +1092,7 @@ document.addEventListener('DOMContentLoaded', function () { let productionMapRendered = false; let cultivationMapRendered = false; let actualProductivityRendered = false; + let mapInformationRequestId = 0; function hasData(data) { return data && Array.isArray(data.series) && data.series.some(function (value) { @@ -1119,7 +1122,7 @@ document.addEventListener('DOMContentLoaded', function () { const indexValue = mapInformation.indeks === null || mapInformation.indeks === undefined ? '-' : `${formatNumber(mapInformation.indeks, 2)} / 5`; - const renderBars = function (rows, emptyLabel) { + const renderBars = function (rows, emptyLabel, unitLabel) { if (!rows.length) { return `
${emptyLabel}
`; } @@ -1134,7 +1137,7 @@ document.addEventListener('DOMContentLoaded', function () {
${escapeHtml(row.nama || 'Belum ditentukan')} - ${formatNumber(value, 0)} + ${formatNumber(value, 0)} ${unitLabel}
Jumlah Petani / Komoditas -
${renderBars(commodityRows, 'Belum ada data komoditas.')}
+
${renderBars(commodityRows, 'Belum ada data komoditas.', 'petani')}
${includeOpt ? `
OPT (Organisme Pengganggu Tanaman) -
${renderBars(optRows, 'Belum ada data OPT.')}
+
${renderBars(optRows, 'Belum ada data OPT.', 'lahan')}
` : ''} `; } + async function loadProductionMapInformation(level = null, id = null) { + const requestId = ++mapInformationRequestId; + const params = new URLSearchParams(); + + Object.entries(dashboardFilters || {}).forEach(function ([key, value]) { + if (value !== null && value !== undefined && value !== '') { + params.set(key, value); + } + }); + + if (level && id !== null && id !== undefined) { + params.set('level', level); + params.set('id', id); + } + + try { + const response = await fetch(`${mapInformationUrl}?${params.toString()}`, { + headers: { Accept: 'application/json' }, + }); + + if (!response.ok) return; + + const information = await response.json(); + if (requestId !== mapInformationRequestId) return; + + Object.assign(mapInformation, information); + renderProductionMapInformation('produksi-map-information'); + renderProductionMapInformation('produksi-budidaya-map-information', false); + } catch (error) { + console.error('Gagal memuat informasi wilayah pada peta.', error); + } + } + function toGroupedBarData(data, seriesName) { if (!data || !Array.isArray(data.labels) || !Array.isArray(data.series)) { return data; @@ -1410,6 +1446,8 @@ document.addEventListener('DOMContentLoaded', function () { empty: null, group: null, history: [], + informationId: null, + informationLevel: null, layers: {}, map: null, }; @@ -1447,6 +1485,9 @@ document.addEventListener('DOMContentLoaded', function () { if (previous) { renderProductionMapLevel(previous.level, previous.parentId); + productionMapState.informationLevel = previous.informationLevel; + productionMapState.informationId = previous.informationId; + loadProductionMapInformation(previous.informationLevel, previous.informationId); if (previous.bounds?.isValid()) { productionMapState.map.fitBounds(previous.bounds, { padding: [30, 30] }); @@ -1534,7 +1575,12 @@ document.addEventListener('DOMContentLoaded', function () { level: nextLevel, parentId, bounds: productionMapState.group.getBounds(), + informationLevel: productionMapState.informationLevel, + informationId: productionMapState.informationId, }); + productionMapState.informationLevel = nextLevel; + productionMapState.informationId = item.id; + loadProductionMapInformation(nextLevel, item.id); renderProductionMapLevel(nextProductionMapLevel(nextLevel), item.id, event.target.getBounds()); }); } @@ -1627,6 +1673,8 @@ document.addEventListener('DOMContentLoaded', function () { empty: null, group: null, history: [], + informationId: null, + informationLevel: null, layers: {}, map: null, }; @@ -1664,6 +1712,9 @@ document.addEventListener('DOMContentLoaded', function () { if (previous) { renderCultivationMapLevel(previous.level, previous.parentId); + cultivationMapState.informationLevel = previous.informationLevel; + cultivationMapState.informationId = previous.informationId; + loadProductionMapInformation(previous.informationLevel, previous.informationId); if (previous.bounds?.isValid()) { cultivationMapState.map.fitBounds(previous.bounds, { padding: [30, 30] }); @@ -1751,7 +1802,12 @@ document.addEventListener('DOMContentLoaded', function () { level: nextLevel, parentId, bounds: cultivationMapState.group.getBounds(), + informationLevel: cultivationMapState.informationLevel, + informationId: cultivationMapState.informationId, }); + cultivationMapState.informationLevel = nextLevel; + cultivationMapState.informationId = item.id; + loadProductionMapInformation(nextLevel, item.id); renderCultivationMapLevel(nextProductionMapLevel(nextLevel), item.id, event.target.getBounds()); }); } diff --git a/routes/web.php b/routes/web.php index a3958b4..ba27b28 100644 --- a/routes/web.php +++ b/routes/web.php @@ -41,6 +41,7 @@ Route::middleware('auth') // Home + Route::get('/dashboard/produktivitas/map-information', [App\Http\Controllers\Web\Dashboard\SummaryController::class, 'mapInformation'])->name('dashboard.produktivitas.map-information'); Route::get('/dashboard/produktivitas', [App\Http\Controllers\Web\Dashboard\SummaryController::class, 'produktivitas'])->name('dashboard.produktivitas'); Route::get('/dashboard/pasar', [App\Http\Controllers\Web\Dashboard\SummaryController::class, 'pasar'])->name('dashboard.pasar'); From 2e8271d27ab32cdca6ac69da1363c778297048e6 Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Thu, 15 Oct 2026 14:05:01 +0700 Subject: [PATCH 4/5] cars --- .../Web/Dashboard/SummaryController.php | 17 +- .../views/dashboard/produktivitas.blade.php | 167 +++++++++--------- 2 files changed, 97 insertions(+), 87 deletions(-) diff --git a/app/Http/Controllers/Web/Dashboard/SummaryController.php b/app/Http/Controllers/Web/Dashboard/SummaryController.php index 7ff615d..427c19e 100644 --- a/app/Http/Controllers/Web/Dashboard/SummaryController.php +++ b/app/Http/Controllers/Web/Dashboard/SummaryController.php @@ -233,14 +233,14 @@ class SummaryController extends Controller $aksesPasar = $this->filteredAksesPasar($request); $kpiCards = [ - [ - 'value' => number_format($totalPetani, 0, ',', '.'), - 'unit' => 'orang', - 'label' => 'Jumlah Petani', - 'description' => 'Petani sesuai filter yang dipilih', - 'icon' => 'tabler-users', - 'variant' => 'primary', - ], + // [ + // 'value' => number_format($totalPetani, 0, ',', '.'), + // 'unit' => 'orang', + // 'label' => 'Jumlah Petani', + // 'description' => 'Petani sesuai filter yang dipilih', + // 'icon' => 'tabler-users', + // 'variant' => 'primary', + // ], [ 'value' => number_format($totalLuasLahan, 2, ',', '.'), 'unit' => 'ha', @@ -418,6 +418,7 @@ class SummaryController extends Controller return [ 'jumlah_petani' => $petaniIds->count(), + 'jumlah_lahan' => $mapLahanRows->count(), 'total_luas_lahan' => (float) $mapLahanRows->sum('luas_lahan'), 'petani_per_komoditas' => $commodityRows, 'opt' => collect($optCounts) diff --git a/resources/views/dashboard/produktivitas.blade.php b/resources/views/dashboard/produktivitas.blade.php index 2833828..c1b7c21 100644 --- a/resources/views/dashboard/produktivitas.blade.php +++ b/resources/views/dashboard/produktivitas.blade.php @@ -752,7 +752,7 @@
Peta Sebaran
-

Lahan, tanaman, OPT, dan praktik budidaya berdasarkan filter wilayah.

+

Lahan, tanaman, dan OPT berdasarkan filter wilayah.