Track public JS yg dipakai view (tooltips, dhcp, stasiunkerja, tarifstiker, member-details/extend) biar ikut ke-deploy
This commit is contained in:
@@ -0,0 +1,382 @@
|
||||
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 = '<span class="spinner-border spinner-border-sm me-1"></span>' + "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 = '<i class="bi bi-check-lg me-1"></i>' + "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 = '<span class="spinner-border spinner-border-sm me-1"></span>' + "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 = `
|
||||
<span class="badge rounded-pill bg-success bg-opacity-10 text-success">
|
||||
<span
|
||||
class="d-inline-block rounded-circle bg-success me-1"
|
||||
style="width:7px;height:7px;"
|
||||
></span>
|
||||
Running
|
||||
</span>
|
||||
`;
|
||||
} else {
|
||||
badge.innerHTML = `
|
||||
<span class="badge rounded-pill bg-danger bg-opacity-10 text-danger">
|
||||
<span
|
||||
class="d-inline-block rounded-circle bg-danger me-1"
|
||||
style="width:7px;height:7px;"
|
||||
></span>
|
||||
Stopped
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// ACTIVE CLIENTS
|
||||
clients.innerHTML =
|
||||
(data.reservations_count ?? data.active_clients ?? 0) +
|
||||
' <span class="text-muted fw-normal small">registered</span>';
|
||||
|
||||
// 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 = `
|
||||
<div class="text-muted p-4">
|
||||
Loading...
|
||||
</div>
|
||||
`;
|
||||
|
||||
fetch(dhcpConfig.logsUrl)
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("HTTP " + response.status);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
|
||||
.then((data) => {
|
||||
if (!data.logs || !data.logs.length) {
|
||||
container.innerHTML = `
|
||||
<div class="text-muted p-4">
|
||||
No DHCP events found.
|
||||
</div>
|
||||
`;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = data.logs
|
||||
.map((log) => {
|
||||
const escaped = escapeHtml(log);
|
||||
|
||||
let className = "text-muted";
|
||||
|
||||
if (log.includes("DHCPACK")) {
|
||||
className = "text-success";
|
||||
} else if (log.includes("DHCPNAK")) {
|
||||
className = "text-danger";
|
||||
} else if (log.includes("DHCPREQUEST")) {
|
||||
className = "text-primary";
|
||||
} else if (log.includes("DHCPDISCOVER")) {
|
||||
className = "text-warning";
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="px-4 py-2 border-bottom
|
||||
${className}">
|
||||
${escaped}
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
console.error("DHCP logs error:", error);
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="text-danger p-4">
|
||||
Failed to load DHCP logs.
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
const div = document.createElement("div");
|
||||
|
||||
div.textContent = value;
|
||||
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
refreshDhcpStatus();
|
||||
loadDhcpLogs();
|
||||
setInterval(refreshDhcpStatus, 5000, loadDhcpLogs, 5000);
|
||||
Reference in New Issue
Block a user