All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
107 lines
3.2 KiB
JavaScript
107 lines
3.2 KiB
JavaScript
const cards = [...document.querySelectorAll('.service-card')];
|
|
const sections = [...document.querySelectorAll('.section')];
|
|
const searchInput = document.querySelector('#serviceSearch');
|
|
const segments = [...document.querySelectorAll('.segment')];
|
|
const emptyState = document.querySelector('#emptyState');
|
|
const onlineCount = document.querySelector('#onlineCount');
|
|
const lastChecked = document.querySelector('#lastChecked');
|
|
|
|
let activeFilter = 'all';
|
|
|
|
function normalize(value) {
|
|
return value.toLowerCase().trim();
|
|
}
|
|
|
|
function updateVisibility() {
|
|
const term = normalize(searchInput.value);
|
|
let visibleCards = 0;
|
|
|
|
cards.forEach((card) => {
|
|
const categoryMatches = activeFilter === 'all' || card.dataset.category === activeFilter;
|
|
const searchMatches = !term || normalize(card.dataset.name).includes(term);
|
|
const isVisible = categoryMatches && searchMatches;
|
|
card.hidden = !isVisible;
|
|
if (isVisible) visibleCards += 1;
|
|
});
|
|
|
|
sections.forEach((section) => {
|
|
const hasVisibleCards = [...section.querySelectorAll('.service-card')].some((card) => !card.hidden);
|
|
section.hidden = !hasVisibleCards;
|
|
});
|
|
|
|
emptyState.hidden = visibleCards !== 0;
|
|
}
|
|
|
|
function setActiveFilter(nextFilter) {
|
|
activeFilter = nextFilter;
|
|
segments.forEach((segment) => {
|
|
const isActive = segment.dataset.filter === nextFilter;
|
|
segment.classList.toggle('is-active', isActive);
|
|
segment.setAttribute('aria-selected', String(isActive));
|
|
});
|
|
updateVisibility();
|
|
}
|
|
|
|
async function probeService(card) {
|
|
const controller = new AbortController();
|
|
const timeout = window.setTimeout(() => controller.abort(), 4500);
|
|
|
|
try {
|
|
await fetch(card.dataset.url, {
|
|
method: 'GET',
|
|
mode: 'no-cors',
|
|
cache: 'no-store',
|
|
signal: controller.signal
|
|
});
|
|
card.classList.add('is-online');
|
|
card.classList.remove('is-offline');
|
|
card.querySelector('.status-dot').setAttribute('aria-label', 'Online');
|
|
return true;
|
|
} catch {
|
|
card.classList.add('is-offline');
|
|
card.classList.remove('is-online');
|
|
card.querySelector('.status-dot').setAttribute('aria-label', 'Nicht erreichbar');
|
|
return false;
|
|
} finally {
|
|
window.clearTimeout(timeout);
|
|
}
|
|
}
|
|
|
|
async function refreshStatus() {
|
|
const results = await Promise.allSettled(cards.map(probeService));
|
|
const online = results.filter((result) => result.status === 'fulfilled' && result.value).length;
|
|
onlineCount.textContent = String(online);
|
|
lastChecked.textContent = `Status geprueft: ${new Date().toLocaleTimeString('de-DE', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})}`;
|
|
}
|
|
|
|
segments.forEach((segment) => {
|
|
segment.addEventListener('click', () => setActiveFilter(segment.dataset.filter));
|
|
});
|
|
|
|
searchInput.addEventListener('input', updateVisibility);
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
window.addEventListener('load', () => {
|
|
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
|
});
|
|
}
|
|
|
|
window.addEventListener('load', () => {
|
|
const run = () => refreshStatus().catch(() => {
|
|
onlineCount.textContent = '--';
|
|
lastChecked.textContent = 'Status aktuell nicht verfuegbar.';
|
|
});
|
|
|
|
if ('requestIdleCallback' in window) {
|
|
window.requestIdleCallback(run, { timeout: 2000 });
|
|
} else {
|
|
window.setTimeout(run, 600);
|
|
}
|
|
});
|
|
|
|
updateVisibility();
|
|
|