// Application Name Validation
function validateAppName() {
const nameField = document.getElementById('app-name');
const serverField = document.getElementById('server-id');
const feedbackElement = document.getElementById('name-feedback');
const submitButton = document.querySelector('button[type="submit"]');
if (!nameField || !serverField) return;
const name = nameField.value.trim();
const serverId = serverField.value;
const appId = document.getElementById('app-id')?.value;
if (!name || !serverId) {
clearFeedback(feedbackElement);
return;
}
// Debounce to avoid too many requests
clearTimeout(nameField.timer);
nameField.timer = setTimeout(() => {
fetch(`/api/validate/app-name?name=${encodeURIComponent(name)}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`)
.then(response => response.json())
.then(data => {
if (!data.valid) {
showError(feedbackElement, data.message);
if (data.edit_url) {
feedbackElement.innerHTML += ` Edit this app instead?`;
}
submitButton.disabled = true;
} else if (data.warning) {
showWarning(feedbackElement, data.warning);
if (data.similar_apps && data.similar_apps.length > 0) {
feedbackElement.innerHTML += '
';
data.similar_apps.forEach(app => {
feedbackElement.innerHTML += `- ${app.name}
`;
});
feedbackElement.innerHTML += '
';
}
submitButton.disabled = false;
} else {
showSuccess(feedbackElement, "App name is available");
submitButton.disabled = false;
}
})
.catch(error => {
console.error('Validation error:', error);
clearFeedback(feedbackElement);
});
}, 300);
}
// Port Validation
function validatePort(portField, protocolField) {
const serverField = document.getElementById('server-id');
const feedbackElement = portField.nextElementSibling;
const submitButton = document.querySelector('button[type="submit"]');
if (!portField || !serverField || !protocolField) return;
const port = portField.value.trim();
const protocol = protocolField.value;
const serverId = serverField.value;
const appId = document.getElementById('app-id')?.value;
if (!port || !serverId) {
clearFeedback(feedbackElement);
return;
}
// Debounce to avoid too many requests
clearTimeout(portField.timer);
portField.timer = setTimeout(() => {
fetch(`/api/validate/app-port?port_number=${encodeURIComponent(port)}&protocol=${protocol}&server_id=${serverId}${appId ? '&app_id=' + appId : ''}`)
.then(response => response.json())
.then(data => {
if (!data.valid) {
showError(feedbackElement, data.message);
if (data.edit_url) {
feedbackElement.innerHTML += ` Edit the conflicting app?`;
}
portField.classList.add('is-invalid');
submitButton.disabled = true;
} else {
clearFeedback(feedbackElement);
portField.classList.remove('is-invalid');
portField.classList.add('is-valid');
submitButton.disabled = false;
}
})
.catch(error => {
console.error('Validation error:', error);
clearFeedback(feedbackElement);
});
}, 300);
}
// Helper functions for feedback display
function showError(element, message) {
if (!element) return;
element.className = 'invalid-feedback d-block';
element.textContent = message;
}
function showWarning(element, message) {
if (!element) return;
element.className = 'text-warning d-block';
element.textContent = message;
}
function showSuccess(element, message) {
if (!element) return;
element.className = 'valid-feedback d-block';
element.textContent = message;
}
function clearFeedback(element) {
if (!element) return;
element.className = '';
element.textContent = '';
}
// Set up event listeners when the document is loaded
document.addEventListener('DOMContentLoaded', function () {
const nameField = document.getElementById('app-name');
const serverField = document.getElementById('server-id');
if (nameField) {
nameField.addEventListener('input', validateAppName);
if (serverField) {
serverField.addEventListener('change', validateAppName);
}
}
// Set up validation for existing port fields
setupPortValidation();
// Make sure new port rows get validation too
const addPortButton = document.getElementById('add-port-btn');
if (addPortButton) {
addPortButton.addEventListener('click', function () {
// Wait a moment for the new row to be added
setTimeout(setupPortValidation, 100);
});
}
});
function setupPortValidation() {
const portFields = document.querySelectorAll('input[name="port_numbers[]"]');
const protocolFields = document.querySelectorAll('select[name="protocols[]"]');
portFields.forEach((field, index) => {
if (!field.hasValidationSetup) {
const protocolField = protocolFields[index];
field.addEventListener('input', function () {
validatePort(field, protocolField);
});
if (protocolField) {
protocolField.addEventListener('change', function () {
validatePort(field, protocolField);
});
}
// Add a div for feedback if it doesn't exist
if (!field.nextElementSibling || !field.nextElementSibling.classList.contains('feedback')) {
const feedback = document.createElement('div');
feedback.className = 'feedback';
field.parentNode.insertBefore(feedback, field.nextSibling);
}
field.hasValidationSetup = true;
}
});
}