422 lines
18 KiB
Plaintext
422 lines
18 KiB
Plaintext
@{
|
|
ViewData["Title"] = "Capaian BPS RW DKI Jakarta";
|
|
}
|
|
|
|
<div class="breadcrumbs text-sm">
|
|
<ul>
|
|
<li class="text-gray-500"><a>Laporan</a></li>
|
|
<li>Capaian BPS RW DKI Jakarta</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
<div class="prose">
|
|
<h3 class="mb-2">Capaian BPS RW DKI Jakarta</h3>
|
|
</div>
|
|
<div class="flex justify-end items-center gap-2">
|
|
<span class="whitespace-nowrap text-sm font-medium text-gray-700">Filter Bulan:</span>
|
|
<input type="month" id="filterBulan" class="px-4 py-2 border bg-white rounded-md w-[180px] sm:w-[200px]"
|
|
min="2020-01" max="@DateTime.Now.ToString("yyyy-MM")" value="@DateTime.Now.ToString("yyyy-MM")" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="h-6"></div>
|
|
|
|
<!-- Tingkat Kepatuhan -->
|
|
<div class="bg-white rounded-sm shadow p-6">
|
|
<h3 class="text-lg font-semibold mb-4">Tingkat Kepatuhan dalam Menjalankan Instruksi Kepala Dinas</h3>
|
|
|
|
<div class="border border-gray-400 rounded-sm p-4 min-h-80 grid grid-cols-1 md:grid-cols-3 gap-6 items-start">
|
|
<!-- Pie Chart Total -->
|
|
<div class="flex flex-col items-center col-span-3 md:col-span-1">
|
|
<h4 class="font-medium text-sm text-center" id="titleTotalPjlp">Kepatuhan PJLP Pendamping</h4>
|
|
<div class="w-full h-[250px] flex items-center justify-center mt-2">
|
|
<canvas id="pieTotal"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col items-start col-span-3 md:col-span-2">
|
|
<h4 class="font-medium text-sm text-left">Kepatuhan PJLP Pendamping</h4>
|
|
<div class="w-full h-[250px] mt-2">
|
|
<canvas id="barWilayah"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detail Kepatuhan Per Wilayah -->
|
|
<div class="bg-white rounded-sm shadow p-6 mt-6">
|
|
<h3 class="text-lg font-semibold mb-4">Detail Kepatuhan PJLP Pendamping</h3>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6" id="detailPjlpContainer">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Validasi SATPEL Oktober 2025 -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mt-6" id="satpelContainer"></div>
|
|
|
|
<!-- Capaian Program Pendampingan -->
|
|
<div class="bg-white rounded-sm shadow p-6 mt-6">
|
|
<h3 class="text-lg font-semibold">Capaian Program Pendampingan BPS RW</h3>
|
|
<p class="text-sm text-gray-600 mb-4" id="descPeriodeRumah">Capaian Rumah memilah dalam pelaksanaan instruksi Kepala
|
|
Dinas Lingkungan Hidup</p>
|
|
<div class="border border-gray-400 rounded-sm p-4 min-h-80 grid grid-cols-1 md:grid-cols-3 gap-6 items-start">
|
|
|
|
<div class="flex flex-col items-center col-span-1 w-full">
|
|
<h4 class="font-medium text-sm text-center" id="titleTargetRumah">Total Target Rumah Tangga Memilah</h4>
|
|
<div class="w-full h-[250px] flex items-center justify-center mt-2">
|
|
<canvas id="pieRumah"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col items-start col-span-2">
|
|
<div class="w-full h-[250px] mt-2">
|
|
<canvas id="barRumahWilayah"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detail Rumah Memilah -->
|
|
<div class="bg-white rounded-sm shadow p-6 mt-6">
|
|
<h3 class="text-lg font-semibold mb-4">Detail Jumlah Rumah Memilah</h3>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6" id="detailRumahContainer">
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/lib/chart.js/chart.umd.js"></script>
|
|
<script src="/lib/chartjs-plugin-datalabels/chartjs-plugin-datalabels.min.js"></script>
|
|
<script>
|
|
Chart.register(ChartDataLabels);
|
|
|
|
Chart.defaults.animation = { duration: 500, easing: 'easeOutQuart' };
|
|
Chart.defaults.animations = {
|
|
numbers: { duration: 500, easing: 'easeOutQuart' },
|
|
colors: { duration: 500, easing: 'easeOutQuart' }
|
|
};
|
|
|
|
let chartInstances = {
|
|
pieTotal: null,
|
|
barWilayah: null,
|
|
pieRumah: null,
|
|
barRumahWilayah: null,
|
|
piePerWilayah: {},
|
|
satpel: {},
|
|
detailRumah: {}
|
|
};
|
|
|
|
function updateCharts(data) {
|
|
if (chartInstances.pieTotal) chartInstances.pieTotal.destroy();
|
|
if (chartInstances.barWilayah) chartInstances.barWilayah.destroy();
|
|
if (chartInstances.pieRumah) chartInstances.pieRumah.destroy();
|
|
if (chartInstances.barRumahWilayah) chartInstances.barRumahWilayah.destroy();
|
|
|
|
Object.values(chartInstances.piePerWilayah).forEach(chart => chart && chart.destroy());
|
|
chartInstances.piePerWilayah = {};
|
|
|
|
Object.values(chartInstances.satpel).forEach(chart => chart && chart.destroy());
|
|
chartInstances.satpel = {};
|
|
|
|
Object.values(chartInstances.detailRumah).forEach(chart => chart && chart.destroy());
|
|
chartInstances.detailRumah = {};
|
|
|
|
renderCharts(data);
|
|
}
|
|
|
|
function renderCharts(data) {
|
|
document.getElementById('titleTargetRumah').textContent = `Total Target ${data.rumah.total} Rumah Tangga Memilah`;
|
|
document.getElementById('descPeriodeRumah').textContent = `Capaian Rumah memilah dalam pelaksanaan instruksi Kepala Dinas Lingkungan Hidup Periode ${data.periode}`;
|
|
|
|
// Pie Kepatuhan PJLP
|
|
chartInstances.pieTotal = new Chart(document.getElementById('pieTotal'), {
|
|
type: 'pie',
|
|
data: {
|
|
labels: ['Ceklis', 'Belum Ceklis'],
|
|
datasets: [{ data: [data.pieTotal.ceklis, data.pieTotal.belumCeklis], backgroundColor: ['#9FCE62', '#EF4444'] }]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
aspectRatio: 1,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: {
|
|
color: '#fff',
|
|
formatter: (val, ctx) => {
|
|
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
|
const percent = Math.round(val / total * 100);
|
|
|
|
return `${val} PJLP\n${percent}%`;
|
|
},
|
|
font: { weight: 'bold', size: 12 },
|
|
align: 'center',
|
|
anchor: 'center'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// bar Kepatuhan PJLP
|
|
chartInstances.barWilayah = new Chart(document.getElementById('barWilayah'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: data.barWilayah.labels,
|
|
datasets: [
|
|
{ label: 'Ceklis', data: data.barWilayah.ceklis, backgroundColor: '#9FCE62' },
|
|
{ label: 'Belum Ceklis', data: data.barWilayah.belumCeklis, backgroundColor: '#EF4444' },
|
|
{ label: 'Total', data: data.barWilayah.total, backgroundColor: '#C6C6C6' }
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 20
|
|
}
|
|
},
|
|
scales: { y: { beginAtZero: true } },
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: { anchor: 'end', align: 'top', color: '#444', font: { size: 10 } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Pie Detail Kepatuhan
|
|
const detailPjlpContainer = document.getElementById('detailPjlpContainer');
|
|
detailPjlpContainer.innerHTML = '';
|
|
|
|
data.piePerWilayah.forEach((item, i) => {
|
|
const wilayahId = item.wilayah.replace(/\s+/g, '_');
|
|
|
|
const card = document.createElement('div');
|
|
card.className = 'relative border border-gray-400 rounded-sm p-4 text-center space-y-2 h-[300px] flex flex-col items-center justify-between';
|
|
card.innerHTML = `
|
|
<h4 class="font-semibold text-sm leading-tight">${item.wilayah} : ${data.wilayahPjlp[i].jumlahPjlp} PJLP<br />Pendamping BPS RW</h4>
|
|
<div class="w-full h-[200px]">
|
|
<canvas id="pie_${wilayahId}"></canvas>
|
|
</div>
|
|
`;
|
|
detailPjlpContainer.appendChild(card);
|
|
|
|
// chart wilayah
|
|
const ctx = document.getElementById(`pie_${wilayahId}`);
|
|
if (ctx) {
|
|
chartInstances.piePerWilayah[wilayahId] = new Chart(ctx, {
|
|
type: 'pie',
|
|
data: {
|
|
labels: ['Ceklis', 'Belum Ceklis'],
|
|
datasets: [{
|
|
data: [item.ceklis, item.belumCeklis],
|
|
backgroundColor: ['#9FCE62', '#EF4444']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
aspectRatio: 1,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: {
|
|
color: '#fff',
|
|
formatter: (val, ctx) => {
|
|
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
|
return Math.round(val / total * 100) + '%';
|
|
},
|
|
font: { weight: 'bold', size: 12 }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Status Validasi
|
|
const satpelContainer = document.getElementById('satpelContainer');
|
|
satpelContainer.innerHTML = '';
|
|
|
|
data.satpel.forEach((item, i) => {
|
|
const card = document.createElement('div');
|
|
card.className = 'bg-base-100 rounded-sm p-4 space-y-4';
|
|
card.innerHTML = `
|
|
<div>
|
|
<h3 class="text-lg font-semibold">Status Validasi ${item.validator} ${data.periode}</h3>
|
|
<span class="text-sm text-gray-500">DKI Jakarta</span>
|
|
<div class="bg-white rounded-sm border border-gray-400 p-6 mt-6">
|
|
<div class="w-full h-[250px] flex items-center justify-center">
|
|
<canvas id="satpel_${i}"></canvas>
|
|
</div>
|
|
<div class="text-sm leading-relaxed text-gray-700">
|
|
Terhadap status validasi yang dilakukan ${item.validator}:<br />
|
|
Periode bulan ini terdapat <strong>${item.sudah.toLocaleString()}</strong> aktifitas PJLP pendamping BPS RW melakukan checklist pada sistem
|
|
informasi Dashboard BPS RW yang sudah divalidasi,
|
|
sedangkan <strong>${item.belum.toLocaleString()}</strong> aktifitas tidak tervalidasi
|
|
(Terlewat/diragukan kebenaran checklist).
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
satpelContainer.appendChild(card);
|
|
|
|
const ctx = document.getElementById(`satpel_${i}`);
|
|
if (ctx) {
|
|
chartInstances.satpel[i] = new Chart(ctx, {
|
|
type: 'pie',
|
|
data: {
|
|
labels: ['Sudah', 'Belum'],
|
|
datasets: [{
|
|
data: [item.sudah, item.belum],
|
|
backgroundColor: ['#93C5FD ', '#FACC15']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
aspectRatio: 1,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: {
|
|
color: '#fff',
|
|
formatter: (val, ctx) => {
|
|
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
|
return val.toLocaleString() + '\n' + Math.round(val / total * 100) + '%';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Pie Capaian Program
|
|
chartInstances.pieRumah = new Chart(document.getElementById('pieRumah'), {
|
|
type: 'pie',
|
|
data: { labels: ['Rumah Memilih Konsisten', 'Rumah Memilih Tidak Konsisten'], datasets: [{ data: [data.rumah.konsisten, data.rumah.tidakKonsisten], backgroundColor: ['#9FCE62', '#FDBA74'] }] },
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
aspectRatio: 1,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: {
|
|
color: '#fff',
|
|
formatter: (val, ctx) => {
|
|
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
|
return val + '\n' + Math.round(val / total * 100) + '%';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Bar Capaian Program
|
|
chartInstances.barRumahWilayah = new Chart(document.getElementById('barRumahWilayah'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: data.barRumah.labels,
|
|
datasets: [
|
|
{ label: 'Rumah Memilah Konsisten', data: data.barRumah.konsisten, backgroundColor: '#9FCE62' },
|
|
{ label: 'Rumah Memilih Tidak Konsisten', data: data.barRumah.tidakKonsisten, backgroundColor: '#FDBA74' },
|
|
{ label: 'Target', data: data.barRumah.target, backgroundColor: '#C6C6C6' }
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: {
|
|
top: 20
|
|
}
|
|
},
|
|
scales: { y: { beginAtZero: true } },
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: { anchor: 'end', align: 'top', color: '#444', font: { size: 10 } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// Pie Detail Rumah Memilah
|
|
const detailRumahContainer = document.getElementById('detailRumahContainer');
|
|
detailRumahContainer.innerHTML = '';
|
|
|
|
data.detailRumahPerWilayah.forEach((item, i) => {
|
|
const wilayahId = item.wilayah.replace(/\s+/g, '_');
|
|
|
|
const card = document.createElement('div');
|
|
card.className = 'border border-gray-400 rounded-sm p-4 space-y-4';
|
|
card.innerHTML = `
|
|
<h4 class="font-semibold text-sm text-center">${item.wilayah}</h4>
|
|
<div class="flex justify-center">
|
|
<div class="w-[200px] h-[200px]">
|
|
<canvas id="rm_${wilayahId}"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="text-sm text-gray-700 border border-gray-400 leading-relaxed mt-2 bg-gray-50 p-2 rounded">
|
|
Target Rumah Pemilah : ${item.target.toLocaleString()} <br />
|
|
Rumah Memilah Konsisten : ${item.konsisten.toLocaleString()} <br />
|
|
Rumah Memilah Tidak Konsisten : ${item.tidakKonsisten.toLocaleString()}
|
|
</div>
|
|
`;
|
|
detailRumahContainer.appendChild(card);
|
|
|
|
// Buat chart untuk wilayah ini
|
|
const ctx = document.getElementById(`rm_${wilayahId}`);
|
|
if (ctx) {
|
|
chartInstances.detailRumah[wilayahId] = new Chart(ctx, {
|
|
type: 'pie',
|
|
data: {
|
|
labels: ['Rumah Memilih Konsisten', 'Rumah Memilih Tidak Konsisten'],
|
|
datasets: [{
|
|
data: [item.konsisten, item.tidakKonsisten],
|
|
backgroundColor: ['#9FCE62', '#FDBA74']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
aspectRatio: 1,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: "circle" } },
|
|
datalabels: {
|
|
color: '#fff',
|
|
formatter: (val, ctx) => {
|
|
const total = ctx.dataset.data.reduce((a, b) => a + b, 0);
|
|
return Math.round(val / total * 100) + '%';
|
|
},
|
|
font: { weight: 'bold', size: 12 }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
const filterBulan = document.getElementById('filterBulan');
|
|
|
|
function loadDataByDate(date) {
|
|
const url = `/LaporanCapaian/GetData?date=${encodeURIComponent(date)}`;
|
|
fetch(url, {
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => updateCharts(data))
|
|
.catch(err => console.error('Gagal memuat data:', err));
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const defaultDate = filterBulan.value;
|
|
loadDataByDate(defaultDate);
|
|
});
|
|
|
|
filterBulan.addEventListener('change', () => {
|
|
const selectedDate = filterBulan.value;
|
|
loadDataByDate(selectedDate);
|
|
});
|
|
</script> |