sigd/resources/views/auth/dashboard-mitigation.blade.php

286 lines
9.4 KiB
PHP

@extends('layouts.master')
@section('content')
<div class="card shadow-sm" style="font-family: 'Inter', sans-serif;">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Dasbor Mitigasi</h5>
<form
method="GET"
id="yearForm"
action="{{ route('modules.dashboard.mitigasi.index') }}"
class="d-inline"
>
<x-mitigation-year-select
:selectedYear="$selectedYear"
name="mitigationYear"
onchange="document.getElementById('yearForm').submit()"
/>
</form>
</div>
<div class="card-body">
<div class="row flex justify-content-end items-center ml-6 mb-4">
<div class="flex gap-2 mr-4">
<a href="{{ route('modules.formMitigasi.create', ['reset' => 1]) }}">
<button class="button-action btn btn-success">
Aksi Baru
</button>
</a>
<a href="{{ route($route.'.aksi') }}">
<button class="button-action btn btn-primary text-white hover:bg-green-700">
Aksi Perubahan Iklim
</button>
</a>
</div>
</div>
<br/>
{{-- Charts Row --}}
<div class="row mb-5">
<div class="col-lg-4">
<p class="h6">AKSI</p>
<canvas id="aksiChart" height="100"></canvas>
</div>
<div class="col-lg-4">
<p class="h6">ALOKASI (Miliar Rupiah)</p>
<canvas id="anggaranChart" height="100"></canvas>
</div>
<div class="col-lg-4">
<p class="h6">REALISASI (Miliar Rupiah)</p>
<canvas id="realisasiChart" height="100"></canvas>
</div>
</div>
{{-- Table --}}
<div class="table-responsive">
<table id="mitigasiTable" class="table table-bordered table-striped align-middle">
<thead class="table-info text-white text-center">
<tr>
<th>Tipe Kegiatan</th>
<th>Nama Kegiatan</th>
<th>Tahun</th>
<th>Sektor</th>
<th>Sub-Sektor</th>
<th>Kategori</th>
<th class="text-end">Penurunan Emisi</th>
<th class="text-center">Revisi</th>
<th style="width:80px;"></th>
</tr>
</thead>
<tbody>
@foreach($tableData as $m)
<tr>
<td>{{ $m->tipe_kegiatan }}</td>
<td>{{ Str::limit($m->nama_kegiatan, 60) }}</td>
<td class="text-center">{{ $m->tahun_kegiatan }}</td>
<td>{{ $m->sektor }}</td>
<td>{{ $m->sub_sektor }}</td>
<td>{{ $m->kategori_perhitungan }}</td>
<td class="text-end">
{{ $m->emission_factor }}
</td>
<td class="text-center">{{ $m->revisi ?? '--' }}</td>
<td class="text-center">
<a href="{{ route($route.'.view', $m->id) }}" class="text-secondary me-2">
<i class="ti ti-eye"></i>
</a>
<a href="{{ route($route.'.edit', $m->id) }}" class="text-secondary">
<i class="ti ti-pencil"></i>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection
@section('css')
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css"/>
@endsection
@section('js')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
// === DataTables (unchanged) ===
$('#mitigasiTable').DataTable({
pageLength: 10, ordering: true, searching: true, lengthChange: true,
columnDefs: [
{ targets: 6, className: 'text-end' },
{ targets: 7, className: 'text-center' },
{ targets: 8, className: 'text-center', orderable: false, searchable: false }
],
language: {
search: "Cari:", lengthMenu: "Tampilkan _MENU_ data per halaman",
zeroRecords: "Tidak ada data ditemukan", emptyTable: "Tidak ada data.",
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
infoEmpty: "Tidak ada data tersedia",
infoFiltered: "(difilter dari _MAX_ total data)"
},
footerCallback: function () {
const api = this.api();
$(api.column(0).footer()).html('TOTAL (Semua Tahun {{ $selectedYear }})');
const parseIdNumber = (v) => {
if (typeof v === 'number') return v;
if (!v) return 0;
const s = String(v).replace(/\./g, '').replace(',', '.');
const n = parseFloat(s);
return isNaN(n) ? 0 : n;
};
const total = api.column(6, { search: 'applied' }).data().toArray()
.reduce((sum, val) => sum + parseIdNumber(val), 0);
const formatted = new Intl.NumberFormat('id-ID', { maximumFractionDigits: 6 }).format(total);
$(api.column(6).footer()).html(formatted);
}
});
// === Colors ===
function getColor(i) {
const p = [
'rgba(255,99,132,0.6)','rgba(54,162,235,0.6)','rgba(255,206,86,0.6)',
'rgba(75,192,192,0.6)','rgba(153,102,255,0.6)','rgba(255,159,64,0.6)',
'rgba(199,199,199,0.6)','rgba(100,181,246,0.6)','rgba(76,175,80,0.6)',
'rgba(121,85,72,0.6)'
];
return p[i % p.length];
}
// === Parser & Formatter (IDR → Miliar, desimal adaptif) ===
function parseIdrToNumber(v) {
if (typeof v === 'number') return v;
if (v == null) return 0;
const s = String(v).replace(/\s/g,'').replace(/\./g,'').replace(',', '.');
const n = Number(s);
return isNaN(n) ? 0 : n;
}
function toBillion(v) { return parseIdrToNumber(v) / 1_000_000_000; }
function pickDecimals(maxAbs) {
if (maxAbs >= 1) return 2;
if (maxAbs >= 0.1) return 3;
if (maxAbs >= 0.01) return 4;
if (maxAbs >= 0.001) return 5;
return 6;
}
function fmtMiliar(v, d) {
return new Intl.NumberFormat('id-ID', { maximumFractionDigits: d }).format(v) + ' M';
}
// === Chart AKSI (bukan rupiah) ===
const barData = @json($barData);
const years = barData.map(d => d.year);
const sectors = barData.length ? Object.keys(barData[0].jumlah || {}) : [];
const aksiDatasets = sectors.map((sec, idx) => ({
label: sec,
data: years.map(y => {
const row = barData.find(d => d.year === y) || {};
return (row.jumlah && row.jumlah[sec]) ? row.jumlah[sec] : 0;
}),
backgroundColor: getColor(idx),
borderColor: getColor(idx).replace('0.6','1'),
borderWidth: 1
}));
new Chart(document.getElementById('aksiChart'), {
type: 'bar',
data: { labels: years, datasets: aksiDatasets },
options: {
plugins: {
legend: { position: 'top' },
tooltip: { callbacks: { label: ctx => `${ctx.dataset.label}: ${ctx.parsed.y}` } }
},
scales: {
x: { stacked: true },
y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Jumlah Kegiatan' } }
}
}
});
// === Chart Anggaran & Realisasi (Rupiah → Miliar) ===
const budget = @json($budgetData);
const realisasi = @json($realisasiData);
const labels = ['APBN','APBD','Swasta','Lain-lain'];
const anggaranValuesB = [
toBillion(budget?.apbn), toBillion(budget?.apbd),
toBillion(budget?.swasta), toBillion(budget?.lainlain)
];
const realisasiValuesB = [
toBillion(realisasi?.apbn), toBillion(realisasi?.apbd),
toBillion(realisasi?.swasta), toBillion(realisasi?.lainlain)
];
// Tentukan jumlah desimal per chart berdasarkan nilai maksimum
const decAnggaran = pickDecimals(Math.max(...anggaranValuesB.map(Math.abs), 0));
const decRealisasi = pickDecimals(Math.max(...realisasiValuesB.map(Math.abs), 0));
const colors = ['#0575E6','#FFA500','#8A2BE2','#00BFA6'];
const realisasiColors = ['#7ed957','#ffd966','#bdb2ff','#80cbc4'];
function createBarChart(canvasId, datasetLabel, valuesB, colorsArr, decimals) {
const ctx = document.getElementById(canvasId).getContext('2d');
const datasets = labels.map((label, i) => ({
label: label,
data: [valuesB[i]], // satu nilai per kategori
backgroundColor: colorsArr[i],
datalabels: {
anchor: 'end',
align: 'right',
formatter: v => fmtMiliar(v, decimals),
clip: false
}
}));
new Chart(ctx, {
type: 'bar',
data: {
labels: [datasetLabel], // cuma 1 kategori "Anggaran" atau "Realisasi"
datasets: datasets
},
options: {
indexAxis: 'y',
plugins: {
legend: { display: true, position: 'top' },
tooltip: {
callbacks: {
label: (ctx) => {
const v = ctx.parsed.x ?? ctx.parsed.y ?? 0;
return `${ctx.dataset.label}: ${fmtMiliar(v, decimals)}`;
}
}
}
},
scales: {
x: {
beginAtZero: true,
suggestedMax: Math.max(...valuesB) * 1.1 || 1,
title: { display: true, text: 'Miliar Rupiah (M)' },
ticks: {
callback: (value) => fmtMiliar(value, decimals)
}
},
y: { ticks: { autoSkip: false } }
}
},
plugins: [ ChartDataLabels ]
});
}
createBarChart('anggaranChart', '', anggaranValuesB, colors, decAnggaran);
createBarChart('realisasiChart', '', realisasiValuesB, realisasiColors, decRealisasi);
});
</script>
@endsection