const persilCfg = {
SHM: { color: '#16a34a', label: 'Sertifikat Hak Milik' },
HGB: { color: '#2563eb', label: 'Hak Guna Bangunan' },
HGU: { color: '#d97706', label: 'Hak Guna Usaha' },
HP: { color: '#7c3aed', label: 'Hak Pakai' }
};
function renderPersilPanel(pb) {
return callAPI('data_persil.php').then(res => {
const data = res.data || [];
const cnt = k => data.filter(d => d.jenis_hak === k).length;
pb.innerHTML = `
Filter Layer
${Object.entries(persilCfg).map(([k, v]) => `
${layerVisible['persil' + k] ? '✓' : ''}
${k} — ${v.label}
${cnt(k)}
`).join('')}
Tambah Data
Data Persil (${data.length})
${data.length ? data.map(d => `
📐
${esc(d.nama_pemilik)}
${d.luas_m2.toFixed(1)} m² · ${d.no_sertifikat || '—'}
`).join('') : '
Belum ada data persil
'}
`;
loadPersilLayer(data);
registerSearchEntries('persil', data.map(d => ({
key: `persil:${d.id}`,
label: d.nama_pemilik,
subtitle: `Persil ${d.jenis_hak} · ${d.luas_m2.toFixed(1)} m²${d.no_sertifikat ? ` · ${d.no_sertifikat}` : ''}`,
typeLabel: 'Persil',
icon: '📐',
searchText: normalizeSearchText(`${d.nama_pemilik} ${d.jenis_hak} persil tanah`),
focus: () => focusGeom(d.id, 'persil')
})));
updateStatusCount();
});
}
function loadPersilLayer(data) {
['SHM', 'HGB', 'HGU', 'HP'].forEach(k => layers['persil' + k].clearLayers());
data.forEach(d => {
const cfg = persilCfg[d.jenis_hak], coords = d.geojson.coordinates[0].map(c => [c[1], c[0]]);
const poly = L.polygon(coords, { color: cfg.color, fillColor: cfg.color, fillOpacity: 0.18, weight: 2 })
.bindPopup(`📐 ${esc(d.nama_pemilik)}
Jenis Hak: ${d.jenis_hak} — ${cfg.label}
Luas: ${d.luas_m2.toFixed(2)} m² (${(d.luas_m2 / 10000).toFixed(4)} ha)
${d.no_sertifikat ? `No. Sertifikat: ${d.no_sertifikat}
` : ''}
${d.keterangan ? `${esc(d.keterangan)}
` : ''}
`);
poly._dataId = d.id;
poly._groupKey = 'persil' + d.jenis_hak;
layers['persil' + d.jenis_hak].addLayer(poly);
});
}
function openPersilForm() {
openModal('Simpan Persil Tanah', `
📐 Luas: ${pendingArea.toFixed(2)} m² = ${(pendingArea / 10000).toFixed(4)} ha
`,
`
`);
}
async function savePersil() {
const body = {
nama_pemilik: document.getElementById('ps-nama').value.trim(),
jenis_hak: document.getElementById('ps-hak').value,
no_sertifikat: document.getElementById('ps-sert').value,
luas_m2: pendingArea,
keterangan: document.getElementById('ps-ket').value,
geojson: pendingGeoJSON
};
if (!body.nama_pemilik) return toast('Nama wajib diisi', 'error');
const res = await callAPI('data_persil.php', 'POST', body);
if (res.status === 'success') {
toast(res.message, 'success');
closeModal();
drawnItems.clearLayers();
pendingGeoJSON = null;
pendingArea = null;
switchTab('persil');
} else {
toast(res.message, 'error');
}
}
async function editPersil(id) {
map.closePopup();
const res = await callAPI(`data_persil.php?id=${id}`);
const data = res.data;
const sourceLayer = findGeomLayer('persil', id);
if (!data) return toast('Data persil tidak ditemukan', 'error');
const groupKey = getGeometryGroupKey('persil', data);
const editLayer = sourceLayer ? cloneEditableLayer('persil', sourceLayer) : buildGeometryLayer('persil', data);
editLayer._dataId = id;
editLayer._groupKey = groupKey;
if (sourceLayer) layers[groupKey].removeLayer(sourceLayer);
drawnItems.clearLayers();
drawnItems.addLayer(editLayer);
geometryEditState = { type: 'persil', id, groupKey, sourceLayer, editLayer };
if (editLayer.editing && editLayer.editing.enable) {
editLayer.editing.enable();
} else {
const handler = getGeometryEditHandler();
if (!handler) {
drawnItems.clearLayers();
if (sourceLayer) layers[groupKey].addLayer(sourceLayer);
geometryEditState = null;
return toast('Mode edit geometri belum siap', 'error');
}
handler.enable();
}
map.fitBounds(editLayer.getBounds(), { padding: [50, 50], duration: 1 });
document.getElementById('panel-title').textContent = 'Edit Persil Tanah';
document.getElementById('panel-body').innerHTML = `
Edit Persil
Geser titik-titik batas bidang di peta lalu simpan perubahan.
`;
document.getElementById('panel-body').insertAdjacentHTML('beforeend', `
`);
}
async function savePersilEdit(id) {
const state = geometryEditState;
if (!state || state.type !== 'persil') return;
const layer = state.editLayer;
const ringLatLngs = layer.getLatLngs()[0];
const ring = ringLatLngs.map(ll => [ll.lng, ll.lat]);
ring.push([ringLatLngs[0].lng, ringLatLngs[0].lat]);
const luas_m2 = turf.area(turf.polygon([ring]));
const body = {
nama_pemilik: document.getElementById('ps-edit-nama').value.trim(),
jenis_hak: document.getElementById('ps-edit-hak').value,
no_sertifikat: document.getElementById('ps-edit-sert').value,
luas_m2,
keterangan: document.getElementById('ps-edit-ket').value,
geojson: { type: 'Polygon', coordinates: [ring] }
};
if (!body.nama_pemilik) return toast('Nama wajib diisi', 'error');
const res = await callAPI(`data_persil.php?id=${id}`, 'PUT', body);
if (res.status === 'success') {
toast(res.message, 'success');
if (layer.editing && layer.editing.disable) layer.editing.disable();
const handler = getGeometryEditHandler();
if (handler) handler.disable();
drawnItems.clearLayers();
geometryEditState = null;
switchTab('persil');
} else {
toast(res.message, 'error');
}
}
async function deletePersil(id) {
if (!confirm('Hapus?')) return;
const r = await callAPI(`data_persil.php?id=${id}`, 'DELETE');
if (r.status === 'success') {
toast('Dihapus', 'success');
map.closePopup();
switchTab('persil');
}
}