Implement search functionality for services and add info button for connection details
This commit is contained in:
58
index.html
58
index.html
@@ -10,6 +10,9 @@
|
||||
<header>
|
||||
<h1>My Services</h1>
|
||||
<p class="subtitle">Quick links to the commonly used containers on this host</p>
|
||||
<div class="search-container">
|
||||
<input type="text" id="search-input" placeholder="🔍 Search services..." />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -33,7 +36,10 @@
|
||||
// Fetch services.xml and render the service cards with logos.
|
||||
(async function(){
|
||||
const grid = document.getElementById('services-grid');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const host = window.location.hostname;
|
||||
let allServices = []; // Store all service elements for filtering
|
||||
|
||||
try{
|
||||
const res = await fetch('/services.xml', {cache: 'no-cache'});
|
||||
if(!res.ok){ throw new Error('Failed to load services.xml'); }
|
||||
@@ -81,6 +87,7 @@
|
||||
a.href = href;
|
||||
a.target = '_blank';
|
||||
a.rel = 'noreferrer';
|
||||
a.dataset.serviceName = name.toLowerCase(); // For search filtering
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.className = 'logo';
|
||||
@@ -93,8 +100,59 @@
|
||||
|
||||
a.appendChild(img);
|
||||
a.appendChild(span);
|
||||
|
||||
// Add info button if both hostname and port are available
|
||||
if((hostAttr || host) && port){
|
||||
const infoBtn = document.createElement('button');
|
||||
infoBtn.className = 'info-btn';
|
||||
infoBtn.title = 'Connection details';
|
||||
infoBtn.innerHTML = 'ⓘ';
|
||||
infoBtn.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const displayHost = hostAttr || host;
|
||||
const details = `Service: ${name}\nHost: ${displayHost}\nPort: ${port}\nProtocol: ${proto}`;
|
||||
alert(details);
|
||||
};
|
||||
a.appendChild(infoBtn);
|
||||
}
|
||||
|
||||
grid.appendChild(a);
|
||||
allServices.push(a);
|
||||
});
|
||||
|
||||
// Search functionality
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase().trim();
|
||||
let visibleCount = 0;
|
||||
allServices.forEach(card => {
|
||||
const serviceName = card.dataset.serviceName;
|
||||
if(serviceName.includes(searchTerm)){
|
||||
card.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
// Show message if no results
|
||||
const existingMsg = grid.querySelector('.no-results');
|
||||
if(existingMsg) existingMsg.remove();
|
||||
if(visibleCount === 0 && searchTerm !== ''){
|
||||
const msg = document.createElement('p');
|
||||
msg.className = 'notes no-results';
|
||||
msg.textContent = `No services found matching "${e.target.value}"`;
|
||||
grid.appendChild(msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Focus search on '/' key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if(e.key === '/' && document.activeElement !== searchInput){
|
||||
e.preventDefault();
|
||||
searchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
}catch(err){
|
||||
console.error(err);
|
||||
grid.innerHTML = '<p class="notes">Error loading services.xml — see console for details.</p>';
|
||||
|
||||
Reference in New Issue
Block a user