update dashboard produksi
This commit is contained in:
@@ -223,7 +223,6 @@ 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);
|
||||
@@ -233,14 +232,6 @@ 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',
|
||||
@@ -294,7 +285,7 @@ class SummaryController extends Controller
|
||||
: $value,
|
||||
),
|
||||
'praktikSortasiPanen' => $this->enumOptionChart($surveyPanen, 'praktik_sortasi', PraktikSortasi::class),
|
||||
'praktikFermentasiPascapanen' => $this->fermentationPracticeChart($aksesPasar),
|
||||
'praktikFermentasiPascapanen' => $this->fermentationPracticeByCommodityChart($aksesPasar),
|
||||
'produktivitasAktualPerKomoditas' => $actualProductivity,
|
||||
],
|
||||
];
|
||||
@@ -312,7 +303,6 @@ class SummaryController extends Controller
|
||||
'desa' => (string) $request->input('desaKelurahan_id', ''),
|
||||
],
|
||||
'levels' => $levels,
|
||||
'cultivationLevels' => $this->productionCultivationMapLevelData($request, $levels),
|
||||
'information' => $this->productionMapInformationData($request),
|
||||
];
|
||||
}
|
||||
@@ -511,6 +501,7 @@ class SummaryController extends Controller
|
||||
'map_lahan.path',
|
||||
'map_lahan.luas_lahan',
|
||||
'map_lahan.jumlah_tanaman',
|
||||
'map_lahan.petani_id',
|
||||
'map_lahan.umur_tanaman',
|
||||
'map_lahan.serangan_hama',
|
||||
'map_lahan.penyakit_tanaman',
|
||||
@@ -537,58 +528,10 @@ class SummaryController extends Controller
|
||||
])
|
||||
->groupBy('lahan_id');
|
||||
|
||||
$geoLahan = $mapLahan
|
||||
->filter(fn ($lahan) => $this->hasMapPolygon($lahan->path))
|
||||
->map(function ($lahan) use ($tanamanByLahan) {
|
||||
$tanamanRows = $tanamanByLahan->get($lahan->id, collect());
|
||||
$komoditas = $this->productionCommodityDetails($lahan, $tanamanRows);
|
||||
$opt = $this->productionOptDetails($lahan, $tanamanRows);
|
||||
|
||||
return [
|
||||
'id' => $lahan->id,
|
||||
'parent_id' => $lahan->desa_kelurahan_id,
|
||||
'polygon' => $lahan->path,
|
||||
'nama_petani' => $lahan->petani_nama ?? $lahan->profile_nama ?? 'N/A',
|
||||
'luas' => (float) $lahan->luas_lahan,
|
||||
'komoditas' => $komoditas,
|
||||
'opt' => $opt,
|
||||
];
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
|
||||
return [
|
||||
'provinsi' => array_values($geoProvinsi),
|
||||
'kabupaten' => array_values($geoKabupaten),
|
||||
'kecamatan' => array_values($geoKecamatan),
|
||||
'desa' => array_values($geoDesa),
|
||||
'lahan' => $geoLahan,
|
||||
];
|
||||
}
|
||||
|
||||
private function productionCultivationMapLevelData(Request $request, array $baseLevels): array
|
||||
{
|
||||
$mapLahan = (clone $this->filteredLahanQuery($request))
|
||||
->whereNotNull('map_lahan.path')
|
||||
->leftJoin('profiles', 'profiles.id', '=', 'map_lahan.profile_id')
|
||||
->leftJoin('petani', 'petani.id', '=', 'map_lahan.petani_id')
|
||||
->tap(fn ($query) => $this->whereActiveMapOwner($query))
|
||||
->orderByDesc('map_lahan.updated_at')
|
||||
->limit(500)
|
||||
->get([
|
||||
'map_lahan.id',
|
||||
'map_lahan.desa_kelurahan_id',
|
||||
'map_lahan.path',
|
||||
'map_lahan.luas_lahan',
|
||||
'map_lahan.petani_id',
|
||||
'profiles.nama as profile_nama',
|
||||
'petani.nama as petani_nama',
|
||||
]);
|
||||
$petaniIds = $mapLahan->pluck('petani_id')->filter()->unique()->values();
|
||||
$practiceByPetani = DB::table('praktik_budidaya')
|
||||
->whereNull('praktik_budidaya.deleted_at')
|
||||
->when($petaniIds->isNotEmpty(), fn ($query) => $query->whereIn('praktik_budidaya.petani_id', $petaniIds))
|
||||
->when($petaniIds->isEmpty(), fn ($query) => $query->whereRaw('1 = 0'))
|
||||
->whereIn('praktik_budidaya.petani_id', $petaniIds)
|
||||
->orderByDesc('praktik_budidaya.updated_at')
|
||||
->get([
|
||||
'praktik_budidaya.petani_id',
|
||||
@@ -601,7 +544,10 @@ class SummaryController extends Controller
|
||||
|
||||
$geoLahan = $mapLahan
|
||||
->filter(fn ($lahan) => $this->hasMapPolygon($lahan->path))
|
||||
->map(function ($lahan) use ($practiceByPetani) {
|
||||
->map(function ($lahan) use ($tanamanByLahan, $practiceByPetani) {
|
||||
$tanamanRows = $tanamanByLahan->get($lahan->id, collect());
|
||||
$komoditas = $this->productionCommodityDetails($lahan, $tanamanRows);
|
||||
$opt = $this->productionOptDetails($lahan, $tanamanRows);
|
||||
$practice = $practiceByPetani->get($lahan->petani_id);
|
||||
|
||||
return [
|
||||
@@ -610,6 +556,8 @@ class SummaryController extends Controller
|
||||
'polygon' => $lahan->path,
|
||||
'nama_petani' => $lahan->petani_nama ?? $lahan->profile_nama ?? 'N/A',
|
||||
'luas' => (float) $lahan->luas_lahan,
|
||||
'komoditas' => $komoditas,
|
||||
'opt' => $opt,
|
||||
'pemangkasan' => $this->enumLabel(FrekuensiPemangkasanUtama::class, $practice->frekuensi_pemangkasan_utama ?? null, 'Belum ada data'),
|
||||
'pemupukan' => $this->enumLabel(PemberianPupuk::class, $practice->pemberian_pupuk ?? null, 'Belum ada data'),
|
||||
'praktik_baik' => $this->arrayValues($practice->kebiasaan_konservasi_tanah ?? null),
|
||||
@@ -619,10 +567,10 @@ class SummaryController extends Controller
|
||||
->all();
|
||||
|
||||
return [
|
||||
'provinsi' => $baseLevels['provinsi'] ?? [],
|
||||
'kabupaten' => $baseLevels['kabupaten'] ?? [],
|
||||
'kecamatan' => $baseLevels['kecamatan'] ?? [],
|
||||
'desa' => $baseLevels['desa'] ?? [],
|
||||
'provinsi' => array_values($geoProvinsi),
|
||||
'kabupaten' => array_values($geoKabupaten),
|
||||
'kecamatan' => array_values($geoKecamatan),
|
||||
'desa' => array_values($geoDesa),
|
||||
'lahan' => $geoLahan,
|
||||
];
|
||||
}
|
||||
@@ -826,7 +774,12 @@ class SummaryController extends Controller
|
||||
return DB::table('akses_pasar')
|
||||
->whereNull('akses_pasar.deleted_at')
|
||||
->whereIn('akses_pasar.petani_id', $petaniIds)
|
||||
->get(['praktik_fermentasi']);
|
||||
->leftJoin('petani as akses_petani', 'akses_petani.id', '=', 'akses_pasar.petani_id')
|
||||
->leftJoin('master_komoditas as akses_komoditas', 'akses_komoditas.id', '=', 'akses_petani.komoditas_id')
|
||||
->get([
|
||||
'akses_pasar.praktik_fermentasi',
|
||||
'akses_komoditas.nama as komoditas_nama',
|
||||
]);
|
||||
}
|
||||
|
||||
private function filteredSurveyProduktivitas(Request $request)
|
||||
@@ -1254,10 +1207,10 @@ class SummaryController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function fermentationPracticeChart($rows): array
|
||||
private function fermentationPracticeByCommodityChart($rows): array
|
||||
{
|
||||
$labels = ['Ya, rutin', 'Pernah mencoba', 'Belum pernah'];
|
||||
$counts = collect($labels)->mapWithKeys(fn ($label) => [$label => 0]);
|
||||
$matrix = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$label = $this->fermentationPracticeLabel($row->praktik_fermentasi ?? null);
|
||||
@@ -1266,12 +1219,21 @@ class SummaryController extends Controller
|
||||
continue;
|
||||
}
|
||||
|
||||
$counts[$label] = (int) $counts[$label] + 1;
|
||||
$commodity = $row->komoditas_nama ?: 'Belum ditentukan';
|
||||
$matrix[$commodity] ??= array_fill_keys($labels, 0);
|
||||
$matrix[$commodity][$label]++;
|
||||
}
|
||||
|
||||
ksort($matrix, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return [
|
||||
'labels' => $labels,
|
||||
'series' => collect($labels)->map(fn ($label) => (int) ($counts[$label] ?? 0))->values()->all(),
|
||||
'labels' => array_keys($matrix),
|
||||
'series' => collect($labels)
|
||||
->map(fn ($label) => [
|
||||
'name' => $label,
|
||||
'data' => collect($matrix)->map(fn ($counts) => (int) $counts[$label])->values()->all(),
|
||||
])
|
||||
->all(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -758,7 +758,7 @@
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Peta Sebaran</h5>
|
||||
<p class="text-body-secondary mb-0">Lahan, tanaman, dan OPT berdasarkan filter wilayah.</p>
|
||||
<p class="text-body-secondary mb-0">Lahan, tanaman, OPT, dan praktik budidaya berdasarkan filter wilayah.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
@@ -773,8 +773,8 @@
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between gap-3 flex-wrap mb-2">
|
||||
<div>
|
||||
<h6 class="mb-0">Peta Sebaran Lahan & Tanaman</h6>
|
||||
<small class="text-body-secondary">Lahan dan tanaman berdasarkan filter wilayah, komoditas, dan segmen</small>
|
||||
<h6 class="mb-0">Peta Sebaran Lahan, Tanaman & Praktik Budidaya</h6>
|
||||
<small class="text-body-secondary">Lahan, tanaman, OPT, pemangkasan, pemupukan, dan praktik baik berdasarkan filter wilayah, komoditas, dan segmen</small>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-secondary active produksi-map-layer-btn" data-map-layer="satellite">Satellite</button>
|
||||
@@ -795,34 +795,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between gap-3 flex-wrap mb-2">
|
||||
<div>
|
||||
<h6 class="mb-0">Peta Praktik Budidaya</h6>
|
||||
<small class="text-body-secondary">Intensitas pemangkasan, pemupukan, dan kebiasaan praktik baik</small>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-secondary active produksi-cultivation-map-layer-btn" data-map-layer="satellite">Satellite</button>
|
||||
<button type="button" class="btn btn-outline-secondary produksi-cultivation-map-layer-btn" data-map-layer="topo">Topographic</button>
|
||||
<button type="button" class="btn btn-outline-secondary produksi-cultivation-map-layer-btn" data-map-layer="parcels">Parcels</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="produksi-budidaya-map" class="produksi-map">
|
||||
<button type="button" id="produksi-budidaya-map-back" class="btn btn-sm btn-dark shadow produksi-map-back">
|
||||
<i class="icon-base ti tabler-arrow-left me-1"></i>
|
||||
Kembali ke Atas
|
||||
</button>
|
||||
<div id="produksi-budidaya-map-empty" class="produksi-map-empty">Belum ada poligon lahan untuk filter ini.</div>
|
||||
<div class="produksi-map-information-overlay" tabindex="0" role="region" aria-label="Informasi peta praktik budidaya">
|
||||
<div class="produksi-map-information" id="produksi-budidaya-map-information"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1044,8 +1016,8 @@
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Praktik Fermentasi atau Pengolahan Pascapanen</h5>
|
||||
<small class="text-body-secondary">Apakah sudah ada praktik fermentasi atau pengolahan pascapanen yang dilakukan</small>
|
||||
<h5 class="m-0 me-2">Praktik Fermentasi atau Pengolahan Pascapanen per Komoditas</h5>
|
||||
<small class="text-body-secondary">Jumlah petani per komoditas berdasarkan praktik fermentasi atau pengolahan pascapanen.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -1079,11 +1051,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const mapData = @json($mapData);
|
||||
const dashboardFilters = @json($dashboardFilters);
|
||||
const mapInformationUrl = @json(route('dashboard.produktivitas.map-information'));
|
||||
const initialMapInformation = mapData.information || {};
|
||||
const mapInformation = {
|
||||
production: { ...initialMapInformation },
|
||||
cultivation: { ...initialMapInformation },
|
||||
};
|
||||
const mapInformation = { ...(mapData.information || {}) };
|
||||
const wilayah = @json($wilayah);
|
||||
const headingColor = config.colors.headingColor;
|
||||
const legendColor = config.colors.bodyColor;
|
||||
@@ -1094,9 +1062,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const labelColor = legendColor;
|
||||
const borderColor = pastelChart.grid || 'rgba(128, 128, 128, .18)';
|
||||
let productionMapRendered = false;
|
||||
let cultivationMapRendered = false;
|
||||
let actualProductivityRendered = false;
|
||||
const mapInformationRequestIds = { production: 0, cultivation: 0 };
|
||||
let mapInformationRequestId = 0;
|
||||
|
||||
function hasData(data) {
|
||||
return data && Array.isArray(data.series) && data.series.some(function (value) {
|
||||
@@ -1114,7 +1081,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
}
|
||||
|
||||
function renderProductionMapInformation(elementId, information, includeOpt = true) {
|
||||
function renderProductionMapInformation(elementId, information) {
|
||||
const element = document.getElementById(elementId);
|
||||
|
||||
if (!element) return;
|
||||
@@ -1193,17 +1160,15 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
<span class="produksi-map-information-section-title">Jumlah Petani / Komoditas</span>
|
||||
<div class="produksi-map-information-bars">${renderBars(commodityRows, 'Belum ada data komoditas.', 'petani')}</div>
|
||||
</section>
|
||||
${includeOpt ? `
|
||||
<section>
|
||||
<span class="produksi-map-information-section-title">OPT (Organisme Pengganggu Tanaman)</span>
|
||||
<div class="produksi-map-information-bars">${renderBars(optRows, 'Belum ada data OPT.', 'lahan')}</div>
|
||||
</section>
|
||||
` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
async function loadProductionMapInformation(mapType, level = null, id = null) {
|
||||
const requestId = ++mapInformationRequestIds[mapType];
|
||||
async function loadProductionMapInformation(level = null, id = null) {
|
||||
const requestId = ++mapInformationRequestId;
|
||||
const params = new URLSearchParams();
|
||||
|
||||
Object.entries(dashboardFilters || {}).forEach(function ([key, value]) {
|
||||
@@ -1225,14 +1190,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (!response.ok) return;
|
||||
|
||||
const information = await response.json();
|
||||
if (requestId !== mapInformationRequestIds[mapType]) return;
|
||||
if (requestId !== mapInformationRequestId) return;
|
||||
|
||||
Object.assign(mapInformation[mapType], information);
|
||||
if (mapType === 'production') {
|
||||
renderProductionMapInformation('produksi-map-information', mapInformation.production);
|
||||
} else {
|
||||
renderProductionMapInformation('produksi-budidaya-map-information', mapInformation.cultivation, false);
|
||||
}
|
||||
Object.assign(mapInformation, information);
|
||||
renderProductionMapInformation('produksi-map-information', mapInformation);
|
||||
} catch (error) {
|
||||
console.error('Gagal memuat informasi wilayah pada peta.', error);
|
||||
}
|
||||
@@ -1425,6 +1386,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const opt = Array.isArray(item.opt) && item.opt.length
|
||||
? item.opt.map(value => `<span class="produksi-opt-chip">${escapeHtml(value)}</span>`).join('')
|
||||
: '<span class="text-body-secondary small">Belum ada data OPT</span>';
|
||||
const praktikBaik = Array.isArray(item.praktik_baik) && item.praktik_baik.length
|
||||
? item.praktik_baik.map(value => `<li>${escapeHtml(value)}</li>`).join('')
|
||||
: '<li>Belum ada data praktik baik</li>';
|
||||
|
||||
return `
|
||||
<div class="produksi-lahan-popup">
|
||||
@@ -1449,6 +1413,19 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
<span class="produksi-popup-label">OPT</span>
|
||||
<div class="produksi-opt-list">${opt}</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<span class="produksi-popup-label">Praktik Budidaya</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="produksi-popup-label">Intensitas Pemangkasan</span>
|
||||
<span class="produksi-popup-value">${escapeHtml(item.pemangkasan || 'Belum ada data')}</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="produksi-popup-label">Intensitas Pemupukan</span>
|
||||
<span class="produksi-popup-value">${escapeHtml(item.pemupukan || 'Belum ada data')}</span>
|
||||
</div>
|
||||
<span class="produksi-popup-label">Kebiasaan praktik baik</span>
|
||||
<ul class="produksi-praktik-list">${praktikBaik}</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1513,7 +1490,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
renderProductionMapLevel(previous.level, previous.parentId);
|
||||
productionMapState.informationLevel = previous.informationLevel;
|
||||
productionMapState.informationId = previous.informationId;
|
||||
loadProductionMapInformation('production', previous.informationLevel, previous.informationId);
|
||||
loadProductionMapInformation(previous.informationLevel, previous.informationId);
|
||||
|
||||
if (previous.bounds?.isValid()) {
|
||||
productionMapState.map.fitBounds(previous.bounds, { padding: [30, 30] });
|
||||
@@ -1585,7 +1562,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
layer.bindPopup(landPopup(item), {
|
||||
className: 'produksi-map-popup',
|
||||
closeButton: true,
|
||||
maxWidth: 320,
|
||||
maxWidth: 340,
|
||||
maxHeight: 360,
|
||||
});
|
||||
} else {
|
||||
layer.bindTooltip(regionTooltip(item), {
|
||||
@@ -1606,7 +1584,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
productionMapState.informationLevel = nextLevel;
|
||||
productionMapState.informationId = item.id;
|
||||
loadProductionMapInformation('production', nextLevel, item.id);
|
||||
loadProductionMapInformation(nextLevel, item.id);
|
||||
renderProductionMapLevel(nextProductionMapLevel(nextLevel), item.id, event.target.getBounds());
|
||||
});
|
||||
}
|
||||
@@ -1641,234 +1619,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function initialCultivationMapLevel() {
|
||||
const levels = mapData.cultivationLevels || {};
|
||||
const filters = mapData.filters || {};
|
||||
|
||||
if (filters.desa && (levels.desa || []).length) return 'desa';
|
||||
if (filters.kecamatan && (levels.kecamatan || []).length) return 'kecamatan';
|
||||
if (filters.kabupaten && (levels.kabupaten || []).length) return 'kabupaten';
|
||||
if ((levels.kabupaten || []).length) return 'kabupaten';
|
||||
if ((levels.kecamatan || []).length) return 'kecamatan';
|
||||
if ((levels.desa || []).length) return 'desa';
|
||||
if ((levels.provinsi || []).length) return 'provinsi';
|
||||
|
||||
return 'lahan';
|
||||
}
|
||||
|
||||
function cultivationPopup(item) {
|
||||
const praktikBaik = Array.isArray(item.praktik_baik) && item.praktik_baik.length
|
||||
? item.praktik_baik.map(value => `<li>${escapeHtml(value)}</li>`).join('')
|
||||
: '<li>Belum ada data praktik baik</li>';
|
||||
|
||||
return `
|
||||
<div class="produksi-lahan-popup">
|
||||
<div class="produksi-lahan-popup-header">
|
||||
<span class="produksi-popup-label">Pemilik Lahan</span>
|
||||
<h6>${escapeHtml(item.nama_petani || 'N/A')}</h6>
|
||||
</div>
|
||||
<div class="produksi-lahan-popup-body">
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6">
|
||||
<span class="produksi-popup-label">Luas Lahan</span>
|
||||
<span class="produksi-popup-value">${formatNumber(item.luas || 0, 2)} Ha</span>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<span class="produksi-popup-label">ID Lahan</span>
|
||||
<span class="produksi-popup-value">TR-${escapeHtml(item.id)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="produksi-popup-label">Intensitas Pemangkasan</span>
|
||||
<span class="produksi-popup-value">${escapeHtml(item.pemangkasan || 'Belum ada data')}</span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<span class="produksi-popup-label">Intensitas Pemupukan</span>
|
||||
<span class="produksi-popup-value">${escapeHtml(item.pemupukan || 'Belum ada data')}</span>
|
||||
</div>
|
||||
<span class="produksi-popup-label">Kebiasaan praktik baik</span>
|
||||
<ul class="produksi-praktik-list">${praktikBaik}</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const cultivationMapState = {
|
||||
activeLayer: 'satellite',
|
||||
backButton: null,
|
||||
empty: null,
|
||||
group: null,
|
||||
history: [],
|
||||
informationId: null,
|
||||
informationLevel: null,
|
||||
layers: {},
|
||||
map: null,
|
||||
};
|
||||
|
||||
function initCultivationMap() {
|
||||
const element = document.getElementById('produksi-budidaya-map');
|
||||
|
||||
if (!element || typeof L === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cultivationMapState.map) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cultivationMapState.layers = {
|
||||
satellite: L.tileLayer('https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { maxZoom: 22 }),
|
||||
topo: L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png'),
|
||||
parcels: L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'),
|
||||
};
|
||||
cultivationMapState.map = L.map('produksi-budidaya-map', {
|
||||
center: [-8.67, 121.0],
|
||||
zoom: 10,
|
||||
layers: [cultivationMapState.layers.satellite],
|
||||
zoomControl: false,
|
||||
});
|
||||
cultivationMapState.group = L.featureGroup().addTo(cultivationMapState.map);
|
||||
cultivationMapState.backButton = document.getElementById('produksi-budidaya-map-back');
|
||||
cultivationMapState.empty = document.getElementById('produksi-budidaya-map-empty');
|
||||
enableMapInformationInteraction(element);
|
||||
|
||||
L.control.zoom({ position: 'topleft' }).addTo(cultivationMapState.map);
|
||||
|
||||
cultivationMapState.backButton?.addEventListener('click', function () {
|
||||
const previous = cultivationMapState.history.pop();
|
||||
|
||||
if (previous) {
|
||||
renderCultivationMapLevel(previous.level, previous.parentId);
|
||||
cultivationMapState.informationLevel = previous.informationLevel;
|
||||
cultivationMapState.informationId = previous.informationId;
|
||||
loadProductionMapInformation('cultivation', previous.informationLevel, previous.informationId);
|
||||
|
||||
if (previous.bounds?.isValid()) {
|
||||
cultivationMapState.map.fitBounds(previous.bounds, { padding: [30, 30] });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function setCultivationMapLayer(type, button) {
|
||||
if (!cultivationMapState.map || !cultivationMapState.layers[type]) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.values(cultivationMapState.layers).forEach(function (layer) {
|
||||
cultivationMapState.map.removeLayer(layer);
|
||||
});
|
||||
cultivationMapState.layers[type].addTo(cultivationMapState.map);
|
||||
cultivationMapState.activeLayer = type;
|
||||
|
||||
document.querySelectorAll('.produksi-cultivation-map-layer-btn').forEach(function (item) {
|
||||
item.classList.toggle('active', item === button);
|
||||
});
|
||||
}
|
||||
|
||||
function renderCultivationMapLevel(level = null, parentId = null, fallbackBounds = null) {
|
||||
if (!initCultivationMap()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const levels = mapData.cultivationLevels || {};
|
||||
const nextLevel = level || initialCultivationMapLevel();
|
||||
let dataToRender = levels[nextLevel] || [];
|
||||
|
||||
if (parentId !== null) {
|
||||
dataToRender = dataToRender.filter(item => String(item.parent_id) === String(parentId));
|
||||
}
|
||||
|
||||
cultivationMapState.group.clearLayers();
|
||||
|
||||
if (nextLevel === 'lahan' && parentId !== null) {
|
||||
const parentDesa = (levels.desa || []).find(item => String(item.id) === String(parentId));
|
||||
const desaLayer = parentDesa
|
||||
? makeLayerFromPolygon(parentDesa.polygon, {
|
||||
color: '#F47C86',
|
||||
dashArray: '5, 8',
|
||||
fillOpacity: 0,
|
||||
interactive: false,
|
||||
weight: 2,
|
||||
})
|
||||
: null;
|
||||
|
||||
if (desaLayer) {
|
||||
cultivationMapState.group.addLayer(desaLayer);
|
||||
}
|
||||
}
|
||||
|
||||
dataToRender.forEach(function (item) {
|
||||
const layer = makeLayerFromPolygon(item.polygon, nextLevel === 'lahan'
|
||||
? { color: '#5FC7D6', fillColor: '#5FC7D6', fillOpacity: .58, weight: 2 }
|
||||
: { color: '#9D8EF0', fillColor: '#9D8EF0', fillOpacity: .3, weight: 2 });
|
||||
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextLevel === 'lahan') {
|
||||
layer.bindPopup(cultivationPopup(item), {
|
||||
className: 'produksi-map-popup',
|
||||
closeButton: true,
|
||||
maxWidth: 340,
|
||||
});
|
||||
} else {
|
||||
layer.bindTooltip(regionTooltip(item), {
|
||||
className: 'produksi-map-tooltip',
|
||||
direction: 'top',
|
||||
offset: [0, -10],
|
||||
sticky: true,
|
||||
});
|
||||
|
||||
layer.on('click', function (event) {
|
||||
L.DomEvent.stopPropagation(event);
|
||||
cultivationMapState.history.push({
|
||||
level: nextLevel,
|
||||
parentId,
|
||||
bounds: cultivationMapState.group.getBounds(),
|
||||
informationLevel: cultivationMapState.informationLevel,
|
||||
informationId: cultivationMapState.informationId,
|
||||
});
|
||||
cultivationMapState.informationLevel = nextLevel;
|
||||
cultivationMapState.informationId = item.id;
|
||||
loadProductionMapInformation('cultivation', nextLevel, item.id);
|
||||
renderCultivationMapLevel(nextProductionMapLevel(nextLevel), item.id, event.target.getBounds());
|
||||
});
|
||||
}
|
||||
|
||||
cultivationMapState.group.addLayer(layer);
|
||||
});
|
||||
|
||||
if (cultivationMapState.backButton) {
|
||||
cultivationMapState.backButton.style.display = cultivationMapState.history.length > 0 ? 'block' : 'none';
|
||||
}
|
||||
|
||||
const layersCount = cultivationMapState.group.getLayers().length;
|
||||
|
||||
if (cultivationMapState.empty) {
|
||||
cultivationMapState.empty.style.display = layersCount > 0 ? 'none' : 'flex';
|
||||
}
|
||||
|
||||
if (layersCount > 0) {
|
||||
const bounds = cultivationMapState.group.getBounds();
|
||||
|
||||
if (bounds.isValid()) {
|
||||
cultivationMapState.map.fitBounds(bounds, { padding: [30, 30], maxZoom: 18 });
|
||||
}
|
||||
} else if (fallbackBounds?.isValid()) {
|
||||
cultivationMapState.map.fitBounds(fallbackBounds, { padding: [30, 30], maxZoom: 18 });
|
||||
} else {
|
||||
cultivationMapState.map.setView([-8.67, 121.0], 10);
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
cultivationMapState.map.invalidateSize();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
function renderDonut(selector, data, options = {}) {
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) return;
|
||||
@@ -2231,7 +1981,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
renderGroupedBar('#chart-sumber-air-kebun', chartData.sumberAirKebun, { unit: 'petani', yTitle: 'Jumlah Petani', height: 330 });
|
||||
renderGroupedBar('#chart-kebiasaan-praktik-baik', chartData.kebiasaanPraktikBaik, { unit: 'petani', yTitle: 'Jumlah Petani', height: 330 });
|
||||
renderPieWithLegend('#chart-praktik-sortasi-panen', chartData.praktikSortasiPanen, { unit: 'petani' });
|
||||
renderBar('#chart-praktik-fermentasi-pascapanen', chartData.praktikFermentasiPascapanen, { height: 330, color: pastelSemantic.primary || '#9D8EF0', unit: 'petani' });
|
||||
renderGroupedBar('#chart-praktik-fermentasi-pascapanen', chartData.praktikFermentasiPascapanen, { unit: 'petani', yTitle: 'Jumlah Petani', height: 330 });
|
||||
renderActualProductivityIfVisible();
|
||||
}
|
||||
|
||||
@@ -2261,37 +2011,15 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
productionMapRendered = true;
|
||||
}
|
||||
|
||||
function renderCultivationMapIfOpen() {
|
||||
if (!document.getElementById('peta-sebaran-panel')?.classList.contains('show')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cultivationMapRendered) {
|
||||
renderCultivationMapLevel();
|
||||
cultivationMapRendered = true;
|
||||
return;
|
||||
}
|
||||
|
||||
cultivationMapState.map?.invalidateSize();
|
||||
cultivationMapRendered = true;
|
||||
}
|
||||
|
||||
renderProductionMapInformation('produksi-map-information', mapInformation.production);
|
||||
renderProductionMapInformation('produksi-budidaya-map-information', mapInformation.cultivation, false);
|
||||
renderProductionMapInformation('produksi-map-information', mapInformation);
|
||||
|
||||
if (typeof L === 'undefined') {
|
||||
const mapEmpty = document.getElementById('produksi-map-empty');
|
||||
const cultivationMapEmpty = document.getElementById('produksi-budidaya-map-empty');
|
||||
|
||||
if (mapEmpty) {
|
||||
mapEmpty.textContent = 'Leaflet belum dimuat.';
|
||||
mapEmpty.style.display = 'flex';
|
||||
}
|
||||
|
||||
if (cultivationMapEmpty) {
|
||||
cultivationMapEmpty.textContent = 'Leaflet belum dimuat.';
|
||||
cultivationMapEmpty.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('.produksi-map-layer-btn').forEach(function (button) {
|
||||
@@ -2300,12 +2028,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.produksi-cultivation-map-layer-btn').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
setCultivationMapLayer(button.dataset.mapLayer, button);
|
||||
});
|
||||
});
|
||||
|
||||
$('#filter-provinsi, #filter-kabkota, #filter-kecamatan, #filter-desa, #filter-komoditas, #filter-segmen').select2({ width: '100%' });
|
||||
|
||||
$('#filter-provinsi').on('change', function () {
|
||||
@@ -2388,7 +2110,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
if (panel.id === 'peta-sebaran-panel' && typeof L !== 'undefined') {
|
||||
renderProductionMapIfOpen();
|
||||
renderCultivationMapIfOpen();
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\AksesPasar\PraktikFermentasi;
|
||||
use App\Http\Controllers\Web\Dashboard\SummaryController;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
beforeEach(function () {
|
||||
Schema::create('master_komoditas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
});
|
||||
foreach ([
|
||||
'master_kabupaten_kota' => 'provinsi_id',
|
||||
'master_kecamatan' => 'kabupaten_kota_id',
|
||||
'master_desa_kelurahan' => 'kecamatan_id',
|
||||
] as $name => $parent) {
|
||||
Schema::create($name, function (Blueprint $table) use ($parent) {
|
||||
$table->id();
|
||||
$table->integer($parent);
|
||||
});
|
||||
}
|
||||
Schema::create('petani', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('komoditas_id')->nullable();
|
||||
$table->integer('desa_kelurahan_id');
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::create('akses_pasar', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('petani_id');
|
||||
$table->string('praktik_fermentasi')->nullable();
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::create('petani_skor', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('petani_id');
|
||||
$table->float('skor_kesiapan_adopsi');
|
||||
$table->softDeletes();
|
||||
});
|
||||
DB::table('master_komoditas')->insert([
|
||||
['id' => 1, 'nama' => 'Kakao'], ['id' => 2, 'nama' => 'Kopi'], ['id' => 3, 'nama' => 'Vanili'],
|
||||
]);
|
||||
DB::table('master_kabupaten_kota')->insert(['id' => 1, 'provinsi_id' => 1]);
|
||||
DB::table('master_kecamatan')->insert(['id' => 1, 'kabupaten_kota_id' => 1]);
|
||||
DB::table('master_desa_kelurahan')->insert([
|
||||
['id' => 1, 'kecamatan_id' => 1], ['id' => 2, 'kecamatan_id' => 1],
|
||||
]);
|
||||
|
||||
foreach ([
|
||||
[1, 1, 1, PraktikFermentasi::RUTIN_SENDIRI->value],
|
||||
[2, 1, 1, PraktikFermentasi::RUTIN_KELOMPOK->value],
|
||||
[3, 2, 2, PraktikFermentasi::PERNAH_SENDIRI->value],
|
||||
[4, 2, 2, PraktikFermentasi::PERNAH_KELOMPOK->value],
|
||||
[5, 3, 2, PraktikFermentasi::BELUM_PERNAH->value],
|
||||
[6, 3, 2, null],
|
||||
[7, 1, 1, ''],
|
||||
[8, 2, 2, PraktikFermentasi::RUTIN_SENDIRI->value],
|
||||
[9, null, 2, PraktikFermentasi::BELUM_PERNAH->value],
|
||||
[10, 1, 1, PraktikFermentasi::RUTIN_SENDIRI->value],
|
||||
[11, 1, 1, 'Tidak dikenal'],
|
||||
] as [$id, $commodity, $village, $practice]) {
|
||||
DB::table('petani')->insert([
|
||||
'id' => $id, 'komoditas_id' => $commodity, 'desa_kelurahan_id' => $village,
|
||||
'deleted_at' => $id === 10 ? '2026-01-01' : null,
|
||||
]);
|
||||
DB::table('akses_pasar')->insert([
|
||||
'petani_id' => $id, 'praktik_fermentasi' => $practice,
|
||||
'deleted_at' => $id === 8 ? '2026-01-01' : null,
|
||||
]);
|
||||
}
|
||||
DB::table('petani_skor')->insert([
|
||||
['petani_id' => 1, 'skor_kesiapan_adopsi' => 4.1, 'deleted_at' => null],
|
||||
['petani_id' => 2, 'skor_kesiapan_adopsi' => 2.6, 'deleted_at' => null],
|
||||
['petani_id' => 3, 'skor_kesiapan_adopsi' => 4.4, 'deleted_at' => '2026-01-01'],
|
||||
]);
|
||||
|
||||
$controller = app(SummaryController::class);
|
||||
$query = new ReflectionMethod($controller, 'filteredAksesPasar');
|
||||
$chart = new ReflectionMethod($controller, 'fermentationPracticeByCommodityChart');
|
||||
$this->fermentationChart = fn (array $filters = []) => $chart->invoke(
|
||||
$controller, $query->invoke($controller, new Request($filters)),
|
||||
);
|
||||
});
|
||||
|
||||
test('fermentation groups individual and collective responses by commodity', function () {
|
||||
expect(($this->fermentationChart)())->toBe([
|
||||
'labels' => ['Belum ditentukan', 'Kakao', 'Kopi', 'Vanili'],
|
||||
'series' => [
|
||||
['name' => 'Ya, rutin', 'data' => [0, 2, 0, 0]],
|
||||
['name' => 'Pernah mencoba', 'data' => [0, 0, 2, 0]],
|
||||
['name' => 'Belum pernah', 'data' => [1, 0, 0, 1]],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('fermentation respects region and commodity filters', function () {
|
||||
$chart = ($this->fermentationChart)(['desaKelurahan_id' => 2, 'komoditas_id' => 2]);
|
||||
expect($chart['labels'])->toBe(['Kopi']);
|
||||
expect(array_column($chart['series'], 'data'))->toBe([[0], [2], [0]]);
|
||||
expect(($this->fermentationChart)(['desaKelurahan_id' => 1])['labels'])->toBe(['Kakao']);
|
||||
});
|
||||
|
||||
test('fermentation respects segment filters and excludes deleted scores', function () {
|
||||
$chart = ($this->fermentationChart)(['segmen' => 'Pemerhati']);
|
||||
expect($chart['labels'])->toBe(['Kakao']);
|
||||
expect(array_column($chart['series'], 'data'))->toBe([[1], [0], [0]]);
|
||||
expect(($this->fermentationChart)(['segmen' => 'Penggerak'])['labels'])->toBe(['Kakao']);
|
||||
});
|
||||
|
||||
test('fermentation returns empty grouped series when no matching responses exist', function () {
|
||||
$chart = ($this->fermentationChart)(['provinsi_id' => 99]);
|
||||
expect($chart['labels'])->toBe([]);
|
||||
expect(array_column($chart['series'], 'data'))->toBe([[], [], []]);
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PraktikBudidaya\FrekuensiPemangkasanUtama;
|
||||
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||
use App\Http\Controllers\Web\Dashboard\SummaryController;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
beforeEach(function () {
|
||||
foreach ([
|
||||
'master_provinsi' => null,
|
||||
'master_kabupaten_kota' => 'provinsi_id',
|
||||
'master_kecamatan' => 'kabupaten_kota_id',
|
||||
'master_desa_kelurahan' => 'kecamatan_id',
|
||||
] as $name => $parent) {
|
||||
Schema::create($name, function (Blueprint $table) use ($parent) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
$table->text('path')->nullable();
|
||||
if ($parent) {
|
||||
$table->integer($parent);
|
||||
}
|
||||
$table->softDeletes();
|
||||
});
|
||||
DB::table($name)->insert(['id' => 1, 'nama' => 'Wilayah'] + ($parent ? [$parent => 1] : []));
|
||||
}
|
||||
Schema::create('master_komoditas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
});
|
||||
foreach (['petani', 'profiles'] as $name) {
|
||||
Schema::create($name, function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama');
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
foreach (['map_lahan', 'map_tanaman'] as $name) {
|
||||
Schema::create($name, function (Blueprint $table) use ($name) {
|
||||
$table->id();
|
||||
$table->integer('komoditas_id');
|
||||
$table->integer('umur_tanaman')->nullable();
|
||||
foreach (['serangan_hama', 'penyakit_tanaman', 'jenis_hama', 'jenis_penyakit'] as $column) {
|
||||
$table->text($column)->nullable();
|
||||
}
|
||||
if ($name === 'map_lahan') {
|
||||
$table->integer('petani_id')->nullable();
|
||||
$table->integer('profile_id')->nullable();
|
||||
$table->integer('desa_kelurahan_id');
|
||||
$table->text('path');
|
||||
$table->float('luas_lahan');
|
||||
$table->integer('jumlah_tanaman');
|
||||
} else {
|
||||
$table->integer('lahan_id');
|
||||
}
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
Schema::create('praktik_budidaya', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('petani_id');
|
||||
$table->integer('frekuensi_pemangkasan_utama');
|
||||
$table->integer('pemberian_pupuk');
|
||||
$table->text('kebiasaan_konservasi_tanah');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
DB::table('master_komoditas')->insert(['id' => 1, 'nama' => 'Kakao']);
|
||||
DB::table('petani')->insert(['id' => 1, 'nama' => 'Petani A']);
|
||||
DB::table('profiles')->insert(['id' => 1, 'nama' => 'Pemilik B']);
|
||||
$land = [
|
||||
'komoditas_id' => 1, 'desa_kelurahan_id' => 1, 'luas_lahan' => 1.5,
|
||||
'jumlah_tanaman' => 50, 'umur_tanaman' => 8,
|
||||
'path' => '[[[-8.1,121.1],[-8.2,121.1],[-8.1,121.2]]]',
|
||||
'jenis_hama' => '["Penggerek buah"]',
|
||||
];
|
||||
DB::table('map_lahan')->insert($land + ['id' => 1, 'petani_id' => 1]);
|
||||
DB::table('map_lahan')->insert($land + ['id' => 2, 'profile_id' => 1]);
|
||||
DB::table('map_lahan')->insert($land + ['id' => 3, 'petani_id' => 1, 'deleted_at' => '2026-01-01']);
|
||||
DB::table('map_tanaman')->insert([
|
||||
'lahan_id' => 1, 'komoditas_id' => 1, 'umur_tanaman' => 4,
|
||||
'jenis_penyakit' => '["Busuk buah"]',
|
||||
]);
|
||||
foreach ([
|
||||
['2026-01-01', 1, null],
|
||||
['2026-02-01', 4, null],
|
||||
['2026-03-01', 5, '2026-03-02'],
|
||||
] as [$updatedAt, $value, $deletedAt]) {
|
||||
DB::table('praktik_budidaya')->insert([
|
||||
'petani_id' => 1, 'frekuensi_pemangkasan_utama' => $value, 'pemberian_pupuk' => $value,
|
||||
'kebiasaan_konservasi_tanah' => '["Menumpuk daun kering/sisa tanaman di sekitar pohon"]',
|
||||
'updated_at' => $updatedAt, 'deleted_at' => $deletedAt,
|
||||
]);
|
||||
}
|
||||
$controller = app(SummaryController::class);
|
||||
$method = new ReflectionMethod($controller, 'productionMapLevelData');
|
||||
$this->mapLevels = fn (array $filters = []) => $method->invoke($controller, new Request($filters));
|
||||
});
|
||||
|
||||
test('land map combines plants and OPT with the latest active cultivation practice', function () {
|
||||
$lands = collect(($this->mapLevels)()['lahan'])->keyBy('id');
|
||||
|
||||
expect($lands->keys()->all())->toBe([1, 2]);
|
||||
expect($lands[1])->toMatchArray([
|
||||
'nama_petani' => 'Petani A',
|
||||
'luas' => 1.5,
|
||||
'komoditas' => [['nama' => 'Kakao', 'jumlah' => 1, 'umur' => 4.0]],
|
||||
'opt' => ['Busuk Buah', 'Penggerek Buah'],
|
||||
'pemangkasan' => FrekuensiPemangkasanUtama::DUA_KALI_TAHUN->label(),
|
||||
'pemupukan' => PemberianPupuk::JENIS_DOSIS_SESUAI_WAKTU_CUKUP_TEPAT->label(),
|
||||
'praktik_baik' => ['Menumpuk daun kering/sisa tanaman di sekitar pohon'],
|
||||
]);
|
||||
expect($lands[2])->toMatchArray([
|
||||
'nama_petani' => 'Pemilik B',
|
||||
'komoditas' => [['nama' => 'Kakao', 'jumlah' => 50, 'umur' => 8.0]],
|
||||
'pemangkasan' => 'Belum ada data',
|
||||
'pemupukan' => 'Belum ada data',
|
||||
'praktik_baik' => [],
|
||||
]);
|
||||
});
|
||||
|
||||
test('combined map respects region and commodity filters', function () {
|
||||
expect(($this->mapLevels)(['desaKelurahan_id' => 1, 'komoditas_id' => 1])['lahan'])->toHaveCount(2);
|
||||
expect(($this->mapLevels)(['desaKelurahan_id' => 99])['lahan'])->toBe([]);
|
||||
expect(($this->mapLevels)(['komoditas_id' => 99])['lahan'])->toBe([]);
|
||||
});
|
||||
Reference in New Issue
Block a user