Files
database-pertani-web/resources/views/dashboard/karakteristik-petani.blade.php
T

3481 lines
138 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('layout.main')
@push('style')
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/apex-charts/apex-charts.css') }}" />
<style>
.readiness-score-list { display: grid; gap: 12px; }
.readiness-score-row { display: flex; align-items: baseline; gap: 10px; font-size: 0.8125rem; line-height: 1.5; }
.readiness-score-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.readiness-score-name { flex: 1; min-width: 0; }
.readiness-score-value { flex: 0 0 4ch; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--bs-heading-color); font-size: 0.875rem; font-weight: 600; }
#farmerPagination .page-link {
padding: 0.4809rem;
font-size: 0.9375rem;
line-height: 1.375;
border-radius: 0.375rem;
}
#farmerPagination .pagination {
flex-wrap: wrap;
}
@media (max-width: 575.98px) {
#farmerPagination .page-link {
font-size: 0.8125rem;
min-block-size: calc(1.7501875rem + 2px);
min-inline-size: calc(1.7509515rem + 2px);
padding-block: 0.211rem;
padding-inline: 0.1635rem;
}
}
.summary-card .card-body {
padding: 22px 22px;
}
.summary-card .avatar-lg {
width: 48px;
height: 48px;
flex-shrink: 0;
}
.summary-card .avatar-lg .avatar-initial {
width: 48px;
height: 48px;
}
.summary-card h4 {
font-size: 1.6rem;
line-height: 1.2;
}
.summary-card h5 {
font-size: 1.05rem;
}
.demografi-kpi-value {
overflow-wrap: anywhere;
}
#genderChart,
#commodityChart,
#farmerGroupChart,
#landOwnershipStatusChart {
min-height: 365px;
}
#genderChart .apexcharts-canvas,
#genderChart svg,
#commodityChart .apexcharts-canvas,
#commodityChart svg,
#farmerGroupChart .apexcharts-canvas,
#farmerGroupChart svg,
#landOwnershipStatusChart .apexcharts-canvas,
#landOwnershipStatusChart svg {
max-width: 100% !important;
}
#genderChart .apexcharts-tooltip,
#ageGroupChart .apexcharts-tooltip,
#educationChart .apexcharts-tooltip,
#dependentChart .apexcharts-tooltip,
#farmerGroupChart .apexcharts-tooltip,
#commodityChart .apexcharts-tooltip,
#landOwnershipStatusChart .apexcharts-tooltip,
#landAreaChart .apexcharts-tooltip,
#incomeChart .apexcharts-tooltip {
background: transparent !important;
border: none !important;
box-shadow: none !important;
overflow: visible !important;
}
</style>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<style>
#farmer-map-shell:fullscreen {
width: 100%;
height: 100%;
background: var(--bs-body-bg);
}
#farmer-map-shell:fullscreen #map-sebaran-petani {
height: 100% !important;
}
.clickable-row {
cursor: pointer;
}
.clickable-row:hover {
background-color: rgba(0, 0, 0, 0.03);
}
.segment-distribution {
align-items: center;
display: grid;
gap: 1rem;
grid-template-columns: minmax(180px, 240px) minmax(0, 1fr);
max-width: 100%;
overflow: hidden;
}
.segment-chart-shell {
min-width: 0;
width: 100%;
}
#chart-segmentasi-petani,
#chart-segmentasi-petani .apexcharts-canvas,
#chart-segmentasi-petani svg,
.segment-commodity-chart,
.segment-commodity-chart .apexcharts-canvas,
.segment-commodity-chart svg {
max-width: 100% !important;
}
.segment-table-shell {
min-width: 0;
overflow-x: auto;
width: 100%;
}
.segment-table {
table-layout: fixed;
width: 100%;
}
.segment-table th:first-child,
.segment-table td:first-child {
min-width: 0;
width: 52%;
}
.segment-table th:nth-child(2),
.segment-table td:nth-child(2),
.segment-table th:nth-child(3),
.segment-table td:nth-child(3) {
white-space: nowrap;
width: 24%;
}
.segment-table td {
vertical-align: middle;
}
.segment-table td:first-child .d-flex {
min-width: 0;
}
.segment-table td:first-child span {
display: inline-block;
min-width: 0;
overflow-wrap: anywhere;
word-break: normal;
}
.segment-commodity-chart {
min-height: 260px;
overflow: hidden;
width: 100%;
}
@media (max-width: 1399.98px) {
.segment-distribution {
grid-template-columns: 1fr;
}
.segment-chart-shell {
margin-left: auto;
margin-right: auto;
max-width: 260px;
}
}
@media (max-width: 575.98px) {
.segment-table {
min-width: 320px;
}
}
</style>
<style>
.profile-farming-shell {
max-width: none;
width: 100%;
}
.profile-farming-tabs {
gap: .2rem;
margin-bottom: 0 !important;
overflow-x: auto;
scrollbar-width: none;
width: 100%;
}
.profile-farming-tabs::-webkit-scrollbar {
display: none;
}
.profile-farming-tabs .nav-item {
flex: 1 1 0;
min-width: 0;
}
.profile-farming-tabs .nav-link {
align-items: center;
display: flex;
justify-content: center;
min-height: 38px;
padding-left: .75rem;
padding-right: .75rem;
white-space: nowrap;
width: 100%;
}
.profile-farming-tab-content {
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
@media (max-width: 767.98px) {
.profile-farming-tabs .nav-item {
flex-basis: auto;
}
}
</style>
@endpush
@section('content')
@php
$activeTab = $activeTab ?? 'demografi-petani';
@endphp
<div class="container-fluid flex-grow-1 container-p-y pb-0 profile-farming-shell">
@php($showTabFilterInput = true)
<div class="row g-6 py-4">
<form id="form-filter" class="col-12" method="GET" action="{{ $indeksFilterAction ?? route('dashboard.indeks-petani') }}">
@if(isset($showTabFilterInput) && $showTabFilterInput)
<input type="hidden" name="tab" id="profile-filter-tab" value="{{ $activeTab ?? 'demografi-petani' }}">
@elseif(isset($activeTab) && $activeTab === 'indeks-petani')
<input type="hidden" name="tab" value="indeks-petani">
@endif
@csrf
<div class="col-12">
<div class="card card-border-shadow-primary profile-filter-card">
<div class="card-body py-3">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="d-flex align-items-center me-2">
<i class="icon-base ti tabler-map-2 me-2"></i>
<span class="fw-medium">Wilayah</span>
</div>
<div class="filter-item">
<select id="filter-provinsi" class="form-select form-select-sm" name="provinsi_id" data-default="{{ $provinsi_id }}">
<option value="">Semua Provinsi</option>
@foreach($provinsi as $p)
<option value="{{ $p->provinsi_id }}" {{ old('provinsi_id', $provinsi_id) == $p->provinsi_id ? 'selected' : '' }}>
{{ $p->provinsi }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select id="filter-kabkota" class="form-select form-select-sm" name="kabupatenKota_id" data-default="{{ $kabupatenKota_id }}" disabled>
<option value="">Semua Kabupaten/Kota</option>
</select>
</div>
<div class="filter-item">
<select id="filter-kecamatan" class="form-select form-select-sm" name="kecamatan_id" data-default="{{ $kecamatan_id }}" disabled>
<option value="">Semua Kecamatan</option>
</select>
</div>
<div class="filter-item">
<select id="filter-desa" class="form-select form-select-sm" name="desaKelurahan_id" data-default="{{ $desaKelurahan_id }}" disabled>
<option value="">Semua Desa/Kelurahan</option>
</select>
</div>
</div>
<hr class="my-3">
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="d-flex align-items-center me-2">
<i class="icon-base ti tabler-adjustments-horizontal me-2"></i>
<span class="fw-medium">Data</span>
</div>
<div class="filter-item">
<select id="filter-komoditas" class="form-select form-select-sm" name="komoditas_id">
<option value="">Semua Komoditas</option>
@foreach ($komoditas as $item)
<option value="{{ $item->id }}" {{ old('komoditas_id', $komoditas_id) == $item->id ? 'selected' : '' }}>
{{ $item->nama }}
</option>
@endforeach
</select>
</div>
<div class="filter-item">
<select id="filter-segmen" class="form-select form-select-sm" name="segmen">
<option value="">Semua Segmen</option>
@foreach ($segments as $segment)
<option value="{{ $segment['name'] }}" {{ old('segmen', $segmen) == $segment['name'] ? 'selected' : '' }}>
{{ $segment['name'] }}
</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-outline-secondary btn-sm">
<i class="icon-base ti tabler-filter me-1"></i>
Filter
</button>
<button type="button" id="btn-reset-dashboard-filter" class="btn btn-outline-secondary btn-sm">
<i class="icon-base ti tabler-refresh me-1"></i>
Reset
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="nav-align-top">
<ul class="nav nav-pills flex-column flex-md-row mb-6 row-gap-2 profile-farming-tabs" role="tablist">
<li class="nav-item">
<button
type="button"
class="nav-link {{ $activeTab === 'demografi-petani' ? 'active' : '' }}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#profile-tab-demografi-petani"
aria-controls="profile-tab-demografi-petani"
aria-selected="{{ $activeTab === 'demografi-petani' ? 'true' : 'false' }}"
>
<i class="icon-base ti tabler-users icon-sm me-1_5"></i>
Demografi Petani
</button>
</li>
<li class="nav-item">
<button
type="button"
class="nav-link {{ $activeTab === 'indeks-petani' ? 'active' : '' }}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#profile-tab-indeks-petani"
aria-controls="profile-tab-indeks-petani"
aria-selected="{{ $activeTab === 'indeks-petani' ? 'true' : 'false' }}"
>
<i class="icon-base ti tabler-chart-radar icon-sm me-1_5"></i>
Indeks Petani
</button>
</li>
</ul>
</div>
</div>
<div class="tab-content profile-farming-tab-content">
<div
class="tab-pane fade {{ $activeTab === 'demografi-petani' ? 'show active' : '' }}"
id="profile-tab-demografi-petani"
role="tabpanel"
>
<div class="container-fluid flex-grow-1 container-p-y px-0">
<div class="row g-6">
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-primary h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-primary">
<i class="icon-base ti tabler-shovel-pitchforks icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
{{ $data['total_petani'] }}
</h4>
<small class="text-body-secondary">
petani
</small>
</div>
<p class="mb-0 mt-1">
Total Petani
</p>
<small class="text-body-secondary">
Petani yang telah terdata
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-secondary h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-secondary">
<i class="icon-base ti tabler-plant-2 icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
{{ number_format($data['total_komoditas'] ?? 0, 0, ',', '.') }}
</h4>
<small class="text-body-secondary">
komoditas
</small>
</div>
<p class="mb-0 mt-1">
Komoditas Utama
</p>
<small class="text-body-secondary">
Komoditas utama yang dikelola
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-warning h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-warning">
<i class="icon-base ti tabler-map-2 icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
{{ number_format($data['desa_sasaran']['total'] ?? 0, 0, ',', '.') }}
</h4>
<small class="text-body-secondary">
desa
</small>
</div>
<p class="mb-0 mt-1">
Desa Sasaran
</p>
<small class="text-body-secondary">
Asal desa/kelurahan petani
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-info h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-info">
<i class="icon-base ti tabler-wallet icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
Rp {{ number_format($data['ratarata_penghasilan'] ?? 0, 0, ',', '.') }}
</h4>
</div>
<p class="mb-0 mt-1">
Rata-rata Penghasilan Petani
</p>
<small class="text-body-secondary">
Dari pendapatan komoditas tercatat
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-success h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-success">
<i class="icon-base ti tabler-chart-area-line icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
{{ number_format($data['total_luas_lahan'], 2) }}
</h4>
<small class="text-body-secondary">
ha
</small>
</div>
<p class="mb-0 mt-1">
Total Luas Lahan
</p>
<small class="text-body-secondary">
Seluruh lahan petani
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-6 col-sm-6">
<div class="card card-border-shadow-danger h-100">
<div class="card-body">
<div class="d-flex align-items-center gap-4">
<div class="avatar avatar-lg">
<span class="avatar-initial rounded bg-label-danger">
<i class="icon-base ti tabler-ruler-measure icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0 demografi-kpi-value">
{{ number_format($data['ratarata_luas_lahan'], 2) }}
</h4>
<small class="text-body-secondary">
ha
</small>
</div>
<p class="mb-0 mt-1">
Rata-rata Luas Lahan
</p>
<small class="text-body-secondary">
Rata-rata per petani
</small>
</div>
</div>
</div>
</div>
</div>
<!--/ Card Border Shadow -->
<!-- Vehicles overview -->
<div class="col-12">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Profil Demografi</h5><p class="text-body-secondary mb-0">Komposisi petani berdasarkan gender, kelompok umur, dan pendidikan.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#demografi-charts" aria-expanded="true" aria-controls="demografi-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="demografi-charts">
<div class="row g-6 pt-4">
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header d-flex align-items-center justify-content-between">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Gender</h5>
<small class="text-body-secondary">Distribusi petani berdasarkan gender</small>
</div>
</div>
<div class="card-body">
<div id="genderChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Kelompok Umur</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan kelompok umur
</small>
</div>
</div>
<div class="card-body">
<div id="ageGroupChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Tingkat Pendidikan</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan pendidikan terakhir
</small>
</div>
</div>
<div class="card-body">
<div id="educationChart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Sosial Ekonomi</h5><p class="text-body-secondary mb-0">Kondisi sosial ekonomi petani dilihat dari jumlah tanggungan keluarga, penghasilan usaha tani, dan keterlibatan dalam kelompok tani.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#rumah-tangga-charts" aria-expanded="true" aria-controls="rumah-tangga-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="rumah-tangga-charts">
<div class="row g-6 pt-4">
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Jumlah Tanggungan</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan jumlah tanggungan
</small>
</div>
</div>
<div class="card-body">
<div id="dependentChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">
Rentang Penghasilan per Bulan
</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan penghasilan dari usaha tani per musim
</small>
</div>
</div>
<div class="card-body">
<div id="incomeChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Status Keanggotaan Kelompok Tani</h5>
<small class="text-body-secondary">
Petani yang terdaftar di kelompok tani
</small>
</div>
</div>
<div class="card-body">
<div id="farmerGroupChart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Profil Lahan</h5><p class="text-body-secondary mb-0">Karakteristik lahan usaha tani berdasarkan komoditas utama, luas lahan garapan, dan status kepemilikan lahan.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#usaha-tani-charts" aria-expanded="true" aria-controls="usaha-tani-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="usaha-tani-charts">
<div class="row g-6 pt-4">
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header d-flex align-items-center justify-content-between">
<div class="card-title mb-0">
<h5 class="m-0 me-2">
Komoditas Utama yang Dikelola
</h5>
<small class="text-body-secondary">
Jumlah petani berdasarkan komoditas utama
</small>
</div>
</div>
<div class="card-body">
<div id="commodityChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">
Luas Lahan yang Dimiliki
</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan luas lahan garapan
</small>
</div>
</div>
<div class="card-body">
<div id="landAreaChart"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-4">
<div class="card h-100">
<div class="card-header">
<div class="card-title mb-0">
<h5 class="m-0 me-2">
Status Kepemilikan Lahan
</h5>
<small class="text-body-secondary">
Distribusi petani berdasarkan status lahan
</small>
</div>
</div>
<div class="card-body">
<div id="landOwnershipStatusChart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xxl-12 col-lg-12">
<div class="card">
<div class="card-header d-flex flex-wrap justify-content-between align-items-center gap-3">
<div>
<div class="d-flex align-items-center gap-2">
<h5 class="card-title mb-0">
Daftar Petani
</h5>
<span
class="badge bg-label-primary"
id="farmerTotalBadge"
>
{{ $data['total_petani'] }} petani
</span>
</div>
<small class="text-body-secondary">
Menampilkan data petani sesuai data yang tersedia
</small>
</div>
<div class="d-flex align-items-center gap-2">
<div
class="input-group input-group-merge"
style="width: 220px;"
>
<span class="input-group-text">
<i class="icon-base ti tabler-search"></i>
</span>
<input
type="text"
id="farmerSearch"
class="form-control"
placeholder="Cari petani..."
>
</div>
<button type="button" class="btn btn-label-secondary btn-icon" data-bs-toggle="collapse" data-bs-target="#demografi-table-filter-panel" aria-expanded="false" aria-controls="demografi-table-filter-panel" aria-label="Filter tabel" title="Filter tabel"><i class="icon-base ti tabler-filter"></i></button>
</div>
</div>
<div class="collapse" id="demografi-table-filter-panel"><div class="card-body border-top">
<div class="row g-3">
@foreach ($tableFilters as $key => [$label, $options])
<div class="col-12 col-sm-6 col-lg-4">
<label class="form-label" for="farmer-filter-{{ $key }}">{{ $label }}</label>
<select id="farmer-filter-{{ $key }}" class="form-select form-select-sm farmer-table-filter" data-filter="{{ $key }}">
<option value="">Semua</option>
@foreach ($options as $value => $text)
<option value="{{ $value }}">{{ $text }}</option>
@endforeach
</select>
</div>
@endforeach
</div>
<small class="text-body-secondary d-block mt-3">Filter tabel mengikuti filter dashboard yang telah diterapkan.</small>
<div class="d-flex gap-2 mt-3"><button type="button" id="applyFarmerFilter" class="btn btn-primary btn-sm">Terapkan Filter</button><button type="button" id="resetFarmerFilter" class="btn btn-label-secondary btn-sm">Clear Filter</button></div>
</div></div>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>No.</th>
<th>Nama Petani</th>
<th>Gender</th>
<th>Umur</th>
<th>Komoditas</th>
<th>Luas Lahan</th>
<th>Pendidikan</th>
<th>Rentang Pendapatan</th>
</tr>
</thead>
<tbody id="farmerTableBody">
<tr><td colspan="8" class="text-center text-body-secondary py-5">Memuat data petani...</td></tr>
</tbody>
</table>
</div>
<div class="card-footer d-flex flex-wrap justify-content-between align-items-center gap-3">
<small class="text-body-secondary" id="farmerTableInfo">Memuat data petani...</small>
<div id="farmerPagination"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="tab-pane fade {{ $activeTab === 'indeks-petani' ? 'show active' : '' }}"
id="profile-tab-indeks-petani"
role="tabpanel"
>
@php($showIndeksFilter = false)
<div class="container-fluid flex-grow-1 container-p-y px-0">
<div class="row g-6">
<div class="col-lg-3 col-sm-6">
<div class="card card-border-shadow-primary h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<div class="avatar me-4">
<span class="avatar-initial rounded bg-label-primary">
<i class="icon-base ti tabler-user-check icon-28px"></i>
</span>
</div>
<h4 class="mb-0" id="count-petani">0</h4>
</div>
<p class="mb-1">Total Petani</p>
<p class="mb-0">
<span class="text-heading fw-medium me-2" id="count-komoditas">0</span>
<small class="text-body-secondary">komoditas utama</small>
</p>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6">
<div class="card card-border-shadow-warning h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<div class="avatar me-4">
<span class="avatar-initial rounded bg-label-warning">
<i class="icon-base ti tabler-trees icon-28px"></i>
</span>
</div>
<h4 class="mb-0" id="sum-lahan">0</h4>
<small class="text-body-warning">ha</small>
</div>
<p class="mb-1">Total Luas Lahan</p>
<p class="mb-0">
<small class="text-body-warning">rata-rata</small>
<span class="text-heading fw-medium" id="avg-lahan"></span>
<small class="text-body-warning">ha/petani</small>
</p>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6">
<div class="card card-border-shadow-info h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<div class="avatar me-4">
<span class="avatar-initial rounded bg-label-info">
<i class="icon-base ti tabler-star icon-28px"></i>
</span>
</div>
<h4 class="mb-0" id="avg-skor"></h4>
</div>
<p class="mb-1">Rata-rata Indeks Kesiapan Adopsi</p>
<p class="mb-0">
<small class="text-body-secondary">Kategori Indeks:</small>
<span class="text-heading fw-medium me-2" id="avg-segmen"></span>
</p>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6">
<div class="card card-border-shadow-success h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-2">
<div class="avatar me-4">
<span class="avatar-initial rounded bg-label-success">
<i class="icon-base ti tabler-map-2 icon-28px"></i>
</span>
</div>
<h4 class="mb-0" id="count-desa">0</h4>
</div>
<p class="mb-1">Jumlah Desa Sasaran</p>
<p class="mb-0">
<span class="text-heading fw-medium me-2" id="count-kecamatan">0</span>
<small class="text-body-secondary">kecamatan sasaran</small>
</p>
</div>
</div>
</div>
</div>
<div class="py-4">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Indeks Petani</h5><p class="text-body-secondary mb-0">Komposisi petani menurut tingkat kesiapan adopsi, secara keseluruhan dan pada setiap komoditas.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#indeks-segmentasi-charts" aria-expanded="true" aria-controls="indeks-segmentasi-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="indeks-segmentasi-charts">
<div class="row g-6 py-4">
<div class="col-lg-6 col-sm-12">
<div class="card card-border-shadow-primary h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Distribusi Indeks Petani</h5>
<p class="card-subtitle">Jumlah dan proporsi petani pada setiap segmen kesiapan adopsi.</p>
</div>
</div>
<div class="card-body">
<div class="segment-distribution">
<div class="segment-chart-shell">
<div id="chart-segmentasi-petani"></div>
</div>
<div class="segment-table-shell table-responsive">
<table class="table card-table table-border-top-0 table-border-bottom-0 segment-table mb-0">
<thead>
<tr>
<th>Segmen</th>
<th class="text-end">Petani</th>
<th class="text-end">%</th>
</tr>
</thead>
<tbody id="table-segmentasi-petani"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-12">
<div class="card card-border-shadow-secondary h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Distribusi Indeks Petani</h5>
<p class="card-subtitle">Perbandingan komposisi segmen kesiapan adopsi petani antar-komoditas.</p>
</div>
</div>
<div class="card-body">
<div class="segment-commodity-chart" id="chart-segmentasi-petani-komoditas"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="py-4">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Dimensi dan Komponen Kesiapan Adopsi</h5><p class="text-body-secondary mb-0">Perbandingan skor tiap dimensi dan komponen untuk mengenali aspek kesiapan adopsi yang kuat dan yang perlu ditingkatkan.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#indeks-kesiapan-charts" aria-expanded="true" aria-controls="indeks-kesiapan-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="indeks-kesiapan-charts">
<div class="row g-6 py-4">
<div class="col-lg-6 col-sm-12">
<div class="card card-border-shadow-info h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Dimensi Kesiapan Adopsi</h5>
<p class="card-subtitle">Rata-rata skor budidaya, sosial ekonomi rumah tangga, serta rantai nilai dan akses pasar.</p>
</div>
</div>
<div class="card-body">
<div id="chart-dimensi"></div>
<p class="small text-body-secondary text-center mb-0">Skala skor 0–5</p>
<div id="chart-dimensi-scores" class="readiness-score-list mt-3" aria-live="polite"><p class="text-body-secondary small mb-0">Memuat informasi skor…</p></div>
</div>
</div>
</div>
<div class="col-lg-6 col-sm-12">
<div class="card card-border-shadow-warning h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Komponen Kesiapan Adopsi</h5>
<p class="card-subtitle">Rata-rata skor enam komponen yang membentuk kesiapan adopsi petani.</p>
</div>
</div>
<div class="card-body">
<div id="chart-komponen"></div>
<p class="small text-body-secondary text-center mb-0">Skala skor 0–5</p>
<div id="chart-komponen-scores" class="readiness-score-list mt-3" aria-live="polite"><p class="text-body-secondary small mb-0">Memuat informasi skor…</p></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="py-4">
<div class="d-flex justify-content-between align-items-start gap-3">
<div><h5 class="mb-1">Sebaran wilayah petani</h5><p class="text-body-secondary mb-0">Perbandingan tingkat kesiapan adopsi antarwilayah melalui peta dan komposisi segmen petani di setiap desa/kelurahan.</p></div>
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#indeks-wilayah-charts" aria-expanded="true" aria-controls="indeks-wilayah-charts">Sembunyikan Chart</button>
</div>
<div class="collapse show chart-group-panel" id="indeks-wilayah-charts">
<div class="row g-6 py-4">
<div class="col-lg-12 col-sm-12">
<div class="card card-border-shadow-danger h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Peta Sebaran</h5>
<p class="card-subtitle">Warna wilayah menunjukkan kategori rata-rata indeks kesiapan adopsi petani di setiap desa/kelurahan.</p>
</div>
</div>
<div class="card-body">
<div id="farmer-map-shell" class="position-relative">
<button type="button" id="farmer-map-fullscreen" class="btn btn-light btn-sm shadow-sm position-absolute top-0 end-0 m-3" style="z-index: 1000;" aria-label="Buka peta layar penuh" title="Buka peta layar penuh">
<i class="icon-base ti tabler-maximize"></i>
</button>
<div id="map-sebaran-petani" style="height: 430px;"></div>
</div>
<span class="small text-muted">Klik wilayah desa/kelurahan untuk melihat rincian dan menyaring daftar petani.</span>
</div>
</div>
</div>
<div class="col-lg-12 col-sm-12">
<div class="card card-border-shadow-success h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="card-title mb-0">
<h5 class="m-0 me-2">Tabel Sebaran</h5>
<p class="card-subtitle">Jumlah dan persentase petani pada setiap segmen kesiapan adopsi serta kategori belum lengkap, dikelompokkan menurut desa/kelurahan.</p>
</div>
</div>
<div class="card-body">
<table id="table-desa" class="table card-table table-border-top-0 table-border-bottom-0">
<thead>
<tr>
<th class="pe-0">Desa/kelurahan</th>
<th class="text-end pe-0">Total Petani</th>
<th class="text-end text-danger pe-0">Pemula</th>
<th class="text-end text-info pe-0">Perintis</th>
<th class="text-end text-warning pe-0">Pemerhati</th>
<th class="text-end text-success pe-0">Penggerak</th>
<th class="text-end text-secondary pe-0">Belum lengkap</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-6 py-4">
<div class="col-lg-12 col-sm-12">
<div class="card card-border-shadow-primary h-100">
<div class="card-header d-flex flex-wrap justify-content-between align-items-center gap-3">
<div>
<div class="d-flex align-items-center gap-2">
<h5 class="card-title mb-0">Daftar Petani</h5>
<span class="badge bg-label-primary" id="indexFarmerTotalBadge">0 petani</span>
</div>
<small class="text-body-secondary">Rincian wilayah, komoditas, luas lahan, skor kesiapan adopsi, dan segmen setiap petani.</small>
</div>
<div class="d-flex align-items-center gap-2">
<div class="input-group input-group-merge" style="width: 220px;">
<span class="input-group-text"><i class="icon-base ti tabler-search"></i></span>
<input type="search" id="indexFarmerSearch" class="form-control" placeholder="Cari petani..." aria-label="Cari petani pada tabel indeks">
</div>
<button type="button" class="btn btn-label-secondary btn-icon" data-bs-toggle="collapse" data-bs-target="#index-table-filter-panel" aria-expanded="false" aria-controls="index-table-filter-panel" aria-label="Filter tabel" title="Filter tabel"><i class="icon-base ti tabler-filter"></i></button>
</div>
</div>
<div class="collapse" id="index-table-filter-panel"><div class="card-body border-top">
<div class="row g-3">
<div class="col-12 col-md-4">
<label class="form-label" for="index-table-desa">Desa/Kelurahan</label>
<select id="index-table-desa" class="form-select form-select-sm index-table-filter" data-column="1"><option value="">Semua Desa/Kelurahan</option></select>
</div>
<div class="col-12 col-md-4">
<label class="form-label" for="index-table-komoditas">Komoditas</label>
<select id="index-table-komoditas" class="form-select form-select-sm index-table-filter" data-column="2"><option value="">Semua Komoditas</option></select>
</div>
<div class="col-12 col-md-4">
<label class="form-label" for="index-table-segmen">Segmen</label>
<select id="index-table-segmen" class="form-select form-select-sm index-table-filter" data-column="5"><option value="">Semua Segmen</option></select>
</div>
</div>
</div>
<div class="d-flex gap-2 px-6 pb-4"><button type="button" id="applyIndexTableFilter" class="btn btn-primary btn-sm">Terapkan Filter</button><button type="button" id="btn-reset-filter" class="btn btn-label-secondary btn-sm">Clear Filter</button></div></div>
<div class="card-body">
<table id="table-petani" class="table table-hover">
<thead>
<tr>
<th class="pe-0">Nama Petani</th>
<th class="pe-0">Desa/Kelurahan</th>
<th class="pe-0">Komoditas</th>
<th class="text-end pe-0">Luas Lahan (ha)</th>
<th class="text-end pe-0">Indeks Kesiapan Adopsi</th>
<th class="pe-0">Segmen</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
const dashboardFilters = @json($dashboardFilters);
const dashboardFilterParams = new URLSearchParams();
Object.entries(dashboardFilters).forEach(([key, value]) => {
if (value !== null && value !== '') {
dashboardFilterParams.set(key, value);
}
});
document.addEventListener('DOMContentLoaded', function () {
const headingColor = config.colors.headingColor;
const legendColor = config.colors.bodyColor;
const fontFamily = config.fontFamily;
const farmerDataUrl = @json(route('dashboard.demografi-petani.data'));
const farmerProfileUrl = @json(url('/dashboard/profil/petani'));
let activeFilters = {
gender: null,
umur: null,
pendidikan: null,
jumlah_tanggungan: null,
kelompok_tani: null,
komoditas: null,
luas_lahan: null,
status_kepemilikan_lahan: null,
penghasilan: null
};
let searchTimer = null;
let farmerTableRequest = null;
function escapeHtml(value) {
if (value === null || value === undefined) {
return '';
}
return String(value)
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#039;');
}
function formatLuas(value) {
const number = Number(value || 0);
if (number <= 0) {
return '-';
}
return number.toLocaleString('id-ID', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' ha';
}
async function loadFarmerTable(page = 1) {
farmerTableRequest?.abort();
const request = new AbortController();
farmerTableRequest = request;
const searchInput = document.querySelector('#farmerSearch');
const search = searchInput ? searchInput.value.trim() : '';
const params = new URLSearchParams();
params.set('page', page);
dashboardFilterParams.forEach((value, key) => params.set(key, value));
Object.entries(activeFilters).forEach(([key, value]) => {
if (value !== null && value !== undefined && value !== '') {
params.set(key, value);
}
});
if (search) {
params.set('search', search);
}
const tableBody = document.querySelector('#farmerTableBody');
if (tableBody) {
tableBody.innerHTML = `
<tr>
<td colspan="8" class="text-center py-5">
<div class="spinner-border spinner-border-sm text-primary me-2" role="status"></div>
<span class="text-body-secondary">Memuat data petani...</span>
</td>
</tr>
`;
}
try {
const response = await fetch(`${farmerDataUrl}?${params.toString()}`, {
method: 'GET',
signal: request.signal,
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
if (!response.ok) {
throw new Error(`HTTP Error ${response.status}`);
}
const result = await response.json();
if (!request.signal.aborted) renderFarmerTable(result);
} catch (error) {
if (error.name === 'AbortError') return;
console.error('Gagal memuat tabel petani:', error);
if (tableBody) {
tableBody.innerHTML = '<tr><td colspan="8" class="text-center text-danger py-5">Data gagal dimuat. Silakan coba kembali.</td></tr>';
}
}
}
function renderFarmerTable(result) {
const tbody = document.querySelector('#farmerTableBody');
if (!tbody) {
return;
}
const rows = result.data || [];
if (rows.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="text-center py-5 text-body-secondary">
Tidak ada data petani yang sesuai dengan filter.
</td>
</tr>
`;
updateFarmerPagination(result.pagination);
return;
}
tbody.innerHTML = rows.map((petani, index) => {
const rowNumber = ((result.pagination.current_page - 1) * result.pagination.per_page) + index + 1;
const genderBadge = Number(petani.jenis_kelamin) === 1
? `<span class="badge bg-label-info">Laki-laki</span>`
: `<span class="badge" style="background-color:#ffe8f1 !important; color:#ff5598 !important;">Perempuan</span>`;
const umur = (petani.umur !== null && petani.umur !== undefined) ? `${petani.umur} tahun` : '-';
return `
<tr class="farmer-row" data-id="${petani.id}" style="cursor: pointer;">
<td><span class="text-heading fw-medium">${rowNumber}</span></td>
<td><span class="text-heading fw-medium">${escapeHtml(petani.nama)}</span></td>
<td>${genderBadge}</td>
<td>${escapeHtml(umur)}</td>
<td>${escapeHtml(petani.komoditas || '-')}</td>
<td>${formatLuas(petani.luas_lahan)}</td>
<td>${escapeHtml(petani.pendidikan || '-')}</td>
<td>${escapeHtml(petani.rentang_penghasilan || '-')}</td>
</tr>
`;
}).join('');
updateFarmerPagination(result.pagination);
}
function updateFarmerPagination(pagination) {
if (!pagination) {
return;
}
const totalBadge = document.querySelector('#farmerTotalBadge');
if (totalBadge) {
totalBadge.textContent = `${pagination.total} petani`;
}
const tableInfo = document.querySelector('#farmerTableInfo');
if (tableInfo) {
tableInfo.textContent = `Menampilkan ${pagination.from ?? 0} - ${pagination.to ?? 0} dari ${pagination.total ?? 0} petani`;
}
const paginationEl = document.querySelector('#farmerPagination');
if (!paginationEl) {
return;
}
if (pagination.last_page <= 1) {
paginationEl.innerHTML = '';
return;
}
let html = `<nav><ul class="pagination mb-0">`;
html += `
<li class="page-item ${pagination.current_page === 1 ? 'disabled' : ''}">
<button type="button" class="page-link farmer-page" data-page="${pagination.current_page - 1}">
<i class="icon-base ti tabler-chevron-left"></i>
</button>
</li>
`;
html = html.replace('<ul class="pagination mb-0">', '<ul class="pagination mb-0">' +
'<li class="page-item ' + (pagination.current_page === 1 ? 'disabled' : '') + '">' +
'<button type="button" class="page-link farmer-page" data-page="1" aria-label="Halaman pertama"><i class="icon-base ti tabler-chevrons-left"></i></button></li>');
const currentPage = pagination.current_page;
const lastPage = pagination.last_page;
const startPage = Math.max(1, currentPage - 2);
const endPage = Math.min(lastPage, currentPage + 2);
if (startPage > 1) {
html += `
<li class="page-item">
<button type="button" class="page-link farmer-page" data-page="1">1</button>
</li>
`;
if (startPage > 2) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
}
for (let page = startPage; page <= endPage; page++) {
html += `
<li class="page-item ${page === currentPage ? 'active' : ''}">
<button type="button" class="page-link farmer-page" data-page="${page}">${page}</button>
</li>
`;
}
if (endPage < lastPage) {
if (endPage < lastPage - 1) {
html += `<li class="page-item disabled"><span class="page-link">...</span></li>`;
}
html += `
<li class="page-item">
<button type="button" class="page-link farmer-page" data-page="${lastPage}">${lastPage}</button>
</li>
`;
}
html += `
<li class="page-item ${currentPage === lastPage ? 'disabled' : ''}">
<button type="button" class="page-link farmer-page" data-page="${currentPage + 1}">
<i class="icon-base ti tabler-chevron-right"></i>
</button>
</li>
`;
html += '<li class="page-item ' + (currentPage === lastPage ? 'disabled' : '') + '">' +
'<button type="button" class="page-link farmer-page" data-page="' + lastPage + '" aria-label="Ke halaman terakhir" title="Ke halaman terakhir"><i class="icon-base ti tabler-chevrons-right"></i></button></li>';
html += `</ul></nav>`;
paginationEl.innerHTML = html;
}
function applyFarmerFilter(filter, value) {
if (activeFilters[filter] !== null && String(activeFilters[filter]) === String(value)) {
activeFilters[filter] = null;
} else {
activeFilters[filter] = value;
}
document.querySelectorAll('.farmer-table-filter').forEach((select) => {
select.value = activeFilters[select.dataset.filter] ?? '';
});
loadFarmerTable(1);
}
function resetFarmerFilter() {
activeFilters = {
gender: null,
umur: null,
pendidikan: null,
jumlah_tanggungan: null,
kelompok_tani: null,
komoditas: null,
luas_lahan: null,
status_kepemilikan_lahan: null,
penghasilan: null
};
const searchInput = document.querySelector('#farmerSearch');
if (searchInput) {
searchInput.value = '';
}
document.querySelectorAll('.farmer-table-filter').forEach((select) => {
select.value = activeFilters[select.dataset.filter] ?? '';
});
loadFarmerTable(1);
}
document.querySelector('#applyFarmerFilter').addEventListener('click', function () {
document.querySelectorAll('.farmer-table-filter').forEach((select) => {
activeFilters[select.dataset.filter] = select.value || null;
});
loadFarmerTable(1);
});
document.addEventListener('input', function (event) {
if (event.target.id !== 'farmerSearch') {
return;
}
clearTimeout(searchTimer);
searchTimer = setTimeout(function () {
loadFarmerTable(1);
}, 350);
});
document.addEventListener('click', function (event) {
const button = event.target.closest('.farmer-page');
if (!button) {
return;
}
if (button.closest('.page-item.disabled')) {
return;
}
const page = Number(button.dataset.page);
if (!page || page < 1) {
return;
}
loadFarmerTable(page);
});
document.addEventListener('click', function (event) {
const button = event.target.closest('#resetFarmerFilter');
if (!button) {
return;
}
resetFarmerFilter();
});
const genderChartEl = document.querySelector('#genderChart');
const lakiLaki = Number(@json($data['petani_lakilaki'] ?? 0));
const perempuan = Number(@json($data['petani_perempuan'] ?? 0));
const totalPetani = lakiLaki + perempuan;
if (genderChartEl) {
const genderChartConfig = {
chart: {
height: 365,
parentHeightOffset: 0,
type: 'pie',
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
const values = [1, 0];
applyFarmerFilter('gender', values[index]);
}
}
},
labels: ['Laki-laki', 'Perempuan'],
series: [lakiLaki, perempuan],
colors: [config.colors.info, '#ff5598'],
stroke: { width: 0 },
dataLabels: {
enabled: true,
formatter: function (value, options) {
const count = options.w.globals.series[options.seriesIndex] || 0;
if (count <= 0) {
return '';
}
return `${Number(value).toFixed(1)}%`;
},
style: {
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 600,
colors: ['#fff']
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 2,
opacity: 0.45
}
},
legend: {
show: true,
position: 'bottom',
offsetY: 10,
markers: { size: 4, strokeWidth: 0 },
itemMargin: { horizontal: 15, vertical: 5 },
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 400,
labels: { colors: legendColor, useSeriesColors: false },
formatter: function (seriesName, options) {
const value = options.w.globals.series[options.seriesIndex];
const percentage = totalPetani > 0 ? ((value / totalPetani) * 100).toFixed(1) : '0.0';
return `${seriesName}: ${value} (${percentage}%)`;
}
},
tooltip: {
theme: 'dark',
y: {
formatter: function (value) {
const percentage = totalPetani > 0 ? ((value / totalPetani) * 100).toFixed(1) : '0.0';
return `${value} petani (${percentage}%)`;
}
}
},
grid: { padding: { top: 15 } },
plotOptions: {
pie: {
dataLabels: {
offset: -14,
minAngleToShowLabel: 8
}
}
},
responsive: [
{
breakpoint: 1025,
options: {
chart: { height: 380 }
}
}
]
};
const genderChart = new ApexCharts(genderChartEl, genderChartConfig);
genderChart.render();
}
const ageGroupChartEl = document.querySelector('#ageGroupChart');
if (ageGroupChartEl) {
const ageFilterValues = ['kurang_30', '30_39', '40_49', '50_59', '60_keatas'];
const ageGroupData = [
Number(@json($data['kelompok_umur']['kurang_30'] ?? 0)),
Number(@json($data['kelompok_umur']['30_39'] ?? 0)),
Number(@json($data['kelompok_umur']['40_49'] ?? 0)),
Number(@json($data['kelompok_umur']['50_59'] ?? 0)),
Number(@json($data['kelompok_umur']['60_keatas'] ?? 0))
];
const totalAgeGroup = ageGroupData.reduce((total, value) => total + value, 0);
const ageGroupChartConfig = {
chart: {
height: 320,
type: 'bar',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('umur', ageFilterValues[index]);
}
}
},
series: [
{
name: 'Petani',
data: ageGroupData
}
],
colors: [config.colors.primary],
plotOptions: {
bar: {
borderRadius: 4,
columnWidth: '45%',
distributed: false,
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
offsetY: -20,
style: {
fontSize: '12px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
const percentage = totalAgeGroup > 0 ? (Number(value) / totalAgeGroup) * 100 : 0;
return `${value} (${percentage.toFixed(1)}%)`;
}
},
xaxis: {
categories: ['< 30', '30 - 39', '40 - 49', '50 - 59', '≥ 60'],
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return value + ' tahun';
}
},
axisBorder: { show: false },
axisTicks: { show: false }
},
yaxis: {
min: 0,
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return Math.floor(value);
}
},
title: {
text: 'Petani',
style: { color: legendColor, fontSize: '12px', fontFamily: fontFamily, fontWeight: 400 }
}
},
grid: {
borderColor: config.colors.borderColor,
strokeDashArray: 5,
xaxis: { lines: { show: false } },
padding: { top: 34 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
const percentage = totalAgeGroup > 0 ? (Number(value) / totalAgeGroup) * 100 : 0;
return `
<div style="background:${config.colors.primary}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label} tahun: <strong>${value} petani (${percentage.toFixed(1)}%)</strong>
</div>
`;
}
}
};
new ApexCharts(ageGroupChartEl, ageGroupChartConfig).render();
}
const educationChartEl = document.querySelector('#educationChart');
if (educationChartEl) {
const educationData = [
Number(@json($data['pendidikan']['tidak_sekolah'] ?? 0)),
Number(@json($data['pendidikan']['sd'] ?? 0)),
Number(@json($data['pendidikan']['smp'] ?? 0)),
Number(@json($data['pendidikan']['sma'] ?? 0)),
Number(@json($data['pendidikan']['diploma'] ?? 0)),
Number(@json($data['pendidikan']['s1_keatas'] ?? 0))
];
const educationFilters = ['tidak_sekolah', 'sd', 'smp', 'sma', 'diploma', 's1_keatas'];
const totalPendidikan = educationData.reduce((total, value) => total + value, 0);
const educationChartConfig = {
chart: {
height: 330,
type: 'bar',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('pendidikan', educationFilters[index]);
}
}
},
series: [{ name: 'Petani', data: educationData }],
colors: [config.colors.success],
plotOptions: {
bar: {
horizontal: true,
borderRadius: 4,
barHeight: '55%',
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
textAnchor: 'start',
offsetX: 6,
style: {
fontSize: '12px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
const percentage = totalPendidikan > 0 ? ((value / totalPendidikan) * 100).toFixed(1) : '0.0';
return `${value} (${percentage}%)`;
}
},
xaxis: {
categories: ['Tidak Sekolah', 'SD / Sederajat', 'SMP / Sederajat', 'SMA / Sederajat', 'Diploma', 'S1 ke atas'],
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return Math.floor(value);
}
},
axisBorder: { show: false },
axisTicks: { show: false },
title: {
text: 'Petani',
style: { color: legendColor, fontSize: '12px', fontFamily: fontFamily, fontWeight: 400 }
}
},
yaxis: {
labels: { style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily } }
},
grid: {
borderColor: config.colors.borderColor,
strokeDashArray: 5,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: { right: 55 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
const percentage = totalPendidikan > 0 ? ((value / totalPendidikan) * 100).toFixed(1) : '0.0';
return `
<div style="background:${config.colors.success}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label}: <strong>${value} petani (${percentage}%)</strong>
</div>
`;
}
}
};
new ApexCharts(educationChartEl, educationChartConfig).render();
}
const dependentChartEl = document.querySelector('#dependentChart');
if (dependentChartEl) {
const dependentData = [
Number(@json($data['jumlah_tanggungan']['0'] ?? 0)),
Number(@json($data['jumlah_tanggungan']['1_2'] ?? 0)),
Number(@json($data['jumlah_tanggungan']['3_4'] ?? 0)),
Number(@json($data['jumlah_tanggungan']['5_keatas'] ?? 0))
];
const dependentFilters = ['0', '1_2', '3_4', '5_keatas'];
const totalDependentsFarmers = dependentData.reduce((total, value) => total + value, 0);
const dependentChartConfig = {
chart: {
height: 330,
type: 'bar',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('jumlah_tanggungan', dependentFilters[index]);
}
}
},
series: [{ name: 'Petani', data: dependentData }],
colors: [config.colors.danger],
plotOptions: {
bar: {
horizontal: true,
borderRadius: 4,
barHeight: '50%',
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
textAnchor: 'start',
offsetX: 6,
style: {
fontSize: '12px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
const percentage = totalDependentsFarmers > 0 ? ((value / totalDependentsFarmers) * 100).toFixed(1) : '0.0';
return `${value} (${percentage}%)`;
}
},
xaxis: {
categories: ['0 orang', '1 - 2 orang', '3 - 4 orang', '>= 5 orang'],
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return Math.floor(value);
}
},
axisBorder: { show: false },
axisTicks: { show: false },
title: {
text: 'Petani',
style: { color: legendColor, fontSize: '12px', fontFamily: fontFamily, fontWeight: 400 }
}
},
yaxis: {
labels: { style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily } }
},
grid: {
borderColor: config.colors.borderColor,
strokeDashArray: 5,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: { right: 55 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
const percentage = totalDependentsFarmers > 0 ? ((value / totalDependentsFarmers) * 100).toFixed(1) : '0.0';
return `
<div style="background:${config.colors.danger}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label}: <strong>${value} petani (${percentage}%)</strong>
</div>
`;
}
}
};
new ApexCharts(dependentChartEl, dependentChartConfig).render();
}
const farmerGroupChartEl = document.querySelector('#farmerGroupChart');
if (farmerGroupChartEl) {
const farmerGroupData = [
Number(@json($data['kelompok_tani']['terdaftar'] ?? 0)),
Number(@json($data['kelompok_tani']['tidak_terdaftar'] ?? 0)),
Number(@json($data['kelompok_tani']['belum_terisi'] ?? 0))
];
const farmerGroupFilters = ['terdaftar', 'tidak_terdaftar', 'belum_terisi'];
const totalFarmerGroup = farmerGroupData.reduce((total, value) => total + value, 0);
const farmerGroupChartConfig = {
chart: {
height: 365,
type: 'pie',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('kelompok_tani', farmerGroupFilters[index]);
}
}
},
labels: ['Terdaftar', 'Tidak Terdaftar', 'Belum Terisi'],
series: farmerGroupData,
colors: [config.colors.success, config.colors.warning, config.colors.secondary],
stroke: { width: 0 },
dataLabels: {
enabled: true,
formatter: function (value, options) {
const count = options.w.globals.series[options.seriesIndex] || 0;
if (count <= 0) {
return '';
}
return `${Number(value).toFixed(1)}%`;
},
style: {
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 600,
colors: ['#fff']
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 2,
opacity: 0.45
}
},
legend: {
show: true,
position: 'bottom',
offsetY: 10,
markers: { size: 4, strokeWidth: 0 },
itemMargin: { horizontal: 15, vertical: 5 },
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 400,
labels: { colors: legendColor, useSeriesColors: false },
formatter: function (seriesName, options) {
const value = options.w.globals.series[options.seriesIndex];
const percentage = totalFarmerGroup > 0 ? ((value / totalFarmerGroup) * 100).toFixed(1) : '0.0';
return `${seriesName}: ${value} (${percentage}%)`;
}
},
tooltip: {
theme: 'dark',
y: {
formatter: function (value) {
const percentage = totalFarmerGroup > 0 ? ((value / totalFarmerGroup) * 100).toFixed(1) : '0.0';
return `${value} petani (${percentage}%)`;
}
}
},
plotOptions: {
pie: {
dataLabels: {
offset: -14,
minAngleToShowLabel: 8
}
}
},
responsive: [
{
breakpoint: 1025,
options: {
chart: { height: 380 }
}
}
]
};
new ApexCharts(farmerGroupChartEl, farmerGroupChartConfig).render();
}
const commodityChartEl = document.querySelector('#commodityChart');
if (commodityChartEl) {
const commodityIds = @json($data['komoditas_utama']['ids'] ?? []);
const commodityLabels = @json($data['komoditas_utama']['labels'] ?? []);
const commoditySeries = @json($data['komoditas_utama']['series'] ?? []);
const totalCommodityFarmers = commoditySeries.reduce((total, value) => total + Number(value), 0);
const commodityChartConfig = {
chart: {
height: 365,
type: 'pie',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('komoditas', commodityIds[index]);
}
}
},
labels: commodityLabels,
series: commoditySeries.map(Number),
colors: [
config.colors.primary,
config.colors.warning,
config.colors.success,
config.colors.info,
config.colors.danger,
config.colors.secondary
],
stroke: { width: 0 },
dataLabels: {
enabled: true,
formatter: function (value, options) {
const count = options.w.globals.series[options.seriesIndex] || 0;
if (count <= 0) {
return '';
}
return `${Number(value).toFixed(1)}%`;
},
style: {
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 600,
colors: ['#fff']
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 2,
opacity: 0.45
}
},
legend: {
show: true,
position: 'bottom',
offsetY: 10,
markers: { size: 4, strokeWidth: 0 },
itemMargin: { horizontal: 15, vertical: 5 },
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 400,
labels: { colors: legendColor, useSeriesColors: false },
formatter: function (seriesName, options) {
const value = options.w.globals.series[options.seriesIndex];
const percentage = totalCommodityFarmers > 0 ? ((value / totalCommodityFarmers) * 100).toFixed(1) : '0.0';
return `${seriesName}: ${value} (${percentage}%)`;
}
},
tooltip: {
custom: function ({ series, seriesIndex, w }) {
const value = series[seriesIndex];
const label = w.globals.labels[seriesIndex];
const percentage = totalCommodityFarmers > 0 ? ((value / totalCommodityFarmers) * 100).toFixed(1) : '0.0';
const color = w.globals.colors[seriesIndex];
return `
<div style="background:${color}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label}: <strong>${value} petani (${percentage}%)</strong>
</div>
`;
}
},
plotOptions: {
pie: {
dataLabels: {
offset: -14,
minAngleToShowLabel: 8
}
}
},
responsive: [
{
breakpoint: 1400,
options: {
chart: { height: 390 },
legend: { position: 'bottom' }
}
},
{
breakpoint: 576,
options: {
chart: { height: 380 },
legend: { position: 'bottom' }
}
}
]
};
new ApexCharts(commodityChartEl, commodityChartConfig).render();
}
const landAreaChartEl = document.querySelector('#landAreaChart');
if (landAreaChartEl) {
const landAreaData = [
Number(@json($data['luas_lahan']['kurang_05'] ?? 0)),
Number(@json($data['luas_lahan']['05_1'] ?? 0)),
Number(@json($data['luas_lahan']['1_2'] ?? 0)),
Number(@json($data['luas_lahan']['lebih_2'] ?? 0))
];
const landAreaFilters = ['kurang_05', '05_1', '1_2', 'lebih_2'];
const totalLandFarmers = landAreaData.reduce((total, value) => total + value, 0);
const landAreaChartConfig = {
chart: {
height: 330,
type: 'bar',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('luas_lahan', landAreaFilters[index]);
}
}
},
series: [{ name: 'Petani', data: landAreaData }],
colors: [config.colors.warning],
plotOptions: {
bar: {
horizontal: true,
borderRadius: 4,
barHeight: '50%',
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
textAnchor: 'start',
offsetX: 6,
style: {
fontSize: '12px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
const percentage = totalLandFarmers > 0 ? ((value / totalLandFarmers) * 100).toFixed(1) : '0.0';
return `${value} (${percentage}%)`;
}
},
xaxis: {
categories: ['< 0,5 ha', '0,5 - 1 ha', '1 - 2 ha', '> 2 ha'],
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return Math.floor(value);
}
},
axisBorder: { show: false },
axisTicks: { show: false },
title: {
text: 'Petani',
style: { color: legendColor, fontSize: '12px', fontFamily: fontFamily, fontWeight: 400 }
}
},
yaxis: {
labels: { style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily } }
},
grid: {
borderColor: config.colors.borderColor,
strokeDashArray: 5,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: { right: 55 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
const percentage = totalLandFarmers > 0 ? ((value / totalLandFarmers) * 100).toFixed(1) : '0.0';
return `
<div style="background:${config.colors.warning}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label}: <strong>${value} petani (${percentage}%)</strong>
</div>
`;
}
}
};
new ApexCharts(landAreaChartEl, landAreaChartConfig).render();
}
const landOwnershipStatusChartEl = document.querySelector('#landOwnershipStatusChart');
if (landOwnershipStatusChartEl) {
const landOwnershipStatusKeys = @json($data['status_kepemilikan_lahan']['keys'] ?? []);
const landOwnershipStatusLabels = @json($data['status_kepemilikan_lahan']['labels'] ?? []);
const landOwnershipStatusSeries = (@json($data['status_kepemilikan_lahan']['series'] ?? [])).map(Number);
const totalLandOwnershipStatusFarmers = landOwnershipStatusSeries.reduce((total, value) => total + value, 0);
const landOwnershipStatusChartConfig = {
chart: {
height: 365,
type: 'pie',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('status_kepemilikan_lahan', landOwnershipStatusKeys[index]);
}
}
},
labels: landOwnershipStatusLabels,
series: landOwnershipStatusSeries,
colors: [
config.colors.success,
config.colors.warning,
config.colors.info,
config.colors.secondary
],
stroke: { width: 0 },
dataLabels: {
enabled: true,
formatter: function (value, options) {
const count = options.w.globals.series[options.seriesIndex] || 0;
if (count <= 0) {
return '';
}
return `${Number(value).toFixed(1)}%`;
},
style: {
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 600,
colors: ['#fff']
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 2,
opacity: 0.45
}
},
legend: {
show: true,
position: 'bottom',
offsetY: 10,
markers: { size: 4, strokeWidth: 0 },
itemMargin: { horizontal: 15, vertical: 5 },
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 400,
labels: { colors: legendColor, useSeriesColors: false },
formatter: function (seriesName, options) {
const value = options.w.globals.series[options.seriesIndex];
const percentage = totalLandOwnershipStatusFarmers > 0 ? ((value / totalLandOwnershipStatusFarmers) * 100).toFixed(1) : '0.0';
return `${seriesName}: ${value} (${percentage}%)`;
}
},
tooltip: {
theme: 'dark',
y: {
formatter: function (value) {
const percentage = totalLandOwnershipStatusFarmers > 0 ? ((value / totalLandOwnershipStatusFarmers) * 100).toFixed(1) : '0.0';
return `${value} petani (${percentage}%)`;
}
}
},
plotOptions: {
pie: {
dataLabels: {
offset: -14,
minAngleToShowLabel: 8
}
}
},
responsive: [
{
breakpoint: 1025,
options: {
chart: { height: 380 }
}
}
]
};
new ApexCharts(landOwnershipStatusChartEl, landOwnershipStatusChartConfig).render();
}
const incomeChartEl = document.querySelector('#incomeChart');
if (incomeChartEl) {
const incomeData = [
Number(@json($data['penghasilan']['kurang_1'] ?? 0)),
Number(@json($data['penghasilan']['1_3'] ?? 0)),
Number(@json($data['penghasilan']['3_5'] ?? 0)),
Number(@json($data['penghasilan']['5_10'] ?? 0)),
Number(@json($data['penghasilan']['lebih_10'] ?? 0))
];
const incomeFilters = ['kurang_1', '1_3', '3_5', '5_10', 'lebih_10'];
const totalIncomeFarmers = incomeData.reduce((total, value) => total + value, 0);
const incomeChartConfig = {
chart: {
height: 330,
type: 'bar',
parentHeightOffset: 0,
toolbar: { show: false },
events: {
dataPointSelection: function (event, chartContext, options) {
const index = options.dataPointIndex;
if (index < 0) {
return;
}
applyFarmerFilter('penghasilan', incomeFilters[index]);
}
}
},
series: [{ name: 'Petani', data: incomeData }],
colors: [config.colors.info],
plotOptions: {
bar: {
horizontal: true,
borderRadius: 4,
barHeight: '50%',
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
textAnchor: 'start',
offsetX: 6,
style: {
fontSize: '12px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
const percentage = totalIncomeFarmers > 0 ? ((value / totalIncomeFarmers) * 100).toFixed(1) : '0.0';
return `${value} (${percentage}%)`;
}
},
xaxis: {
categories: ['< Rp1 juta', 'Rp1 - 3 juta', 'Rp3 - 5 juta', 'Rp5 - 10 juta', '> Rp10 juta'],
labels: {
style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily },
formatter: function (value) {
return Math.floor(value);
}
},
axisBorder: { show: false },
axisTicks: { show: false },
title: {
text: 'Petani',
style: { color: legendColor, fontSize: '12px', fontFamily: fontFamily, fontWeight: 400 }
}
},
yaxis: {
labels: { style: { colors: legendColor, fontSize: '12px', fontFamily: fontFamily } }
},
grid: {
borderColor: config.colors.borderColor,
strokeDashArray: 5,
xaxis: { lines: { show: true } },
yaxis: { lines: { show: false } },
padding: { right: 55 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
const percentage = totalIncomeFarmers > 0 ? ((value / totalIncomeFarmers) * 100).toFixed(1) : '0.0';
return `
<div style="background:${config.colors.info}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
${label}: <strong>${value} petani (${percentage}%)</strong>
</div>
`;
}
}
};
new ApexCharts(incomeChartEl, incomeChartConfig).render();
}
loadFarmerTable(1);
document.addEventListener(
'click',
function (event) {
const row = event.target.closest('.farmer-row');
if (!row) { return; }
const petaniId = row.dataset.id;
if (!petaniId) { return; }
window.location.href = `${farmerProfileUrl}/${petaniId}`;
}
);
});
</script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script>
const wilayah = @json($wilayah);
function fillSelect($select, data, valueField, textField, placeholder) {
$select.empty().append(
`<option value="">${placeholder}</option>`
);
data.forEach(function (item) {
$select.append(
`<option value="${item[valueField]}">${item[textField]}</option>`
);
});
$select.trigger('change.select2');
}
$('#filter-provinsi').select2().on('change', function () {
console.log('change provinsi');
let provinsiId = $(this).val();
fillSelect($('#filter-kabkota'), [], 'id', 'nama', 'Semua Kabupaten/Kota');
fillSelect($('#filter-kecamatan'), [], 'id', 'nama', 'Semua Kecamatan');
fillSelect($('#filter-desa'), [], 'id', 'nama', 'Semua Desa/Kelurahan');
if (!provinsiId) {
$('#filter-kabkota, #filter-kecamatan, #filter-desa')
.prop('disabled', true);
return;
}
let provinsi = wilayah.find(x => x.id == provinsiId);
if (!provinsi) {
return;
}
let kabupaten = provinsi.kabupaten || [];
fillSelect($('#filter-kabkota'), kabupaten, 'id', 'nama', 'Semua Kabupaten/Kota');
$('#filter-kabkota').prop('disabled', false);
$('#filter-kecamatan, #filter-desa').prop('disabled', true);
let defaultKabupaten = $('#filter-kabkota').data('default');
$('#filter-kabkota').removeData('default');
if (defaultKabupaten) {
$('#filter-kabkota').val(defaultKabupaten).trigger('change');
}
});
$('#filter-kabkota').select2().on('change', function () {
let kabupatenId = $(this).val();
fillSelect($('#filter-kecamatan'), [], 'id', 'nama', 'Semua Kecamatan');
fillSelect($('#filter-desa'), [], 'id', 'nama', 'Semua Desa/Kelurahan');
$('#filter-desa').prop('disabled', true);
if (!kabupatenId) {
$('#filter-kecamatan').prop('disabled', true);
return;
}
// Cari kabupaten di semua provinsi
let kabupaten = null;
wilayah.forEach(function (provinsi) {
let found = (provinsi.kabupaten || []).find(
x => x.id == kabupatenId
);
if (found) {
kabupaten = found;
}
});
if (!kabupaten) {
return;
}
let kecamatan = kabupaten.kecamatan || [];
fillSelect($('#filter-kecamatan'), kecamatan, 'id', 'nama', 'Semua Kecamatan');
$('#filter-kecamatan').prop('disabled', false);
let defaultKecamatan = $('#filter-kecamatan').data('default');
$('#filter-kecamatan').removeData('default');
if (defaultKecamatan) {
console.log('defaultKecamatan', defaultKecamatan);
$('#filter-kecamatan').val(defaultKecamatan).trigger('change');
}
});
$('#filter-kecamatan').select2().on('change', function () {
let kecamatanId = $(this).val();
fillSelect($('#filter-desa'), [], 'id', 'nama', 'Semua Desa/Kelurahan');
if (!kecamatanId) {
$('#filter-desa').prop('disabled', true);
return;
}
let kecamatan = null;
wilayah.forEach(function (provinsi) {
(provinsi.kabupaten || []).forEach(function (kabupaten) {
let found = (kabupaten.kecamatan || []).find(
x => x.id == kecamatanId
);
if (found) {
kecamatan = found;
}
});
});
if (!kecamatan) {
return;
}
let desa = kecamatan.desa || [];
fillSelect($('#filter-desa'), desa, 'id', 'nama', 'Semua Desa/Kelurahan');
$('#filter-desa').prop('disabled', false);
let defaultDesa = $('#filter-desa').data('default');
$('#filter-desa').removeData('default');
if (defaultDesa) {
console.log('default desa', defaultDesa);
$('#filter-desa').val(defaultDesa).trigger('change');
}
});
$('#filter-desa, #filter-segmen, #filter-komoditas').select2();
let defaultProvinsi = $('#filter-provinsi').data('default');
if (defaultProvinsi) {
$('#filter-provinsi')
.val(defaultProvinsi)
.trigger('change');
}
$('#btn-reset-dashboard-filter').on('click', function () {
const $form = $('#form-filter');
const action = $form.attr('action') || window.location.pathname;
const url = new URL(action, window.location.origin);
const activeTab = $('#profile-filter-tab').val();
if (activeTab) {
url.searchParams.set('tab', activeTab);
}
window.location.href = url.toString();
});
$('.data-table').DataTable({
"paging": false,
"lengthChange": true,
"searching": false,
"ordering": false,
"info": false,
"autoWidth": false,
"responsive": false,
"scrollX": true
});
let selectedDesa = null;
let selectedKomoditas = null;
let selectedSegmen = null;
// Chart Dimensi dan Komponen Kesiapan Adopsi
let cardColor, labelColor, fontFamily, headingColor, shadeColor, legendColor, borderColor, barBgColor;
if (isDarkStyle) {
barBgColor = '#3d4157';
shadeColor = 'dark';
} else {
barBgColor = '#efeef0';
shadeColor = '';
}
cardColor = config.colors.cardColor;
labelColor = config.colors.textMuted;
legendColor = config.colors.bodyColor;
borderColor = config.colors.borderColor;
headingColor = config.colors.headingColor;
</script>
<script>
$(document).ready(function () {
$.ajax({
url: '{{ route('dashboard.indeks-petani.summary') }}',
type: 'GET',
data: dashboardFilterParams.toString(),
dataType: 'json',
success: function (data) {
$('#count-petani').text(data.petani);
$('#count-komoditas').text(data.komoditas);
$('#sum-lahan').text(new Intl.NumberFormat('id-ID', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(data.sum_lahan));
$('#avg-lahan').text(new Intl.NumberFormat('id-ID', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(data.avg_lahan));
$('#count-desa').text(data.desa);
$('#count-kecamatan').text(data.kecamatan);
$('#avg-skor').text(new Intl.NumberFormat('id-ID', {minimumFractionDigits: 2, maximumFractionDigits: 2}).format(data.skor));
$('#avg-segmen').text(data.segmen);
}
});
})
</script>
<script>
$(document).ready(function () {
const segments = @json($segments);
// Chart Segmentasi Petani
var chartSegmentasiPetani;
let distributionLoading = false;
function loadChartSegmentasiPetani() {
const container = document.querySelector('#chart-segmentasi-petani');
if (!container.clientWidth || distributionLoading || chartSegmentasiPetani) return;
distributionLoading = true;
$.ajax({
url: '{{ route("dashboard.indeks-petani.segmen") }}',
type: 'GET',
data: dashboardFilterParams.toString(),
dataType: 'json',
success: function(response) {
if (chartSegmentasiPetani) {
chartSegmentasiPetani.destroy();
}
var optionsSegmentasiPetani = {
series: response.series.map((value) => Number(value) || 0),
chart: {
type: 'donut',
width: '100%',
height: 280,
events: {
dataPointSelection: function(event, chartContext, config) {
const segmen =
chartContext.w.config.labels[
config.dataPointIndex
];
console.log('Segmen dipilih:', segmen);
// Set filter segmen
selectedSegmen = segmen;
}
}
},
labels: @json(collect($segments)->pluck('name')->values()),
colors: @json($colors),
plotOptions: {
pie: {
borderRadius: 12,
spacing: 5,
donut: {
size: '68%',
labels: {
show: true,
total: {
show: true,
label: 'Petani',
},
},
},
},
},
stroke: {
width: 0,
},
dataLabels: {
enabled: false,
},
legend: {
show: false,
},
responsive: [
{
breakpoint: 360,
options: {
chart: {
width: 220,
},
},
},
],
};
chartSegmentasiPetani = new ApexCharts(
document.querySelector('#chart-segmentasi-petani'),
optionsSegmentasiPetani
);
chartSegmentasiPetani.render().catch(function (error) {
chartSegmentasiPetani = null;
console.error('Gagal merender distribusi indeks:', error);
});
let html = '';
segments.forEach(function(segment, index) {
const total = response.series[index] ?? 0;
const persen = response.persentase[index] ?? 0;
html += `
<tr>
<td>
<div class="d-flex justify-content-start align-items-center gap-2">
<div class="flex-shrink-0">
<i class="icon-base ti ${segment.icon} icon-lg text-${segment.color}"></i>
</div>
<span class="text-${segment.color}">
${segment.name}
</span>
</div>
</td>
<td class="text-end pe-0">
${Number(total).toLocaleString('id-ID')}
</td>
<td class="text-end pe-0">
${Number(persen).toLocaleString('id-ID', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}%
</td>
</tr>
`;
});
$('#table-segmentasi-petani').html(html);
},
error: function(xhr) {
distributionLoading = false;
console.error('Gagal mengambil data segmentasi:', xhr);
}
});
}
document.addEventListener('shown.bs.tab', loadChartSegmentasiPetani);
document.getElementById('indeks-segmentasi-charts').addEventListener('shown.bs.collapse', loadChartSegmentasiPetani);
loadChartSegmentasiPetani();
})
</script>
<script>
$(document).ready(function () {
var optionsSegmentasiPetaniByKomoditas = {
series: [],
colors: @json($colors),
chart: {
type: 'bar',
height: 260,
stacked: true,
parentHeightOffset: 0,
toolbar: {
show: false
},
events: {
dataPointSelection: function(event, chartContext, config) {
const komoditas =
config.w.config.xaxis.categories[config.dataPointIndex];
console.log('Komoditas dipilih:', komoditas);
selectedKomoditas = komoditas;
}
}
},
plotOptions: {
bar: {
horizontal: true,
},
},
stroke: {
width: 1,
colors: ['#fff'],
},
title: {
show: false,
},
xaxis: {
categories: [],
labels: {
trim: true,
hideOverlappingLabels: true,
},
},
yaxis: {
labels: {
maxWidth: 118,
style: {
fontSize: '12px',
},
},
},
tooltip: {
y: {
formatter: function(val) {
return val + ' petani';
},
},
},
fill: {
opacity: 1,
},
legend: {
position: 'top',
horizontalAlign: 'left',
offsetX: 0,
fontSize: '12px',
itemMargin: {
horizontal: 8,
vertical: 2,
},
},
};
let chartSegmentasiPetaniByKomoditas;
function loadChartSegmentasiKomoditas() {
$.ajax({
url: "{{ route('dashboard.indeks-petani.segmen.komoditas') }}",
type: "GET",
data: dashboardFilterParams.toString(),
dataType: "json",
beforeSend: function() {
// optional
// tampilkan loading
},
success: function(response) {
chartSegmentasiPetaniByKomoditas.updateOptions({
xaxis: { categories: response.categories },
series: response.series.map((series) => ({
...series,
data: series.data.map((value) => Number(value) || 0)
}))
});
},
error: function(xhr) {
console.error(
'Gagal mengambil data chart:',
xhr.responseText
);
}
});
}
function initializeCommodityDistribution() {
const container = document.querySelector('#chart-segmentasi-petani-komoditas');
if (!container.clientWidth || chartSegmentasiPetaniByKomoditas) return;
chartSegmentasiPetaniByKomoditas = new ApexCharts(container, optionsSegmentasiPetaniByKomoditas);
chartSegmentasiPetaniByKomoditas.render().then(loadChartSegmentasiKomoditas).catch(function (error) {
chartSegmentasiPetaniByKomoditas = null;
console.error('Gagal merender distribusi indeks per komoditas:', error);
});
}
document.addEventListener('shown.bs.tab', initializeCommodityDistribution);
document.getElementById('indeks-segmentasi-charts').addEventListener('shown.bs.collapse', initializeCommodityDistribution);
initializeCommodityDistribution();
})
</script>
<script>
function formatReadinessScore(value) {
if (value === null || value === undefined || value === '' || !Number.isFinite(Number(value))) return '—';
return Number(value).toLocaleString('id-ID', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function spaceReadinessRadarLabels(chart) {
chart.el.querySelectorAll('.apexcharts-radar-series .apexcharts-xaxis-label').forEach(function (label) {
var x = Number(label.getAttribute('x')) || 0;
var y = Number(label.getAttribute('y')) || 0;
var dx = Math.abs(x) > 10 ? (x > 0 ? 16 : -16) : 0;
var dy = Math.abs(x) <= 10 ? (y > 0 ? 20 : -20) : 0;
label.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
});
var categoryBoxes = Array.from(chart.el.querySelectorAll('.apexcharts-radar-series .apexcharts-xaxis-label'), function (label) { return label.getBoundingClientRect(); });
var scoreLabels = chart.el.querySelectorAll('.apexcharts-datalabels .apexcharts-datalabel');
scoreLabels.forEach(function (label, index) {
var angle = (2 * Math.PI * index / chart.w.config.labels.length) - Math.PI / 2;
var x = Number(label.getAttribute('x')) || 0;
var y = Number(label.getAttribute('y')) || 0;
var radius = Math.sqrt(x * x + y * y);
var distance = Math.max(18, 30 - radius);
var dx = Math.cos(angle) * distance;
var dy = Math.sin(angle) * distance;
label.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
label.setAttribute('text-anchor', Math.abs(dx) < 5 ? 'middle' : (dx > 0 ? 'start' : 'end'));
label.style.paintOrder = 'stroke';
label.style.stroke = config.colors.cardColor;
label.style.strokeWidth = '3px';
label.style.strokeLinejoin = 'round';
label.style.pointerEvents = 'none';
// ApexCharts moves the score group outside the series after rendering.
// Keep a clear gap from category labels even when a score reaches 5.
for (var attempt = 0; attempt < 7; attempt++) {
var scoreBox = label.getBoundingClientRect();
var overlapsCategory = categoryBoxes.some(function (box) {
return scoreBox.left < box.right + 10 && scoreBox.right > box.left - 10 &&
scoreBox.top < box.bottom + 10 && scoreBox.bottom > box.top - 10;
});
if (!overlapsCategory || distance <= 12) break;
distance = Math.max(12, distance - 4);
dx = Math.cos(angle) * distance;
dy = Math.sin(angle) * distance;
label.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
}
});
}
function readinessRadarOptions(title, labels, categories, pointColors) {
return {
chart: {
height: 420, type: 'radar', toolbar: { show: false }, animations: { enabled: false },
events: { mounted: spaceReadinessRadarLabels, updated: spaceReadinessRadarLabels }
},
series: [{ name: title, data: [] }],
labels: labels,
plotOptions: { radar: { polygons: { strokeColors: borderColor, connectorColors: borderColor } } },
stroke: { width: 2 },
legend: { show: false },
colors: [config.colors.primary],
fill: { opacity: 0.12 },
markers: {
size: 5, strokeWidth: 2, strokeColors: config.colors.cardColor, hover: { sizeOffset: 2 },
discrete: pointColors.map(function (color, index) {
return { seriesIndex: 0, dataPointIndex: index, fillColor: color, strokeColor: config.colors.cardColor, size: 6 };
})
},
dataLabels: {
enabled: true,
formatter: formatReadinessScore,
offsetX: 0, offsetY: 0,
style: { fontSize: '12px', fontFamily: fontFamily, fontWeight: 600, colors: [config.colors.headingColor] },
background: { enabled: false },
dropShadow: { enabled: false }
},
tooltip: { enabled: false },
grid: { show: false, padding: { top: 28, bottom: 24 } },
xaxis: { categories: categories, labels: { show: true, style: { colors: labels.map(function () { return legendColor; }), fontSize: '13px', fontFamily: fontFamily, fontWeight: 500 } } },
yaxis: {
show: false, min: 0, max: 5, tickAmount: 5,
labels: { show: false }
},
responsive: [{ breakpoint: 769, options: { chart: { height: 420 }, xaxis: { labels: { style: { fontSize: '11px' } } } } }]
};
}
function updateReadinessScoreLegend(chart, containerId, values, colors) {
var container = document.getElementById(containerId);
var fragment = document.createDocumentFragment();
chart.w.config.labels.forEach(function (label, index) {
var row = document.createElement('div');
row.className = 'readiness-score-row';
var dot = document.createElement('span');
dot.className = 'readiness-score-dot';
dot.style.backgroundColor = colors[index];
dot.setAttribute('aria-hidden', 'true');
var name = document.createElement('span');
name.className = 'readiness-score-name';
name.textContent = label;
var score = document.createElement('strong');
score.className = 'readiness-score-value';
score.textContent = formatReadinessScore(values[index]);
row.append(dot, name, score);
fragment.appendChild(row);
});
container.replaceChildren(fragment);
}
</script>
<script>
$(document).ready(function () {
var palette = [config.colors.success, config.colors.primary, config.colors.warning, config.colors.info, config.colors.danger, config.colors.secondary];
[
{ id: 'dimensi', key: 'radarDimensi', url: "{{ route('dashboard.indeks-petani.dimensi') }}", title: "Dimensi Kesiapan Adopsi", labels: ["Sistem & Praktik Budidaya","Sosial Ekonomi Rumah Tangga","Rantai Nilai & Akses Pasar"], categories: [["Sistem & Praktik","Budidaya"],["Sosial Ekonomi","Rumah Tangga"],["Rantai Nilai &","Akses Pasar"]] },
{ id: 'komponen', key: 'radarKomponen', url: "{{ route('dashboard.indeks-petani.komponen') }}", title: "Komponen Kesiapan Adopsi", labels: ["Motivasi & Kapasitas Adopsi","Kondisi Ekonomi Rumah Tangga","Praktik Budidaya & GAP","Keterlibatan Kelembagaan","Akses & Kebebasan Keuangan","Rantai Nilai & Akses Pasar"], categories: [["Motivasi & Kapasitas","Adopsi"],["Kondisi Ekonomi","Rumah Tangga"],["Praktik Budidaya","& GAP"],["Keterlibatan","Kelembagaan"],["Akses & Kebebasan","Keuangan"],["Rantai Nilai &","Akses Pasar"]] }
].forEach(function (definition) {
var pointColors = palette.slice(0, definition.labels.length);
var chart = new ApexCharts(document.getElementById('chart-' + definition.id), readinessRadarOptions(definition.title, definition.labels, definition.categories, pointColors));
chart.render().then(function () {
return $.ajax({
url: definition.url,
type: 'GET',
data: dashboardFilterParams.toString(),
dataType: 'json'
}).then(function (response) {
var values = response[definition.key];
if (!Array.isArray(values)) throw new Error('Data radar tidak valid');
updateReadinessScoreLegend(chart, 'chart-' + definition.id + '-scores', values, pointColors);
return chart.updateSeries([{ name: definition.title, data: values }]);
});
}).catch(function (error) {
document.getElementById('chart-' + definition.id + '-scores').textContent = 'Informasi skor belum dapat dimuat.';
console.error('Gagal memuat radar ' + definition.id + ':', error);
});
});
});
</script>
<script>
$(document).ready(function () {
// Map Sebaran Petani
var map = L.map('map-sebaran-petani').setView([-0.789, 113.921], 5);
const mapShell = document.getElementById('farmer-map-shell');
const fullscreenButton = document.getElementById('farmer-map-fullscreen');
if (!document.fullscreenEnabled || !mapShell.requestFullscreen) {
fullscreenButton.hidden = true;
}
fullscreenButton.addEventListener('click', async function () {
try {
if (document.fullscreenElement === mapShell) {
await document.exitFullscreen();
} else {
await mapShell.requestFullscreen();
}
} catch (error) {
console.error('Tidak dapat membuka peta layar penuh:', error);
}
});
document.addEventListener('fullscreenchange', function () {
const isFullscreen = document.fullscreenElement === mapShell;
fullscreenButton.innerHTML = '<i class="icon-base ti ' + (isFullscreen ? 'tabler-minimize' : 'tabler-maximize') + '"></i>';
const label = isFullscreen ? 'Keluar dari layar penuh' : 'Buka peta layar penuh';
fullscreenButton.setAttribute('aria-label', label);
fullscreenButton.title = label;
requestAnimationFrame(focusMapData);
});
document.getElementById('indeks-wilayah-charts').addEventListener('shown.bs.collapse', function () {
focusMapData();
if (window.jQuery?.fn?.dataTable) {
window.jQuery.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
}
});
document.addEventListener('shown.bs.tab', function (event) {
if (event.target.getAttribute('data-bs-target') === '#profile-tab-indeks-petani') {
focusMapData();
}
});
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
const warnaSegmen = {
@foreach($segments as $segment)
"{{ $segment['name'] }}": "var(--bs-{{ $segment['color'] }})",
@endforeach
};
let selectedDesa = null;
const group = L.featureGroup().addTo(map);
function focusMapData() {
const container = map.getContainer();
if (!container.clientWidth || !container.clientHeight) {
return;
}
map.invalidateSize();
const bounds = group.getBounds();
if (bounds.isValid()) {
map.fitBounds(bounds, {
padding: [32, 32],
maxZoom: 14,
animate: false
});
}
}
function loadMapDesa() {
$.ajax({
url: "{{ route('dashboard.indeks-petani.map') }}",
type: "GET",
data: dashboardFilterParams.toString(),
beforeSend: function () {
// Optional
// $('#map-loading').show();
},
success: function (desaData) {
group.clearLayers();
if (!desaData || desaData.length === 0) {
return;
}
desaData.forEach(function (desa) {
if (!desa.path) {
return;
}
let coordinates;
try {
coordinates = typeof desa.path === 'string'
? JSON.parse(desa.path)
: desa.path;
} catch (error) {
console.error('Path polygon tidak valid:', desa);
return;
}
const polygon = L.polygon(coordinates, {
color: '#666',
weight: 1,
fillColor:
warnaSegmen[desa.segmentasi?.segmen] || '#ccc',
fillOpacity: 0.7
});
polygon.desaId = desa.desa_kelurahan_id;
polygon.bindPopup(`
<b>Desa/Kel.: ${desa.desa_kelurahan ?? '-'}</b><br>
Kec.: ${desa.kecamatan ?? '-'}<br>
Kab.: ${desa.kabupaten ?? '-'}<br>
<hr>
Petani: ${desa.segmentasi?.jumlah_petani ?? 0}<br>
Indeks:
${Number(desa.segmentasi?.skor ?? 0).toFixed(2)}
<br>
Segmen:
<b>${desa.segmentasi?.segmen ?? '-'}</b>
<br>
Segmen Dominan:
<b>${desa.segmentasi?.segmen_dominan ?? '-'}</b>
(${desa.segmentasi?.jumlah_dominan ?? 0} petani)
`);
polygon.addTo(group);
});
requestAnimationFrame(focusMapData);
},
error: function (xhr) {
console.error(
'Gagal mengambil data map:',
xhr.responseText
);
},
complete: function () {
// Optional
// $('#map-loading').hide();
}
});
}
loadMapDesa();
})
</script>
<script>
$(document).ready(function () {
var tableDesa = $('#table-desa').DataTable({
"paging": true,
"pageLength": 5,
"lengthChange": false,
"searching": false,
"ordering": true,
"info": false,
"autoWidth": false,
"responsive": false,
"scrollX": true,
"serverSide": false,
"processing": true,
"ajax": {
"url": "{{ route('dashboard.indeks-petani.desa') }}",
"type": "GET",
"data": function (data) {
data = dashboardFilterParams.toString();
return data;
},
"dataSrc": function(json) {
return json;
},
"dataType": "json"
},
columns: [
{ data: 'desa', render: $.fn.dataTable.render.text() },
{
data: 'jumlah_petani', className: 'text-end',
render: function (data, type) {
return type === 'display' ? Number(data).toLocaleString('id-ID') : Number(data);
}
}
].concat(['pemula', 'perintis', 'pemerhati', 'penggerak', 'belum_lengkap'].map(function (segment) {
return {
data: 'segments.' + segment,
className: 'text-end text-nowrap',
render: function (data, type) {
var count = Number(data && data.count) || 0;
if (type !== 'display') return count;
var percentage = Number(data && data.percentage || 0).toLocaleString('id-ID', { maximumFractionDigits: 2 });
return '<span class="fw-semibold text-' + (data && data.color || 'secondary') + '">' + count.toLocaleString('id-ID') +
'</span> <span class="text-body-secondary small">(' + percentage + '%)</span>';
}
};
}))
});
})
</script>
<script>
$(document).ready(function () {
var tablePetani = $('#table-petani').DataTable({
dom: 'rt<"card-footer d-flex flex-wrap justify-content-between align-items-center gap-3"ip>',
pagingType: 'full_numbers',
language: {
info: 'Menampilkan _START_ - _END_ dari _TOTAL_ petani',
infoEmpty: 'Menampilkan 0 - 0 dari 0 petani',
infoFiltered: '',
emptyTable: 'Tidak ada data petani sesuai filter.',
zeroRecords: 'Tidak ada data petani sesuai filter.',
paginate: {
first: '<i class="icon-base ti tabler-chevrons-left"></i>',
previous: '<i class="icon-base ti tabler-chevron-left"></i>',
next: '<i class="icon-base ti tabler-chevron-right"></i>',
last: '<i class="icon-base ti tabler-chevrons-right"></i>'
}
},
drawCallback: function () {
const info = this.api().page.info();
$('#indexFarmerTotalBadge').text(info.recordsDisplay + ' petani');
},
"paging": true,
"pageLength": 10,
"lengthChange": false,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": false,
"responsive": false,
"scrollX": true,
"serverSide": false,
"processing": true,
"ajax": {
"url": "{{ route('dashboard.indeks-petani.data') }}",
"type": "GET",
"data": function (data) {
data = dashboardFilterParams.toString();
return data;
},
"dataSrc": function(json) {
return json;
},
"dataType": "json"
},
columns: [
{
data: 'nama',
name: 'nama',
className: 'pe-0'
},
{
data: 'desa',
name: 'desa',
className: 'pe-0'
},
{
data: 'komoditas',
name: 'komoditas',
className: 'pe-0'
},
{
data: 'luas_lahan',
name: 'luas_lahan',
className: 'text-end pe-0',
render: function(data) {
return Number(data ?? 0).toLocaleString('id-ID', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
},
{
data: 'skor',
name: 'skor',
className: 'text-end pe-0',
render: function(data) {
if (data === null || data === '') {
return '';
}
return Number(data).toLocaleString('id-ID', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
},
{
data: null,
name: 'segmen',
className: 'pe-0',
orderable: false,
render: function(data, type) {
if (type !== 'display') {
return data.segmen ?? '';
}
if (!data.skor || !data.segmen) {
return '';
}
return `
<span class="badge bg-${data.color ?? 'secondary'}">
${data.segmen}
</span>
`;
}
}
],
order: [
[0, 'asc']
],
createdRow: function(row, data) {
$(row)
.addClass('clickable-row')
.attr('data-url', data.url);
}
});
$('#indexFarmerSearch').on('input', function () { tablePetani.search(this.value).draw(); });
tablePetani.on('xhr.dt', function (event, settings, rows) {
if (!Array.isArray(rows)) return;
const fields = { 1: 'desa', 2: 'komoditas', 5: 'segmen' };
document.querySelectorAll('.index-table-filter').forEach((select) => {
const placeholder = select.options[0].text;
select.replaceChildren(new Option(placeholder, ''));
const values = [...new Set(rows.map((row) => row[fields[select.dataset.column]]).filter(Boolean))];
values.sort((a, b) => a.localeCompare(b, 'id')).forEach((value) => {
select.add(new Option(value, value));
});
});
});
$('#applyIndexTableFilter').on('click', function () {
$('.index-table-filter').each(function () {
const value = $.fn.dataTable.util.escapeRegex(this.value);
tablePetani.column(Number(this.dataset.column)).search(value ? '^' + value + '$' : '', true, false);
});
tablePetani.draw();
});
$('#btn-reset-filter').on('click', function () {
$('.index-table-filter').val('');
$('#indexFarmerSearch').val('');
selectedDesa = null;
selectedKomoditas = null;
selectedSegmen = null;
tablePetani.search('').columns().search('').draw();
});
$('#table-petani tbody').on('click', 'tr.clickable-row', function() {
const url = $(this).data('url');
if (url) {
window.location.href = url;
}
});
})
</script>
<script>
document.addEventListener('shown.bs.tab', function (event) {
const filterTab = document.getElementById('profile-filter-tab');
const target = event.target?.getAttribute('data-bs-target');
if (filterTab && target === '#profile-tab-demografi-petani') {
filterTab.value = 'demografi-petani';
}
if (filterTab && target === '#profile-tab-indeks-petani') {
filterTab.value = 'indeks-petani';
}
if (event.target?.getAttribute('data-bs-target') === '#profile-tab-indeks-petani') {
setTimeout(function () {
if (window.jQuery?.fn?.dataTable) {
window.jQuery.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
}
window.dispatchEvent(new Event('resize'));
}, 150);
}
});
</script>
<script>
document.querySelectorAll('.chart-group-panel').forEach((panel) => {
const button = document.querySelector('[aria-controls="' + panel.id + '"]');
panel.addEventListener('hidden.bs.collapse', function (event) {
if (event.target !== panel) return;
button.textContent = 'Tampilkan Chart';
});
panel.addEventListener('shown.bs.collapse', function (event) {
if (event.target !== panel) return;
button.textContent = 'Sembunyikan Chart';
window.dispatchEvent(new Event('resize'));
});
});
</script>
@endpush