Files
SIG_Tugas_JalanRusak_Marker/index.html
T

258 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Sistem Pelaporan Jalan Rusak V3 - Full Draggable</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
#map { height: 600px; width: 100%; border-radius: 15px; border: 2px solid #dee2e6; }
.sidebar-container { height: 850px; display: flex; flex-direction: column; background: white; padding: 20px; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
.form-section { border-bottom: 2px solid #f8f9fa; padding-bottom: 20px; margin-bottom: 20px; }
.list-section { flex-grow: 1; overflow-y: auto; padding-right: 5px; }
.report-card {
border: 1px solid #eee; border-radius: 10px; padding: 15px;
margin-bottom: 15px; transition: 0.3s; cursor: pointer;
}
.report-card:hover { border-color: #ffc107; background: #fffdf5; transform: translateY(-2px); }
.thumb-img { width: 100%; height: 160px; object-fit: cover; border-radius: 8px; margin-top: 10px; }
.address-text { font-size: 0.85rem; color: #666; display: block; margin-top: 5px; }
#catatan { min-height: 120px; resize: vertical; }
.btn-add-mode {
margin-top: 15px;
border: 2px dashed #dc3545;
font-weight: bold;
font-size: 1.1rem;
transition: all 0.3s;
}
.btn-active {
background-color: #dc3545 !important;
color: white !important;
border-style: solid;
box-shadow: 0 0 15px rgba(220, 53, 69, 0.4);
}
</style>
</head>
<body class="bg-light">
<div class="container-fluid py-4">
<div class="row g-4">
<div class="col-md-7">
<div id="map" class="shadow-sm"></div>
<button id="btnEnableAdd" class="btn btn-outline-danger w-100 btn-add-mode py-3">
<i class="fas fa-exclamation-triangle me-2"></i> KLIK UNTUK AKTIFKAN MODE TAMBAH MARKER
</button>
<div class="alert alert-info mt-3 small">
<i class="fas fa-info-circle me-2"></i>
<strong>Tips:</strong> Semua marker (baru maupun lama) dapat digeser untuk menyesuaikan posisi koordinat secara presisi.
</div>
</div>
<div class="col-md-5">
<div class="sidebar-container">
<div class="form-section">
<h4 class="fw-bold text-dark mb-3"><i class="fas fa-file-signature text-warning"></i> Detail Laporan</h4>
<form id="uploadForm">
<div class="mb-2">
<label class="small fw-bold">Nama Jalan / Lokasi</label>
<input type="text" name="nama" id="nama" class="form-control" placeholder="Masukan nama lokasi" required>
</div>
<div class="row mb-2">
<div class="col">
<label class="small fw-bold">Latitude</label>
<input type="text" name="latitude" id="lat" class="form-control form-control-sm bg-light" readonly required>
</div>
<div class="col">
<label class="small fw-bold">Longitude</label>
<input type="text" name="longitude" id="lng" class="form-control form-control-sm bg-light" readonly required>
</div>
</div>
<div class="mb-2">
<label class="small fw-bold text-secondary">Alamat Terdeteksi</label>
<textarea name="alamat" id="alamat" class="form-control form-control-sm bg-light" rows="2" readonly placeholder="Klik/Geser marker untuk mendapatkan alamat..."></textarea>
</div>
<div class="mb-3">
<label class="small fw-bold">Catatan Kerusakan</label>
<textarea name="catatan" id="catatan" class="form-control" placeholder="Detail kerusakan..."></textarea>
</div>
<div class="mb-3">
<label class="small fw-bold">Upload Foto</label>
<input type="file" name="foto_file" id="foto_file" class="form-control" accept="image/*" required>
</div>
<button type="submit" class="btn btn-warning w-100 fw-bold py-2 shadow-sm text-dark" id="btnSimpan" disabled>
<i class="fas fa-save me-2"></i> SIMPAN LAPORAN BARU
</button>
</form>
</div>
<div class="list-section">
<h5 class="fw-bold mb-3">Daftar Jalan Rusak</h5>
<div id="reportList"></div>
</div>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
const map = L.map('map').setView([-0.026330, 109.342519], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
const warningIcon = L.icon({
iconUrl: 'https://cdn-icons-png.flaticon.com/512/564/564619.png',
iconSize: [40, 40],
iconAnchor: [20, 40]
});
let inputMarker = null;
let addMode = false;
let markerLayers = L.layerGroup().addTo(map); // Menggunakan layer group agar mudah dibersihkan
const btnAdd = document.getElementById('btnEnableAdd');
btnAdd.onclick = function() {
addMode = true;
this.classList.add('btn-active');
this.innerHTML = "<i class='fas fa-mouse-pointer me-2'></i> MODE AKTIF: KLIK PADA PETA";
map.getContainer().style.cursor = 'crosshair';
};
function updateGeocode(lat, lng, targetId = 'alamat') {
return fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`)
.then(res => res.json())
.then(data => {
const addr = data.display_name || "Alamat tidak ditemukan";
if(document.getElementById(targetId)) {
document.getElementById(targetId).value = addr;
}
return addr;
});
}
// UPDATE KOORDINAT KE DATABASE SAAT MARKER LAMA DIGESER
async function updateMarkerLocation(id, lat, lng) {
const alamatBaru = await updateGeocode(lat, lng, null); // dapatkan alamat baru tanpa mengisi form
const formData = new FormData();
formData.append('id', id);
formData.append('latitude', lat);
formData.append('longitude', lng);
formData.append('alamat', alamatBaru);
// Pastikan api_jalan_rusak.php mendukung action 'updateCoords' atau sesuaikan logicnya
fetch(`api_jalan_rusak.php?action=updateCoords`, {
method: 'POST',
body: formData
}).then(res => res.json()).then(res => {
console.log("Posisi diperbarui di database");
loadReports(); // Reload list untuk sinkronisasi alamat
});
}
// KLIK PETA (MARKER BARU)
map.on('click', function(e) {
if (!addMode) return;
const { lat, lng } = e.latlng;
document.getElementById('lat').value = lat.toFixed(7);
document.getElementById('lng').value = lng.toFixed(7);
document.getElementById('btnSimpan').disabled = false;
if (inputMarker) map.removeLayer(inputMarker);
inputMarker = L.marker([lat, lng], {
icon: warningIcon,
draggable: true
}).addTo(map);
updateGeocode(lat, lng);
inputMarker.on('dragend', function(event) {
const pos = event.target.getLatLng();
document.getElementById('lat').value = pos.lat.toFixed(7);
document.getElementById('lng').value = pos.lng.toFixed(7);
updateGeocode(pos.lat, pos.lng);
});
});
// LOAD DATA DARI DATABASE
async function loadReports() {
const res = await fetch('api_jalan_rusak.php?action=getAll');
const json = await res.json();
const listContainer = document.getElementById('reportList');
listContainer.innerHTML = '';
markerLayers.clearLayers(); // Bersihkan marker lama sebelum load ulang
json.data.forEach(item => {
// BUAT MARKER YANG SUDAH ADA MENJADI DRAGGABLE JUGA
const m = L.marker([item.latitude, item.longitude], {
icon: warningIcon,
draggable: true
}).addTo(markerLayers);
// Jika marker lama digeser, simpan perubahan posisi ke database
m.on('dragend', function(event) {
const pos = event.target.getLatLng();
if(confirm("Simpan perubahan posisi untuk lokasi ini?")) {
updateMarkerLocation(item.id, pos.lat.toFixed(7), pos.lng.toFixed(7));
} else {
loadReports(); // Kembalikan ke posisi semula jika batal
}
});
const card = document.createElement('div');
card.className = 'report-card';
card.onclick = () => map.flyTo([item.latitude, item.longitude], 17);
card.innerHTML = `
<div class="d-flex justify-content-between align-items-center">
<h6 class="fw-bold mb-0 text-dark"><i class="fas fa-map-marker-alt text-danger me-2"></i>${item.nama}</h6>
<button class="btn btn-sm text-danger" onclick="event.stopPropagation(); deleteReport(${item.id})">
<i class="fas fa-trash"></i>
</button>
</div>
<small class="address-text">${item.alamat}</small>
${item.foto ? `<img src="uploads/${item.foto}" class="thumb-img">` : ''}
`;
listContainer.appendChild(card);
});
}
// SIMPAN DATA BARU
document.getElementById('uploadForm').onsubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const res = await fetch('api_jalan_rusak.php?action=create', { method: 'POST', body: formData });
const result = await res.json();
if (result.success) {
alert("Laporan Berhasil!");
e.target.reset();
addMode = false;
document.getElementById('btnSimpan').disabled = true;
btnAdd.classList.remove('btn-active');
btnAdd.innerHTML = "<i class='fas fa-exclamation-triangle me-2'></i> KLIK UNTUK AKTIFKAN MODE TAMBAH MARKER";
if (inputMarker) map.removeLayer(inputMarker);
map.getContainer().style.cursor = '';
loadReports();
}
};
async function deleteReport(id) {
if(confirm('Hapus laporan?')) {
await fetch(`api_jalan_rusak.php?action=delete&id=${id}`, { method: 'DELETE' });
loadReports();
}
}
loadReports();
</script>
</body>
</html>