308 lines
13 KiB
PHP
308 lines
13 KiB
PHP
@extends('admin.layouts.app')
|
|
@section('title', 'GIS Penerima BLT')
|
|
|
|
@section('content')
|
|
|
|
{{-- Page Header --}}
|
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
<div>
|
|
<h5 class="fw-bold mb-0" style="color:#1a3c2e;">
|
|
<i class="bi bi-geo-alt-fill me-2" style="color:#2d7a4f;"></i>GIS Sebaran Penerima BLT
|
|
</h5>
|
|
<nav aria-label="breadcrumb" class="mt-1">
|
|
<ol class="breadcrumb mb-0" style="font-size:0.8rem;">
|
|
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}" class="text-decoration-none" style="color:#2d7a4f;">Dashboard</a></li>
|
|
<li class="breadcrumb-item active text-muted">GIS Penerima</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
|
|
{{-- Filter Periode --}}
|
|
<form method="GET" action="{{ route('admin.gis.index') }}" class="d-flex align-items-center gap-2" id="formPeriode">
|
|
<label class="form-label mb-0 fw-semibold text-muted" style="font-size:0.85rem; white-space:nowrap;">
|
|
<i class="bi bi-funnel me-1"></i>Filter Periode:
|
|
</label>
|
|
<select name="periode_id" id="selectPeriode" class="form-select form-select-sm" style="min-width:220px;" onchange="this.form.submit()">
|
|
@foreach($periodeList as $p)
|
|
<option value="{{ $p->id }}" {{ $periodeAktif && $periodeAktif->id == $p->id ? 'selected' : '' }}>
|
|
{{ $p->nama }}
|
|
</option>
|
|
@endforeach
|
|
@if($periodeList->isEmpty())
|
|
<option value="">-- Belum ada periode selesai --</option>
|
|
@endif
|
|
</select>
|
|
</form>
|
|
</div>
|
|
|
|
{{-- Stat Cards --}}
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-6 col-md-3">
|
|
<div class="card p-3 text-center">
|
|
<div style="font-size:1.6rem;font-weight:800;color:#2d7a4f;" id="statPenerima">{{ $totalPenerima }}</div>
|
|
<div style="font-size:0.78rem;color:#6b7280;">Penerima Periode Ini</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card p-3 text-center">
|
|
<div style="font-size:1.6rem;font-weight:800;color:#2563eb;" id="statWilayah">{{ $totalWilayah }}</div>
|
|
<div style="font-size:0.78rem;color:#6b7280;">RW Terdampak</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card p-3 text-center">
|
|
<div style="font-size:1.6rem;font-weight:800;color:#d97706;">{{ $periodeAktif ? $periodeAktif->kuota_penerima : '-' }}</div>
|
|
<div style="font-size:0.78rem;color:#6b7280;">Kuota Periode</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card p-3 text-center">
|
|
<div style="font-size:1.6rem;font-weight:800;color:#7c3aed;">{{ $periodeList->count() }}</div>
|
|
<div style="font-size:0.78rem;color:#6b7280;">Total Periode Selesai</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
{{-- PETA --}}
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span>
|
|
<i class="bi bi-map me-2"></i>Peta Sebaran per RW
|
|
@if($periodeAktif)
|
|
<span class="badge ms-2" style="background:#e0f2e9;color:#2d7a4f;font-size:0.75rem;">
|
|
{{ $periodeAktif->nama }}
|
|
</span>
|
|
@endif
|
|
</span>
|
|
<div class="d-flex gap-2 align-items-center" style="font-size:0.78rem;">
|
|
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#2d7a4f;"></span> Ada penerima</span>
|
|
<span><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:#6c757d;"></span> Tidak ada</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0" style="position:relative;">
|
|
<div id="map" style="height:480px; width:100%; border-radius:0 0 12px 12px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- RINGKASAN PER DUSUN --}}
|
|
<div class="col-lg-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<i class="bi bi-bar-chart-fill me-2"></i>Ringkasan per Dusun
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Dusun</th>
|
|
<th class="text-center">Penerima</th>
|
|
<th class="text-center">%</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($statistikDusun as $dusun => $jumlah)
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-geo-alt-fill text-success me-1"></i>
|
|
<strong>{{ $dusun }}</strong>
|
|
</td>
|
|
<td class="text-center">
|
|
<span class="badge" style="background:#e0f2e9;color:#2d7a4f;">{{ $jumlah }}</span>
|
|
</td>
|
|
<td class="text-center text-muted" style="font-size:0.8rem;">
|
|
{{ $totalPenerima > 0 ? number_format($jumlah / $totalPenerima * 100, 0) : 0 }}%
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="3" class="text-center text-muted py-4">
|
|
<i class="bi bi-inbox d-block mb-1 fs-5"></i>
|
|
Belum ada data
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- TABEL DETAIL PER RW --}}
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-table me-2"></i>Detail Per RW
|
|
@if($periodeAktif)
|
|
— <span class="text-muted fw-normal" style="font-size:0.85rem;">{{ $periodeAktif->nama }}</span>
|
|
@endif
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Dusun</th>
|
|
<th>RW</th>
|
|
<th class="text-center">Jumlah Penerima</th>
|
|
<th class="text-center">Rata-rata Skor SAW</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($markers as $m)
|
|
<tr>
|
|
<td><i class="bi bi-geo-alt-fill text-success me-1"></i>{{ $m['dusun'] }}</td>
|
|
<td>RW {{ $m['rw'] }}</td>
|
|
<td class="text-center">
|
|
@if($m['jumlah_penerima'] > 0)
|
|
<span class="badge" style="background:#e0f2e9;color:#2d7a4f;">
|
|
{{ $m['jumlah_penerima'] }} orang
|
|
</span>
|
|
@else
|
|
<span class="text-muted" style="font-size:0.8rem;">—</span>
|
|
@endif
|
|
</td>
|
|
<td class="text-center font-monospace" style="font-size:0.82rem;">
|
|
{{ $m['avg_skor'] > 0 ? $m['avg_skor'] : '—' }}
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="4" class="text-center text-muted py-4">
|
|
<i class="bi bi-inbox d-block mb-1 fs-4"></i>
|
|
Belum ada data penerima.
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@endsection
|
|
|
|
@push('styles')
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
|
<style>
|
|
#map { z-index: 1; }
|
|
</style>
|
|
@endpush
|
|
|
|
@push('scripts')
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
<script>
|
|
var map = L.map('map', {
|
|
center: [-7.529855, 112.578704],
|
|
zoom: 14,
|
|
zoomControl: true,
|
|
});
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
|
|
maxZoom: 19,
|
|
}).addTo(map);
|
|
|
|
var markersData = JSON.parse('{!! addslashes(json_encode($markers->values())) !!}');
|
|
var wargaData = {}; // akan diisi via AJAX
|
|
var leafletMarkers = [];
|
|
|
|
function warnaMaker(jumlah) {
|
|
if (jumlah === 0) return '#6c757d';
|
|
if (jumlah <= 1) return '#86efac';
|
|
if (jumlah <= 2) return '#4ade80';
|
|
if (jumlah <= 3) return '#22c55e';
|
|
return '#15803d';
|
|
}
|
|
|
|
function buatIcon(jumlah) {
|
|
var warna = warnaMaker(jumlah);
|
|
return L.divIcon({
|
|
className: '',
|
|
html: `<div style="
|
|
background:${warna};
|
|
width:38px; height:38px;
|
|
border-radius:50% 50% 50% 0;
|
|
transform: rotate(-45deg);
|
|
border: 3px solid white;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
display:flex; align-items:center; justify-content:center;">
|
|
<span style="transform:rotate(45deg);color:white;font-weight:bold;font-size:13px;">${jumlah}</span>
|
|
</div>`,
|
|
iconSize: [38, 38],
|
|
iconAnchor: [19, 38],
|
|
popupAnchor: [0, -40],
|
|
});
|
|
}
|
|
|
|
function buatPopup(item, wargaList) {
|
|
var daftarWarga = '';
|
|
if (wargaList && wargaList.length > 0) {
|
|
daftarWarga = '<div style="margin-top:8px;border-top:1px solid #e5e7eb;padding-top:8px;">' +
|
|
'<div style="font-size:11px;font-weight:600;color:#374151;margin-bottom:4px;">Daftar Penerima:</div>';
|
|
wargaList.forEach(function(w) {
|
|
daftarWarga += `<div style="font-size:11px;padding:2px 0;">
|
|
<span style="color:#2d7a4f;font-weight:bold;">#${w.ranking}</span>
|
|
${w.nama}
|
|
<span style="color:#9ca3af;font-size:10px;">(${w.skor})</span>
|
|
</div>`;
|
|
});
|
|
daftarWarga += '</div>';
|
|
} else if (item.jumlah_penerima === 0) {
|
|
daftarWarga = '<div style="margin-top:8px;font-size:11px;color:#9ca3af;text-align:center;">Tidak ada penerima</div>';
|
|
}
|
|
|
|
return `<div style="min-width:220px;font-family:sans-serif;">
|
|
<div style="background:#2d7a4f;color:white;padding:8px 12px;margin:-14px -20px 10px;border-radius:4px 4px 0 0;">
|
|
<strong>📍 ${item.label}</strong>
|
|
</div>
|
|
<table style="width:100%;font-size:12px;">
|
|
<tr><td style="color:#6b7280;padding:3px 0;">Dusun</td><td><strong>${item.dusun}</strong></td></tr>
|
|
<tr><td style="color:#6b7280;padding:3px 0;">RW</td><td><strong>${item.rw}</strong></td></tr>
|
|
<tr><td style="color:#6b7280;padding:3px 0;">Penerima</td>
|
|
<td><strong style="color:${item.jumlah_penerima > 0 ? '#2d7a4f' : '#9ca3af'};">
|
|
${item.jumlah_penerima} orang</strong></td></tr>
|
|
${item.jumlah_penerima > 0 ? `<tr><td style="color:#6b7280;padding:3px 0;">Rata Skor</td><td><strong>${item.avg_skor}</strong></td></tr>` : ''}
|
|
</table>
|
|
${daftarWarga}
|
|
</div>`;
|
|
}
|
|
|
|
function renderMarkers(data, wargaPerWilayah) {
|
|
leafletMarkers.forEach(m => map.removeLayer(m));
|
|
leafletMarkers = [];
|
|
|
|
data.forEach(function(item) {
|
|
var key = item.dusun + '_' + item.rw;
|
|
var wargaList = wargaPerWilayah[key] || [];
|
|
var marker = L.marker([item.latitude, item.longitude], { icon: buatIcon(item.jumlah_penerima) });
|
|
marker.bindPopup(buatPopup(item, wargaList), { maxWidth: 280 });
|
|
marker.addTo(map);
|
|
leafletMarkers.push(marker);
|
|
});
|
|
|
|
if (data.length > 0) {
|
|
var bounds = data.map(m => [m.latitude, m.longitude]);
|
|
map.fitBounds(bounds, { padding: [60, 60], maxZoom: 14 });
|
|
}
|
|
}
|
|
|
|
// Load AJAX data untuk daftar nama penerima
|
|
var periodeId = '{{ $periodeAktif ? $periodeAktif->id : "" }}';
|
|
if (periodeId) {
|
|
fetch('{{ route("admin.gis.data") }}?periode_id=' + periodeId)
|
|
.then(r => r.json())
|
|
.then(function(res) {
|
|
wargaData = res.wargaPerWilayah;
|
|
renderMarkers(markersData, wargaData);
|
|
});
|
|
} else {
|
|
renderMarkers(markersData, {});
|
|
}
|
|
</script>
|
|
@endpush
|