first commit

This commit is contained in:
2026-05-14 15:28:23 +08:00
commit 4fad704fd2
4034 changed files with 1093582 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
/**
* papar_permohonan.js
* Modular JS untuk papar_permohonan.blade.php
* Requires: jQuery, Leaflet, Bootstrap (for toasts/modals if used)
*
* Usage: ensure window.PermohonanConfig object exists before loading this file.
*/
(function ($, window, document, undefined) {
'use strict';
// read config (set by blade)
const cfg = window.PermohonanConfig || {};
$(function () {
/* ===========================
* Helper / Utilities
* =========================== */
function isEmpty(v) { return v === null || v === undefined || v === ''; }
function toast(msg, level = 'info') {
// simple bootstrap toast fallback (console if not present)
if (typeof bootstrap !== 'undefined' && $('#globalToast').length) {
$('#globalToast .toast-body').text(msg);
let toastEl = new bootstrap.Toast($('#globalToast')[0]);
toastEl.show();
} else {
console[level === 'error' ? 'error' : 'log'](msg);
}
}
/* ============================================================
* 1. FIELD TOGGLE HANDLERS
* ============================================================ */
function handleJenisPenjaja() {
const selected = $("#jenis_penjaja").val();
$("#jenis_penjaja_lain").prop("disabled", selected !== "lain-lain");
if (selected !== "lain-lain") $("#jenis_penjaja_lain").val("");
}
function handleJenisKenderaan() {
const jenis = $("#jenis_kenderaan").val();
const disable = ["kereta sorong", "basikal", ""].includes(jenis);
$("#no_pendaftaran").prop("disabled", disable);
if (disable) $("#no_pendaftaran").val("");
}
$("#jenis_penjaja").on("change", handleJenisPenjaja);
$("#jenis_kenderaan").on("change", handleJenisKenderaan);
// run initial toggles
handleJenisPenjaja();
handleJenisKenderaan();
/* ============================================================
* 2. LEAFLET MAP + NOMINATIM SEARCH (with debounce)
* ============================================================ */
const defaultLat = parseFloat(cfg.latitude) || 1.48348;
const defaultLng = parseFloat(cfg.longitude) || 103.58197;
const defaultZoom = cfg.latitude ? 16 : 12;
const map = L.map('viewDiv', { preferCanvas: true }).setView([defaultLat, defaultLng], defaultZoom);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
let marker = null;
if (!isEmpty(cfg.latitude) && !isEmpty(cfg.longitude)) {
marker = L.marker([cfg.latitude, cfg.longitude], { draggable: true }).addTo(map);
marker.on('dragend', updateInputByMarker);
}
map.on('click', function (e) {
placeMarker(e.latlng);
updateInputFromLatLng(e.latlng);
});
function placeMarker(latlng) {
if (!marker) {
marker = L.marker(latlng, { draggable: true }).addTo(map);
marker.on('dragend', updateInputByMarker);
} else {
marker.setLatLng(latlng);
}
}
function updateInputByMarker(e) {
updateInputFromLatLng(e.target.getLatLng());
}
function updateInputFromLatLng(latlng) {
$('#latitude').val(latlng.lat);
$('#longitude').val(latlng.lng);
}
// Debounce util
function debounce(fn, wait) {
let t;
return function () {
const args = arguments;
clearTimeout(t);
t = setTimeout(function () { fn.apply(null, args); }, wait);
};
}
// Nominatim search (uses fetch)
function _cariKoordinat(rawQuery) {
const query = rawQuery || $('#searchBox').val();
if (typeof query !== 'string' || query.trim().length < 3) {
toast('Masukkan nama tempat yang lebih spesifik.', 'info');
return;
}
const url = 'https://nominatim.openstreetmap.org/search?format=json&q=' + encodeURIComponent(query);
fetch(url, { headers: { 'Accept': 'application/json' } })
.then(res => res.json())
.then(data => {
if (!Array.isArray(data) || data.length === 0) {
toast('Lokasi tak dijumpai.', 'info');
return;
}
const place = data[0];
const lat = parseFloat(place.lat);
const lon = parseFloat(place.lon);
map.setView([lat, lon], 17);
placeMarker({ lat: lat, lng: lon });
updateInputFromLatLng({ lat: lat, lng: lon });
})
.catch(err => {
console.error('Nominatim error', err);
toast('Ralat semasa mencari lokasi.', 'error');
});
}
const cariKoordinat = debounce(_cariKoordinat, 450);
$('#searchBtn').on('click', function (e) {
e.preventDefault();
cariKoordinat($('#searchBox').val());
});
// optional: live suggestions trigger (not heavy)
// $('#searchBox').on('input', debounce(function(){ cariKoordinat($(this).val()); }, 800));
/* ============================================================
* 3. CASCADING DROPDOWN: KAWASAN → TAMAN → JALAN
* ============================================================ */
function loadKawasan() {
return $.getJSON('/get_kawasan').done(function (data) {
const $kawasan = $('#kawasan').empty().append('<option value="">-- Sila Pilih Kawasan --</option>');
data.forEach(k => {
$kawasan.append($('<option>', {
value: k.id,
text: k.nama,
selected: k.id == cfg.defaultKawasanId
}));
});
if (cfg.defaultKawasanId && cfg.defaultTamanId) {
loadTaman(cfg.defaultKawasanId, cfg.defaultTamanId);
}
}).fail(function () {
toast('Gagal load kawasan.', 'error');
});
}
function loadPenempatan() {
return $.getJSON('/get_penempatan').done(function (data) {
const $penempatan = $('#penempatan').empty().append('<option value="">-- Sila Pilih Penempatan --</option>');
data.forEach(p => {
$penempatan.append($('<option>', {
value: p.id,
text: p.nama,
selected: p.id == cfg.defaultPenempatanId
}));
});
}).fail(function () {
toast('Gagal load penempatan.', 'error');
});
}
function loadTaman(kawasanId, selectedId) {
if (!kawasanId) return;
return $.getJSON('/get_taman', { kawasan_id: kawasanId }).done(function (data) {
const $taman = $('#taman').empty().append('<option value="">-- Sila Pilih Taman --</option>');
data.forEach(t => {
$taman.append($('<option>', {
value: t.id,
text: t.nama,
selected: t.id == selectedId
}));
});
$taman.prop('disabled', false);
if (selectedId && cfg.defaultJalanId) {
loadJalan(selectedId, cfg.defaultJalanId);
}
}).fail(function () {
toast('Gagal load taman.', 'error');
});
}
function loadJalan(tamanId, selectedId) {
if (!tamanId) return;
return $.getJSON('/get_jalan', { taman_id: tamanId }).done(function (data) {
const $jalan = $('#jalan').empty().append('<option value="">-- Sila Pilih Jalan --</option>');
data.forEach(j => {
$jalan.append($('<option>', {
value: j.id,
text: j.nama,
selected: j.id == selectedId
}));
});
$jalan.prop('disabled', false);
}).fail(function () {
toast('Gagal load jalan.', 'error');
});
}
// Event listeners
$('#kawasan').on('change', function () {
const kawasanId = $(this).val();
$('#taman').empty().append('<option value="">-- Sila Pilih Taman --</option>');
$('#jalan').empty().append('<option value="">-- Sila Pilih Jalan --</option>').prop('disabled', true);
if (kawasanId) loadTaman(kawasanId);
else $('#taman').prop('disabled', true);
});
$('#taman').on('change', function () {
const tid = $(this).val();
const textTaman = $(this).find('option:selected').text();
$('#jalan').empty().append('<option value="">-- Sila Pilih Jalan --</option>');
if (tid) {
loadJalan(tid);
$('#searchBox').val(textTaman);
cariKoordinat(textTaman);
} else {
$('#jalan').prop('disabled', true);
}
});
$('#jalan').on('change', function () {
const jalanText = $(this).find('option:selected').text();
const tamanText = $('#taman').find('option:selected').text();
if (jalanText) {
$('#searchBox').val(jalanText + ', ' + tamanText);
cariKoordinat(jalanText + ', ' + tamanText);
}
});
// Init
loadKawasan();
loadPenempatan();
}); // end jQuery ready
})(jQuery, window, document);