`;
dnsSummaryDiv.innerHTML = summaryHtml;
}
/**
* Renders the table of all DNS records.
*/
function renderDNSRecordsTable() {
if (!dnsRecordsTableBody) return; // Null check
dnsRecordsTableBody.innerHTML = ''; // Clear existing rows
// Sort data by domain name for consistent display
const sortedRecords = [...dnsRecords].sort((a, b) => a.domainName.localeCompare(b.domainName));
sortedRecords.forEach(record => {
const row = dnsRecordsTableBody.insertRow();
row.innerHTML = `
${record.domainName}
${record.recordType}
${record.expectedValue}
${record.queryTime.toFixed(1)}
${record.uptime.toFixed(2)}%
${record.successRate.toFixed(2)}%
${record.errorRate.toFixed(2)}%
${record.lastChecked || 'N/A'}
`;
});
}
/**
* Renders the entire dashboard content.
*/
function renderDashboard() {
renderDNSSummary();
renderDNSRecordsTable();
}
// --- Data Configuration Logic ---
/**
* Generates a unique ID for a new record entry.
* @returns {string} A unique ID.
*/
function generateUniqueId() {
return 'dnsrec' + Date.now() + Math.floor(Math.random() * 1000);
}
// Add/Update Record Form Submission
if (addUpdateRecordForm) {
addUpdateRecordForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const id = document.getElementById('recordId').value;
const domainName = document.getElementById('domainName').value;
const recordType = document.getElementById('recordType').value;
const expectedValue = document.getElementById('expectedValue').value;
const queryTime = parseFloat(document.getElementById('queryTime').value);
const uptime = parseFloat(document.getElementById('uptime').value);
const successRate = parseFloat(document.getElementById('successRate').value);
const errorRate = parseFloat(document.getElementById('errorRate').value);
const lastChecked = document.getElementById('lastChecked').value;
// Basic validation
if (!domainName.trim() || !expectedValue.trim()) {
showCustomModal('Input Error', 'Domain Name and Expected Value cannot be empty.');
return;
}
if (isNaN(queryTime) || queryTime < 0 || isNaN(uptime) || uptime < 0 || uptime > 100 ||
isNaN(successRate) || successRate < 0 || successRate > 100 || isNaN(errorRate) || errorRate < 0 || errorRate > 100) {
showCustomModal('Input Error', 'Please ensure all numerical fields are filled correctly with valid ranges (percentages 0-100).');
return;
}
// Ensure successRate + errorRate is approximately 100%
if (Math.abs((successRate + errorRate) - 100) > 0.01) {
showCustomModal('Input Error', 'Success Rate and Error Rate should sum up to approximately 100%.');
return;
}
const newRecord = {
id: id || generateUniqueId(), // Use existing ID if editing, otherwise generate new
domainName: domainName,
recordType: recordType,
expectedValue: expectedValue,
queryTime: queryTime,
uptime: uptime,
successRate: successRate,
errorRate: errorRate,
lastChecked: lastChecked
};
if (id) {
// Update existing entry
const index = dnsRecords.findIndex(entry => entry.id === id);
if (index > -1) {
dnsRecords[index] = newRecord;
} else {
console.error('DNS record entry not found for update:', id);
}
} else {
// Add new entry
dnsRecords.push(newRecord);
}
renderDashboard(); // Re-render dashboard with new/updated data
addUpdateRecordForm.reset(); // Clear the form
recordIdInput.value = ''; // Clear hidden ID
formSubmitBtn.textContent = 'Add Record Data'; // Reset button text
switchTab('dashboard'); // Switch back to dashboard to see changes
});
}
/**
* Populates the form for editing an existing DNS record entry.
* @param {string} id - The ID of the record entry to edit.
*/
window.editRecord = function(id) {
const entryToEdit = dnsRecords.find(entry => entry.id === id);
if (!entryToEdit) {
console.error('DNS record entry not found for editing:', id);
return;
}
document.getElementById('recordId').value = entryToEdit.id;
document.getElementById('domainName').value = entryToEdit.domainName;
document.getElementById('recordType').value = entryToEdit.recordType;
document.getElementById('expectedValue').value = entryToEdit.expectedValue;
document.getElementById('queryTime').value = entryToEdit.queryTime;
document.getElementById('uptime').value = entryToEdit.uptime;
document.getElementById('successRate').value = entryToEdit.successRate;
document.getElementById('errorRate').value = entryToEdit.errorRate;
document.getElementById('lastChecked').value = entryToEdit.lastChecked;
formSubmitBtn.textContent = 'Update Record Data'; // Change button text
switchTab('data-config'); // Switch to data config tab
};
/**
* Deletes a DNS record entry from the array after confirmation.
* @param {string} id - The ID of the record entry to delete.
*/
window.deleteRecord = function(id) {
showCustomModal('Confirm Deletion', 'Are you sure you want to delete this DNS record entry?', () => {
dnsRecords = dnsRecords.filter(entry => entry.id !== id);
renderDashboard(); // Re-render dashboard
});
};
// --- Custom Modal Logic (replaces alert/confirm) ---
let currentConfirmCallback = null;
/**
* Shows a custom modal with a message and optional confirmation.
* @param {string} title - The title of the modal.
* @param {string} message - The message to display.
* @param {function} [onConfirm] - Callback function to execute on confirm. If null, it's an alert.
*/
function showCustomModal(title, message, onConfirm = null) {
if (!confirmModal || !modalTitle || !modalMessage || !confirmActionBtn) return;
modalTitle.textContent = title;
modalMessage.textContent = message;
if (onConfirm) {
confirmActionBtn.style.display = 'inline-block'; // Show confirm button
confirmActionBtn.textContent = 'Confirm';
confirmActionBtn.classList.remove('btn-primary');
confirmActionBtn.classList.add('btn-danger');
currentConfirmCallback = onConfirm;
} else {
confirmActionBtn.style.display = 'none'; // Hide confirm button for alerts
currentConfirmCallback = null;
}
confirmModal.style.display = 'flex'; // Show modal
}
/**
* Closes the specified modal.
* @param {string} modalId - The ID of the modal to close.
*/
window.closeModal = function(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
}
};
// Event listener for the custom modal's confirm button
if (confirmActionBtn) {
confirmActionBtn.addEventListener('click', function() {
if (currentConfirmCallback) {
currentConfirmCallback();
}
closeModal('confirmModal');
});
}
// Close modal when clicking outside of it
window.onclick = function(event) {
if (event.target === confirmModal) {
closeModal('confirmModal');
}
};
// --- PDF Download Logic ---
/**
* Downloads the dashboard content as a PDF.
*/
if (downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', function() {
const input = document.getElementById('dashboardTabContent'); // Target the dashboard content
if (!input) {
console.error('Dashboard content element not found for PDF generation.');
return;
}
// Temporarily hide elements that should not be in the PDF
const elementsToHide = input.querySelectorAll('.no-print');
elementsToHide.forEach(el => el.style.display = 'none');
// Use html2canvas to capture the content as an image
html2canvas(input, {
scale: 2, // Increase scale for better quality
useCORS: true, // Needed if external images are used (not in this case, but good practice)
logging: false // Disable logging for cleaner console
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new window.jspdf.jsPDF('p', 'mm', 'a4'); // 'p' for portrait, 'mm' for units, 'a4' for size
const imgWidth = 210; // A4 width in mm
const pageHeight = 297; // A4 height in mm
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// Add image to PDF
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// If content spans multiple pages, add new pages
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('DNS_Performance_Dashboard.pdf');
// Restore visibility of hidden elements
elementsToHide.forEach(el => el.style.display = '');
}).catch(error => {
console.error('Error generating PDF:', error);
// Restore visibility of hidden elements in case of error
elementsToHide.forEach(el => el.style.display = '');
});
});
}
// Initial render of the dashboard when the page loads
renderDashboard();
});