From 9b5c0a730972b3e4be358c44113b8785abfb7fcf Mon Sep 17 00:00:00 2001 From: "fauzgabriel@gmail.com" Date: Thu, 15 Oct 2026 13:02:42 +0700 Subject: [PATCH] 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');