Files
database-pertani-web/resources/views/dashboard/demografi-petani.blade.php
T
2026-08-11 15:13:28 +07:00

1325 lines
49 KiB
PHP

@extends('layout.main')
@push('styles')
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/apex-charts/apex-charts.css') }}" />
<style>
.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;
}
#ageGroupChart .apexcharts-tooltip,
#educationChart .apexcharts-tooltip,
#landAreaChart .apexcharts-tooltip,
#incomeChart .apexcharts-tooltip {
background: transparent !important;
border: none !important;
box-shadow: none !important;
overflow: visible !important;
}
</style>
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row g-6">
<div class="col-lg-4 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">
{{ $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-lg-2 col-sm-6">
<div class="card card-border-shadow-secondary 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-secondary"
><i class="icon-base ti tabler-gender-male icon-28px"></i
></span>
</div>
<h4 class="mb-0">{{ $data['petani_lakilaki'] }}</h4>
</div>
<p class="mb-1">Petani Laki-laki</p>
<p class="mb-0">
<span class="text-heading fw-medium me-2">{{ $data['total_petani'] > 0 ? round(($data['petani_lakilaki'] / $data['total_petani']) * 100) : 0 }}%</span>
<small class="text-body-secondary">Dari Total Petani</small>
</p>
</div>
</div>
</div>
<div class="col-lg-2 col-sm-6">
<div class="card card-border-shadow-danger 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-danger"
><i class="icon-base ti tabler-gender-female icon-28px"></i
></span>
</div>
<h4 class="mb-0">{{ $data['petani_perempuan'] }}</h4>
</div>
<p class="mb-1">Petani Perempuan</p>
<p class="mb-0">
<span class="text-heading fw-medium me-2">{{ $data['total_petani'] > 0 ? round(($data['petani_perempuan'] / $data['total_petani']) * 100) : 0 }}%</span>
<small class="text-body-secondary">Dari Total Petani</small>
</p>
</div>
</div>
</div>
-->
<div class="col-lg-4 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-plant icon-28px"></i>
</span>
</div>
<div>
<div class="d-flex align-items-baseline gap-2">
<h4 class="mb-0">
{{ $data['total_komoditas'] }}
</h4>
<small class="text-body-secondary">
komoditas
</small>
</div>
<p class="mb-0 mt-1">
Total Komoditas
</p>
<small class="text-body-secondary">
Komoditas utama yang dikelola petani
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-12">
<div class="card card-border-shadow-success h-100">
<div class="card-body">
<div class="d-flex align-items-center justify-content-between gap-4">
<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">
{{ 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 class="border-start ps-4 text-end">
<h5 class="mb-1">
{{ number_format($data['ratarata_luas_lahan'], 2) }}
<small class="text-body-secondary">
ha
</small>
</h5>
<small class="text-body-secondary">
Rata-rata
<br>
per Petani
</small>
</div>
</div>
</div>
</div>
</div>
<!--/ Card Border Shadow -->
<!-- Vehicles overview -->
<div class="col-xxl-4 col-lg-6">
<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-xxl-4 col-lg-6">
<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-xxl-4 col-lg-6">
<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 class="col-xxl-4 col-lg-6">
<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-xxl-4 col-lg-6">
<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-xxl-4 col-lg-6">
<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-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"
>
{{ $daftarPetani->total() }} 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"
id="resetFarmerFilter"
class="btn btn-label-secondary"
>
Reset
</button>
</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">
@foreach ($daftarPetani as $petani)
{{-- isi tabel kamu yang sekarang --}}
@endforeach
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<small class="text-body-secondary" id="farmerTableInfo">
Menampilkan
{{ $daftarPetani->firstItem() ?? 0 }}
-
{{ $daftarPetani->lastItem() ?? 0 }}
dari
{{ $daftarPetani->total() }}
petani
</small>
<div id="farmerPagination">
{{ $daftarPetani
->onEachSide(1)
->links('pagination::bootstrap-5')
}}
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="{{ asset('assets/vendor/libs/apex-charts/apexcharts.js') }}"></script>
<script>
'use strict';
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'));
let activeFilters = {
gender: null,
umur: null,
pendidikan: null,
komoditas: null,
luas_lahan: null,
penghasilan: null
};
let searchTimer = 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) {
const searchInput = document.querySelector('#farmerSearch');
const search = searchInput ? searchInput.value.trim() : '';
const params = new URLSearchParams();
params.set('page', page);
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',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
if (!response.ok) {
throw new Error(`HTTP Error ${response.status}`);
}
const result = await response.json();
renderFarmerTable(result);
} catch (error) {
console.error('Gagal memuat tabel petani:', error);
}
}
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>
<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 pagination-sm 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>
`;
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 += `</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;
}
loadFarmerTable(1);
}
function resetFarmerFilter() {
activeFilters = {
gender: null,
umur: null,
pendidikan: null,
komoditas: null,
luas_lahan: null,
penghasilan: null
};
const searchInput = document.querySelector('#farmerSearch');
if (searchInput) {
searchInput.value = '';
}
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: 'donut',
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: false },
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: {
donut: {
size: '75%',
labels: {
show: true,
value: {
fontSize: '38px',
fontFamily: fontFamily,
color: headingColor,
fontWeight: 500,
offsetY: -20,
formatter: function (value) {
return parseInt(value);
}
},
name: { offsetY: 30, fontFamily: fontFamily },
total: {
show: true,
showAlways: true,
fontSize: '15px',
fontFamily: fontFamily,
color: legendColor,
label: 'Total Petani',
formatter: function () {
return totalPetani;
}
}
}
}
}
},
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 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: [
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))
]
}
],
colors: [config.colors.primary],
plotOptions: {
bar: {
borderRadius: 4,
columnWidth: '45%',
distributed: false,
dataLabels: { position: 'top' }
}
},
dataLabels: {
enabled: true,
offsetY: -20,
style: {
fontSize: '13px',
fontFamily: fontFamily,
fontWeight: 500,
colors: [headingColor]
},
formatter: function (value) {
return value;
}
},
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: 20 }
},
legend: { show: false },
tooltip: {
custom: function ({ series, seriesIndex, dataPointIndex, w }) {
const value = series[seriesIndex][dataPointIndex];
const label = w.globals.labels[dataPointIndex];
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</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 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: 330,
type: 'donut',
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: false },
legend: {
show: true,
position: 'right',
horizontalAlign: 'center',
markers: { size: 5, strokeWidth: 0 },
itemMargin: { horizontal: 8, vertical: 7 },
fontSize: '12px',
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: {
donut: {
size: '68%',
labels: {
show: true,
value: {
fontSize: '30px',
fontFamily: fontFamily,
color: headingColor,
fontWeight: 500,
offsetY: -15,
formatter: function (value) {
return parseInt(value);
}
},
name: { offsetY: 25, fontFamily: fontFamily },
total: {
show: true,
showAlways: true,
fontSize: '13px',
fontFamily: fontFamily,
color: legendColor,
label: 'Petani',
formatter: function () {
return totalCommodityFarmers;
}
}
}
}
}
},
responsive: [
{
breakpoint: 1400,
options: {
chart: { height: 350 },
legend: { position: 'bottom' }
}
},
{
breakpoint: 576,
options: {
chart: { height: 330 },
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 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);
});
</script>
@endpush