31 jan 26

This commit is contained in:
rahmagustin 2026-01-31 13:58:11 +07:00
parent b0f777db46
commit a1abb3fd2d
7 changed files with 276 additions and 198 deletions

View File

@ -17,6 +17,7 @@ public function index()
'id_tps', 'id_tps',
'nama_tps', 'nama_tps',
'alamat_tps', 'alamat_tps',
'kecamatan',
'status_tps', 'status_tps',
'latitude', 'latitude',
'longitude', 'longitude',
@ -24,6 +25,13 @@ public function index()
'kategori_tps_id' 'kategori_tps_id'
)->get(); )->get();
$kecamatanList = LokasiTps::select('kecamatan')
->distinct()
->orderBy('kecamatan')
->pluck('kecamatan');
return view('user.sig-tps', compact('tps', 'kecamatanList'));
return view('user.sig-tps', compact('tps')); return view('user.sig-tps', compact('tps'));
} }

View File

@ -19,6 +19,7 @@ class LokasiTps extends Model
'kategori_tps_id', 'kategori_tps_id',
'nama_tps', 'nama_tps',
'alamat_tps', 'alamat_tps',
'kecamatan',
'status_tps', 'status_tps',
'tahun_pembuatan', 'tahun_pembuatan',
'kapasitas_tps', 'kapasitas_tps',

View File

@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('lokasi_tps', function (Blueprint $table) {
$table->string('kecamatan')->after('alamat_tps');
});
}
public function down(): void
{
Schema::table('lokasi_tps', function (Blueprint $table) {
$table->dropColumn('kecamatan');
});
}
};

View File

@ -35,6 +35,12 @@
required> required>
</div> </div>
<div class="form-group">
<label>Kecamatan</label>
<input type="text" name="kecamatan" class="form-control" placeholder="Kecamatan TPS"
required>
</div>
<!-- KATEGORI TPS --> <!-- KATEGORI TPS -->
<div class="form-group"> <div class="form-group">
<label>Kategori TPS</label> <label>Kategori TPS</label>
@ -101,7 +107,7 @@
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary mr-2">Simpan</button> <button type="submit" class="mr-2 btn btn-primary">Simpan</button>
<a href="{{ route('admin.tps.index') }}" class="btn btn-light">Batal</a> <a href="{{ route('admin.tps.index') }}" class="btn btn-light">Batal</a>
</form> </form>

View File

@ -52,6 +52,12 @@ class="forms-sample">
value="{{ old('alamat_tps', $tps->alamat_tps) }}" required> value="{{ old('alamat_tps', $tps->alamat_tps) }}" required>
</div> </div>
<div class="form-group">
<label>Kecamatan</label>
<input type="text" name="kecamatan" class="form-control"
value="{{ old('kecamatan', $tps->kecamatan) }}" required>
</div>
{{-- Kategori TPS --}} {{-- Kategori TPS --}}
<div class="form-group"> <div class="form-group">
<label>Kategori TPS</label> <label>Kategori TPS</label>
@ -138,7 +144,7 @@ class="img-thumbnail">
</div> </div>
{{-- Button --}} {{-- Button --}}
<button type="submit" class="btn btn-primary mr-2"> <button type="submit" class="mr-2 btn btn-primary">
Simpan Simpan
</button> </button>
<a href="{{ route('admin.tps.index') }}" class="btn btn-light"> <a href="{{ route('admin.tps.index') }}" class="btn btn-light">

View File

