// modals.js
import { setInconsistencyData, inconsistencyData } from './global.js';
import { loadAllData } from './data_loader.js'; // Will be imported later
import { configStore, API_BASE_URL } from './global.js';
// =========================================================================
// GENERIC MODAL HANDLERS
// =========================================================================
/**
* Shows any modal element by its ID. (NEW GENERIC FUNCTION)
* @param {string} modalId - The ID of the modal element to display.
*/
export function showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'block';
}
}
/**
* Hides any modal element by its ID. (NEW GENERIC FUNCTION)
* @param {string} modalId - The ID of the modal element to hide. Defaults to 'configModal'.
*/
export function hideModal(modalId = 'configModal') {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
}
}
// =========================================================================
// CONFIGURATION DISPLAY MODAL HANDLERS (JSON/YAML tabs)
// =========================================================================
/**
* Switches between the JSON and YAML tabs in the main config modal.
* This function MUST be exported as it's used directly in HTML/inline handlers.
* @param {HTMLElement} modalContent - The parent container (modal-content) for tabs.
* @param {string} tabName - 'json' or 'yaml'.
*/
export function switchTab(modalContent, tabName) {
// Deactivate all buttons and hide all content
modalContent.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active'));
modalContent.querySelectorAll('.code-block').forEach(content => content.style.display = 'none');
// Activate the selected button and show the corresponding content
const activeBtn = modalContent.querySelector(`.tab-button[data-tab="${tabName}"]`);
const activeContent = document.getElementById(`modal-${tabName}-content`);
if (activeBtn) activeBtn.classList.add('active');
if (activeContent) activeContent.style.display = 'block';
}
/**
* Displays the main configuration modal (with JSON/YAML tabs). (RENAMED FROM showModal)
* @param {string} title - The modal title.
* @param {object} jsonData - The configuration data object (for JSON tab).
* @param {string} yamlData - The configuration data as a YAML string.
* @param {string} [defaultTab='yaml'] - The tab to show by default.
*/
export function showConfigModal(title, jsonData, yamlData, defaultTab = 'yaml') {
document.getElementById('modal-title').textContent = title;
// Populate JSON content
document.getElementById('modal-json-content').textContent =
JSON.stringify(jsonData, null, 2);
// Populate YAML content
document.getElementById('modal-yaml-content').textContent = yamlData;
// Default to the specified tab
const modalContent = document.getElementById('configModal')?.querySelector('.modal-content');
if (modalContent) {
switchTab(modalContent, defaultTab);
}
// Use generic showModal
showModal('configModal');
}
/**
* Hides the main configuration modal (configModal). (Uses generic hideModal)
*/
export function hideConfigModal() {
hideModal('configModal');
}
/**
* Sets up click handlers for the tab buttons in the main modal.
*/
export function setupModalTabs() {
const modalContent = document.getElementById('configModal')?.querySelector('.modal-content');
if (!modalContent) return;
modalContent.querySelectorAll('.tab-button').forEach(button => {
button.addEventListener('click', (event) => {
const tabName = event.target.getAttribute('data-tab');
switchTab(modalContent, tabName);
});
});
}
// =========================================================================
// ADD FILTER CHAIN MODAL HANDLERS
// =========================================================================
/**
* Shows the modal for adding a new filter chain to a listener.
* @param {string} listenerName - The name of the listener to modify.
*/
export function showAddFilterChainModal(listenerName) {
// 1. Set the listener name in the hidden input for form submission
document.getElementById('add-fc-listener-name').value = listenerName;
// 2. Set the title
document.getElementById('add-fc-modal-title').textContent =
`Add New Filter Chain to: ${listenerName}`;
// 3. Clear any previous YAML content
const yamlInput = document.getElementById('add-fc-yaml-input');
yamlInput.value = '';
// 4. Show the modal (using generic showModal)
showModal('addFilterChainModal');
// 5. Provide a template to guide the user (optional)
yamlInput.placeholder =
`# Paste your new Filter Chain YAML here.
# NOTE: The root key should be the filter chain object itself.
filter_chain_match:
server_names: ["new.example.com"]
...`;
}
/**
* Closes the Add Filter Chain modal. (Uses generic hideModal)
*/
export function hideAddFilterChainModal() {
hideModal('addFilterChainModal');
}
// =========================================================================
// CONSISTENCY MODAL HANDLERS
// =========================================================================
export function showConsistencyModal() {
if (!inconsistencyData || inconsistencyData.inconsistent === false) return;
// Populate modal content
const cacheOnly = inconsistencyData['cache-only'] || {};
const dbOnly = inconsistencyData['db-only'] || {};
document.getElementById('cache-only-count').textContent =
Object.keys(cacheOnly).length;
document.getElementById('cache-only-data').textContent =
JSON.stringify(cacheOnly, null, 2);
document.getElementById('db-only-count').textContent =
Object.keys(dbOnly).length;
document.getElementById('db-only-data').textContent =
JSON.stringify(dbOnly, null, 2);
// Use generic showModal
showModal('consistencyModal');
}
export function hideConsistencyModal() {
hideModal('consistencyModal');
}
// =========================================================================
// ADD CLUSTER MODAL HANDLERS
// =========================================================================
/**
* Shows the modal for adding a new cluster.
*/
export function showAddClusterModal() {
showModal('addClusterModal');
}
/**
* Hides the Add Cluster modal.
*/
function hideAddClusterModal() {
hideModal('addClusterModal');
}
// =========================================================================
// WINDOW EVENT LISTENERS
// =========================================================================
window.addEventListener('keydown', (event) => {
// Check for Escape key to close all modals
if (event.key === 'Escape') {
hideConfigModal();
hideAddFilterChainModal();
hideConsistencyModal();
hideAddClusterModal();
// Assume hideAddListenerModal is also attached to window/global scope if not in modals.js
// If it is in listeners.js and attached to window: window.hideAddListenerModal();
}
});
// Close modal when clicking outside of the content (on the backdrop)
window.addEventListener('click', (event) => {
const modal = document.getElementById('configModal');
const addFCModal = document.getElementById('addFilterChainModal');
const consistencyModal = document.getElementById('consistencyModal');
const addListenerModal = document.getElementById('addListenerModal'); // NEW
const addClusterModal = document.getElementById('addClusterModal');
if (event.target === modal) {
hideConfigModal();
}
if (event.target === addFCModal) {
hideAddFilterChainModal();
}
if (event.target === consistencyModal) {
hideConsistencyModal();
}
// Added for the new listener modal
if (event.target === addListenerModal) {
// If hideAddListenerModal is in listeners.js, you must call it from window
// window.hideAddListenerModal();
// OR, if you decide to move it here:
hideModal('addListenerModal');
}
if (event.target === addClusterModal) {
hideAddClusterModal();
}
});
// =========================================================================
// UTILITY HANDLERS (Download)
// =========================================================================
export function downloadYaml() {
const yamlContent = document.getElementById('modal-yaml-content').textContent;
if (!yamlContent || yamlContent.trim() === '') {
alert("No YAML content available to download.");
return;
}
// Use modal title as filename fallback
const title = document.getElementById('modal-title').textContent
.replace(/\s+/g, '_')
.replace(/[^\w\-]/g, '');
const blob = new Blob([yamlContent], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = title ? `${title}.yaml` : 'config.yaml';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
/**
* Fetches and displays the full configuration for a listener in the tabbed modal.
*/
export async function showListenerConfigModal(listenerName) {
const config = configStore.listeners[listenerName];
if (!config) {
showConfigModal(`🚨 Error: Listener Not Found`, { name: listenerName, error: 'Configuration data missing from memory.' }, 'Error: Listener not in memory.');
return;
}
let yamlData = configStore.listeners[listenerName]?.yaml || 'Loading YAML...';
if (yamlData === 'Loading YAML...') {
try {
const response = await fetch(`${API_BASE_URL}/get-listener?name=${listenerName}&format=yaml`);
if (!response.ok) {
yamlData = `Error fetching YAML: ${response.status} ${response.statusText}`;
} else {
yamlData = await response.text();
configStore.listeners[listenerName].yaml = yamlData; // Store YAML
}
} catch (error) {
console.error("Failed to fetch YAML listener config:", error);
yamlData = `Network Error fetching YAML: ${error.message}`;
}
}
// Use the renamed function
showConfigModal(`Full Config for Listener: ${listenerName}`, config, yamlData);
}