This commit is contained in:
pika 2025-03-31 13:51:49 +02:00
parent 1ab129b798
commit af4b75acb4
3 changed files with 93 additions and 6 deletions

View file

@ -546,4 +546,60 @@
.app-link:hover {
color: var(--highlight-color);
}
/* Validation styles */
.is-invalid {
border-color: #dc3545 !important;
padding-right: calc(1.5em + 0.75rem);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.is-valid {
border-color: #198754 !important;
padding-right: calc(1.5em + 0.75rem);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.invalid-feedback {
display: none;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #dc3545;
}
.invalid-feedback.d-block {
display: block !important;
}
.valid-feedback {
display: none;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #198754;
}
.valid-feedback.d-block {
display: block !important;
}
/* Fix position for validation messages in table cells */
td.position-relative {
position: relative;
}
td .feedback {
position: absolute;
width: 100%;
left: 0;
top: 100%;
z-index: 5;
}

View file

@ -53,6 +53,13 @@ function validateAppName() {
// Port Validation
function validatePort(portField, protocolField) {
const serverField = document.getElementById('server-id');
// Create feedback element if it doesn't exist
if (!portField.nextElementSibling || !portField.nextElementSibling.classList.contains('feedback')) {
const feedback = document.createElement('div');
feedback.className = 'feedback';
portField.parentNode.insertBefore(feedback, portField.nextSibling);
}
const feedbackElement = portField.nextElementSibling;
const submitButton = document.querySelector('button[type="submit"]');
@ -65,6 +72,26 @@ function validatePort(portField, protocolField) {
if (!port || !serverId) {
clearFeedback(feedbackElement);
portField.classList.remove('is-invalid');
return;
}
// Check for duplicate ports within the form first
const allPortFields = document.querySelectorAll('input[name="port_numbers[]"]');
const allProtocolFields = document.querySelectorAll('select[name="protocols[]"]');
let duplicateFound = false;
allPortFields.forEach((field, index) => {
if (field !== portField &&
field.value === port &&
allProtocolFields[index].value === protocol) {
duplicateFound = true;
}
});
if (duplicateFound) {
portField.classList.add('is-invalid');
showError(feedbackElement, `Duplicate port ${port}/${protocol} in your form`);
return;
}
@ -75,22 +102,22 @@ function validatePort(portField, protocolField) {
.then(response => response.json())
.then(data => {
if (!data.valid) {
portField.classList.add('is-invalid');
showError(feedbackElement, data.message);
if (data.edit_url) {
feedbackElement.innerHTML += ` <a href="${data.edit_url}" class="alert-link">Edit the conflicting app?</a>`;
feedbackElement.innerHTML += ` <a href="${data.edit_url}" class="alert-link">Edit conflicting app</a>`;
}
portField.classList.add('is-invalid');
submitButton.disabled = true;
} else {
clearFeedback(feedbackElement);
portField.classList.remove('is-invalid');
portField.classList.add('is-valid');
submitButton.disabled = false;
showSuccess(feedbackElement, "Port available");
}
})
.catch(error => {
console.error('Validation error:', error);
clearFeedback(feedbackElement);
portField.classList.remove('is-invalid');
portField.classList.remove('is-valid');
});
}, 300);
}

View file

@ -256,9 +256,10 @@
const tbody = document.querySelector('#ports-table tbody');
const newRow = document.createElement('tr');
newRow.innerHTML = `
<td>
<td class="position-relative">
<input type="number" name="port_numbers[]" class="form-control"
min="1" max="65535" value="${portNumber}" required>
<div class="feedback"></div>
</td>
<td>
<select name="protocols[]" class="form-select">
@ -279,6 +280,9 @@
</td>
`;
tbody.appendChild(newRow);
// Set up validation for the new row
setTimeout(setupPortValidation, 50);
}
function removePortRow(button) {