@ -4,7 +4,7 @@
<!-- Hero Section --> <!-- Hero Section -->
<section id="hero" class="hero section"> <section id="hero" class="hero section">
<div class="container d-flex flex-column justify-content-center align-items-center text-center position-relative"> <div class="container text-center d-flex flex-column justify-content-center align-items-center position-relative">
<h1>Selamat Datang di <span>SIG TPS</span></h1> <h1>Selamat Datang di <span>SIG TPS</span></h1>
<p>Sistem Informasi Geografis Pemetaan Tempat Pembuangan Sampah di Kabupaten Nganjuk</p> <p>Sistem Informasi Geografis Pemetaan Tempat Pembuangan Sampah di Kabupaten Nganjuk</p>
<div class="d-flex"> <div class="d-flex">
@ -20,7 +20,7 @@
<div class="container"> <div class="container">
<!-- Section Title --> <!-- Section Title -->
<div class="section-title text-center" data-aos="fade-up"> <div class="text-center section-title" data-aos="fade-up">
<h2>Timbulan Sampah</h2> <h2>Timbulan Sampah</h2>
<p>Data berikut menunjukkan total timbulan, pengelolaan, daur ulang, dan sisa sampah dalam <strong>ton per <p>Data berikut menunjukkan total timbulan, pengelolaan, daur ulang, dan sisa sampah dalam <strong>ton per
hari</strong>.</p> hari</strong>.</p>
@ -30,7 +30,7 @@
<!-- Total Timbulan Sampah --> <!-- Total Timbulan Sampah -->
<div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="100"> <div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="100">
<div class="service-item position-relative text-center"> <div class="text-center service-item position-relative">
<div class="icon"> <div class="icon">
<i class="bi bi-trash2 icon"></i> <i class="bi bi-trash2 icon"></i>
</div> </div>
@ -43,7 +43,7 @@
<!-- Total Sampah Dikelola --> <!-- Total Sampah Dikelola -->
<div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="200"> <div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="200">
<div class="service-item position-relative text-center"> <div class="text-center service-item position-relative">
<div class="icon"> <div class="icon">
<i class="bi bi-gear icon"></i> <i class="bi bi-gear icon"></i>
</div> </div>
@ -56,7 +56,7 @@
<!-- Total Sampah Didaur Ulang --> <!-- Total Sampah Didaur Ulang -->
<div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="300"> <div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="300">
<div class="service-item position-relative text-center"> <div class="text-center service-item position-relative">
<div class="icon"> <div class="icon">
<i class="bi bi-recycle icon"></i> <i class="bi bi-recycle icon"></i>
</div> </div>
@ -69,7 +69,7 @@
<!-- Total Sisa Sampah --> <!-- Total Sisa Sampah -->
<div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="400"> <div class="col-xl-3 col-md-6 d-flex" data-aos="fade-up" data-aos-delay="400">
<div class="service-item position-relative text-center"> <div class="text-center service-item position-relative">
<div class="icon"> <div class="icon">
<i class="bi bi-exclamation-triangle icon"></i> <i class="bi bi-exclamation-triangle icon"></i>
</div> </div>
@ -118,21 +118,21 @@
<p class="fst-italic">Sistem Informasi Geografis yang menampilkan visualisasi lokasi Tempat <p class="fst-italic">Sistem Informasi Geografis yang menampilkan visualisasi lokasi Tempat
Pembuangan Sampah di Kabupaten Nganjuk secara mudah, cepat, dan interaktif.</p> Pembuangan Sampah di Kabupaten Nganjuk secara mudah, cepat, dan interaktif.</p>
<div class="d-flex align-items-center mt-4"> <div class="mt-4 d-flex align-items-center">
<i class="bi bi-check2"></i> <i class="bi bi-check2"></i>
<h4>Peta Interaktif Lokasi TPS</h4> <h4>Peta Interaktif Lokasi TPS</h4>
</div> </div>
<p>Peta digital yang memudahkan pengguna menemukan lokasi TPS terdekat, lengkap dengan tampilan <p>Peta digital yang memudahkan pengguna menemukan lokasi TPS terdekat, lengkap dengan tampilan
yang intuitif sehingga informasi dapat diakses dengan cepat dan jelas.</p> yang intuitif sehingga informasi dapat diakses dengan cepat dan jelas.</p>
<div class="d-flex align-items-center mt-4"> <div class="mt-4 d-flex align-items-center">
<i class="bi bi-check2"></i> <i class="bi bi-check2"></i>
<h4>Informasi TPS Lengkap</h4> <h4>Informasi TPS Lengkap</h4>
</div> </div>
<p>Menyajikan berbagai informasi penting seperti kapasitas, kondisi, status, hingga jenis <p>Menyajikan berbagai informasi penting seperti kapasitas, kondisi, status, hingga jenis
pengelolaan TPS, sehingga pengguna dapat memahami keadaan TPS secara menyeluruh.</p> pengelolaan TPS, sehingga pengguna dapat memahami keadaan TPS secara menyeluruh.</p>
<div class="d-flex align-items-center mt-4"> <div class="mt-4 d-flex align-items-center">
<i class="bi bi-check2"></i> <i class="bi bi-check2"></i>
<h4>Layanan Aduan TPS</h4> <h4>Layanan Aduan TPS</h4>
</div> </div>
@ -189,8 +189,9 @@
</a> </a>
<p> <p>
{{ $item->deskripsi }} {{ explode('.', $item->deskripsi)[0] }}.
</p> </p>
</div> </div>
</div> </div>
@ -208,8 +209,8 @@
<style> <style>
/* ============================= /* =============================
MAP SECTION MAP SECTION
============================= */ ============================= */
#call-to-action.call-to-action { #call-to-action.call-to-action {
padding: 0 !important; padding: 0 !important;
margin: 0 !important; margin: 0 !important;
@ -242,8 +243,8 @@
} }
/* ============================= /* =============================
FIX LEAFLET VS NAVBAR FIX LEAFLET VS NAVBAR
============================= */ ============================= */
header, header,
.navbar, .navbar,
#header { #header {
@ -265,8 +266,8 @@
} }
/* ============================= /* =============================
Legend Legend
============================= */ ============================= */
.legend { .legend {
background: white; background: white;
padding: 10px 12px; padding: 10px 12px;
@ -413,7 +414,7 @@ function markerIcon(color) {
</p> </p>
<div class="info-item d-flex"> <div class="info-item d-flex">
<i class="bi bi-exclamation-triangle-fill flex-shrink-0"></i> <i class="flex-shrink-0 bi bi-exclamation-triangle-fill"></i>
<div> <div>
<h4>Cepat & Mudah</h4> <h4>Cepat & Mudah</h4>
<p>Aduan dapat dikirim hanya dalam beberapa langkah.</p> <p>Aduan dapat dikirim hanya dalam beberapa langkah.</p>
@ -421,7 +422,7 @@ function markerIcon(color) {
</div> </div>
<div class="info-item d-flex"> <div class="info-item d-flex">
<i class="bi bi-geo-alt-fill flex-shrink-0"></i> <i class="flex-shrink-0 bi bi-geo-alt-fill"></i>
<div> <div>
<h4>Berbasis Lokasi TPS</h4> <h4>Berbasis Lokasi TPS</h4>
<p>Pilih TPS langsung dari peta.</p> <p>Pilih TPS langsung dari peta.</p>
@ -429,7 +430,7 @@ function markerIcon(color) {
</div> </div>
<div class="info-item d-flex"> <div class="info-item d-flex">
<i class="bi bi-chat-dots-fill flex-shrink-0"></i> <i class="flex-shrink-0 bi bi-chat-dots-fill"></i>
<div> <div>
<h4>Ditanggapi Admin</h4> <h4>Ditanggapi Admin</h4>
<p>Aduan akan diproses oleh admin terkait.</p> <p>Aduan akan diproses oleh admin terkait.</p>
@ -439,18 +440,18 @@ function markerIcon(color) {
</div> </div>
<!-- KANAN: CTA BUTTON --> <!-- KANAN: CTA BUTTON -->
<div class="col-lg-6 text-center"> <div class="text-center col-lg-6">
<div class="card shadow-sm p-4"> <div class="p-4 shadow-sm card">
<h4 class="mb-3">Ingin Mengajukan Aduan?</h4> <h4 class="mb-3">Ingin Mengajukan Aduan?</h4>
<p class="mb-4"> <p class="mb-4">
Klik tombol di bawah ini untuk memilih TPS dan mengisi form aduan. Klik tombol di bawah ini untuk memilih TPS dan mengisi form aduan.
</p> </p>
{{-- <a href="#map" class="btn btn-outline-success mb-2 w-100"> {{-- <a href="#map" class="mb-2 btn btn-outline-success w-100">
<i class="bi bi-map-fill me-1"></i> Pilih TPS di Peta <i class="bi bi-map-fill me-1"></i> Pilih TPS di Peta
</a> </a>
<p class="small text-muted my-2">atau</p> --}} <p class="my-2 small text-muted">atau</p> --}}
<a href="{{ route('user.aduan', $tpsPertama->id ?? 1) }}" class="btn btn-danger w-100"> <a href="{{ route('user.aduan', $tpsPertama->id ?? 1) }}" class="btn btn-danger w-100">
<i class="bi bi-megaphone-fill me-1"></i> Laporkan Aduan <i class="bi bi-megaphone-fill me-1"></i> Laporkan Aduan

View File

@ -1,189 +1,224 @@
@extends('user.template') @extends('user.template')
@section('content') @section('content')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style> <style>
#call-to-action.call-to-action { header, .navbar, #header {
padding: 0 !important; position: sticky;
margin: 0 !important; top: 0;
} z-index: 2000 !important;
background: #fff;
}
#call-to-action .container { .leaflet-top,
display: flex; .leaflet-bottom,
justify-content: center; .leaflet-control {
padding: 20px 0; z-index: 400 !important;
} }
.map-wrapper { #mapTPS {
width: 100%; width: 100%;
max-width: 1100px; height: 450px;
border-radius: 12px; border-radius: 12px;
overflow: hidden; box-shadow: 0 6px 20px rgba(0,0,0,.12);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); }
position: relative;
}
#mapTPS { .map-filter {
width: 100%; display: flex;
height: 450px; flex-wrap: wrap;
position: relative; gap: 10px;
z-index: 1; margin-bottom: 12px;
} align-items: center;
}
.leaflet-popup-content-wrapper { .map-info {
border-radius: 14px !important; font-size: 14px;
} color: #555;
}
header, .legend {
.navbar, background: #fff;
#header { padding: 10px 12px;
position: sticky; border-radius: 10px;
top: 0; font-size: 14px;
z-index: 1000; box-shadow: 0 2px 12px rgba(0,0,0,.15);
background: #fff; line-height: 18px;
} }
.leaflet-top { .legend i {
top: 0px !important; width: 16px;
z-index: 400 !important; height: 16px;
} float: left;
margin-right: 8px;
}
</style>
.leaflet-bottom { <div class="page-title">
bottom: 0px !important; <div class="container d-lg-flex justify-content-between align-items-center">
} <h1>Sebaran TPS di Kabupaten Nganjuk</h1>
<nav class="breadcrumbs">
.legend { <ol>
background: white; <li><a href="/">Beranda</a></li>
padding: 10px 12px; <li class="current">Sebaran TPS</li>
border-radius: 10px; </ol>
line-height: 18px; </nav>
color: #333;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
font-size: 14px;
}
.legend i {
width: 16px;
height: 16px;
float: left;
margin-right: 8px;
opacity: 1;
}
</style>
<!-- Page Title -->
<div class="page-title">
<div class="container d-lg-flex justify-content-between align-items-center">
<h1 class="mb-2 mb-lg-0">Sebaran TPS di Kabupaten Nganjuk</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="index.html">Beranda</a></li>
<li class="current">Sebaran TPS</li>
</ol>
</nav>
</div>
</div><!-- End Page Title -->
<section class="section">
<div class="container">
<div id="mapTPS"></div>
</div>
</section>
<script>
// =============================
// INIT MAP
// =============================
var map = L.map('mapTPS', {
zoomControl: true
}).setView([-7.6078, 111.903], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: "© OpenStreetMap contributors",
maxZoom: 19
}).addTo(map);
// =============================
// ICON MARKER PER KATEGORI
// =============================
function markerIcon(color) {
return new L.Icon({
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
}
var iconTPS = markerIcon('green'); // TPS
var iconTPS3R = markerIcon('blue'); // TPS 3R
var iconTPA = markerIcon('red'); // TPA
var iconDefault = markerIcon('black');
// =============================
// DATA TPS DARI DATABASE
// =============================
var tpsData = @json($tps);
tpsData.forEach(function(tps) {
if (tps.latitude && tps.longitude) {
let iconKategori = iconDefault;
// SESUAIKAN ID KATEGORI
if (tps.kategori_tps_id == 3) {
iconKategori = iconTPS;
} else if (tps.kategori_tps_id == 5) {
iconKategori = iconTPS3R;
} else if (tps.kategori_tps_id == 6) {
iconKategori = iconTPA;
}
L.marker([tps.latitude, tps.longitude], {
icon: iconKategori
})
.addTo(map)
.bindPopup(`
<div style="min-width:220px">
<strong style="font-size:15px">${tps.nama_tps}</strong><br>
<small>${tps.alamat_tps ?? '-'}</small><br>
<span class="badge bg-success mt-1">${tps.status_tps ?? '-'}</span>
<div style="margin-top:10px; display:flex; gap:6px;">
<a href="{{ url('tps') }}/${tps.id_tps}"
style="flex:1; text-align:center; padding:6px 8px; background:#0d6efd; color:#fff; border-radius:6px; font-size:13px; text-decoration:none;">
Detail TPS
</a>
<a href="{{ url('aduan') }}/${tps.id_tps}"
style="flex:1; text-align:center; padding:6px 8px; background:#dc3545; color:#fff; border-radius:6px; font-size:13px; text-decoration:none;">
Aduan
</a>
</div>
</div> </div>
`); </div>
<section class="section">
<div class="container">
<!-- INFO & BUTTON -->
<div class="map-filter">
<div class="map-info">
Klik tombol di samping untuk menemukan <b>TPS terdekat</b> dari lokasi Anda saat ini.
</div>
<button id="btnLokasi" class="btn btn-primary btn-sm">
Cari TPS Terdekat
</button>
</div>
<div id="mapTPS"></div>
</div>
</section>
<script>
/* =============================
INIT MAP
============================= */
var map = L.map('mapTPS').setView([-7.6078, 111.903], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
/* =============================
ICON TPS
============================= */
function icon(color) {
return new L.Icon({
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34]
});
}
var iconTPS = icon('green');
var iconTPS3R = icon('blue');
var iconTPA = icon('red');
var iconNear = icon('yellow');
/* =============================
DATA TPS
============================= */
var tpsData = @json($tps);
var markers = [];
/* =============================
MARKER TPS
============================= */
tpsData.forEach(tps => {
if (!tps.latitude || !tps.longitude) return;
let iconUse = iconTPS;
if (tps.kategori_tps_id == 5) iconUse = iconTPS3R;
if (tps.kategori_tps_id == 6) iconUse = iconTPA;
let marker = L.marker([tps.latitude, tps.longitude], { icon: iconUse })
.bindPopup(`
<strong>${tps.nama_tps}</strong><br>
<small>${tps.alamat_tps ?? '-'}</small><br>
<span class="mt-1 badge bg-success">${tps.status_tps ?? '-'}</span>
<hr style="margin:6px 0">
<a href="/tps/${tps.id_tps}" style="font-size:13px">Detail TPS</a>
`)
.addTo(map);
marker.tpsData = tps;
markers.push(marker);
});
/* =============================
LEGEND
============================= */
var legend = L.control({ position: 'bottomleft' });
legend.onAdd = function () {
var div = L.DomUtil.create('div', 'legend');
div.innerHTML = `
<strong>Kategori TPS</strong><br>
<i style="background:#198754"></i> TPS<br>
<i style="background:#0d6efd"></i> TPS 3R<br>
<i style="background:#dc3545"></i> TPA
`;
return div;
};
legend.addTo(map);
/* =============================
HITUNG JARAK
============================= */
function getDistance(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = (lat2-lat1) * Math.PI/180;
const dLon = (lon2-lon1) * Math.PI/180;
const a =
Math.sin(dLat/2)**2 +
Math.cos(lat1*Math.PI/180) *
Math.cos(lat2*Math.PI/180) *
Math.sin(dLon/2)**2;
return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));
}
/* =============================
TPS TERDEKAT
============================= */
document.getElementById('btnLokasi').addEventListener('click', function () {
if (!navigator.geolocation) {
alert('Browser tidak mendukung GPS');
return;
}
navigator.geolocation.getCurrentPosition(pos => {
let userLat = pos.coords.latitude;
let userLng = pos.coords.longitude;
// LINGKARAN BIRU (USER)
L.circleMarker([userLat, userLng], {
radius: 8,
color: '#0d6efd',
fillColor: '#0d6efd',
fillOpacity: 0.8
}).addTo(map)
.bindPopup('Lokasi Anda')
.openPopup();
let nearest = null;
let minDist = Infinity;
markers.forEach(m => {
let tps = m.tpsData;
let dist = getDistance(userLat, userLng, tps.latitude, tps.longitude);
if (dist < minDist) {
minDist = dist;
nearest = m;
nearest.distance = dist;
} }
}); });
var legend = L.control({ if (nearest) {
position: "bottomleft" nearest.setIcon(iconNear);
}); nearest.openPopup();
map.setView(nearest.getLatLng(), 15);
legend.onAdd = function() { }
var div = L.DomUtil.create("div", "legend"); });
div.innerHTML = ` });
<strong>Kategori TPS</strong><br> </script>
<i style="background:#198754"></i> TPS<br>
<i style="background:#0d6efd"></i> TPS 3R<br>
<i style="background:#dc3545"></i> TPA
`;
return div;
};
legend.addTo(map);
</script>
@endsection @endsection