initial commit
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
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 = `
|
||||
<div class="layer-section">
|
||||
<div class="layer-section-title">Filter Layer</div>
|
||||
${Object.entries(persilCfg).map(([k, v]) => `
|
||||
<div class="layer-item ${layerVisible['persil' + k] ? 'checked' : ''}" onclick="toggleLayer('persil${k}',this)">
|
||||
<div class="layer-check">${layerVisible['persil' + k] ? '✓' : ''}</div>
|
||||
<div class="layer-dot" style="background:${v.color}"></div>
|
||||
<div class="layer-label">${k} — ${v.label}</div>
|
||||
<div class="layer-count">${cnt(k)}</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
<div class="layer-section">
|
||||
<div class="layer-section-title">Tambah Data</div>
|
||||
<button class="btn-add" onclick="startDrawPersil()">+ Tambah Persil</button>
|
||||
</div>
|
||||
<div class="layer-section">
|
||||
<div class="layer-section-title">Data Persil (${data.length})</div>
|
||||
<div class="data-list">
|
||||
${data.length ? data.map(d => `
|
||||
<div class="data-item" onclick="focusGeom(${d.id},'persil')">
|
||||
<div class="item-icon">📐</div>
|
||||
<div class="item-info">
|
||||
<div class="item-name">${esc(d.nama_pemilik)}</div>
|
||||
<div class="item-sub">${d.luas_m2.toFixed(1)} m² · ${d.no_sertifikat || '—'}</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:5px">
|
||||
<div class="item-badge" style="background:${persilCfg[d.jenis_hak].color}18;color:${persilCfg[d.jenis_hak].color};border:1px solid ${persilCfg[d.jenis_hak].color}44">${d.jenis_hak}</div>
|
||||
<button class="btn btn-ghost btn-sm" onclick="event.stopPropagation();editPersil(${d.id})">✏</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="event.stopPropagation();deletePersil(${d.id})">✕</button>
|
||||
</div>
|
||||
</div>`).join('') : '<div class="loading">Belum ada data persil</div>'}
|
||||
</div>
|
||||
</div>`;
|
||||
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(`<h4>📐 ${esc(d.nama_pemilik)}</h4>
|
||||
<p><b>Jenis Hak:</b> ${d.jenis_hak} — ${cfg.label}</p>
|
||||
<p><b>Luas:</b> ${d.luas_m2.toFixed(2)} m² (${(d.luas_m2 / 10000).toFixed(4)} ha)</p>
|
||||
${d.no_sertifikat ? `<p><b>No. Sertifikat:</b> ${d.no_sertifikat}</p>` : ''}
|
||||
${d.keterangan ? `<p>${esc(d.keterangan)}</p>` : ''}
|
||||
<div class="popup-actions"><button class="btn btn-ghost btn-sm" onclick="editPersil(${d.id})">✏ Edit</button><button class="btn btn-danger btn-sm" onclick="deletePersil(${d.id})">✕ Hapus</button></div>`);
|
||||
poly._dataId = d.id;
|
||||
poly._groupKey = 'persil' + d.jenis_hak;
|
||||
layers['persil' + d.jenis_hak].addLayer(poly);
|
||||
});
|
||||
}
|
||||
|
||||
function openPersilForm() {
|
||||
openModal('Simpan Persil Tanah', `
|
||||
<div class="form-calc">📐 Luas: <b>${pendingArea.toFixed(2)} m²</b> = ${(pendingArea / 10000).toFixed(4)} ha </div>
|
||||
<div class="form-group"><label class="form-label">Nama Pemilik *</label>
|
||||
<input class="form-input" id="ps-nama" placeholder="Budi Santoso…"/></div>
|
||||
<div class="form-row">
|
||||
<div class="form-group"><label class="form-label">Jenis Hak *</label>
|
||||
<select class="form-select" id="ps-hak">
|
||||
<option value="SHM">SHM — Hak Milik</option>
|
||||
<option value="HGB">HGB — Guna Bangunan</option>
|
||||
<option value="HGU">HGU — Guna Usaha</option>
|
||||
<option value="HP">HP — Hak Pakai</option>
|
||||
</select></div>
|
||||
<div class="form-group"><label class="form-label">No. Sertifikat</label>
|
||||
<input class="form-input" id="ps-sert" placeholder="00001/Kel/…"/></div>
|
||||
</div>
|
||||
<div class="form-group"><label class="form-label">Keterangan</label>
|
||||
<input class="form-input" id="ps-ket" placeholder="Opsional…"/></div>`,
|
||||
`<button class="btn btn-ghost" onclick="closeModal();drawnItems.clearLayers()">Batal</button>
|
||||
<button class="btn btn-primary" onclick="savePersil()">💾 Simpan</button>`);
|
||||
}
|
||||
|
||||
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 = `
|
||||
<div class="layer-section">
|
||||
<div class="layer-section-title">Edit Persil</div>
|
||||
<div style="font-size:11px;line-height:1.7;color:var(--text2);padding:10px 12px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:12px">
|
||||
Geser titik-titik batas bidang di peta lalu simpan perubahan.
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Nama Pemilik *</label>
|
||||
<input class="form-input" id="ps-edit-nama" value="${esc(data.nama_pemilik)}" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Jenis Hak *</label>
|
||||
<select class="form-select" id="ps-edit-hak">
|
||||
${Object.entries(persilCfg).map(([k, v]) => `<option value="${k}"${data.jenis_hak === k ? ' selected' : ''}>${k} — ${v.label}</option>`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Luas Otomatis</label>
|
||||
<input class="form-input" id="ps-edit-luas" value="${data.luas_m2.toFixed(2)} m²" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">No. Sertifikat</label>
|
||||
<input class="form-input" id="ps-edit-sert" value="${esc(data.no_sertifikat || '')}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Keterangan</label>
|
||||
<input class="form-input" id="ps-edit-ket" value="${esc(data.keterangan || '')}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
document.getElementById('panel-body').insertAdjacentHTML('beforeend', `
|
||||
<div style="display:flex;gap:8px">
|
||||
<button class="btn btn-ghost" style="flex:1" onclick="cancelGeometryEdit()">Batal</button>
|
||||
<button class="btn btn-primary" style="flex:1" onclick="savePersilEdit(${id})">Simpan Perubahan</button>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user