let editing = false; /** Create */ function openCreateModal() { editing = false; document.getElementById("reservationModalTitle").innerText = "Add DHCP Reservation"; document.getElementById("reservationForm").reset(); document.getElementById("reservationId").value = ""; document.getElementById("enabled").checked = true; clearValidation(); } /*** Edit*/ function editReservation(hostname, mac, ip, enabled) { editing = true; document.getElementById("reservationModalTitle").innerText = "Edit DHCP Reservation"; document.getElementById("hostname").value = hostname; document.getElementById("macAddress").value = mac; document.getElementById("ipAddress").value = ip; document.getElementById("enabled").checked = enabled; const modal = new bootstrap.Modal(document.getElementById("reservationModal")); modal.show(); } /*** MAC formatting */ document.getElementById("macAddress").addEventListener("input", function () { let value = this.value.replace(/[^a-fA-F0-9]/g, "").toUpperCase(); let pairs = value.match(/.{1,2}/g); this.value = pairs ? pairs.join(":").substring(0, 17) : ""; }); function clearValidation() { document.getElementById("macAddress").classList.remove("is-invalid"); document.getElementById("ipAddress").classList.remove("is-invalid"); document.getElementById("macError").innerText = "Invalid MAC address."; document.getElementById("ipError").innerText = "Invalid IP address."; } document.getElementById("reservationForm").addEventListener("submit", function (event) { event.preventDefault(); clearValidation(); const id = document.getElementById("reservationId").value; const payload = { hostname: document.getElementById("hostname").value.trim(), mac_address: document.getElementById("macAddress").value.trim(), ip_address: document.getElementById("ipAddress").value.trim(), enabled: document.getElementById("enabled").checked, }; const macValid = /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/i.test(payload.mac_address); const ipValid = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/.test( payload.ip_address, ); if (!macValid) { document.getElementById("macAddress").classList.add("is-invalid"); return; } if (!ipValid) { document.getElementById("ipAddress").classList.add("is-invalid"); return; } const url = id ? dhcpConfig.reservationBaseUrl + "/" + id : dhcpConfig.reservationStoreUrl; const method = id ? "PUT" : "POST"; const submitButton = this.querySelector('button[type="submit"]'); if (submitButton) { submitButton.disabled = true; submitButton.innerHTML = '' + "Saving..."; } fetch(url, { method: method, headers: { "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content"), Accept: "application/json", "Content-Type": "application/json", }, body: JSON.stringify(payload), }) .then(async (response) => { const data = await response.json(); if (!response.ok) { throw data; } return data; }) .then((data) => { if (!data.success) { throw data; } const modal = bootstrap.Modal.getInstance(document.getElementById("reservationModal")); if (modal) { modal.hide(); } window.location.reload(); }) .catch((error) => { console.error("Reservation save error:", error); if (error.errors && error.errors.mac_address) { document.getElementById("macAddress").classList.add("is-invalid"); document.getElementById("macError").innerText = error.errors.mac_address[0]; } if (error.errors && error.errors.ip_address) { document.getElementById("ipAddress").classList.add("is-invalid"); document.getElementById("ipError").innerText = error.errors.ip_address[0]; } Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: error.message || "Failed to save reservation."}); }) .finally(() => { if (submitButton) { submitButton.disabled = false; submitButton.innerHTML = '' + "Save Reservation"; } }); }); /*** Search*/ document.getElementById("searchReservation").addEventListener("input", function () { const keyword = this.value.toLowerCase(); document.querySelectorAll("#reservationTable tbody tr").forEach(function (row) { row.style.display = row.innerText.toLowerCase().includes(keyword) ? "" : "none"; }); }); /*** Delete*/ function deleteReservation(hostname) { Swal.fire({title: 'Hapus reservasi "' + hostname + '"?', icon: 'warning', showCancelButton: true, confirmButtonText: 'Ya, hapus', cancelButtonText: 'Batal', confirmButtonColor: '#dc3545'}) .then((res) => { if (!res.isConfirmed) return; Swal.fire({icon: 'info', title: 'Mode demo', text: 'Reservation "' + hostname + '" deleted.'}); }); } /** * Test DHCP */ function testDhcp() { Swal.fire({icon: 'info', title: 'Mode demo', text: "DHCP configuration is valid."}); } /** * Apply */ function applyDhcp() { const modal = new bootstrap.Modal(document.getElementById("applyModal")); modal.show(); } /*** Confirm Apply */ function confirmApply() { const modalElement = document.getElementById("applyModal"); const modal = bootstrap.Modal.getInstance(modalElement); if (modal) { modal.hide(); } const button = document.querySelector('#applyModal [onclick="confirmApply()"]'); if (button) { button.disabled = true; button.innerHTML = '' + "Applying..."; } fetch(dhcpConfig.applyUrl, { method: "POST", headers: { "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content"), Accept: "application/json", }, }) .then(async (response) => { const data = await response.json(); if (!response.ok) { throw data; } return data; }) .then((data) => { if (!data.success) { throw data; } Swal.fire({icon: 'success', title: 'Berhasil', text: data.message || "DHCP configuration applied successfully."}); refreshDhcpStatus(); window.location.reload(); }) .catch((error) => { console.error("DHCP apply error:", error); Swal.fire({icon: 'error', title: 'Gagal apply', text: error.message || "Failed to apply DHCP configuration."}); }) .finally(() => { if (button) { button.disabled = false; button.innerHTML = "Apply Configuration"; } }); } function refreshDhcpStatus() { fetch(dhcpConfig.statusUrl) .then(async (response) => { if (!response.ok) { throw new Error("HTTP " + response.status); } return response.json(); }) .then((data) => { document.getElementById("dhcpServerHostname").innerText = data.hostname ?? "DHCP Server"; const badge = document.getElementById("dhcpStatusBadge"); const clients = document.getElementById("activeClients"); const lastApplied = document.getElementById("lastApplied"); const serverIp = document.getElementById("dhcpServerIp"); const serverInterface = document.getElementById("dhcpServerInterface"); serverIp.innerText = data.ip ?? "-"; serverInterface.innerText = data.interface ?? "-"; // STATUS if (data.running) { badge.innerHTML = ` Running `; } else { badge.innerHTML = ` Stopped `; } // ACTIVE CLIENTS clients.innerHTML = (data.reservations_count ?? data.active_clients ?? 0) + ' registered'; // LAST APPLIED if (data.last_applied) { const date = new Date(data.last_applied.replace(" ", "T")); lastApplied.innerText = date.toLocaleString("id-ID", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit", }); } else { lastApplied.innerText = "Never"; } }) .catch((error) => { console.error("DHCP status error:", error); }); } function loadDhcpLogs() { const container = document.getElementById("dhcpLogs"); container.innerHTML = `